Threat hunting with PowerShell and Microsoft Defender Advanced Threat Protection

Microsoft Defender’s Advanced Threat Protection API allows for operators to use PowerShell to create applications that can help automate securing, detecting and investigating threats. Its a phenomenal feature of a remarkable platform that every blue team should be using.

In this post I’ll go over how to create an application that uses the API and some ATP queries that can be automated.

The first step is to create a web API app in Azure AD. From the dashboard select App Registrations and click New App Registration. Give your app a name and make sure to select Web App / API for application type. In the last box provide a URL where users can sign in to use the app.

Once the app is created you have to grant API access for the WindowsDefenderATP API. It is best practice to use the principle of least privilege when select permissions for your app. Make sure to click Grant Permissions after you create them.

The last step for setting up your app is generating a key and getting the application token. Save the application password, ID, and tenant ID.

Application Skeleton

To receive the API token we will need this snippet first:

$tenantId = '00000000-0000-0000-0000-000000000000' # Paste your own tenant ID here
$appId = '11111111-1111-1111-1111-111111111111' # Paste your own app ID here
$appSecret = '22222222-2222-2222-2222-222222222222' # Paste your own app secret here

$resourceAppIdUri = 'https://api.securitycenter.windows.com'
$oAuthUri = "https://login.windows.net/$TenantId/oauth2/token"
$body = [Ordered] @{
    resource = "$resourceAppIdUri"
    client_id = "$appId"
    client_secret = "$appSecret"
    grant_type = 'client_credentials'
}
$response = Invoke-RestMethod -Method Post -Uri $oAuthUri -Body $body -ErrorAction Stop
$aadToken = $response.access_token

Now that the token has been received you can use the API to run Microsoft Defender ATP queries like this:

$query = 'QueryExample | limit 10' # Paste your own query here

$url = "https://api.securitycenter.windows.com/api/advancedqueries/run"
$headers = @{ 
    'Content-Type' = 'application/json'
    Accept = 'application/json'
    Authorization = "Bearer $aadToken" 
}
$body = ConvertTo-Json -InputObject @{ 'Query' = $query }
$webResponse = Invoke-WebRequest -Method Post -Uri $url -Headers $headers -Body $body -ErrorAction Stop
$response =  $webResponse | ConvertFrom-Json
$results = $response.Results
$schema = $response.Schema

*The $results variable contains the content of your query and can be manipulated with PowerShell in typical fashion (ConvertTo-Csv, Select-String, etc.)

Advanced Queries

With a basic understanding of setting up and using Microsoft Defender Advanced Threat Protection API lets look at some more advanced queries that we can automate.

To run more advanced queries with multiple lines we need to save them in a separate text file. We can then point to the text file with this line:

$query = [IO.File]::ReadAllText("C:\QueryExample.txt");

And here are some examples of advanced ATP queries that we can automate with PowerShell:

// Detect RDP tunneling
ProcessCreationEvents 
| where EventTime > ago(10d)
| where (ProcessCommandLine contains ":3389" or ProcessCommandLine contains ":6511")
| project EventTime, ComputerName, AccountName, InitiatingProcessFileName, ActionType, 
FileName, ProcessCommandLine, InitiatingProcessCommandLine
// Account Creation
ProcessCreationEvents  
| where EventTime > ago(7d)
| where ProcessCommandLine contains "net user" and ProcessCommandLine contains "/add"
| summarize makeset(ComputerName), makeset(AccountName), makeset(ProcessCommandLine)  by InitiatingProcessFileName
| project EventTime, ComputerName, ProcessCommandLine, InitiatingProcessFileName, AccountName
// Local Accounts Activation
ProcessCreationEvents  
| where EventTime > ago(7d)
| where ProcessCommandLine contains "Administrator /active:yes" or ProcessCommandLine contains "guest /active:yes" 
| summarize makeset(ComputerName), makeset(AccountName), makeset(ProcessCommandLine)  by InitiatingProcessFileName
| project EventTime, ComputerName, ProcessCommandLine, InitiatingProcessFileName, AccountName
// User Addition to Local Groups
ProcessCreationEvents  
| where EventTime > ago(7d)
| where ProcessCommandLine contains "localgroup" and ProcessCommandLine contains "/add" 
and ( ProcessCommandLine contains "Remote Desktop Users" or ProcessCommandLine contains "administrators")
| summarize makeset(ComputerName), makeset(AccountName), makeset(ProcessCommandLine)  by InitiatingProcessFileName
| project EventTime, ComputerName, ProcessCommandLine, InitiatingProcessFileName, AccountName
// Service Creation
ProcessCreationEvents  
| where EventTime > ago(7d)
| where FileName contains "SECEDIT" 
| where ProcessCommandLine == @"secedit.exe /export /cfg ** .inf"
| summarize makeset(ComputerName), makeset(AccountName), makeset(ProcessCommandLine)  by InitiatingProcessFileName
// All Alert Events
AlertEvents 
| where EventTime > ago(7d)
| summarize makeset(FileName), dcount(FileName), makeset(ComputerName), makeset(Category), 
dcount(ComputerName) by Title 
| sort by dcount_ComputerName desc
// Alert Events from Windows Defender
MiscEvents    
| where EventTime > ago(17d)
| where ActionType == "WDAVDetection"
| summarize  makeset(FileName), makeset(InitiatingProcessParentFileName), 
makeset(InitiatingProcessFileName), makeset(InitiatingProcessCommandLine), 
makeset(FolderPath), makeset(InitiatingProcessFolderPath) , makeset(AccountName )  by ComputerName 
// Clearing of Event Log
ProcessCreationEvents  
| where EventTime > ago(10d)
| where ProcessCommandLine contains "call ClearEventlog" or InitiatingProcessCommandLine contains "call ClearEventlog" 
| summarize makeset(ComputerName), makeset(AccountName), dcount(ComputerName)   by InitiatingProcessFileName, ProcessCommandLine
| sort by dcount_ComputerName desc 
// Output Redirection
ProcessCreationEvents  
| where EventTime > ago(10d)
| where ProcessCommandLine contains "2>&1"
| summarize makeset(ComputerName), makeset(AccountName), dcount(ComputerName)   by InitiatingProcessFileName, ProcessCommandLine
| sort by dcount_ComputerName desc 
// Process Dump
ProcessCreationEvents  
| where EventTime > ago(10d)
| where (ProcessCommandLine contains "-accepteula" and ProcessCommandLine contains "1>") 
or (ProcessCommandLine contains "-accepteula" and ProcessCommandLine contains "-ma")
| summarize makeset(ComputerName), makeset(AccountName), dcount(ComputerName)   by InitiatingProcessFileName, ProcessCommandLine
| sort by dcount_ComputerName desc 
// Windows Script Host Network Activity
NetworkCommunicationEvents 
| where EventTime > ago(7d)
| where InitiatingProcessFileName in ("cscript.exe", "wscript.exe")
| summarize makeset(InitiatingProcessParentName), makeset(RemoteUrl), makeset(RemotePort), makeset(InitiatingProcessAccountName)  ,dcount(RemoteUrl) by InitiatingProcessCommandLine
| sort by dcount_RemoteUrl desc 
// PowerShell Network Activity
NetworkCommunicationEvents 
| where EventTime > ago(1d)
| where InitiatingProcessFileName =~ "powershell.exe"
| summarize makeset(RemoteUrl), makeset(RemotePort), makeset(InitiatingProcessAccountName)  ,dcount(RemoteUrl) by InitiatingProcessCommandLine
| sort by dcount_RemoteUrl desc 
// Bitsadmin Executions
ProcessCreationEvents  
| where EventTime > ago(7d)
| where FileName contains "bitsadmin.exe"
| where ProcessCommandLine contains "/TRANSFER" or ProcessCommandLine contains "/CREATE" or ProcessCommandLine contains "/ADDFILE"
or ProcessCommandLine contains "/SETPROXY" or ProcessCommandLine contains "/SETNOTIFYCMDLINE" or ProcessCommandLine contains "/SETCUSTOMHEADERS"
or ProcessCommandLine contains "/SETSECURITYFLAGS" or ProcessCommandLine contains "/SETREPLYFILENAME"
| project EventTime, ComputerName, ProcessCommandLine, InitiatingProcessFileName, AccountName  
| top 1000 by EventTime
// Bitsadmin Transfers
ProcessCreationEvents  
| where EventTime > ago(7d)
| where FileName =~ "bitsadmin.exe"
| where ProcessCommandLine contains "/transfer"
| project EventTime, ComputerName, ProcessCommandLine, InitiatingProcessFileName, AccountName  
| top 1000 by EventTime
// Microsoft Office Abuse 
ProcessCreationEvents  
| where EventTime > ago(1d)
| where InitiatingProcessParentName contains "winword.exe" or InitiatingProcessParentName contains "excel.exe" or InitiatingProcessParentName contains  "powerpnt.exe"
| where FileName contains "cscript" or FileName contains "wscript" or FileName contains "powershell"
| project EventTime, ComputerName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessParentName,  AccountName  
| top 1000 by EventTime
// LOLbin RunDLL32 Activity
ProcessCreationEvents  
| where EventTime > ago(7d)
| where FileName =~ "rundll32.exe"
| where ProcessCommandLine contains ",Control_RunDLL"
| summarize makeset(ComputerName), makeset(AccountName), dcount(ComputerName)   by InitiatingProcessFileName, ProcessCommandLine
| sort by dcount_ComputerName desc
// LOLbin RunDLL32 Register Server Activity
ProcessCreationEvents  
| where EventTime > ago(7d)
| where FileName =~ "rundll32.exe"
| where ProcessCommandLine contains "DllRegisterServer"
| summarize makeset(ComputerName), makeset(AccountName)  by InitiatingProcessFileName, ProcessCommandLine
| sort by InitiatingProcessFileName asc 
// LOLbin RunDLL32 Suspicious Executions
ProcessCreationEvents  
| where EventTime > ago(7d)
| where FileName =~ "rundll32.exe"
| where InitiatingProcessFileName in ("winword.exe" , "excel.exe" , "cscript.exe" , "wscript.exe" , "mshta.exe" )
| summarize makeset(ComputerName), makeset(AccountName)  by InitiatingProcessFileName, ProcessCommandLine
| sort by InitiatingProcessFileName asc 
// Curl Executions
ProcessCreationEvents  
| where EventTime > ago(7d)
| where FileName =~ "curl.exe"
| project EventTime, ComputerName, ProcessCommandLine, InitiatingProcessFileName, AccountName  
| top 1000 by EventTime
// At Executions
ProcessCreationEvents  
| where EventTime > ago(7d)
| where FileName =~ "at.exe"
| project EventTime, ComputerName, ProcessCommandLine, InitiatingProcessFileName, AccountName  
| top 1000 by EventTime
// WMIC Process Call
ProcessCreationEvents  
| where EventTime > ago(7d)
| where FileName =~ "WMIC.exe"
| where ProcessCommandLine contains "process call create"
| project EventTime, ComputerName, ProcessCommandLine, InitiatingProcessFileName, AccountName  
| top 1000 by EventTime

In the next post in this series I’ll take the skeleton application along with one of these advanced queries and show how to develop it into a working application.

References:

https://docs.microsoft.com/en-us/windows/security/threat-protection/microsoft-defender-atp/run-advanced-query-sample-powershell

https://github.com/beahunt3r/Windows-Hunting/tree/master/WindowsDefenderATP%20Hunting%20Queries%20