Skip to content

Commit

Permalink
M365D tutorials and tools (Azure#3186)
Browse files Browse the repository at this point in the history
* M365D tutorials and tools

Added webcasts and pbi to the right folders in Sentinel repo

* Update Episode 1 - KQL Fundamentals.txt

* Update Episode 2 - Joins.txt

removed en-us from links

* Update Episode 4 - Lets Hunt.txt

removed en-us from links

* Update MCAS - The Hunt.txt

removed links with en-us

* Update Performance, Json and dynamics operator, external data.txt

removed en-us from links

* Update MCAS - The Hunt.txt

removed en-us

* Update Airlift 2021 - Lets Invoke.csl

removed en-us
  • Loading branch information
tali-ash authored Jan 19, 2022
1 parent f0f7e9e commit 903c6fb
Show file tree
Hide file tree
Showing 12 changed files with 2,421 additions and 0 deletions.
Binary file not shown.
1 change: 1 addition & 0 deletions Tools/M365-PowerBi Dashboard/readme.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This folder contains some PowerBi dashboard that can be helpful to visualize data from Microsoft Threat Protection using the built-in APIs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
print Session = 'Best practices for hunting across domains with Microsoft 365 Defender', Presenter = 'Michael Melone, Tali Ash', Company = 'Microsoft'

// Schema Reference (upper right corner)

// Explore identities data
// From IdentityDirectoryEvents schema reference click one of the Action type
IdentityDirectoryEvents | where ActionType == 'SMB session'

// using extend to extract information form json column of AdditionalFields
// From IdentityDirectoryEvents schema reference click sample query of “Group
// modifications”
let group = 'Domain Admins';
IdentityDirectoryEvents
| where ActionType == 'Group Membership changed'
| extend AddedToGroup = AdditionalFields['TO.GROUP']
| extend RemovedFromGroup = AdditionalFields['FROM.GROUP']
| extend TargetAccount = AdditionalFields['TARGET_OBJECT.USER']
| where AddedToGroup == group or RemovedFromGroup == group
| project-reorder Timestamp, ActionType, AddedToGroup, RemovedFromGroup, TargetAccount
| limit 100

// Explore emails data
//Find who sent emails identified with malware/phishing
EmailEvents
| where (PhishFilterVerdict == "Phish" or MalwareFilterVerdict == "Malware") and DeliveryAction == "Delivered"
//| where SenderFromDomain != "gmail.com"
| project DeliveryAction, MalwareFilterVerdict, PhishFilterVerdict, Timestamp, SenderFromAddress, RecipientEmailAddress, Subject, AttachmentCount

// Finds the first appearance of files sent by a malicious sender in your organization
let MaliciousSenders = pack_array("[email protected]");
EmailAttachmentInfo
| where SenderFromAddress in~(MaliciousSenders)
| join kind=leftouter(
DeviceFileEvents
) on SHA256, $left.RecipientObjectId == $right.InitiatingProcessAccountObjectId
| summarize FirstAppearance = min(Timestamp1) by SenderFromAddress, RecipientEmailAddress, DeviceName, DeviceId, SHA256, FileName

// Functions are a special sort of join which let you pull more static data about a file (more are
// planned in the future, stay tuned!). This is really helpful when you want to get information about
// file prevalence or antimalware detections.
// Get more details on the malicous files using FileProfile function enrichment
let MaliciousSender = dynamic(["[email protected]"]);
EmailAttachmentInfo
| where SenderFromAddress in~ (MaliciousSender)
| join (
DeviceFileEvents
) on SHA256
| distinct SHA1
| invoke FileProfile()
| project SHA1, SHA256 , FileSize , GlobalFirstSeen , GlobalLastSeen , GlobalPrevalence , IsExecutable

//Get alerted every time an email from malicious sender was received
let MaliciousSender = "[email protected]";
EmailEvents
| where SenderFromAddress =~ MaliciousSender and DeliveryAction == "Delivered"


// Detection name - Email from malicious sender
// Alert title - Email from malicious sender - [email protected]
// Description - Email from malicious sender [email protected] was delivered to // users in the org

/////////////////////////////
// Get to know useful operators
/////////////////////////////

// Dealing with Phishing using Advanced Hunting

// parse_url()
// Breaks down a URL into its individual parts – including each
// query parameter

print url = parse_url("https://www.bing.com/search?q=tracking+the+adversary+mtp+advanced+hunting&qs=AS&pq=tracking+the+adversary+&sc=1-23&cvid=81318E9030D74B31A876FDE99603EE60&FORM=QBRE&sp=1")
| evaluate bag_unpack(url)

// Let’s use parse_url() to analyze some phishing activity

let Phishurls = toscalar(
EmailEvents
| where PhishFilterVerdict == "Phish"
| join EmailUrlInfo on NetworkMessageId
| extend host = parse_url(Url).Host
| where isnotempty(host)
| summarize makeset(host)
);
DeviceNetworkEvents
| where isnotempty(RemoteUrl)
| extend NetworkEventHost = parse_url(RemoteUrl).Host
| where isnotempty(NetworkEventHost)
| extend PhishHost = Phishurls
| mvexpand PhishHost to typeof(string)
| where NetworkEventHost == PhishHost


// In practice, you're likely to encounter a bunch of false positives
// due to common domains being mixed with phish domains. To accommodateTo accomodate that
// we can just reduce the dataset based on a threshold


let MaxConnections = 10; // This will be our cutoff threshold
let Phishurls = toscalar(
EmailEvents
| where PhishFilterVerdict == "Phish"
| join EmailUrlInfo on NetworkMessageId
| extend host = parse_url(Url).Host
| where isnotempty(host)
EmailEvents
| where PhishFilterVerdict == "Phish"
| join EmailUrlInfo on NetworkMessageId
| extend host = parse_url(Url).Host
| where isnotempty(host)
| summarize makeset(host)
);
// We will use this portion of the query twice now - better to make it a variable
let DeviceConnections = (
DeviceNetworkEvents
| where isnotempty(RemoteUrl)
| extend NetworkEventHost = tostring(parse_url(RemoteUrl).Host)
DeviceNetworkEvents
| where isnotempty(RemoteUrl)
| extend NetworkEventHost = tostring(parse_url(RemoteUrl).Host)
| where isnotempty(NetworkEventHost)
);
DeviceConnections
| summarize count() by NetworkEventHost // Count the number of connections by FQDN
| where count_ < MaxConnections // Filter to only domains with less than MaxConnections connections
| join kind=rightsemi DeviceConnections on NetworkEventHost // Filter our dataset to only those FQDNs
| extend PhishHost = Phishurls
| mvexpand PhishHost to typeof(string)
| where NetworkEventHost == PhishHost



// Using the bin() function you can group events by a period of time.
// Let's take a look at some logon statistics on a daily basis

// Let's look at account logon activity over time on a
// daily basis by UPN.

IdentityLogonEvents
| where isnotempty(AccountUpn)
| summarize NumberOfLogons = count() by AccountUpn, bin(Timestamp, 1d)
| render timechart

// render - creates a chart

// We can also use this bin'ed data to determine min, max, and average daily logons

IdentityLogonEvents
| where isnotempty(AccountUpn)
| summarize NumberOfLogons = count() // first get a calculation of how many logons the user does per day
by AccountUpn
, bin(Timestamp, 1d)
| summarize TotalLogons = sum(NumberOfLogons) // Then average all of them together to get average daily logons
, AverageDailyLogons = avg(NumberOfLogons)
, FewestLogonsInADay = min(NumberOfLogons)
, MostLogonsInADay = max(NumberOfLogons)
by AccountUpn
| top 10 by TotalLogons desc
| render columnchart

// New Table - IdentityDirectoryEvents
// Contains Active Directory \ domain controller operational information

IdentityDirectoryEvents
| distinct ActionType

IdentityDirectoryEvents
| where ActionType == 'Directory Services replication'
| summarize count() by IPAddress, tolower(DeviceName), AccountUpn

// Interesting - looks like a couple of replication attempts from workstations...

IdentityDirectoryEvents
| where ActionType == 'Directory Services replication' and DeviceName !startswith 'mtp-air-aad'
| join kind=inner DeviceNetworkEvents on $left.IPAddress == $right.LocalIP and $left.Port == $right.LocalPort and $left.DestinationIPAddress == $right.RemoteIP and $left .DestinationPort == $right.RemotePort
| project-reorder Timestamp1, DeviceName1, InitiatingProcessId, InitiatingProcessCommandLine


// ---------------------------

// The FileProfile function lets you pull more static data about a file (more are planned in the future, stay tuned!).
// This is really helpful when you want to get information about file prevalence or antimalware detections.

// Let's say we wanted information about rare files involved in a process creation event

DeviceProcessEvents
| invoke FileProfile() // Call the FileProfile function
| where isnotempty(GlobalPrevalence) and GlobalPrevalence < 1000 // Note that in the real world you might want to include empty GlobalPrevalence
| project-reorder DeviceName, FileName, ProcessCommandLine, FileSize, GlobalPrevalence, GlobalFirstSeen, GlobalLastSeen, ThreatName, Publisher, SoftwareName
| top 100 by GlobalPrevalence asc

// ---------------------------

// AssignedIPAddresses() function
// Lists last known IP addresses that were assigned to a given device around the date specified

AssignedIPAddresses('6c27842721799deb6420b094044d26e15e87a37b', now())

// ---------------------------

// Go hunt from incidents.

14 changes: 14 additions & 0 deletions Tutorials/Microsoft 365 Defender/Webcasts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Webcasts

This repository will contain query files used in our public training \ webcasts for reuse within your instance of Microsoft 365 Defender

---

## Tracking the Adversary
[Signup Link](https://techcommunity.microsoft.com/t5/microsoft-threat-protection/webinar-series-unleash-the-hunter-in-you/ba-p/1509232?ranMID=24542&ranEAID=msYS1Nvjv4c&ranSiteID=msYS1Nvjv4c-_joxReUxkmQPGUkIGSbqzg&epi=msYS1Nvjv4c-_joxReUxkmQPGUkIGSbqzg&irgwc=1&OCID=AID2000142_aff_7593_1243925&tduid=(ir__inwuq92cqkkft0kikk0sohziz32xi1kvkgq9mksc00)(7593)(1243925)(msYS1Nvjv4c-_joxReUxkmQPGUkIGSbqzg)()&irclickid=_inwuq92cqkkft0kikk0sohziz32xi1kvkgq9mksc00)

This four-part series provides an introduction to advanced hunting in Microsoft Threat Protection including
- An introduction to Kusto Query Language (KQL)
- Descriptions of each table available (as of the date of the webcast)
- Examples to help maximize your hunting skills in Advanced Hunting
- An example incident triage almost exclusively using Advanced Hunting
Loading

0 comments on commit 903c6fb

Please sign in to comment.