Skip to content
This repository was archived by the owner on Nov 16, 2023. It is now read-only.

Commit f8d1cb1

Browse files
authored
Merge pull request #189 from microsoft/mjmelone-patch-34
Create Episode 4 - Lets Hunt.csl
2 parents 6148746 + e551d25 commit f8d1cb1

File tree

1 file changed

+256
-0
lines changed

1 file changed

+256
-0
lines changed
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
print Series = 'Tracking the Adversary with MTP Advanced Hunting', EpisodeNumber = 4, Topic = 'Lets Hunt! Applying KQL to Incident Tracking', Presenter = 'Michael Melone, Tali Ash', Company = 'Microsoft'
2+
3+
4+
// Schema Reference (upper right corner)
5+
6+
7+
// The ABC's of Security
8+
// - Authentication
9+
// - Backdoors
10+
// - Communication
11+
// - Data
12+
// Authentication
13+
// - How is the attacker establishing identity to the system?
14+
// - What identities do we consider compromised?
15+
// - What are our administrative identities?
16+
// Backdoors
17+
// - How is the attacker controlling the system?
18+
// - Is the service used by the attacker legitimate or illegitimate?
19+
// - Where is this capability or condition present?
20+
// Communication
21+
// - How is the attacker communicating with the system?
22+
// Let's see what the malware fairy has brought us today...
23+
24+
AlertInfo
25+
| take 10
26+
27+
// AlertInfo
28+
// Table containing alerts identified by MTP. By itself does not have the entities and evidence
29+
// associated with the alert. To get that we will need the AlertEvidence table.
30+
31+
AlertEvidence
32+
| take 10
33+
34+
// AlertEvidence
35+
// Details about alerts including associated entities
36+
// Let's find out which of our accounts has the most alerts associated with them
37+
38+
AlertEvidence
39+
| where Timestamp > ago(19d) and EntityType == "User" and isnotempty(AccountObjectId) // Look for user entities
40+
| summarize Alerts = dcount(AlertId) by AccountObjectId, AccountName , AccountDomain
41+
| project Alerts, AccountDomain, AccountName, AccountObjectId
42+
| order by Alerts desc
43+
44+
// That's suspicious... Let's see what kinds of alerts these are...
45+
46+
AlertEvidence
47+
| where Timestamp > ago(19d) and EntityType == "User" and AccountObjectId == 'ab653b2a-d23e-49df-9493-c26590f8f319'
48+
| join kind=inner AlertInfo on AlertId
49+
| summarize Alerts = count(), First = min(Timestamp), Last = max(Timestamp) by Title
50+
| order by Alerts desc
51+
52+
// That doesn't look good. Let's find out when and where this happened...
53+
54+
AlertEvidence
55+
| where Timestamp > ago(19d) and AccountObjectId == 'ab653b2a-d23e-49df-9493-c26590f8f319' // associated with the suspicious account
56+
| join kind=rightsemi AlertEvidence on AlertId // rejoin with evidence...
57+
| where EntityType == 'Machine' // and get the machines.
58+
| join kind=leftouter (
59+
DeviceInfo
60+
| summarize DeviceName = any(DeviceName) by DeviceId // Get the device name
61+
) on DeviceId
62+
| summarize dcount(AlertId) by DeviceName , bin(Timestamp, 1d) // Plot it in 30 minute intervals
63+
| render timechart // Make a timechart
64+
65+
// OK! We have some boxes of interest and it looks like it started on barbaram-pc.
66+
// We can also see an uptick in activity on July 19th
67+
// Let's timeline alerts on barbaram-pc.
68+
69+
AlertEvidence
70+
| where Timestamp > ago(19d) and DeviceId == '87da11a9257988b2fc090c9f05c72f6453bc53de'
71+
| join kind=inner AlertInfo on AlertId
72+
| summarize min(Timestamp) by Title
73+
| order by min_Timestamp asc
74+
75+
// Looks like we detected something malicious from Office 365... Let's see what it was
76+
77+
AlertInfo
78+
| where Timestamp > ago(19d) and Title == 'Post-delivery detection of suspicious attachment'
79+
| join kind=rightsemi AlertEvidence on AlertId
80+
| where EntityType == 'File'
81+
82+
// OK, all of this JSON is great, but how about a table instead
83+
84+
AlertInfo
85+
| where Timestamp > ago(19d) and Title == 'Post-delivery detection of suspicious attachment'
86+
| join kind=rightsemi AlertEvidence on AlertId
87+
| where EntityType == 'File'
88+
| extend AFDynamic = parse_json(AdditionalFields) // Turn JSON into a dynamic column
89+
| evaluate bag_unpack(AFDynamic) // ...and turn the JSON into columns
90+
| project-reorder Name, Directory, Host, SHA256
91+
92+
// parse_json() - parses a JSON string and turns it into a dynamic
93+
// bag_unpack() - takes the first-level properties from a dynamic and promotes them to columns
94+
95+
// Looks like the file was called Doodles_SOW_07102020.doc...
96+
97+
DeviceProcessEvents
98+
| where Timestamp > ago(19d)
99+
and ProcessCommandLine contains 'UpdatedPolicy_SOW_07182020.doc'
100+
and AccountObjectId == 'ab653b2a-d23e-49df-9493-c26590f8f319'
101+
102+
103+
// ...and we can see that Barbara launched it. Process ID 10460
104+
105+
106+
// Looks like Barbara used Word to open it a couple times...
107+
// Let's see what happened when she opened it...
108+
109+
search in (DeviceProcessEvents, DeviceNetworkEvents, DeviceFileEvents, DeviceRegistryEvents, DeviceEvents )
110+
Timestamp > ago(19d)
111+
and DeviceId == '87da11a9257988b2fc090c9f05c72f6453bc53de'
112+
and InitiatingProcessId == 8060
113+
| where RegistryKey !contains @'\Software\Microsoft\Office\16.0\Common\Internet\Server Cache' // Filtering out cache registry key changes
114+
| order by Timestamp asc
115+
| project-reorder Timestamp, $table, ActionType, RemoteIP, RemoteUrl, FileName, SHA256, RegistryKey, RegistryValueData, ActionType, AdditionalFields
116+
117+
// Interesting. Word is allocating writable and executable memory right after launch, but
118+
// nothing too interesting otherwise.
119+
// ref: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntallocatevirtualmemory
120+
121+
// So that doc is on SharePoint. How did it get there?
122+
AppFileEvents
123+
| where Timestamp > ago(19d) and FileName =~ 'UpdatedPolicy_SOW_07182020.doc'
124+
| project-reorder Timestamp, ActionType, Application, FolderPath, IPAddress, Location, ISP
125+
| order by Timestamp asc
126+
127+
// Looks like we have a couple strange IPs interacting with the file: 178.32.124.142 and 51.83.139.56.
128+
// It was uploaded using Barbara's account - that's the Authentication
129+
// The "backdoor" is just a publicly available service (SharePoint)
130+
// The Communication channel are those IPs. Let's see what else was involved with them...
131+
132+
search Timestamp > ago(19d) and ('178.32.124.142' or '51.83.139.56')
133+
| project-reorder $table, Timestamp, AccountName, AccountDomain, ActionType, FileName, FolderPath
134+
135+
// ...looks like there was another doc uploaded from that same user and IP (BYODRegistration (1).docm).
136+
// Maybe we'll investigate that later.
137+
138+
// We also had a couple alerts. Let's dig deeper.
139+
140+
AlertEvidence
141+
| where RemoteIP in ('178.32.124.142', '51.83.139.56')
142+
| join kind=rightsemi AlertInfo on AlertId
143+
144+
// Aha! Those are our Tor addresses.
145+
146+
// So we know there was credential theft going on. Let's see what other accounts logged on
147+
// to that compromised system...
148+
149+
DeviceLogonEvents
150+
| where DeviceName == 'barbaram-pc.mtpdemos.net' and Timestamp > ago(19d) and ActionType == 'LogonSuccess'
151+
| where AccountDomain !in ('font driver host', 'window manager') // Ignoring internal system identities at the moment
152+
| extend Account = strcat(AccountDomain, '\\', AccountName )
153+
| summarize count() by Account, bin(Timestamp, 1h)
154+
| render timechart
155+
156+
// Interesting. What does Eric Gubbels do?
157+
158+
IdentityInfo
159+
| where GivenName =~ 'Eric' and Surname =~ "Gubbels"
160+
| take 1
161+
162+
// OK, so he's the help desk supervisor. He probably has elevated permissions.
163+
// Another account. Where else did he log on?
164+
165+
IdentityLogonEvents
166+
| where Timestamp > todatetime('2020-07-17') and AccountObjectId == '993788dd-7c13-4db8-9b0a-6297fcb8d5b3' and isnotempty(DeviceName)
167+
| summarize count() by DeviceName, bin(Timestamp, 1d)
168+
| render timechart
169+
170+
// Ok, what alerts do we have with his account?
171+
172+
let EricGAlerts = (
173+
AlertEvidence
174+
| where Timestamp > todatetime('2020-07-17') and AccountObjectId == '993788dd-7c13-4db8-9b0a-6297fcb8d5b3'
175+
); // Get all alerts for EricG's account
176+
EricGAlerts
177+
| join kind=rightsemi AlertInfo on AlertId // Get the alertinfo
178+
| join AlertEvidence on AlertId // Join back on AlertEvidence to get other evidence
179+
| join kind = leftouter (
180+
DeviceInfo
181+
| summarize DeviceName = any(DeviceName) by DeviceId) on DeviceId // This creates a mapping table between DeviceId and DeviceName since we only have ID in AlertEvidence
182+
| extend DomainAndAccount = strcat(AccountDomain, '\\', AccountName)
183+
| summarize Timestamp = min(Timestamp)
184+
, Device = make_set_if(DeviceName, isnotempty(DeviceName)
185+
)
186+
, SHA1 = make_set_if(SHA1,isnotempty(SHA1))
187+
, SHA256 = make_set_if(SHA256, isnotempty(SHA256))
188+
, RemoteIP = make_set_if(RemoteIP, isnotempty(RemoteIP))
189+
, RemoteUrl = make_set_if(RemoteUrl, isnotempty(RemoteUrl))
190+
, Account = make_set_if(DomainAndAccount, DomainAndAccount != '\\') by AlertId, Title // Build a nice JSON report of each alert
191+
| order by Timestamp asc
192+
193+
// make_set_if() - Creates a list of unique values from the specified column when they match the
194+
// condition in the second parameter.
195+
// makeset() - same thing without the conditional operator
196+
// makelist() \ make_list_if() - same as makeset but without deduplication
197+
198+
// OK! We have some interesting things here
199+
// - A new device of interest - robertot-pc
200+
// - We've found out that the attacker may have created a malicious inbox forwarding rule (backdoor) set from 52.137.127.6 (communication)
201+
// - We can see evidence of a possible skeleton key attack (Authentication)
202+
// - A few logons using potentially stolen credentials [mtp-air-aadconnect01 and mtp-air-dc01] (Authentication)
203+
// I wonder if that IP address is one of our devices...
204+
205+
DeviceInfo
206+
| where PublicIP == "52.137.127.6"
207+
| distinct DeviceName
208+
209+
// Bingo! Back to barbaram-pc. Yup, we'll have to queue that up for investigation.
210+
// Let's look for that other Word doc...
211+
212+
DeviceFileEvents
213+
| where Timestamp > ago(19d) and FileName =~ "BYODRegistration (1).docm"
214+
| summarize count() by SHA1, SHA256, MD5
215+
216+
// Got our file hash - let's see what the world knows about it
217+
// Backdoor: c18732c861641a5a91d1578efad6f1a2546dc4bd97c68a5f6a6ba5d4f5d76242
218+
219+
DeviceFileEvents
220+
| where SHA256 == 'c18732c861641a5a91d1578efad6f1a2546dc4bd97c68a5f6a6ba5d4f5d76242'
221+
| take 1
222+
| invoke FileProfile() // Note you need the SHA1 for this to work
223+
| project-reorder GlobalPrevalence, GlobalFirstSeen, GlobalLastSeen , Signer, Issuer, SignerHash, IsCertificateValid, IsRootSignerMicrosoft, IsExecutable, ThreatName, Publisher, SoftwareName
224+
225+
// Low prevalence, first seen April of 2020. Might be targeted, but it is a Word doc
226+
// so global prevalence might be misleading...
227+
228+
//////////////////////////////////////////////////////////////////
229+
// As you can see, using the ABC method is a quick way to pivot
230+
// through an incident. But Advanced Hunting doesn't stop there.
231+
///////////////////////////////////////////////////////////////////
232+
233+
// It is clear this file is malicious, we don’t want it in our env.
234+
// We would like to take action on the malicious file – quarantine it
235+
236+
DeviceFileEvents
237+
| where SHA256 == 'c18732c861641a5a91d1578efad6f1a2546dc4bd97c68a5f6a6ba5d4f5d76242'
238+
239+
240+
// We found several IOCs during this investigation, like IPs and file hashes.
241+
// We would like to make sure we will get alerted next time we see one of the IOCs in
242+
// our env, therefore we will create a custom detection rule.
243+
244+
// Custom detection rule to get alerted on every future activity involving IP:
245+
// '178.32.124.142', '51.83.139.56'
246+
247+
search in (DeviceNetworkEvents, DeviceEvents, AppFileEvents, IdentityLogonEvents)
248+
RemoteIP in ('178.32.124.142', '51.83.139.56') or FileOriginIP in ('178.32.124.142', '51.83.139.56') or IPAddress in ('178.32.124.142', '51.83.139.56')
249+
250+
// Detection name – Activity involving malicious IP ('178.32.124.142', '51.83.139.56')
251+
// Alert title – Activity involving malicious IP
252+
// Category – Suspicious activity
253+
// MITRE techniques -
254+
// Description – Activity with '178.32.124.142', '51.83.139.56' was observed
255+
256+
// Go Hunt

0 commit comments

Comments
 (0)