├── README.md ├── Cobalt-Strike-detection-notes.md ├── sysmon_custom_function.txt ├── BD-Threat Hunting for Malware Beacons.ipynb └── Graphing Parent-Child Processes.ipynb /README.md: -------------------------------------------------------------------------------- 1 | # Threat Hunting Jupyter Notebooks 2 | This repository contains Jupyter Notebooks that the Binary Defense threat hunting team has created and found to be useful, and which are able to be shared publicly (not including private/customized notebooks for clients). 3 | 4 | We are sharing this work with the community to help other threat hunters find malicious activity hiding in their networks, even when the threat actors operate in sneaky ways that are hard to detect with behavior rules alone. 5 | -------------------------------------------------------------------------------- /Cobalt-Strike-detection-notes.md: -------------------------------------------------------------------------------- 1 | # Notes on Detecting Cobalt Strike Activity 2 | from Binary Defense webinar on 2021-02-24 3 | 4 | ## Hunting Netflow Patterns 5 | > This is the most reliable and fastest way to detect not only CS Beacon but many other RATs as well. 6 | - Expect false positives, because legitimate software checks in regularly with servers, too 7 | - Tune out any known good beacon-like activity in the hunting rule to avoid repeat investigations 8 | - Don't dismiss activity just because it comes from signed system utilities - it could be process injection 9 | 10 | ### Binary Defense Jupyter Notebook for Beacon Hunting 11 | https://github.com/BinaryDefense/ThreatHuntingJupyterNotebooks 12 | - Note: This notebook requires either Sysmon or Microsoft Defender for Endpoint data in Sentinel 13 | 14 | ### RITA from Active Countermeasures 15 | https://www.activecountermeasures.com/free-tools/rita/ 16 | - Note: RITA requires Zeek logs 17 | 18 | ### KQL Query for Hunting in Sysmon using Sentinel 19 | 20 | 21 | let starttime = 48h; // Go back as many days as you want to look 22 | let endtime = 1m; // Usually you want to check up to the current time but set this if not 23 | let TimeDeltaThreshold = 2; // don't count anything under 2 seconds between connections 24 | let TotalEventsThreshold = 15; // only show beaconing that had at least this many connections 25 | let DurationThreshold = 900; // only show beaconing that lasted at least this many seconds 26 | let StandardDeviationThreshold = 100; // Set to filter out false positives: lower number is tighter filtering/fewer results 27 | Sysmon 28 | | where EventID==3 29 | | where TimeGenerated between (ago(starttime)..ago(endtime)) 30 | | project TimeGenerated, Computer, process_path, src_ip, src_port, dst_ip, dst_port 31 | | sort by src_ip asc, dst_ip asc, TimeGenerated asc // sort to put all connections between two hosts next to each other in time order 32 | | serialize 33 | | extend nextTimeGenerated = next(TimeGenerated, 1), nextDeviceId = next(Computer, 1), nextDstIP = next(dst_ip, 1) 34 | | extend TimeDeltaInSeconds = datetime_diff("second", nextTimeGenerated, TimeGenerated) // compute time difference between subsequent connections 35 | | where Computer == nextDeviceId and nextDstIP == dst_ip // only compute time difference if next host pair is the same as current 36 | | where TimeDeltaInSeconds > TimeDeltaThreshold // filter out connections that happen too close together 37 | | project TimeGenerated, TimeDeltaInSeconds, Computer, process_path, src_ip, src_port, dst_ip, dst_port 38 | | summarize avg(TimeDeltaInSeconds), count(), min(TimeGenerated), max(TimeGenerated), // compute statistics including standard deviation 39 | Duration=datetime_diff("second", max(TimeGenerated), min(TimeGenerated)), 40 | StandardDeviation=stdev(TimeDeltaInSeconds), TimeDeltaList=make_list(TimeDeltaInSeconds) by Computer, src_ip, dst_ip, process_path 41 | | where count_ > TotalEventsThreshold 42 | // comment out the next line if you don't want to filter out short-term beacons that aren't still active 43 | //| where count_ > datetime_diff("second", ago(endtime), min_TimeGenerated) / (avg_TimeDeltaInSeconds*2) 44 | | where StandardDeviation < StandardDeviationThreshold 45 | | where Duration >= DurationThreshold 46 | | order by StandardDeviation asc 47 | 48 | 49 | 50 | ### KQL Query for Hunting in Microsoft Defender for Endpoint 51 | 52 | 53 | let TimeDeltaThreshold = 2; // don't count connections less than this many seconds after the last connection to the same IP 54 | let TotalEventsThreshold = 15; // only show beaconing that had at least this many connections 55 | let DurationThreshold = 1200; // only show beaconing that lasted at least this many seconds 56 | let StandardDeviationThreshold = 100; // Set to filter out false positives: lower number is tighter filtering/fewer results 57 | DeviceNetworkEvents 58 | | where RemoteIPType !in ("Reserved", "Private", "LinkLocal", "Loopback") 59 | | where ActionType in ("ConnectionSuccess", "ConnectionRequest", "ConnectionFailed") 60 | | project Timestamp, DeviceId, DeviceName, InitiatingProcessFileName, LocalIP, LocalPort, RemoteIP, RemotePort 61 | | sort by LocalIP asc, RemoteIP asc, Timestamp asc 62 | | serialize 63 | | extend nextTimeGenerated = next(Timestamp, 1), nextDeviceId = next(DeviceId, 1), nextRemoteIP = next(RemoteIP, 1) 64 | | extend TimeDeltaInSeconds = datetime_diff("second", nextTimeGenerated, Timestamp) 65 | | where DeviceId == nextDeviceId and RemoteIP == nextRemoteIP 66 | | where TimeDeltaInSeconds > TimeDeltaThreshold 67 | | project Timestamp, TimeDeltaInSeconds, DeviceName, InitiatingProcessFileName, LocalIP, LocalPort, RemoteIP, RemotePort 68 | | summarize avg(TimeDeltaInSeconds), count(), min(Timestamp), max(Timestamp), Duration=datetime_diff("second", max(Timestamp), min(Timestamp)), StandardDeviation=stdev(TimeDeltaInSeconds), TimeDeltaList=make_list(TimeDeltaInSeconds) by DeviceName, LocalIP, RemoteIP, InitiatingProcessFileName 69 | | where count_ > TotalEventsThreshold 70 | | where StandardDeviation < StandardDeviationThreshold 71 | | where Duration >= DurationThreshold 72 | | order by StandardDeviation asc 73 | 74 | 75 | ### KQL Query for Hunting in Sentinel using Defender for Endpoint Records 76 | 77 | 78 | let starttime = 2d; // Go back as many days as you want to look 79 | let endtime = 1m; // Usually you want to check up to the current time but set this if not 80 | let TimeDeltaThreshold = 2; // don't count anything under 2 seconds between connections 81 | let TotalEventsThreshold = 15; // only show beaconing that had at least this many connections 82 | let DurationThreshold = 1200; // only show beaconing that lasted at least this many seconds 83 | let StandardDeviationThreshold = 100; // Set to filter out false positives: lower number is tighter filtering/fewer results 84 | DeviceNetworkEvents 85 | | where RemoteIPType !in ("Reserved", "Private", "LinkLocal", "Loopback") 86 | | where isnotempty(RemoteIP) and RemoteIP !in ("0.0.0.0") 87 | | where ActionType in ("ConnectionSuccess", "ConnectionRequest", "ConnectionFailed") 88 | | project TimeGenerated, DeviceId, DeviceName, InitiatingProcessFileName, LocalIP, LocalPort, RemoteIP, RemotePort 89 | | sort by LocalIP asc, RemoteIP asc, TimeGenerated asc 90 | | serialize 91 | | extend nextTimeGenerated = next(TimeGenerated, 1), nextDeviceId = next(DeviceId, 1), nextRemoteIP = next(RemoteIP, 1) 92 | | extend TimeDeltaInSeconds = datetime_diff("second", nextTimeGenerated, TimeGenerated) 93 | | where DeviceId == nextDeviceId and RemoteIP == nextRemoteIP 94 | | where TimeDeltaInSeconds > TimeDeltaThreshold 95 | | project TimeGenerated, TimeDeltaInSeconds, DeviceName, InitiatingProcessFileName, LocalIP, LocalPort, RemoteIP, RemotePort 96 | | summarize avg(TimeDeltaInSeconds), count(), min(TimeGenerated), max(TimeGenerated), Duration=datetime_diff("second", max(TimeGenerated), min(TimeGenerated)), StandardDeviation=stdev(TimeDeltaInSeconds), TimeDeltaList=make_list(TimeDeltaInSeconds) by DeviceName, LocalIP, RemoteIP, InitiatingProcessFileName 97 | | where count_ > TotalEventsThreshold 98 | // comment out the next line if you don't want to filter out short-term beacons that aren't still active 99 | //| where count_ > datetime_diff("second", ago(endtime), min_TimeGenerated) / (avg_TimeDeltaInSeconds*2) 100 | | where StandardDeviation < StandardDeviationThreshold 101 | | where Duration >= DurationThreshold 102 | | order by StandardDeviation asc 103 | | extend HostCustomEntity = DeviceName 104 | | extend IPCustomEntity = RemoteIP 105 | | extend TimestampCustomEntity = max_TimeGenerated 106 | 107 | ## Time Series Analysis of Process Access (Sysmon Event ID 10) 108 | - In practical testing with Cobalt Strike Beacon, something that the threat actor did caused the number of Process Access events (EID 10 in Sysmon) to jump from an average of 150 events per hour on a particular machine to over 30,000 EID 10 events in the timespan of 5 minutes. 109 | - The target_processes included firefox.exe, Teams, powershell, conhost, sihost, etc. and varied from about 600 events to over 5000 events per process, all in the same five minutes. 110 | 111 | 112 | let starttime = 30d; 113 | let endtime = 1d; 114 | let timeframe = 1h; 115 | let TotalEventsThreshold = 50; 116 | let TimeSeriesData = 117 | Sysmon 118 | | where TimeGenerated between (startofday(ago(starttime))..startofday(ago(endtime))) 119 | | where EventID == 10 120 | | make-series PerHourCount=count() 121 | on TimeGenerated from startofday(ago(starttime)) to startofday(ago(endtime)) 122 | step timeframe by Computer; 123 | let TimeSeriesAlerts=TimeSeriesData 124 | | extend (anomalies, score, baseline) = series_decompose_anomalies(PerHourCount, 1.5, -1, 'linefit') 125 | | mv-expand PerHourCount to typeof(double), 126 | TimeGenerated to typeof(datetime), 127 | anomalies to typeof(double), 128 | score to typeof(double), 129 | baseline to typeof(long) 130 | | where anomalies > 0 131 | | where score > 150 132 | | project Computer, TimeGenerated, PerHourCount, baseline, anomalies, score 133 | | where PerHourCount > TotalEventsThreshold; 134 | TimeSeriesAlerts 135 | | order by PerHourCount desc 136 | 137 | ## Detecting Exposed CS Team Servers in the Wild 138 | - JARM hashing of live servers (DIY): https://github.com/salesforce/jarm 139 | - List of JARM hashes for known servers: https://github.com/cedowens/C2-JARM 140 | - Shodan query for Cobalt Strike JARM: https://beta.shodan.io/search?query=ssl.jarm%3A07d14d16d21d21d07c42d41d00041d24a458a375eef0c576d23a7bab9a9fb1 141 | - Nmap script to grab beacon configs from CS stagers: https://github.com/whickey-r7/grab_beacon_config/blob/main/grab_beacon_config.nse 142 | - Over 70% of recent Beacon configs had Polling=60sec. 16% had polling=5sec 143 | - Over 75% of recent Beacon configs had zero Jitter. 10% and 20% Jitter were the next most popular settings. 144 | - Over 90% of recent Beacon configs had rundll32.exe as their spawn target for both x86 and x64. mstsc, gpupdate, mavinject, dllhost and werfault are other popular choices. 145 | 146 | ## Process Injection Patterns in Thread Start Address 147 | - Expect this query to produce some false positives, especially Anti-Virus injecting into other processes. You'll need to filter those out. 148 | 149 | 150 | Sysmon 151 | | where EventID == 8 152 | | where binary_and(tolong(thread_start_address), 0xFFFF) < 0x1000 153 | | where process_path != target_process_path 154 | 155 | ## Default Named Pipe Patterns for Cobalt Strike Beacon as of Feb 2021 156 | - Watch for these to change with new versions of CS 157 | - Reference: https://blog.cobaltstrike.com/2021/02/09/learn-pipe-fitting-for-all-of-your-offense-projects/ 158 | 159 | 160 | Sysmon 161 | | where EventID in (17,18) 162 | | where pipe_name has "\\postex_" 163 | or pipe_name matches regex "MSSE-\\d+-server" 164 | or pipe_name matches regex "status_\\d+" 165 | or pipe_name matches regex "msagent_\\d+" 166 | | extend HostCustomEntity = Computer 167 | 168 | ## User Agent String Anomalies 169 | - Profile your environment for normally observed user agents and alert on new ones that try to blend in but don't 170 | - Pay attention to OS version and browser version (e.g. IE8 on Windows XP probably doesn't match your env!) 171 | - The User Agents below were the most commonly observed settings in CS Beacon Configs (the number in front is the # of times that appeared) 172 | 173 | 174 | 86 "User Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko" 175 | 66 "User Agent": "Windows-Update-Agent/10.0.10011.16384 Client-Protocol/1.40" 176 | 64 "User Agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko)” 177 | 50 "User Agent": "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)" 178 | 40 "User Agent": "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)" 179 | 40 "User Agent": "Mozilla/5.0 (Windows NT 6.1; rv:24.0) Gecko/20100101 Firefox/24.0" 180 | 37 "User Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; BOIE9;ENUS)" 181 | 35 "User Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)" 182 | 34 "User Agent": "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/5.0)" 183 | 32 "User Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)" 184 | 29 "User Agent": "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; QQDownload 733; .NET CLR 2.0.50727)" 185 | 28 "User Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0)" 186 | 28 "User Agent": "Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko" 187 | 27 "User Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; BOIE9;ENUSMSE)" 188 | 27 "User Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; WOW64; Trident/5.0)" 189 | 24 "User Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0) LBBROWSER" 190 | 23 "User Agent": "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)" 191 | 21 "User Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; MALC)” 192 | 20 "User Agent": "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Win64; x64; Trident/6.0)" 193 | 20 "User Agent": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0)" 194 | 19 "User Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; BOIE9;ENGB)" 195 | 19 "User Agent": "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727)" 196 | 197 | ## Files Named as System Utilities Running from the Wrong Location 198 | - Watch out for any executable file named the same as a legitimate system utility but running from the wrong location, not signed by Microsoft, or with the wrong file version information metadata 199 | 200 | ## System Utilities Renamed 201 | - Watch out for any legitimate system utility (e.g. powershell.exe, mshta.exe) that has been copied elsewhere on the system and renamed. 202 | - Olaf Hartong wrote a great blog that explains this hunting technique very well: https://medium.com/falconforce/falconfriday-masquerading-lolbin-file-renaming-0xff0c-b01e0ab5a95d 203 | -------------------------------------------------------------------------------- /sysmon_custom_function.txt: -------------------------------------------------------------------------------- 1 | // To install this function in your Microsoft Sentinel: 2 | // 1) Copy and paste the code below into query window in the "Logs" interface 3 | // 2) Click the Save drop-down button and Save As... function named "Sysmon" 4 | // Now you can use "Sysmon" as a data source, just like it was a table name. 5 | let timeframe = "{time_range}"; 6 | let EventData = Event 7 | | where Source == "Microsoft-Windows-Sysmon" 8 | | extend RenderedDescription = tostring(split(RenderedDescription, ":")[0]) 9 | | extend DeviceName = replace("accountspayableoutsourcing.com", "APO", Computer) 10 | | project TimeGenerated, Source, EventID, DeviceName, UserName, EventData, RenderedDescription 11 | | extend EvData = parse_xml(EventData) 12 | | extend EventDetail = EvData.DataItem.EventData.Data 13 | | project-away EventData, EvData,UserName 14 | ; 15 | let SysmonEvent1_ProcessCreate=() { 16 | let processEvents = EventData 17 | | where EventID == 1 18 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 19 | | extend TimeStamp = EventDetail.[1].["#text"] 20 | | extend ProcessGuid = tostring(EventDetail.[2].["#text"]) // This is unique to sysmon as it create a unique Guid for each process. 21 | | extend ProcessId = toint(EventDetail.[3].["#text"]) 22 | | extend ProcessPath = tostring(EventDetail.[4].["#text"]) 23 | | extend FileVersion = tostring(EventDetail.[5].["#text"]) 24 | | extend FileDescription = tostring(EventDetail.[6].["#text"]) 25 | | extend FileProduct = tostring(EventDetail.[7].["#text"]) 26 | | extend FileCompany = tostring(EventDetail.[8].["#text"]) 27 | | extend FileName = tostring(EventDetail.[9].["#text"]) 28 | | extend ProcessCommandLine = tostring(EventDetail.[10].["#text"]) 29 | | extend FilePath = tostring(EventDetail.[11].["#text"]) 30 | | extend AccountName = tostring(EventDetail.[12].["#text"] ) 31 | | extend UserLogonGuid = tostring(EventDetail.[13].["#text"]) 32 | | extend UserLogonId = EventDetail.[14].["#text"] 33 | | extend UserSessionId = toint(EventDetail.[15].["#text"]) 34 | | extend ProcessIntegrityLevel = tostring(EventDetail.[16].["#text"]) 35 | | extend Hashes = tostring(EventDetail.[17].["#text"]) 36 | | extend InitiatingProcessGuid = tostring(EventDetail.[18].["#text"]) 37 | | extend InitiatingProcessId = toint(EventDetail.[19].["#text"]) 38 | | extend InitiatingProcessFile = tostring(EventDetail.[20].["#text"]) 39 | | extend InitiatingProcessCommandLine = tostring(EventDetail.[21].["#text"]) 40 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 41 | | parse kind=regex Hashes with * '(SHA1|SHA256)=' hash 42 | | project-away EventDetail, RuleName, Hashes 43 | ; 44 | processEvents; 45 | }; 46 | let SysmonEvent2_FileCreateTime=() { 47 | let processEvents = EventData 48 | | where EventID == 2 49 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 50 | | extend TimeStamp = EventDetail.[1].["#text"] 51 | | extend ProcessGuid = tostring(EventDetail.[2].["#text"]) 52 | | extend ProcessId = toint(EventDetail.[3].["#text"]) 53 | | extend ProcessPath = tostring(EventDetail.[4].["#text"]) 54 | | extend FilePath = tostring(EventDetail.[5].["#text"]) 55 | | extend FileCreationTime = EventDetail.[6].["#text"] 56 | | extend FilePreviousCreationTime = EventDetail.[7].["#text"] 57 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 58 | | project-away EventDetail, RuleName 59 | ; 60 | processEvents; 61 | }; 62 | let SysmonEvent3_NetworkConnect=() { 63 | let processEvents = EventData 64 | | where EventID == 3 65 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used) 66 | | extend TimeStamp = EventDetail.[1].["#text"] 67 | | extend ProcessGuid = tostring(EventDetail.[2].["#text"]) 68 | | extend ProcessId = toint(EventDetail.[3].["#text"]) 69 | | extend ProcessPath = tostring(EventDetail.[4].["#text"]) 70 | | extend AccountName = tostring(EventDetail.[5].["#text"]) 71 | | extend Protocol = tostring(EventDetail.[6].["#text"]) 72 | | extend ConnectionInitiated = tobool(EventDetail.[7].["#text"]) 73 | | extend SourceIsIpv6 = tobool(EventDetail.[8].["#text"]) 74 | | extend SourceIp = tostring(EventDetail.[9].["#text"]) 75 | | extend SourceHostname = tostring(EventDetail.[10].["#text"]) 76 | | extend SourcePort = toint(EventDetail.[11].["#text"]) 77 | | extend SourcePortName = tostring(EventDetail.[12].["#text"]) 78 | | extend DestinationIsIpv6 = tobool(EventDetail.[13].["#text"]) 79 | | extend DestinationIp = tostring(EventDetail.[14].["#text"]) 80 | | extend DestinationHostname = tostring(EventDetail.[15].["#text"]) 81 | | extend DestinationPort = toint(EventDetail.[16].["#text"]) 82 | | extend DestinationPortName = tostring(EventDetail.[17].["#text"]) 83 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 84 | | project-away EventDetail, RuleName 85 | ; 86 | processEvents; 87 | }; 88 | let SysmonEvent4_ServiceStateChange=() { 89 | let processEvents = EventData 90 | | where EventID == 4 91 | | extend TimeStamp = EventDetail.[0].["#text"] 92 | | extend ServiceState = tostring(EventDetail.[1].["#text"]) 93 | | extend SysmonSchema = tostring(EventDetail.[2].["#text"]) 94 | | extend SysmonSchemaVersion = tostring(EventDetail.[3].["#text"]) 95 | | project-away EventDetail 96 | ; 97 | processEvents; 98 | }; 99 | let SysmonEvent5_ProcessTerminate=() { 100 | let processEvents = EventData 101 | | where EventID == 5 102 | | extend RulenName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 103 | | extend TimeStamp = EventDetail.[1].["#text"] 104 | | extend ProcessGuid = tostring(EventDetail.[2].["#text"]) 105 | | extend ProcessId = toint(EventDetail.[3].["#text"]) 106 | | extend ProcessPath = tostring(EventDetail.[4].["#text"]) 107 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 108 | | project-away EventDetail, RuleName 109 | ; 110 | processEvents; 111 | }; 112 | let SysmonEvent6_DriverLoad=() { 113 | let processEvents = EventData 114 | | where EventID == 6 115 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 116 | | extend TimeStamp = EventDetail.[1].["#text"] 117 | | extend FileName = tostring(EventDetail.[2].["#text"]) 118 | | extend Hashes = tostring(EventDetail.[3].["#text"]) 119 | | extend IsSigned = tostring(EventDetail.[4].["#text"]) 120 | | extend Signer = tostring(EventDetail.[5].["#text"]) 121 | | extend SignatureStatus = tostring(EventDetail.[6].["#text"]) 122 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 123 | | parse kind=regex Hashes with * '(SHA1|SHA256)=' hash 124 | | project-away EventDetail 125 | | extend RuleName, Hashes 126 | ; 127 | processEvents; 128 | }; 129 | let SysmonEvent7_ImageLoad=() { 130 | let processEvents = EventData 131 | | where EventID == 7 132 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 133 | | extend TimeStamp = EventDetail.[1].["#text"] 134 | | extend ProcessGuid = tostring(EventDetail.[2].["#text"]) 135 | | extend ProcessId = toint(EventDetail.[3].["#text"]) 136 | | extend ProcessPath = tostring(EventDetail.[4].["#text"]) 137 | | extend FileName = tostring(EventDetail.[5].["#text"]) 138 | | extend FileVersion = tostring(EventDetail.[6].["#text"]) 139 | | extend FileDescription = tostring(EventDetail.[7].["#text"]) 140 | | extend FileProduct = tostring(EventDetail.[8].["#text"]) 141 | | extend FileCompany = tostring(EventDetail.[9].["#text"]) 142 | | extend Hashes = tostring(EventDetail.[11].["#text"]) 143 | | extend IsSigned = tostring(EventDetail.[12].["#text"]) 144 | | extend Signer = tostring(EventDetail.[13].["#text"]) 145 | | extend SignatureStatus = tostring(EventDetail.[14].["#text"]) 146 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 147 | | parse kind=regex Hashes with * '(SHA1|SHA256)=' hash 148 | | project-away EventDetail, RuleName, Hashes 149 | ; 150 | processEvents; 151 | }; 152 | let SysmonEvent8_CreateRemoteThread=() { 153 | let processEvents = EventData 154 | | where EventID == 8 155 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 156 | | extend TimeStamp = EventDetail.[1].["#text"] 157 | | extend ProcessGuid = tostring(EventDetail.[2].["#text"]) 158 | | extend ProcessId = toint(EventDetail.[3].["#text"]) 159 | | extend ProcessPath = tostring(EventDetail.[4].["#text"]) 160 | | extend TargetProcessGuid = tostring(EventDetail.[5].["#text"]) 161 | | extend TargetProcessId = toint(EventDetail.[6].["#text"]) 162 | | extend TargetProcessPath = tostring(EventDetail.[7].["#text"]) 163 | | extend NewThreadID = toint(EventDetail.[8].["#text"]) 164 | | extend ThreadStartAddress = tostring(EventDetail.[9].["#text"]) 165 | | extend ThreadStartModule = tostring(EventDetail.[10].["#text"]) 166 | | extend ThreadStartFunction = tostring(EventDetail.[11].["#text"]) 167 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 168 | | project-away EventDetail, RuleName 169 | ; 170 | processEvents; 171 | }; 172 | let SysmonEvent9_RawAccessRead=() { 173 | let processEvents = EventData 174 | | where EventID == 9 175 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 176 | | extend TimeStamp = EventDetail.[1].["#text"] 177 | | extend ProcessGuid = tostring(EventDetail.[2].["#text"]) 178 | | extend ProcessId = toint(EventDetail.[3].["#text"]) 179 | | extend ProcessPath = tostring(EventDetail.[4].["#text"]) 180 | | extend TargetDevice = tostring(EventDetail.[5].["#text"]) 181 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 182 | | project-away EventDetail, RuleName 183 | ; 184 | processEvents; 185 | }; 186 | let SysmonEvent10_ProcessAccess=() { 187 | let processEvents = EventData 188 | | where EventID == 10 189 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 190 | | extend TimeStamp = EventDetail.[1].["#text"] 191 | | extend ProcessGuid = tostring(EventDetail.[2].["#text"]) 192 | | extend ProcessId = toint(EventDetail.[3].["#text"]) 193 | | extend ThreadId = toint(EventDetail.[4].["#text"]) 194 | | extend ProcessPath = tostring(EventDetail.[5].["#text"]) 195 | | extend TargetProcessGuid = tostring(EventDetail.[6].["#text"]) 196 | | extend TargetProcessId = toint(EventDetail.[7].["#text"]) 197 | | extend TargetProcessPath = tostring(EventDetail.[8].["#text"]),process_granted_access = EventDetail.[9].["#text"] 198 | | extend ProcessCallTrace = tostring(EventDetail.[10].["#text"]) 199 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 200 | | project-away EventDetail, RuleName 201 | ; 202 | processEvents; 203 | }; 204 | let SysmonEvent11_FileCreate=() { 205 | let processEvents = EventData 206 | | where EventID == 11 207 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 208 | | extend TimeStamp = EventDetail.[1].["#text"] 209 | | extend ProcessGuid = tostring(EventDetail.[2].["#text"]) 210 | | extend ProcessId = toint(EventDetail.[3].["#text"]) 211 | | extend ProcessPath = tostring(EventDetail.[4].["#text"]) 212 | | extend FileName = tostring(EventDetail.[5].["#text"]) 213 | | extend FileCreationTime = EventDetail.[6].["#text"] 214 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 215 | | project-away EventDetail, RuleName 216 | ; 217 | processEvents; 218 | }; 219 | let SysmonEvent12_RegistryObjectAddDel=() { 220 | let processEvents = EventData 221 | | where EventID == 12 222 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 223 | | extend EventType = tostring(EventDetail.[1].["#text"]) 224 | | extend TimeStamp = EventDetail.[2].["#text"] 225 | | extend ProcessGuid = tostring(EventDetail.[3].["#text"]) 226 | | extend ProcessId = toint(EventDetail.[4].["#text"]) 227 | | extend ProcessPath = tostring(EventDetail.[5].["#text"]) 228 | | extend RegistryKey = tostring(EventDetail.[6].["#text"]) 229 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 230 | | project-away EventDetail, RuleName 231 | ; 232 | processEvents; 233 | }; 234 | let SysmonEvent13_RegistrySetValue=() { 235 | let processEvents = EventData 236 | | where EventID == 13 237 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 238 | | extend EventType = tostring(EventDetail.[1].["#text"]) 239 | | extend TimeStamp = EventDetail.[2].["#text"] 240 | | extend ProcessGuid = tostring(EventDetail.[3].["#text"]) 241 | | extend ProcessId = toint(EventDetail.[4].["#text"]) 242 | | extend ProcessPath = tostring(EventDetail.[5].["#text"]) 243 | | extend RegistryKey = tostring(EventDetail.[6].["#text"]) 244 | | extend RegistryKeyDetails = tostring(EventDetail.[7].["#text"]) 245 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 246 | | project-away EventDetail, RuleName 247 | ; 248 | processEvents; 249 | }; 250 | let SysmonEvent14_RegistryObjectRename=() { 251 | let processEvents = EventData 252 | | where EventID == 14 253 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 254 | | extend EventType = tostring(EventDetail.[1].["#text"]) 255 | | extend TimeStamp = EventDetail.[2].["#text"] 256 | | extend ProcessGuid = tostring(EventDetail.[3].["#text"]) 257 | | extend ProcessId = toint(EventDetail.[4].["#text"]) 258 | | extend ProcessPath = tostring(EventDetail.[5].["#text"]) 259 | | extend RegistryKey = tostring(EventDetail.[6].["#text"]) 260 | | extend RegistryKeyNewName = tostring(EventDetail.[7].["#text"]) 261 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 262 | | project-away EventDetail, RuleName 263 | ; 264 | processEvents; 265 | }; 266 | let SysmonEvent15_FileCreateStreamHash=() { 267 | let processEvents = EventData 268 | | where EventID == 15 269 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 270 | | extend TimeStamp = EventDetail.[1].["#text"] 271 | | extend ProcessGuid = tostring(EventDetail.[2].["#text"]) 272 | | extend ProcessId = toint(EventDetail.[3].["#text"]) 273 | | extend ProcessPath = tostring(EventDetail.[4].["#text"]) 274 | | extend FileName = tostring(EventDetail.[5].["#text"]) 275 | | extend FileCreationTime = EventDetail.[6].["#text"] 276 | | extend Hashes = tostring(EventDetail.[7].["#text"]) 277 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 278 | | parse kind=regex Hashes with * '(SHA1|SHA256)=' hash 279 | | project-away EventDetail, RuleName 280 | ; 281 | processEvents; 282 | }; 283 | let SysmonEvent16_ConfigChange=() { 284 | let processEvents = EventData 285 | | where EventID == 16 286 | | extend TimeStamp = EventDetail.[0].["#text"] 287 | | extend sysmon_configuration = tostring(EventDetail.[1].["#text"]) 288 | | extend sysmon_configuration_hash = tostring(EventDetail.[2].["#text"]) 289 | | project-away EventDetail 290 | ; 291 | processEvents; 292 | }; 293 | let SysmonEvent17_CreateNamedPipe=() { 294 | let processEvents = EventData 295 | | where EventID == 17 296 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 297 | | extend TimeStamp = EventDetail.[2].["#text"] 298 | | extend ProcessGuid = tostring(EventDetail.[3].["#text"]) 299 | | extend ProcessId = toint(EventDetail.[4].["#text"]) 300 | | extend PipeName = tostring(EventDetail.[5].["#text"]) 301 | | extend ProcessPath = tostring(tostring(EventDetail.[6].["#text"])) 302 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 303 | | project-away EventDetail, RuleName 304 | ; 305 | processEvents; 306 | }; 307 | let SysmonEvent18_ConnectNamedPipe=() { 308 | let processEvents = EventData 309 | | where EventID == 18 310 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 311 | | extend EventType = tostring(EventDetail.[1].["#text"]) 312 | | extend TimeStamp = EventDetail.[2].["#text"] 313 | | extend ProcessGuid = tostring(EventDetail.[3].["#text"]) 314 | | extend ProcessId = toint(EventDetail.[4].["#text"]) 315 | | extend PipeName = tostring(EventDetail.[5].["#text"]) 316 | | extend ProcessPath = tostring(EventDetail.[6].["#text"]) 317 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 318 | | project-away EventDetail, RuleName 319 | ; 320 | processEvents; 321 | }; 322 | let SysmonEvent19_WMIEventFilter=() { 323 | let processEvents = EventData 324 | | where EventID == 19 325 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 326 | | extend EventType = tostring(EventDetail.[1].["#text"]) 327 | | extend TimeStamp = EventDetail.[2].["#text"] 328 | | extend WmiOperation = tostring(EventDetail.[3].["#text"]) 329 | | extend AccountName = tostring(EventDetail.[4].["#text"]) 330 | | extend WmiNameSpace = tostring(EventDetail.[5].["#text"]) 331 | | extend WmiFilterName = tostring(EventDetail.[6].["#text"]) 332 | | extend WmiQuery = tostring(EventDetail.[7].["#text"]) 333 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 334 | | project-away EventDetail, RuleName 335 | ; 336 | processEvents; 337 | }; 338 | let SysmonEvent20_WMIEventConsumer=() { 339 | let processEvents = EventData 340 | | where EventID == 20 341 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 342 | | extend EventType = tostring(EventDetail.[1].["#text"]) 343 | | extend TimeStamp = EventDetail.[2].["#text"] 344 | | extend WmiOperation = tostring(EventDetail.[3].["#text"]) 345 | | extend AccountName = tostring(EventDetail.[4].["#text"]) 346 | | extend WmiConsumerName = tostring(EventDetail.[5].["#text"]) 347 | | extend WmiConsumerType = tostring(EventDetail.[6].["#text"]) 348 | | extend WmiConsumerDestination = tostring(EventDetail.[7].["#text"]) 349 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 350 | | project-away EventDetail, RuleName 351 | ; 352 | processEvents; 353 | }; 354 | let SysmonEvent21_WMIEventConsumerToFilter=() { 355 | let processEvents = EventData 356 | | where EventID == 21 357 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 358 | | extend EventType = tostring(EventDetail.[1].["#text"]) 359 | | extend TimeStamp = EventDetail.[2].["#text"] 360 | | extend WmiOperation = tostring(EventDetail.[3].["#text"]) 361 | | extend AccountName = tostring(EventDetail.[4].["#text"]) 362 | | extend WmiConsumerPath = tostring(EventDetail.[5].["#text"]) 363 | | extend Type = tostring(EventDetail.[6].["#text"]) 364 | | extend WmiFilterPath = tostring(EventDetail.[7].["#text"]) 365 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 366 | | project-away EventDetail, RuleName 367 | ; 368 | processEvents; 369 | }; 370 | let SysmonEvent22_DNSEvents=() { 371 | let processEvents = EventData 372 | | where EventID == 22 373 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 374 | | extend TimeStamp = EventDetail.[1].["#text"] 375 | | extend ProcessGuid = tostring(EventDetail.[2].["#text"]) 376 | | extend ProcessId = toint(EventDetail.[3].["#text"]) 377 | | extend DnsQueryName = tostring(EventDetail.[4].["#text"]) 378 | | extend DnsQueryStatus = tostring(EventDetail.[5].["#text"]) 379 | | extend DnsQueryResults = tostring(EventDetail.[6].["#text"]) 380 | | extend ProcessPath = tostring(EventDetail.[7].["#text"]) 381 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 382 | | project-away EventDetail, RuleName 383 | ; 384 | processEvents; 385 | }; 386 | let SysmonEvent23_DeleteEvents=() { 387 | let deleteEvents = EventData 388 | | where EventID == 23 389 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 390 | | extend TimeStamp = EventDetail.[1].["#text"] 391 | | extend ProcessGuid = tostring(EventDetail.[2].["#text"]) 392 | | extend ProcessId = toint(EventDetail.[3].["#text"]) 393 | | extend AccountName = tostring(EventDetail.[4].["#text"]) 394 | | extend InitiatingProcessFile = tostring(EventDetail.[5].["#text"]) 395 | | extend FileName = tostring(EventDetail.[6].["#text"]) 396 | | extend FileHash = tostring(EventDetail.[7].["#text"]) 397 | | extend IsExecutable = tostring(EventDetail[8].["#text"]) 398 | | extend Archived = tostring(EventDetail[9].["#text"]) 399 | | parse RuleName with * 'TechniqueId=' TechniqueId ',' * 'TechniqueName=' TechniqueName ',' * 'PhaseName=' PhaseName 400 | | parse kind=regex file_delete_hashes with '(SHA1|SHA256)=' file_delete_hashes 401 | | project-away EventDetail, RuleName 402 | ; 403 | deleteEvents; 404 | }; 405 | let SysmonEvent24_ClipboardChange=() { 406 | let processEvents = EventData 407 | | where EventID == 24 408 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 409 | | extend TimeStamp = EventDetail.[1].["#text"] 410 | | extend ProcessGuid = tostring(EventDetail.[2].["#text"]) 411 | | extend ProcessId = toint(EventDetail.[3].["#text"]) 412 | | extend ProcessPath = tostring(EventDetail.[4].["#text"]) 413 | | extend Session = EventDetail.[5].["#text"] 414 | | extend ClientInfo = tostring(EventDetail.[6].["#text"]) 415 | | extend Hash = tostring(EventDetail.[7].["#text"]) 416 | | extend Archived = tostring(EventDetail.[8].["#text"]) 417 | | parse kind=regex hash with * '(SHA1|SHA256)=' hash 418 | | project-away EventDetail, RuleName 419 | ; 420 | processEvents; 421 | }; 422 | let SysmonEvent25_ProcessTampering=() { 423 | let processEvents = EventData 424 | | where EventID == 25 425 | | extend RuleName = tostring(EventDetail.[0].["#text"]) // Parsed but never used 426 | | extend TimeStamp = EventDetail.[1].["#text"] 427 | | extend ProcessGuid = tostring(EventDetail.[2].["#text"]) 428 | | extend ProcessId = toint(EventDetail.[3].["#text"]) 429 | | extend ProcessPath = tostring(EventDetail.[4].["#text"]) 430 | | extend ActionType = tostring(EventDetail.[5].["#text"]) 431 | | project-away EventDetail, RuleName 432 | ; 433 | processEvents; 434 | }; 435 | (union isfuzzy=true 436 | SysmonEvent1_ProcessCreate,SysmonEvent2_FileCreateTime,SysmonEvent3_NetworkConnect,SysmonEvent4_ServiceStateChange,SysmonEvent5_ProcessTerminate, 437 | SysmonEvent6_DriverLoad,SysmonEvent7_ImageLoad,SysmonEvent8_CreateRemoteThread,SysmonEvent9_RawAccessRead,SysmonEvent10_ProcessAccess, 438 | SysmonEvent11_FileCreate,SysmonEvent12_RegistryObjectAddDel,SysmonEvent13_RegistrySetValue,SysmonEvent14_RegistryObjectRename, 439 | SysmonEvent15_FileCreateStreamHash,SysmonEvent16_ConfigChange,SysmonEvent17_CreateNamedPipe,SysmonEvent18_ConnectNamedPipe, 440 | SysmonEvent19_WMIEventFilter,SysmonEvent20_WMIEventConsumer,SysmonEvent21_WMIEventConsumerToFilter,SysmonEvent22_DNSEvents,SysmonEvent23_DeleteEvents,SysmonEvent24_ClipboardChange,SysmonEvent25_ProcessTampering) 441 | 442 | -------------------------------------------------------------------------------- /BD-Threat Hunting for Malware Beacons.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Threat Hunting Notebook: Malware Beacons by Network Pattern Analysis\n", 8 | "This notebook connects to Microsoft Azure Sentinel, queries records of network connections from Sysmon (Event ID 3), or Microsoft Defender for Endpoint (DeviceNetworkEvents) and analyzes the pattern of time between network connections to find communication that looks like a regularly repeating beacon used for Command and Control (C2). These are likely to be either an endpoint agent checking in (which should be well known and documented) or malware checking in with its C2 server." 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "metadata": {}, 14 | "source": [ 15 | "Step 1: Run the code block below to import all the Python modules we need to start. If there are any errors, stop and fix them now before going on.\n" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": null, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "import sys, math,requests, json, datetime\n", 25 | "\n", 26 | "MIN_REQ_PYTHON = (3,6)\n", 27 | "if sys.version_info < MIN_REQ_PYTHON:\n", 28 | " print('Check the Kernel->Change Kernel menu and ensure that Python 3.6')\n", 29 | " print('or later is selected as the active kernel.')\n", 30 | " sys.exit(\"Python %s.%s or later is required.\\n\" % MIN_REQ_PYTHON)\n", 31 | "\n", 32 | "#imports\n", 33 | "import yaml\n", 34 | "import msticpy.nbtools as nbtools\n", 35 | "\n", 36 | "#data library imports\n", 37 | "from msticpy.data.data_providers import QueryProvider\n", 38 | "import msticpy.data.data_query_reader as QueryReader\n", 39 | "from msticpy.data.param_extractor import extract_query_params\n", 40 | "import msticpy.nbtools as mas\n", 41 | "\n", 42 | "from msticpy.common.wsconfig import WorkspaceConfig # workspace configurations stored in msticpyconfig.yaml\n", 43 | "##from msticpy.sectools.geoip import GeoLiteLookup # Maxmind GeoCityLite database stored in ~/.msticpy/\n", 44 | "##geoip = GeoLiteLookup()\n", 45 | "\n", 46 | "##import vt # virus total api\n", 47 | "##import shodan\n", 48 | "from ipwhois import IPWhois\n", 49 | "from ipwhois.utils import get_countries\n", 50 | "import ipaddress\n", 51 | "\n", 52 | "from datetime import datetime, timedelta, timezone\n", 53 | "import pytz # handle timezones the same way Pandas does\n", 54 | "import pandas as pd\n", 55 | "import numpy as np\n", 56 | "\n", 57 | "# Import module for progress bars\n", 58 | "from tqdm.notebook import tqdm, trange\n", 59 | "\n", 60 | "# Import the modules needed to create an interactive data frame with IPyWidgets\n", 61 | "import ipywidgets as widgets\n", 62 | "from ipywidgets import interactive\n", 63 | "\n", 64 | "print('Imports Complete! If there are no errors reported, continue on!')" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "#### Optional: Update KQLMagic extension ####\n", 72 | "Every once in a while, it's a good idea to update KQLMagic" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": null, 78 | "metadata": {}, 79 | "outputs": [], 80 | "source": [ 81 | "!pip install Kqlmagic --no-cache-dir --upgrade\n", 82 | "%reload_ext Kqlmagic" 83 | ] 84 | }, 85 | { 86 | "cell_type": "markdown", 87 | "metadata": {}, 88 | "source": [ 89 | "## Connect to Workspace for data\n", 90 | "The next step is to connect this Notebook to your data source. All of the Workspace connection information should be set up in the msticpyconfig.yaml and config.json files, but you still need to authenticate with your user credentials that have read access to the Workspace you wish to connect to.\n", 91 | "\n", 92 | "Unless you specify otherwise, you'll be connected to the default workspace that you configured in your msticpyconfig YAML file.\n", 93 | "\n", 94 | "_Instructions_:\n", 95 | "1. Run the code block below\n", 96 | "2. Wait for the KQL Magic output to appear\n", 97 | "3. Click the button at the bottom of the output to copy the app code and open the authentication window\n", 98 | "4. Authenticate with your user account, then continue running this Notebook" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": null, 104 | "metadata": {}, 105 | "outputs": [], 106 | "source": [ 107 | "## Create a QueryProvider object for running queries in our LogAnalytics workspace\n", 108 | "qry_prov = QueryProvider(data_environment='LogAnalytics')\n", 109 | "## Use the workspace configuration we've set up in msticpyconfig.yaml (you can also choose another workspace here)\n", 110 | "ws_config = WorkspaceConfig(workspace=\"Default\")\n", 111 | "qry_prov.connect(connection_str=ws_config.code_connect_str)" 112 | ] 113 | }, 114 | { 115 | "cell_type": "markdown", 116 | "metadata": {}, 117 | "source": [ 118 | "## Gather Network Event Data ##\n", 119 | "Before we can analyze network event data patterns, we need to get the raw information about timing of connection events. This notebook has KQL queries to get the data from Microsoft Defender for Endpoint if you have that, or Sysmon if you prefer to use that. All you really need from each event is the time, source IP and destination IP, so you could consume logs from Packetbeat or many other sources. If you have Zeek logs, you don't need this and should just use RITA to process those logs directly." 120 | ] 121 | }, 122 | { 123 | "cell_type": "markdown", 124 | "metadata": {}, 125 | "source": [ 126 | "#### Query for Microsoft Defender for Endpoint ####\n", 127 | "Run the code block below if you use Microsoft Defender for Endpoint" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": null, 133 | "metadata": {}, 134 | "outputs": [], 135 | "source": [ 136 | "# Microsoft Defender for Endpoint query that uses DeviceNetworkEvents\n", 137 | "network_conn_query = '''DeviceNetworkEvents\n", 138 | "| where TimeGenerated between (datetime(%s) .. datetime(%s))\n", 139 | "| where ActionType !in (\"InboundConnectionAccepted\", \"ListeningConnectionCreated\")\n", 140 | "| where isnotempty(RemoteIP)\n", 141 | "| project TimeGenerated, LocalIP, RemoteIP\n", 142 | "'''\n", 143 | "process_query = '''DeviceNetworkEvents\n", 144 | "| where TimeGenerated > ago(7d)\n", 145 | "| where LocalIP==\"%s\"\n", 146 | "| where RemoteIP==\"%s\"\n", 147 | "| extend Hostname = tostring(split(DeviceName, \".\")[0])\n", 148 | "| summarize count() by Hostname, \n", 149 | "UserName=InitiatingProcessAccountName, \n", 150 | "FileName=InitiatingProcessFileName,\n", 151 | "CommandLine=InitiatingProcessCommandLine, \n", 152 | "ProcessId=InitiatingProcessId, \n", 153 | "LocalIP, RemoteIP, RemotePort\n", 154 | "'''\n", 155 | "print(\"Defender for Endpoint query loaded.\")\n", 156 | "print(\"Do NOT run the next code block for Sysmon if you are using Defender for Endpoint. Skip ahead to next block.\")" 157 | ] 158 | }, 159 | { 160 | "cell_type": "markdown", 161 | "metadata": {}, 162 | "source": [ 163 | "#### Query for Sysmon ####\n", 164 | "If you use Sysmon to collect Event ID 3 (Network Connections) then uncomment the code block below and run it:" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": null, 170 | "metadata": {}, 171 | "outputs": [], 172 | "source": [ 173 | "# Sysmon Event ID 3 -- if you already have a custom function\n", 174 | "# to parse Sysmon events, you may simplify this query by using\n", 175 | "# that. This notebook makes no assumptions or requirements for \n", 176 | "# custom queries that you might have set up and parses the raw event.\n", 177 | "# If you're looking for a good Sysmon parser custom function, see this:\n", 178 | "# https://github.com/Azure/Azure-Sentinel/tree/master/Parsers/Sysmon\n", 179 | "# or this:\n", 180 | "# https://github.com/BlueTeamLabs/sentinel-attack/tree/master/parser\n", 181 | "network_conn_query = '''Event\n", 182 | "| where Source == \"Microsoft-Windows-Sysmon\"\n", 183 | "| where EventID == 3\n", 184 | "| where TimeGenerated between (datetime(%s) .. datetime(%s))\n", 185 | "| extend EvData = parse_xml(EventData)\n", 186 | "| extend EventDetail = EvData.DataItem.EventData.Data\n", 187 | "| project-away EventData, EvData\n", 188 | "| extend NetworkConnectionInitiated = tobool(EventDetail.[7].[\"#text\"])\n", 189 | "| extend LocalIP = tostring(EventDetail.[9].[\"#text\"])\n", 190 | "| extend RemoteIP = tostring(EventDetail.[14].[\"#text\"])\n", 191 | "| where isnotempty(RemoteIP)\n", 192 | "| where NetworkConnectionInitiated == true\n", 193 | "| project TimeGenerated, LocalIP, RemoteIP\n", 194 | "'''\n", 195 | "process_query = '''\n", 196 | "Event\n", 197 | "| where Source == \"Microsoft-Windows-Sysmon\"\n", 198 | "| where EventID == 3\n", 199 | "| extend EvData = parse_xml(EventData)\n", 200 | "| extend EventDetail = EvData.DataItem.EventData.Data\n", 201 | "| project-away EventData, EvData\n", 202 | "| extend Hostname = tostring(split(Computer, \".\")[0])\n", 203 | "| extend NetworkConnectionInitiated = tobool(EventDetail.[7].[\"#text\"])\n", 204 | "| extend LocalIP = tostring(EventDetail.[9].[\"#text\"])\n", 205 | "| extend RemoteIP = tostring(EventDetail.[14].[\"#text\"])\n", 206 | "| extend RemotePort = tostring(EventDetail.[16].[\"#text\"])\n", 207 | "| extend ProcessId = tostring(EventDetail.[3].[\"#text\"])\n", 208 | "| extend ProcessPath = tostring(EventDetail.[4].[\"#text\"])\n", 209 | "| extend FileName = tostring(split(ProcessPath, @\"\\\\\")[-1])\n", 210 | "| extend UserName = tostring(EventDetail.[5].[\"#text\"])\n", 211 | "| extend NetworkProtocol = tostring(EventDetail.[6].[\"#text\"])\n", 212 | "| where LocalIP==\"%s\" and RemoteIP==\"%s\"\n", 213 | "| project-away EventDetail\n", 214 | "| summarize count() by Hostname, UserName, ProcessPath, \n", 215 | "FileName, ProcessId, LocalIP, RemoteIP, RemotePort\n", 216 | "'''\n", 217 | "print(\"KQL queries for Sysmon loaded. If you're querying Sysmon data, proceed.\")\n", 218 | "print(\"If you meant to query Defender for Endpoint (ATP) go back and run that cell.\")" 219 | ] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "execution_count": null, 224 | "metadata": {}, 225 | "outputs": [], 226 | "source": [ 227 | "## First get raw timing of network connections\n", 228 | "## This is such a massive amount of data that we can only query it\n", 229 | "## for about one hour's worth of results at a time\n", 230 | "num_hours_to_search = 24\n", 231 | "current_time = datetime.now(tz=pytz.utc)\n", 232 | "# If you don't want to query up to the current time but instead\n", 233 | "# you wish to go back a few days and query up to a different time,\n", 234 | "# just change the current_time variable. There's an example below\n", 235 | "#current_time = datetime.now(tz=pytz.utc) - timedelta(days=6)\n", 236 | "connection_timing = {}\n", 237 | "total_connections = 0\n", 238 | "\n", 239 | "for hour in trange(num_hours_to_search):\n", 240 | " ##print(\"Querying network connections between %d and %d hours ago...\" % (hour+1, hour))\n", 241 | " date1 = current_time - timedelta(hours=hour+1)\n", 242 | " date2 = current_time - timedelta(hours=hour)\n", 243 | " hour_query = network_conn_query % (date1, date2)\n", 244 | " #print(hour_query)\n", 245 | " df_hour_network_conns = qry_prov.exec_query(query=hour_query)\n", 246 | " for index, row in df_hour_network_conns.iterrows():\n", 247 | " key = tuple([row['LocalIP'], row['RemoteIP']])\n", 248 | " if not key in connection_timing:\n", 249 | " connection_timing[key] = []\n", 250 | " connection_timing[key].append(row['TimeGenerated']) # add connection time to list for this IP pair\n", 251 | " total_connections += 1\n", 252 | " \n", 253 | "print(\"There were %d unique host pairs with %d total connections\" % (len(connection_timing), total_connections))" 254 | ] 255 | }, 256 | { 257 | "cell_type": "markdown", 258 | "metadata": {}, 259 | "source": [ 260 | "### Hunting for Network Beacons like RITA ###\n", 261 | "This code is inspired by and adapted from the algorithm in the open source project RITA from Active Countermeasures: \n", 262 | "https://github.com/activecm/rita\n", 263 | "The algorithm that analyzes network traffic to compute a beacon score can be found in this file:\n", 264 | "https://github.com/activecm/rita/blob/master/pkg/beacon/analyzer.go\n", 265 | "\n", 266 | "This works by looking at each pair of communicating IP addresses and the timing of all the network connections between them. It computes the difference in time between each connection and then scores them based on the following factors:\n", 267 | "* How regularly spaced apart are the connections?\n", 268 | "* What much dispersion is there between median timing and outliers?\n", 269 | "* How many connections are there over time?\n", 270 | "\n", 271 | "Run the code block below to do all of the calculations for you. It ignores any pairs of hosts that had fewer than six total connections.\n" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": null, 277 | "metadata": {}, 278 | "outputs": [], 279 | "source": [ 280 | "beacon_scores = []\n", 281 | "ip_pair_score_list = [] # for building a selection drop down later\n", 282 | "# current_time was set in last code cell\n", 283 | "earliest_time = current_time\n", 284 | "print(\"Hang on... this might take a minute...\")\n", 285 | "# compute the beacon score for each pair of communicating IPs\n", 286 | "for ip_pair in connection_timing.keys():\n", 287 | " try:\n", 288 | " local_ip = ipaddress.ip_address(ip_pair[0])\n", 289 | " remote_ip = ipaddress.ip_address(ip_pair[1])\n", 290 | " except:\n", 291 | " continue\n", 292 | " if local_ip.is_private and remote_ip.is_private:\n", 293 | " continue\n", 294 | " if local_ip.is_loopback and remote_ip.is_loopback:\n", 295 | " continue\n", 296 | " timing_list = connection_timing[ip_pair]\n", 297 | " if len(timing_list) >= 15:\n", 298 | " timing_list.sort()\n", 299 | " if timing_list[0] < earliest_time:\n", 300 | " earliest_time = timing_list[0]\n", 301 | " time_span = timing_list[-1] - timing_list[0]\n", 302 | " time_span_seconds = time_span.total_seconds()\n", 303 | " diffs = []\n", 304 | " for i in range(len(timing_list)-1):\n", 305 | " timediff = timing_list[i+1] - timing_list[i] # results in a Timedelta instance\n", 306 | " if timediff.total_seconds() > 1.0:\n", 307 | " diffs.append(timediff.total_seconds()) # keep track of the time differences in seconds\n", 308 | " if len(diffs) < 6:\n", 309 | " continue # if we don't have at least 6 connections separated by at least one second, skip this\n", 310 | " # sort the time diffs so we can easily find the low, mid and high values\n", 311 | " diffs.sort()\n", 312 | " # Compute the Bowley number and Bowley density\n", 313 | " tsLow = np.percentile(diffs, 25)\n", 314 | " tsMid = np.percentile(diffs, 50)\n", 315 | " tsHigh = np.percentile(diffs, 75)\n", 316 | " tsBowleyNum = tsLow + tsHigh - 2*tsMid\n", 317 | " tsBowleyDen = tsHigh - tsLow\n", 318 | " # tsSkew should equal zero if the denominator equals zero\n", 319 | " # bowley skew is unreliable if Q2 = Q1 or Q2 = Q3\n", 320 | " if tsBowleyDen != 0 and tsMid != tsLow and tsMid != tsHigh:\n", 321 | " tsSkew = float(tsBowleyNum) / float(tsBowleyDen)\n", 322 | " else:\n", 323 | " tsSkew = 0\n", 324 | " # perfect beacons should have very low dispersion around the\n", 325 | " # median of their delta times\n", 326 | " # Median Absolute Deviation About the Median\n", 327 | " # is used to check dispersion\n", 328 | " deviations = []\n", 329 | " for diff in diffs:\n", 330 | " deviations.append(abs(diff - tsMid))\n", 331 | " deviations.sort() \n", 332 | " tsMadm = np.percentile(deviations, 50) # tsMadm is Median absolute deviation about the median\n", 333 | " # compute range of intervals for human analysis\n", 334 | " tsIntervalRange = diffs[-1] - diffs[0]\n", 335 | " \n", 336 | " # more skewed distributions receive a lower score\n", 337 | " # less skewed distributions receive a higher score\n", 338 | " tsSkewScore = 1.0 - abs(tsSkew) #smush tsSkew\n", 339 | "\n", 340 | " # lower dispersion is better, cutoff dispersion scores at 30 seconds\n", 341 | " tsMadmScore = 1.0 - float(tsMadm)/30.0\n", 342 | " if tsMadmScore < 0: \n", 343 | " tsMadmScore = 0\n", 344 | " \n", 345 | " # connection count scoring: max score of 1.0 means 1+ conn every (mediam beacon interval) seconds or less\n", 346 | " tsTimespanDiv = float((current_time - timing_list[0]).total_seconds()) / tsMid\n", 347 | " tsConnCountScore = float(len(timing_list)) / tsTimespanDiv\n", 348 | " if tsConnCountScore > 1.0:\n", 349 | " tsConnCountScore = 1.0 \n", 350 | " \n", 351 | " # Compute combined score average\n", 352 | " tsSum = tsSkewScore + tsMadmScore + tsConnCountScore\n", 353 | " tsScore = math.ceil((tsSum/3.0)*1000) / 1000\n", 354 | " \n", 355 | " stats = {'score':tsScore, \n", 356 | " 'ip_pair': tuple(ip_pair),\n", 357 | " 'skew_score': tsSkewScore,\n", 358 | " 'madm_score': tsMadmScore,\n", 359 | " 'count_score': tsConnCountScore,\n", 360 | " 'beacon_interval_high':np.percentile(diffs, 90), \n", 361 | " #'beacon_interval_mid':tsMid,\n", 362 | " 'beacon_interval_low':np.percentile(diffs, 10), \n", 363 | " 'earliest_conn':timing_list[0], \n", 364 | " 'latest_conn':timing_list[-1], \n", 365 | " 'num_conns': len(diffs)}\n", 366 | " beacon_scores.append(stats)\n", 367 | " ip_pair_score_list.append(\"%.2f:%s-%s\" % (tsScore, ip_pair[0], ip_pair[1]))\n", 368 | " \n", 369 | "def createIPPairScoreListFromDataFrame(df):\n", 370 | " score_list = []\n", 371 | " for index, row in df.iterrows():\n", 372 | " score_list.append(\"%.2f:%s-%s\" % (row[\"score\"], row[\"ip_pair\"][0], row[\"ip_pair\"][1]))\n", 373 | " score_list.sort(reverse=True)\n", 374 | " return score_list\n", 375 | " \n", 376 | "ip_pair_score_list.sort(reverse=True)\n", 377 | "beacons_df = pd.DataFrame(beacon_scores)\n", 378 | "beacons_df['earliest_conn'] = pd.to_datetime(beacons_df[\"earliest_conn\"].dt.strftime('%Y-%m-%d %H:%M:%S'))\n", 379 | "beacons_df['latest_conn'] = pd.to_datetime(beacons_df[\"latest_conn\"].dt.strftime('%Y-%m-%d %H:%M:%S'))\n", 380 | "print(\"Potential beacons analyzed and stored in DataFrame beacons_df\")\n", 381 | "print(\"You may now proceed to run the next cells to view the results\")" 382 | ] 383 | }, 384 | { 385 | "cell_type": "markdown", 386 | "metadata": {}, 387 | "source": [ 388 | "# Top Potential Beacons by Overall Score\n", 389 | "Now it is time to review the results. We can look at the data in different ways, but the most useful way to sort is by the beacon scores computed above, with the highest scores on top.\n", 390 | "Run the code block below to view the dataframe sorted by score. Adjust the number in the dataframe head() function below to get the top 10, top 25 or whatever number you want to review." 391 | ] 392 | }, 393 | { 394 | "cell_type": "code", 395 | "execution_count": null, 396 | "metadata": {}, 397 | "outputs": [], 398 | "source": [ 399 | "beacons_df.set_index(\"score\")\n", 400 | "beacons_df.sort_values(\"score\", ascending=False, inplace=True)\n", 401 | "\n", 402 | "num_conns_slider = widgets.IntSlider(min=5, max=200, step=5, value=10)\n", 403 | "num_results_slider = widgets.IntSlider(min=1, max=50, step=1, value=10)\n", 404 | " \n", 405 | "def filter_beacons(min_conns=10,num_results=10):\n", 406 | " filtered = beacons_df[beacons_df['num_conns']>=min_conns].head(num_results)\n", 407 | " filtered.set_index(\"score\")\n", 408 | " global ip_pair_score_list\n", 409 | " ip_pair_score_list = createIPPairScoreListFromDataFrame(filtered)\n", 410 | " return filtered\n", 411 | " \n", 412 | "widgets.interact_manual(filter_beacons,min_conns=num_conns_slider,num_results=num_results_slider)\n", 413 | "\n", 414 | "# If you don't want the interactive sliders, comment out above and replace with two lines below:\n", 415 | "#beacons_df.sort_values(\"score\", ascending=False, inplace=True)\n", 416 | "#beacons_df.head(25)" 417 | ] 418 | }, 419 | { 420 | "cell_type": "markdown", 421 | "metadata": {}, 422 | "source": [ 423 | "#### Let's get Visual ####\n", 424 | ">\"Those who do not learn from histograms are destined to keep repeating their analyses\"\n", 425 | "\n", 426 | "Select an IP pair from the list below and click the \"Run interact\" button to generate a histogram of the connection times. The graph shows how many connections were detected in each 10 minute bucket. So, if connections happened once a minute, you should see vertical bars closely spaced, all at the 10 count level. " 427 | ] 428 | }, 429 | { 430 | "cell_type": "code", 431 | "execution_count": null, 432 | "metadata": {}, 433 | "outputs": [], 434 | "source": [ 435 | "current_ip_pair = None\n", 436 | "def viewplot(ip_info):\n", 437 | " # \"linking function with output\"\n", 438 | " score, ip_pair = ip_info.split(\":\", 1)\n", 439 | " global current_ip_pair\n", 440 | " current_ip_pair = tuple(ip_pair.split(\"-\", 1))\n", 441 | " plot = pd.DataFrame(connection_timing[current_ip_pair], columns=['time'])\n", 442 | " # Setting the date as the index since the Grouper works on Index, \n", 443 | " # the date column is not dropped to be able to count\n", 444 | " plot.set_index('time', drop=False, inplace=True)\n", 445 | " # Get the histogram\n", 446 | " mid_time = earliest_time + (current_time-earliest_time)/2\n", 447 | " plot.groupby(pd.Grouper(freq='10Min')).count().plot(kind='bar', \n", 448 | " xticks=(),\n", 449 | " yticks=range(0,11), \n", 450 | " grid=True, legend=False,\n", 451 | " title=str(current_ip_pair),\n", 452 | " color='red',\n", 453 | " xlim=(earliest_time, current_time))\n", 454 | " return plot\n", 455 | "\n", 456 | "# Create an interactive selector\n", 457 | "ip_select = widgets.Select(options=ip_pair_score_list[:num_results_slider.value+1], \n", 458 | " width='400px', height='800px')\n", 459 | "widgets.interact_manual(viewplot, ip_info=ip_select)\n" 460 | ] 461 | }, 462 | { 463 | "cell_type": "markdown", 464 | "metadata": {}, 465 | "source": [ 466 | "#### Investigate Suspicious Processes ####\n", 467 | "Now that you have suspicious connections to check into, we need to see which processes were responsible for those network connections.\n", 468 | "\n", 469 | "Run the code block below to view the results for the IP pair that you selected for the histogram view above.\n", 470 | "\n", 471 | "Note that these results are for processes from the last three days that had communications between the two IP addresses" 472 | ] 473 | }, 474 | { 475 | "cell_type": "code", 476 | "execution_count": null, 477 | "metadata": {}, 478 | "outputs": [], 479 | "source": [ 480 | "ip_process_query = process_query % (current_ip_pair[0], current_ip_pair[1])\n", 481 | "df_matching_processes = qry_prov.exec_query(query=ip_process_query)\n", 482 | "\n", 483 | "df_matching_processes" 484 | ] 485 | }, 486 | { 487 | "cell_type": "markdown", 488 | "metadata": {}, 489 | "source": [ 490 | "If you want to grab the KQL query to investigate further in Sentinel, run the code block below" 491 | ] 492 | }, 493 | { 494 | "cell_type": "code", 495 | "execution_count": null, 496 | "metadata": {}, 497 | "outputs": [], 498 | "source": [ 499 | "print(ip_process_query)" 500 | ] 501 | }, 502 | { 503 | "cell_type": "markdown", 504 | "metadata": {}, 505 | "source": [ 506 | "#### Whois that IP anyway? ####\n", 507 | "If you need more information about that Remote IP address in the results above, run the block below to check it out." 508 | ] 509 | }, 510 | { 511 | "cell_type": "code", 512 | "execution_count": null, 513 | "metadata": {}, 514 | "outputs": [], 515 | "source": [ 516 | "print(current_ip_pair[1])\n", 517 | "remote_ip_result = IPWhois(current_ip_pair[1])\n", 518 | "remote_ip_json = remote_ip_result.lookup_rdap()\n", 519 | "print(json.dumps(remote_ip_json, indent=2))" 520 | ] 521 | }, 522 | { 523 | "cell_type": "markdown", 524 | "metadata": {}, 525 | "source": [ 526 | "### Check MSTIC Threat Intelligence for IP ###\n" 527 | ] 528 | }, 529 | { 530 | "cell_type": "code", 531 | "execution_count": null, 532 | "metadata": {}, 533 | "outputs": [], 534 | "source": [ 535 | "print(current_ip_pair[1])\n", 536 | "# query the remote IP in the MSTIC threat intelligence feed\n", 537 | "df_ip_hits = qry_prov.ThreatIntelligence.list_indicators_by_ip(observables=list(current_ip_pair[1]))\n", 538 | "# display the final result\n", 539 | "df_ip_hits" 540 | ] 541 | }, 542 | { 543 | "cell_type": "code", 544 | "execution_count": null, 545 | "metadata": {}, 546 | "outputs": [], 547 | "source": [ 548 | "# Bonus: Sysmon EventID 3 fields parsed for KQL\n", 549 | "SysmonNetworkEventQuery = '''Event\n", 550 | "| where Source == \"Microsoft-Windows-Sysmon\"\n", 551 | "| extend RenderedDescription = tostring(split(RenderedDescription, \":\")[0])\n", 552 | "| where EventID == 3\n", 553 | "| extend EvData = parse_xml(EventData)\n", 554 | "| extend EventDetail = EvData.DataItem.EventData.Data\n", 555 | "| project-away EventData, EvData\n", 556 | "| extend RuleName = tostring(EventDetail.[0].[\"#text\"])\n", 557 | "| extend EventTime = EventDetail.[1].[\"#text\"]\n", 558 | "| extend ProcessGuid = EventDetail.[2].[\"#text\"]\n", 559 | "| extend ProcessID = EventDetail.[3].[\"#text\"]\n", 560 | "| extend ProcessPath = tostring(EventDetail.[4].[\"#text\"])\n", 561 | "| extend Username = tostring(EventDetail.[5].[\"#text\"])\n", 562 | "| extend NetworkProtocol = tostring(EventDetail.[6].[\"#text\"])\n", 563 | "| extend NetworkConnectionInitiated = EventDetail.[7].[\"#text\"]\n", 564 | "| extend SrcIsIpv6 = EventDetail.[8].[\"#text\"]\n", 565 | "| extend LocalIP = tostring(EventDetail.[9].[\"#text\"])\n", 566 | "| extend LocalHostName = tostring(EventDetail.[10].[\"#text\"])\n", 567 | "| extend LocalPort = EventDetail.[11].[\"#text\"]\n", 568 | "| extend LocalPortName = tostring(EventDetail.[12].[\"#text\"])\n", 569 | "| extend RemoteIsIpv6 = EventDetail.[13].[\"#text\"] \n", 570 | "| extend RemoteIP = tostring(EventDetail.[14].[\"#text\"])\n", 571 | "| extend RemoteHostName = tostring(EventDetail.[15].[\"#text\"])\n", 572 | "| extend RemotePort = EventDetail.[16].[\"#text\"]\n", 573 | "| extend RemotePortName = tostring(EventDetail.[17].[\"#text\"])'''" 574 | ] 575 | } 576 | ], 577 | "metadata": { 578 | "kernelspec": { 579 | "display_name": "Python 3", 580 | "language": "python", 581 | "name": "python3" 582 | }, 583 | "language_info": { 584 | "codemirror_mode": { 585 | "name": "ipython", 586 | "version": 3 587 | }, 588 | "file_extension": ".py", 589 | "mimetype": "text/x-python", 590 | "name": "python", 591 | "nbconvert_exporter": "python", 592 | "pygments_lexer": "ipython3", 593 | "version": "3.7.8" 594 | } 595 | }, 596 | "nbformat": 4, 597 | "nbformat_minor": 4 598 | } 599 | -------------------------------------------------------------------------------- /Graphing Parent-Child Processes.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 47, 6 | "metadata": { 7 | "jupyter": { 8 | "source_hidden": true 9 | } 10 | }, 11 | "outputs": [ 12 | { 13 | "name": "stdout", 14 | "output_type": "stream", 15 | "text": [ 16 | "Using matplotlib backend: agg\n", 17 | "Imports Complete! If there are no errors reported, continue on!\n" 18 | ] 19 | } 20 | ], 21 | "source": [ 22 | "import sys\n", 23 | "import requests\n", 24 | "import json\n", 25 | "import nltk\n", 26 | "import math\n", 27 | "import networkx as nx\n", 28 | "%matplotlib\n", 29 | "%matplotlib inline\n", 30 | "import matplotlib.pyplot as plt\n", 31 | "from IPython.display import Image\n", 32 | "MIN_REQ_PYTHON = (3,6)\n", 33 | "if sys.version_info < MIN_REQ_PYTHON:\n", 34 | " print('Check the Kernel->Change Kernel menu and ensure that Python 3.6')\n", 35 | " print('or later is selected as the active kernel.')\n", 36 | " sys.exit(\"Python %s.%s or later is required.\\n\" % MIN_REQ_PYTHON)\n", 37 | "\n", 38 | "#imports\n", 39 | "import yaml\n", 40 | "import msticpy.nbtools as nbtools\n", 41 | "\n", 42 | "#data library imports\n", 43 | "from msticpy.data.data_providers import QueryProvider\n", 44 | "import msticpy.data.data_query_reader as QueryReader\n", 45 | "from msticpy.data.param_extractor import extract_query_params\n", 46 | "import msticpy.nbtools as mas\n", 47 | "from msticpy.nbtools import *\n", 48 | "from msticpy.sectools import *\n", 49 | "\n", 50 | "from msticpy.common.wsconfig import WorkspaceConfig # workspace configurations stored in msticpyconfig.yaml\n", 51 | "from msticpy.sectools.geoip import GeoLiteLookup # Maxmind GeoCityLite database stored in ~/.msticpy/\n", 52 | "geoip = GeoLiteLookup()\n", 53 | "\n", 54 | "import vt # virus total api\n", 55 | "import shodan \n", 56 | "import datetime\n", 57 | "from datetime import datetime, timedelta\n", 58 | "\n", 59 | "import pandas as pd\n", 60 | "import numpy as np\n", 61 | "import random\n", 62 | "import pickle\n", 63 | "\n", 64 | "from sklearn.model_selection import train_test_split\n", 65 | "from sklearn.feature_extraction.text import TfidfVectorizer\n", 66 | "from sklearn.linear_model import LogisticRegression,BayesianRidge\n", 67 | "\n", 68 | "\n", 69 | "# Import module for progress bars\n", 70 | "from tqdm.notebook import tqdm, trange\n", 71 | "\n", 72 | "# Import the modules needed to create an interactive data frame with IPyWidgets\n", 73 | "import ipywidgets as widgets\n", 74 | "from ipywidgets import interactive\n", 75 | "\n", 76 | "print('Imports Complete! If there are no errors reported, continue on!')" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": 2, 82 | "metadata": { 83 | "jupyter": { 84 | "source_hidden": true 85 | } 86 | }, 87 | "outputs": [], 88 | "source": [ 89 | "def hierarchy_pos(G, root=None, width=10, vert_gap = 0.2, vert_loc = 0, xcenter = .5):\n", 90 | "\n", 91 | " '''\n", 92 | " From Joel's answer at https://stackoverflow.com/a/29597209/2966723. \n", 93 | " Licensed under Creative Commons Attribution-Share Alike \n", 94 | " \n", 95 | " If the graph is a tree this will return the positions to plot this in a \n", 96 | " hierarchical layout.\n", 97 | " \n", 98 | " G: the graph (must be a tree)\n", 99 | " \n", 100 | " root: the root node of current branch \n", 101 | " - if the tree is directed and this is not given, \n", 102 | " the root will be found and used\n", 103 | " - if the tree is directed and this is given, then \n", 104 | " the positions will be just for the descendants of this node.\n", 105 | " - if the tree is undirected and not given, \n", 106 | " then a random choice will be used.\n", 107 | " \n", 108 | " width: horizontal space allocated for this branch - avoids overlap with other branches\n", 109 | " \n", 110 | " vert_gap: gap between levels of hierarchy\n", 111 | " \n", 112 | " vert_loc: vertical location of root\n", 113 | " \n", 114 | " xcenter: horizontal location of root\n", 115 | " '''\n", 116 | " if not nx.is_tree(G):\n", 117 | " raise TypeError('cannot use hierarchy_pos on a graph that is not a tree')\n", 118 | "\n", 119 | " if root is None:\n", 120 | " if isinstance(G, nx.DiGraph):\n", 121 | " root = next(iter(nx.topological_sort(G))) #allows back compatibility with nx version 1.11\n", 122 | " else:\n", 123 | " root = random.choice(list(G.nodes))\n", 124 | "\n", 125 | " def _hierarchy_pos(G, root, width=.1, vert_gap = 0.5, vert_loc = 0, xcenter = .1, pos = None, parent = None):\n", 126 | " '''\n", 127 | " see hierarchy_pos docstring for most arguments\n", 128 | "\n", 129 | " pos: a dict saying where all nodes go if they have been assigned\n", 130 | " parent: parent of this branch. - only affects it if non-directed\n", 131 | "\n", 132 | " '''\n", 133 | " \n", 134 | " if pos is None:\n", 135 | " pos = {root:(xcenter,vert_loc)}\n", 136 | " else:\n", 137 | " pos[root] = (xcenter, vert_loc)\n", 138 | " children = list(G.neighbors(root))\n", 139 | " if not isinstance(G, nx.DiGraph) and parent is not None:\n", 140 | " children.remove(parent) \n", 141 | " if len(children)!=0:\n", 142 | " dx = width/len(children) \n", 143 | " nextx = xcenter - width/2 - dx/2\n", 144 | " for child in children:\n", 145 | " nextx += dx\n", 146 | " pos = _hierarchy_pos(G,child, width = dx, vert_gap = vert_gap, \n", 147 | " vert_loc = vert_loc-vert_gap, xcenter=nextx,\n", 148 | " pos=pos, parent = root)\n", 149 | " return pos\n", 150 | "\n", 151 | " \n", 152 | " return _hierarchy_pos(G, root, width, vert_gap, vert_loc, xcenter)" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": 3, 158 | "metadata": { 159 | "collapsed": true, 160 | "jupyter": { 161 | "outputs_hidden": true 162 | } 163 | }, 164 | "outputs": [ 165 | { 166 | "name": "stdout", 167 | "output_type": "stream", 168 | "text": [ 169 | "Please wait. Loading Kqlmagic extension...\n" 170 | ] 171 | }, 172 | { 173 | "data": { 174 | "application/javascript": [ 175 | "try {IPython.notebook.kernel.reconnect();} catch(err) {;}" 176 | ], 177 | "text/plain": [ 178 | "" 179 | ] 180 | }, 181 | "metadata": {}, 182 | "output_type": "display_data" 183 | }, 184 | { 185 | "data": { 186 | "application/javascript": [ 187 | "try {IPython.notebook.kernel.execute(\"NOTEBOOK_URL = '\" + window.location + \"'\");} catch(err) {;}" 188 | ], 189 | "text/plain": [ 190 | "" 191 | ] 192 | }, 193 | "metadata": {}, 194 | "output_type": "display_data" 195 | }, 196 | { 197 | "data": { 198 | "text/html": [ 199 | "\n", 200 | " \n", 201 | " Kqlmagic - banner\n", 202 | " \n", 214 | " \n", 215 | " \n", 216 | "
\n", 217 | "
\n", 218 | "
\n", 219 | "

Kql Query Language, aka kql, is the query language for advanced analytics on Azure Monitor resources. The current supported data sources are \n", 220 | " Azure Data Explorer (Kusto), Log Analytics and Application Insights. To get more information execute '%kql --help \"kql\"'

\n", 221 | "

\n", 222 | " • \n", 223 | " kql reference: Click on 'Help' tab > and Select 'kql reference' or execute '%kql --help \"kql\"'
\n", 224 | " • \n", 225 | " Kqlmagic configuration: execute '%config Kqlmagic'
\n", 226 | " • \n", 227 | " Kqlmagic usage: execute '%kql --usage'
\n", 228 | "

\n", 229 | "
\n", 230 | "
\n", 231 | " \n", 232 | " " 233 | ], 234 | "text/plain": [ 235 | "" 236 | ] 237 | }, 238 | "metadata": {}, 239 | "output_type": "display_data" 240 | }, 241 | { 242 | "data": { 243 | "text/html": [ 244 | "\n", 245 | " \n", 246 | " \n", 247 | " \n", 248 | " \n", 249 | " \n", 250 | "

Kqlmagic package is updated frequently. Run '!pip install Kqlmagic --no-cache-dir --upgrade' to use the latest version.
Kqlmagic version: 0.1.113.post1, source: https://github.com/Microsoft/jupyter-Kqlmagic

\n", 251 | " \n", 252 | " " 253 | ], 254 | "text/plain": [ 255 | "" 256 | ] 257 | }, 258 | "metadata": {}, 259 | "output_type": "display_data" 260 | }, 261 | { 262 | "data": { 263 | "text/html": [ 264 | "\n", 265 | " \n", 266 | "
\n", 267 | " Click to find out what's new in Kqlmagic \n", 268 | " \n", 272 | "
\n", 273 | "\n", 274 | " \n", 358 | "\n", 359 | " " 360 | ], 361 | "text/plain": [ 362 | "" 363 | ] 364 | }, 365 | "metadata": {}, 366 | "output_type": "display_data" 367 | }, 368 | { 369 | "data": { 370 | "application/javascript": [ 371 | "try {IPython.notebook.kernel.execute(\"NOTEBOOK_URL = '\" + window.location + \"'\");} catch(err) {;}" 372 | ], 373 | "text/plain": [ 374 | "" 375 | ] 376 | }, 377 | "metadata": {}, 378 | "output_type": "display_data" 379 | }, 380 | { 381 | "data": { 382 | "text/html": [ 383 | "\n", 384 | " \n", 385 | "\n", 386 | " \n", 387 | "\n", 388 | " \n", 389 | "\n", 390 | " \n", 391 | "\n", 392 | " \n", 415 | "\n", 416 | " " 417 | ], 418 | "text/plain": [ 419 | "" 420 | ] 421 | }, 422 | "metadata": {}, 423 | "output_type": "display_data" 424 | }, 425 | { 426 | "data": { 427 | "text/html": [ 428 | "\n", 429 | " " 444 | ], 445 | "text/plain": [ 446 | "" 447 | ] 448 | }, 449 | "metadata": {}, 450 | "output_type": "display_data" 451 | }, 452 | { 453 | "data": { 454 | "text/html": [ 455 | "\n", 456 | " \n", 457 | "
\n", 458 | " \n", 459 | " \n", 463 | "
\n", 464 | "\n", 465 | " \n", 549 | "\n", 550 | " " 551 | ], 552 | "text/plain": [ 553 | "" 554 | ] 555 | }, 556 | "metadata": {}, 557 | "output_type": "display_data" 558 | }, 559 | { 560 | "data": { 561 | "application/javascript": [ 562 | "try {IPython.notebook.kernel.execute(\"NOTEBOOK_URL = '\" + window.location + \"'\");} catch(err) {;}" 563 | ], 564 | "text/plain": [ 565 | "" 566 | ] 567 | }, 568 | "metadata": {}, 569 | "output_type": "display_data" 570 | } 571 | ], 572 | "source": [ 573 | "## Create a QueryProvider object for running queries in our LogAnalytics workspace\n", 574 | "qry_prov = QueryProvider(data_environment='LogAnalytics')\n", 575 | "## Use a workspace configuration with the name in msticpyconfig.yaml (you can also choose another workspace here)\n", 576 | "ws_config = WorkspaceConfig(workspace=\"Default\")\n", 577 | "qry_prov.connect(connection_str=ws_config.code_connect_str)" 578 | ] 579 | }, 580 | { 581 | "cell_type": "markdown", 582 | "metadata": {}, 583 | "source": [ 584 | "# Parent Child Relationship Tree\n", 585 | "\n", 586 | "With a collection of identifiers for processes and their parents, we can visualize the relationship through a directed graph which can be viewed to form a hierarchical graph. Focusing on a set of related processes can determine what actions, child processes, or network connections a singular process made in a given period." 587 | ] 588 | }, 589 | { 590 | "cell_type": "markdown", 591 | "metadata": {}, 592 | "source": [ 593 | "## 1a. Query \n", 594 | "\n", 595 | "It should be noted that this query can be changed to any EventID as so long as the writer\n", 596 | "can make the necessary changes in section 1c. In this case, we are focused on the parent-child relationship\n", 597 | "between processes. The process in this case we are focused in on has the GUID of {8b54b755-561e-604f-c263-000000002a00}.\n", 598 | "The query will pull every process who has the parent GUID of {8b54b755-561e-604f-c263-000000002a00} and return:\n", 599 | "- Time \n", 600 | "- New child process path\n", 601 | "- New child process command line\n", 602 | "- New child process ID\n", 603 | "- New child process GUID\n", 604 | "- Parent process ID\n", 605 | "- Parent process GUID" 606 | ] 607 | }, 608 | { 609 | "cell_type": "code", 610 | "execution_count": 61, 611 | "metadata": {}, 612 | "outputs": [ 613 | { 614 | "data": { 615 | "application/javascript": [ 616 | "try {IPython.notebook.kernel.execute(\"NOTEBOOK_URL = '\" + window.location + \"'\");} catch(err) {;}" 617 | ], 618 | "text/plain": [ 619 | "" 620 | ] 621 | }, 622 | "metadata": {}, 623 | "output_type": "display_data" 624 | } 625 | ], 626 | "source": [ 627 | "pid_query = '''\n", 628 | "Sysmon\n", 629 | "| where EventID == 1\n", 630 | "| sort by TimeGenerated desc\n", 631 | "| where InitiatingProcessGuid == \"{8b54b755-561e-604f-c263-000000002a00}\"\n", 632 | "| project TimeGenerated, ProcessPath, ProcessCommandLine, ProcessId ,ProcessGuid, InitiatingProcessFile,InitiatingProcessGuid\n", 633 | "'''\n", 634 | "ProcessSearch = qry_prov.exec_query(query = pid_query)\n" 635 | ] 636 | }, 637 | { 638 | "cell_type": "markdown", 639 | "metadata": {}, 640 | "source": [ 641 | "## 1b. Results\n", 642 | "\n", 643 | "As we can see, there are several different child processes spawned by {8b54b755-561e-604f-c263-000000002a00}, and we need to get a better grasp as to what is going on." 644 | ] 645 | }, 646 | { 647 | "cell_type": "code", 648 | "execution_count": 62, 649 | "metadata": {}, 650 | "outputs": [ 651 | { 652 | "data": { 653 | "text/html": [ 654 | "
\n", 655 | "\n", 668 | "\n", 669 | " \n", 670 | " \n", 671 | " \n", 672 | " \n", 673 | " \n", 674 | " \n", 675 | " \n", 676 | " \n", 677 | " \n", 678 | " \n", 679 | " \n", 680 | " \n", 681 | " \n", 682 | " \n", 683 | " \n", 684 | " \n", 685 | " \n", 686 | " \n", 687 | " \n", 688 | " \n", 689 | " \n", 690 | " \n", 691 | " \n", 692 | " \n", 693 | " \n", 694 | " \n", 695 | " \n", 696 | " \n", 697 | " \n", 698 | " \n", 699 | " \n", 700 | " \n", 701 | " \n", 702 | " \n", 703 | " \n", 704 | " \n", 705 | " \n", 706 | " \n", 707 | " \n", 708 | " \n", 709 | " \n", 710 | " \n", 711 | " \n", 712 | " \n", 713 | " \n", 714 | " \n", 715 | " \n", 716 | " \n", 717 | " \n", 718 | " \n", 719 | " \n", 720 | " \n", 721 | " \n", 722 | " \n", 723 | " \n", 724 | " \n", 725 | " \n", 726 | " \n", 727 | " \n", 728 | " \n", 729 | " \n", 730 | " \n", 731 | " \n", 732 | " \n", 733 | " \n", 734 | " \n", 735 | " \n", 736 | " \n", 737 | " \n", 738 | " \n", 739 | " \n", 740 | " \n", 741 | " \n", 742 | " \n", 743 | " \n", 744 | " \n", 745 | " \n", 746 | " \n", 747 | " \n", 748 | " \n", 749 | " \n", 750 | " \n", 751 | " \n", 752 | " \n", 753 | " \n", 754 | " \n", 755 | " \n", 756 | " \n", 757 | " \n", 758 | " \n", 759 | " \n", 760 | " \n", 761 | " \n", 762 | " \n", 763 | " \n", 764 | " \n", 765 | " \n", 766 | " \n", 767 | " \n", 768 | " \n", 769 | " \n", 770 | " \n", 771 | " \n", 772 | " \n", 773 | " \n", 774 | " \n", 775 | " \n", 776 | " \n", 777 | " \n", 778 | " \n", 779 | " \n", 780 | " \n", 781 | " \n", 782 | " \n", 783 | " \n", 784 | " \n", 785 | " \n", 786 | " \n", 787 | " \n", 788 | " \n", 789 | " \n", 790 | " \n", 791 | " \n", 792 | " \n", 793 | " \n", 794 | " \n", 795 | " \n", 796 | " \n", 797 | " \n", 798 | " \n", 799 | " \n", 800 | " \n", 801 | " \n", 802 | " \n", 803 | " \n", 804 | " \n", 805 | " \n", 806 | " \n", 807 | " \n", 808 | " \n", 809 | " \n", 810 | " \n", 811 | " \n", 812 | " \n", 813 | "
TimeGeneratedProcessPathProcessCommandLineProcessIdProcessGuidInitiatingProcessFileInitiatingProcessGuid
02021-03-15 13:08:47.130000+00:00C:\\Windows\\System32\\cmd.exeC:\\WINDOWS\\system32\\cmd.exe /C psexec.exe -acc...10376{8b54b755-5c5f-604f-7164-000000002a00}C:\\Windows\\SysWOW64\\rundll32.exe{8b54b755-561e-604f-c263-000000002a00}
12021-03-15 13:08:44.443000+00:00C:\\Windows\\System32\\cmd.exeC:\\WINDOWS\\system32\\cmd.exe /C copy 19560.dll ...4636{8b54b755-5c5c-604f-6f64-000000002a00}C:\\Windows\\SysWOW64\\rundll32.exe{8b54b755-561e-604f-c263-000000002a00}
22021-03-15 13:06:58.260000+00:00C:\\Windows\\System32\\cmd.exeC:\\WINDOWS\\system32\\cmd.exe /C net group \"Doma...5320{8b54b755-5bf2-604f-5d64-000000002a00}C:\\Windows\\SysWOW64\\rundll32.exe{8b54b755-561e-604f-c263-000000002a00}
32021-03-15 13:05:26.180000+00:00C:\\Windows\\System32\\cmd.exeC:\\WINDOWS\\system32\\cmd.exe /C nltest /dclist:13896{8b54b755-5b96-604f-4064-000000002a00}C:\\Windows\\SysWOW64\\rundll32.exe{8b54b755-561e-604f-c263-000000002a00}
42021-03-15 13:05:05.317000+00:00C:\\Windows\\System32\\cmd.exeC:\\WINDOWS\\system32\\cmd.exe /C c:\\windows\\sysn...7180{8b54b755-5b81-604f-3c64-000000002a00}C:\\Windows\\SysWOW64\\rundll32.exe{8b54b755-561e-604f-c263-000000002a00}
52021-03-15 13:04:23.320000+00:00C:\\Windows\\SysWOW64\\cmd.exeC:\\WINDOWS\\system32\\cmd.exe /C whoami13840{8b54b755-5b57-604f-3864-000000002a00}C:\\Windows\\SysWOW64\\rundll32.exe{8b54b755-561e-604f-c263-000000002a00}
62021-03-15 12:58:12.520000+00:00C:\\Windows\\SysWOW64\\cmd.exeC:\\WINDOWS\\system32\\cmd.exe /C c:\\windows\\sysn...10924{8b54b755-59e4-604f-1d64-000000002a00}C:\\Windows\\SysWOW64\\rundll32.exe{8b54b755-561e-604f-c263-000000002a00}
72021-03-15 12:55:49.230000+00:00C:\\Windows\\SysWOW64\\cmd.exeC:\\WINDOWS\\system32\\cmd.exe /C nltest /domain_...8596{8b54b755-5955-604f-1064-000000002a00}C:\\Windows\\SysWOW64\\rundll32.exe{8b54b755-561e-604f-c263-000000002a00}
82021-03-15 12:50:20.080000+00:00C:\\Windows\\SysWOW64\\cmd.exeC:\\WINDOWS\\system32\\cmd.exe /C net group \"Ente...7076{8b54b755-580c-604f-f963-000000002a00}C:\\Windows\\SysWOW64\\rundll32.exe{8b54b755-561e-604f-c263-000000002a00}
92021-03-15 12:50:19.637000+00:00C:\\Windows\\SysWOW64\\cmd.exeC:\\WINDOWS\\system32\\cmd.exe /C net group \"Doma...7368{8b54b755-580b-604f-f563-000000002a00}C:\\Windows\\SysWOW64\\rundll32.exe{8b54b755-561e-604f-c263-000000002a00}
102021-03-15 12:50:19.460000+00:00C:\\Windows\\SysWOW64\\cmd.exeC:\\WINDOWS\\system32\\cmd.exe /C nltest /domain_...13900{8b54b755-580b-604f-f363-000000002a00}C:\\Windows\\SysWOW64\\rundll32.exe{8b54b755-561e-604f-c263-000000002a00}
112021-03-15 12:50:18.887000+00:00C:\\Windows\\SysWOW64\\cmd.exeC:\\WINDOWS\\system32\\cmd.exe /C nltest /dclist:10980{8b54b755-580a-604f-f163-000000002a00}C:\\Windows\\SysWOW64\\rundll32.exe{8b54b755-561e-604f-c263-000000002a00}
122021-03-15 12:47:00.610000+00:00C:\\Windows\\SysWOW64\\cmd.exeC:\\WINDOWS\\system32\\cmd.exe /C gpupdate /force2304{8b54b755-5744-604f-d163-000000002a00}C:\\Windows\\SysWOW64\\rundll32.exe{8b54b755-561e-604f-c263-000000002a00}
\n", 814 | "
" 815 | ], 816 | "text/plain": [ 817 | " TimeGenerated ProcessPath \\\n", 818 | "0 2021-03-15 13:08:47.130000+00:00 C:\\Windows\\System32\\cmd.exe \n", 819 | "1 2021-03-15 13:08:44.443000+00:00 C:\\Windows\\System32\\cmd.exe \n", 820 | "2 2021-03-15 13:06:58.260000+00:00 C:\\Windows\\System32\\cmd.exe \n", 821 | "3 2021-03-15 13:05:26.180000+00:00 C:\\Windows\\System32\\cmd.exe \n", 822 | "4 2021-03-15 13:05:05.317000+00:00 C:\\Windows\\System32\\cmd.exe \n", 823 | "5 2021-03-15 13:04:23.320000+00:00 C:\\Windows\\SysWOW64\\cmd.exe \n", 824 | "6 2021-03-15 12:58:12.520000+00:00 C:\\Windows\\SysWOW64\\cmd.exe \n", 825 | "7 2021-03-15 12:55:49.230000+00:00 C:\\Windows\\SysWOW64\\cmd.exe \n", 826 | "8 2021-03-15 12:50:20.080000+00:00 C:\\Windows\\SysWOW64\\cmd.exe \n", 827 | "9 2021-03-15 12:50:19.637000+00:00 C:\\Windows\\SysWOW64\\cmd.exe \n", 828 | "10 2021-03-15 12:50:19.460000+00:00 C:\\Windows\\SysWOW64\\cmd.exe \n", 829 | "11 2021-03-15 12:50:18.887000+00:00 C:\\Windows\\SysWOW64\\cmd.exe \n", 830 | "12 2021-03-15 12:47:00.610000+00:00 C:\\Windows\\SysWOW64\\cmd.exe \n", 831 | "\n", 832 | " ProcessCommandLine ProcessId \\\n", 833 | "0 C:\\WINDOWS\\system32\\cmd.exe /C psexec.exe -acc... 10376 \n", 834 | "1 C:\\WINDOWS\\system32\\cmd.exe /C copy 19560.dll ... 4636 \n", 835 | "2 C:\\WINDOWS\\system32\\cmd.exe /C net group \"Doma... 5320 \n", 836 | "3 C:\\WINDOWS\\system32\\cmd.exe /C nltest /dclist: 13896 \n", 837 | "4 C:\\WINDOWS\\system32\\cmd.exe /C c:\\windows\\sysn... 7180 \n", 838 | "5 C:\\WINDOWS\\system32\\cmd.exe /C whoami 13840 \n", 839 | "6 C:\\WINDOWS\\system32\\cmd.exe /C c:\\windows\\sysn... 10924 \n", 840 | "7 C:\\WINDOWS\\system32\\cmd.exe /C nltest /domain_... 8596 \n", 841 | "8 C:\\WINDOWS\\system32\\cmd.exe /C net group \"Ente... 7076 \n", 842 | "9 C:\\WINDOWS\\system32\\cmd.exe /C net group \"Doma... 7368 \n", 843 | "10 C:\\WINDOWS\\system32\\cmd.exe /C nltest /domain_... 13900 \n", 844 | "11 C:\\WINDOWS\\system32\\cmd.exe /C nltest /dclist: 10980 \n", 845 | "12 C:\\WINDOWS\\system32\\cmd.exe /C gpupdate /force 2304 \n", 846 | "\n", 847 | " ProcessGuid InitiatingProcessFile \\\n", 848 | "0 {8b54b755-5c5f-604f-7164-000000002a00} C:\\Windows\\SysWOW64\\rundll32.exe \n", 849 | "1 {8b54b755-5c5c-604f-6f64-000000002a00} C:\\Windows\\SysWOW64\\rundll32.exe \n", 850 | "2 {8b54b755-5bf2-604f-5d64-000000002a00} C:\\Windows\\SysWOW64\\rundll32.exe \n", 851 | "3 {8b54b755-5b96-604f-4064-000000002a00} C:\\Windows\\SysWOW64\\rundll32.exe \n", 852 | "4 {8b54b755-5b81-604f-3c64-000000002a00} C:\\Windows\\SysWOW64\\rundll32.exe \n", 853 | "5 {8b54b755-5b57-604f-3864-000000002a00} C:\\Windows\\SysWOW64\\rundll32.exe \n", 854 | "6 {8b54b755-59e4-604f-1d64-000000002a00} C:\\Windows\\SysWOW64\\rundll32.exe \n", 855 | "7 {8b54b755-5955-604f-1064-000000002a00} C:\\Windows\\SysWOW64\\rundll32.exe \n", 856 | "8 {8b54b755-580c-604f-f963-000000002a00} C:\\Windows\\SysWOW64\\rundll32.exe \n", 857 | "9 {8b54b755-580b-604f-f563-000000002a00} C:\\Windows\\SysWOW64\\rundll32.exe \n", 858 | "10 {8b54b755-580b-604f-f363-000000002a00} C:\\Windows\\SysWOW64\\rundll32.exe \n", 859 | "11 {8b54b755-580a-604f-f163-000000002a00} C:\\Windows\\SysWOW64\\rundll32.exe \n", 860 | "12 {8b54b755-5744-604f-d163-000000002a00} C:\\Windows\\SysWOW64\\rundll32.exe \n", 861 | "\n", 862 | " InitiatingProcessGuid \n", 863 | "0 {8b54b755-561e-604f-c263-000000002a00} \n", 864 | "1 {8b54b755-561e-604f-c263-000000002a00} \n", 865 | "2 {8b54b755-561e-604f-c263-000000002a00} \n", 866 | "3 {8b54b755-561e-604f-c263-000000002a00} \n", 867 | "4 {8b54b755-561e-604f-c263-000000002a00} \n", 868 | "5 {8b54b755-561e-604f-c263-000000002a00} \n", 869 | "6 {8b54b755-561e-604f-c263-000000002a00} \n", 870 | "7 {8b54b755-561e-604f-c263-000000002a00} \n", 871 | "8 {8b54b755-561e-604f-c263-000000002a00} \n", 872 | "9 {8b54b755-561e-604f-c263-000000002a00} \n", 873 | "10 {8b54b755-561e-604f-c263-000000002a00} \n", 874 | "11 {8b54b755-561e-604f-c263-000000002a00} \n", 875 | "12 {8b54b755-561e-604f-c263-000000002a00} " 876 | ] 877 | }, 878 | "execution_count": 62, 879 | "metadata": {}, 880 | "output_type": "execute_result" 881 | } 882 | ], 883 | "source": [ 884 | "ProcessSearch" 885 | ] 886 | }, 887 | { 888 | "cell_type": "markdown", 889 | "metadata": {}, 890 | "source": [ 891 | "# 2. Visualization with Networkx and Matplotlib" 892 | ] 893 | }, 894 | { 895 | "cell_type": "markdown", 896 | "metadata": {}, 897 | "source": [ 898 | "## 2a. Compile the results into a single dictionary\n" 899 | ] 900 | }, 901 | { 902 | "cell_type": "code", 903 | "execution_count": 65, 904 | "metadata": {}, 905 | "outputs": [], 906 | "source": [ 907 | "ParentChildRelationship = []\n", 908 | "ParentBuffer = []\n", 909 | "ChildBuffer = []\n", 910 | "Child = ProcessSearch[\"ProcessId\"].tolist() # Convert from a Pandas series to a list\n", 911 | "Parent = ProcessSearch[\"InitiatingProcessGuid\"].tolist()\n", 912 | "RelationshipBuffer = dict(zip(\n", 913 | " Child,Parent\n", 914 | " ))" 915 | ] 916 | }, 917 | { 918 | "cell_type": "markdown", 919 | "metadata": {}, 920 | "source": [ 921 | "## 2b. Create a list of parent processes\n" 922 | ] 923 | }, 924 | { 925 | "cell_type": "code", 926 | "execution_count": 66, 927 | "metadata": {}, 928 | "outputs": [], 929 | "source": [ 930 | "for ParentGUID in RelationshipBuffer.values(): # Children are the keys and parents are the values for the Python dictionary\n", 931 | " if ParentGUID not in ParentBuffer: # If a parent is not in the parent buffer, add them\n", 932 | " ParentBuffer.append(ParentGUID)" 933 | ] 934 | }, 935 | { 936 | "cell_type": "markdown", 937 | "metadata": {}, 938 | "source": [ 939 | "## 2c. Enumerate ParentBuffer to create a new relationship list built of tuples \n", 940 | "> Creating a list of tuples ensures that NetworkX can read each of the parents and children as nodes and the process creation and edges" 941 | ] 942 | }, 943 | { 944 | "cell_type": "code", 945 | "execution_count": 67, 946 | "metadata": {}, 947 | "outputs": [], 948 | "source": [ 949 | "for Position,ParentProcessValue in enumerate(ParentBuffer):\n", 950 | " for Key,DictValue in RelationshipBuffer.items():\n", 951 | " if ParentProcessValue == DictValue:\n", 952 | " ParentChildRelationship.append((ParentProcessValue,Key))" 953 | ] 954 | }, 955 | { 956 | "cell_type": "markdown", 957 | "metadata": {}, 958 | "source": [ 959 | "# 2d. Visualizng the Graph with NetworkX and Malplotlib" 960 | ] 961 | }, 962 | { 963 | "cell_type": "code", 964 | "execution_count": 68, 965 | "metadata": {}, 966 | "outputs": [ 967 | { 968 | "data": { 969 | "image/png": "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\n", 970 | "text/plain": [ 971 | "
" 972 | ] 973 | }, 974 | "metadata": {}, 975 | "output_type": "display_data" 976 | } 977 | ], 978 | "source": [ 979 | "G=nx.DiGraph()\n", 980 | "G.add_edges_from(ParentChildRelationship)\n", 981 | "pos = hierarchy_pos(G)\n", 982 | "plt.figure(figsize=(13, 6)) \n", 983 | "nx.draw_networkx_nodes(G, pos, node_shape=\"None\")\n", 984 | "nx.draw_networkx_edges(G, pos)\n", 985 | "nx.draw_networkx_labels(G, pos)\n", 986 | "plt.show()" 987 | ] 988 | }, 989 | { 990 | "cell_type": "markdown", 991 | "metadata": {}, 992 | "source": [ 993 | "# 3. Visualization with command lines" 994 | ] 995 | }, 996 | { 997 | "cell_type": "markdown", 998 | "metadata": {}, 999 | "source": [ 1000 | "## 3b. Compile the results into a single dictionary" 1001 | ] 1002 | }, 1003 | { 1004 | "cell_type": "code", 1005 | "execution_count": 70, 1006 | "metadata": {}, 1007 | "outputs": [], 1008 | "source": [ 1009 | "ChildCommandLine = ProcessSearch[\"ProcessCommandLine\"].tolist()\n", 1010 | "ChildProcID = ProcessSearch[\"ProcessId\"].tolist()\n", 1011 | "Child_Info = [(ChildCommandLine[i], ChildProcID[i]) for i in range(0, len(Child))]\n", 1012 | "ParentGuid = ProcessSearch[\"InitiatingProcessGuid\"].tolist()\n", 1013 | "RelationshipBuffer = dict(zip(\n", 1014 | " Child_Info,ParentGuid\n", 1015 | " ))" 1016 | ] 1017 | }, 1018 | { 1019 | "cell_type": "markdown", 1020 | "metadata": {}, 1021 | "source": [ 1022 | "## 3c. Create a list of parent processes" 1023 | ] 1024 | }, 1025 | { 1026 | "cell_type": "code", 1027 | "execution_count": 71, 1028 | "metadata": {}, 1029 | "outputs": [], 1030 | "source": [ 1031 | "ParentChildRelationshipDictionary = {}\n", 1032 | "ParentBuffer = []\n", 1033 | "\n", 1034 | "for i in RelationshipBuffer.values():\n", 1035 | " if i not in ParentBuffer:\n", 1036 | " ParentBuffer.append(i)\n", 1037 | "\n", 1038 | "for Position,ParentProcessValue in enumerate(ParentBuffer):\n", 1039 | " ChildBuffer = []\n", 1040 | " for Key,DictValue in RelationshipBuffer.items():\n", 1041 | " if ParentProcessValue == DictValue:\n", 1042 | " ChildBuffer.append(Key)\n", 1043 | " ParentChildRelationshipDictionary.update({ParentProcessValue:ChildBuffer})\n" 1044 | ] 1045 | }, 1046 | { 1047 | "cell_type": "markdown", 1048 | "metadata": {}, 1049 | "source": [ 1050 | "## 3d. Visualize command lines with TreeLib\n", 1051 | "```\n", 1052 | "Parent GUID\n", 1053 | "├── Child Proc ID\n", 1054 | " └── Child Commandline\n", 1055 | "```" 1056 | ] 1057 | }, 1058 | { 1059 | "cell_type": "code", 1060 | "execution_count": 72, 1061 | "metadata": {}, 1062 | "outputs": [ 1063 | { 1064 | "name": "stdout", 1065 | "output_type": "stream", 1066 | "text": [ 1067 | "{8b54b755-561e-604f-c263-000000002a00}\n", 1068 | "├── 10376\n", 1069 | "│ └── C:\\WINDOWS\\system32\\cmd.exe /C psexec.exe -accepteula -d -s \\\\[redacted]-dc1 rundll32.exe c:\\windows\\19560.dll,StartW\n", 1070 | "├── 10924\n", 1071 | "│ └── C:\\WINDOWS\\system32\\cmd.exe /C c:\\windows\\sysnative\\nltest /domain_trusts\n", 1072 | "├── 10980\n", 1073 | "│ └── C:\\WINDOWS\\system32\\cmd.exe /C nltest /dclist:\n", 1074 | "├── 13840\n", 1075 | "│ └── C:\\WINDOWS\\system32\\cmd.exe /C whoami\n", 1076 | "├── 13896\n", 1077 | "│ └── C:\\WINDOWS\\system32\\cmd.exe /C nltest /dclist:\n", 1078 | "├── 13900\n", 1079 | "│ └── C:\\WINDOWS\\system32\\cmd.exe /C nltest /domain_trusts\n", 1080 | "├── 2304\n", 1081 | "│ └── C:\\WINDOWS\\system32\\cmd.exe /C gpupdate /force\n", 1082 | "├── 4636\n", 1083 | "│ └── C:\\WINDOWS\\system32\\cmd.exe /C copy 19560.dll \\\\[redacted]-dc1\\ADMIN$\\ /Y /Z\n", 1084 | "├── 5320\n", 1085 | "│ └── C:\\WINDOWS\\system32\\cmd.exe /C net group \"Domain Admins\" /dom\n", 1086 | "├── 7076\n", 1087 | "│ └── C:\\WINDOWS\\system32\\cmd.exe /C net group \"Enterprise admins\" /domain\n", 1088 | "├── 7180\n", 1089 | "│ └── C:\\WINDOWS\\system32\\cmd.exe /C c:\\windows\\sysnative\\nltest /dclist:\n", 1090 | "├── 7368\n", 1091 | "│ └── C:\\WINDOWS\\system32\\cmd.exe /C net group \"Domain admins\" /domain\n", 1092 | "└── 8596\n", 1093 | " └── C:\\WINDOWS\\system32\\cmd.exe /C nltest /domain_trusts\n", 1094 | "\n" 1095 | ] 1096 | } 1097 | ], 1098 | "source": [ 1099 | "from treelib import Node, Tree\n", 1100 | "Tree = Tree()\n", 1101 | "RootNode=\"\"\n", 1102 | "for Key,DictValue in RelationshipBuffer.items():\n", 1103 | " RootNode = DictValue\n", 1104 | "Tree.create_node(str(RootNode),str(RootNode)) # No parent means its the root node and there is only one key\n", 1105 | "for index in ParentChildRelationshipDictionary.values():\n", 1106 | " for ChildInfo in index:\n", 1107 | " Tree.create_node(str(ChildInfo[1]),str(ChildInfo[1]), parent=RootNode)\n", 1108 | " Tree.create_node(str(ChildInfo[0]),parent=str(ChildInfo[1]))\n", 1109 | "Tree.show()" 1110 | ] 1111 | }, 1112 | { 1113 | "cell_type": "markdown", 1114 | "metadata": {}, 1115 | "source": [ 1116 | "\n" 1117 | ] 1118 | } 1119 | ], 1120 | "metadata": { 1121 | "kernelspec": { 1122 | "display_name": "PySpark_Python3", 1123 | "language": "python", 1124 | "name": "pyspark3" 1125 | }, 1126 | "language_info": { 1127 | "codemirror_mode": { 1128 | "name": "ipython", 1129 | "version": 3 1130 | }, 1131 | "file_extension": ".py", 1132 | "mimetype": "text/x-python", 1133 | "name": "python", 1134 | "nbconvert_exporter": "python", 1135 | "pygments_lexer": "ipython3", 1136 | "version": "3.7.8" 1137 | } 1138 | }, 1139 | "nbformat": 4, 1140 | "nbformat_minor": 4 1141 | } 1142 | --------------------------------------------------------------------------------