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

Creating a Resource Group in Azure

A resource group allows for us to leverage a logical container to manage our Azure resources in an efficient way.

For example, let’s say this website requires a VM, some storage, and a user database. All of these resources would then be managed under one resource group for efficiency. By using a resource group we could assign access controls for the right users only to the groups they controlled or we could get a breakdown for how much a specific group costs to run or we can push an update to all resources in the group at once or if it a resource group is no longer needed we can simply blow it all away.

To create a resource group, start by opening the Cloud Shell terminal from the Azure Portal.

$resourceGroupName = Read-Host -Prompt "Enter the name of your Resource Group"
$location = Read-Host -Prompt "Enter the location (i.e. centralus)"

New-AzResourceGroup -Name $resourceGroupName -Location $location

Reference:

https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/manage-resources-powershell

Creating Users in Azure AD

Azure AD introduces a lot of simplicity to working the directory, so much so that creating users and groups in Azure AD is easier than creating them on-prem.

When adding a new user in Azure AD we are given a few ways to do this. You can create the user through the traditional process, you can invite an user from another AD to participate in your AD, or you can add/invite users in bulk. And of course, all of this can be done through PowerShell.

Open the Cloud Shell terminal from the Azure Portal. To work with Azure AD from PowerShell we need to run

Connect-AzureAD

Once connected to Azure AD we must create a password profile object to avoid passing passwords through plaintext. We can do this by creating a variable that instantiates the password profile object

$pwprofile = New-Object -Type Microsoft.Open.AzureAD.Model.PasswordProfile

We then need to store the temporary password in this variable

$pwprofile.password = "hunter2"

Now that the pwprofile variable has the password object we can create the new user

New-AzureADUser -AccountEnabled $true -PasswordProfile $pwprofile -DisplayName "PSEUser" -UserPrincipalName "PSEUser@powershellengineering.com" -MailNickName "PSEUser"

And there you have it, new Azure AD user.

Attack Surface Reduction: Enabling Rules

Microsoft’s Attack Surface Reduction (ASR) helps defend against malware leveraging legitimate applications by implementing rules that actively prevent malicious behavior.

The best part about ASR is that these rules can easily be enabled with a simple PowerShell cmdlet.

For example, rule BE9BA2D9-53EA-4CDC-84E5-9B1EEEE46550 blocks executable content from email.

Enabling these rules can be done by using the Set-MpPreference cmdlet like so:

Set-MpPreference -AttackSurfaceReductionRules_Ids BE9BA2D9-53EA-4CDC-84E5-9B1EEEE46550 -AttackSurfaceReductionRules_Actions Enabled

We call the cmdlet, specify the parameter and the rule ID we want to enable and then enable the specified rule.

Using Add-MpPreference you can also exclude files or entire folders from ASR.

Add-MpPreference -AttackSurfaceReductionOnlyExclusions "<C:\Windows\System32\calc.exe>"

ASR is really robust and is a great way to harden systems. I’ll post more about ASR in the future and show some examples of it in action. 

For now, here is a list of some more ASR rules:

Block Adobe Reader from creating child processes7674ba52-37eb-4a4f-a9a1-f0f9a1619a2cSupportedWindows 10, version 1709 (RS3, build 16299) or greater
Block all Office applications from creating child processesD4F940AB-401B-4EFC-AADC-AD5F3C50688ASupportedWindows 10, version 1709 (RS3, build 16299) or greater
Block credential stealing from the Windows local security authority subsystem (lsass.exe)9e6c4e1f-7d60-472f-ba1a-a39ef669e4b2SupportedWindows 10, version 1709 (RS3, build 16299) or greater
Block executable content from email client and webmailBE9BA2D9-53EA-4CDC-84E5-9B1EEEE46550SupportedWindows 10, version 1709 (RS3, build 16299) or greater
Block executable files from running unless they meet a prevalence, age, or trusted list criterion01443614-cd74-433a-b99e-2ecdc07bfc25SupportedWindows 10, version 1709 (RS3, build 16299) or greater
Block execution of potentially obfuscated scripts5BEB7EFE-FD9A-4556-801D-275E5FFC04CCSupportedWindows 10, version 1709 (RS3, build 16299) or greater
Block JavaScript or VBScript from launching downloaded executable contentD3E037E1-3EB8-44C8-A917-57927947596DSupportedWindows 10, version 1709 (RS3, build 16299) or greater
Block Office applications from creating executable content3B576869-A4EC-4529-8536-B80A7769E899SupportedWindows 10, version 1709 (RS3, build 16299) or greater
Block Office applications from injecting code into other processes75668C1F-73B5-4CF0-BB93-3ECF5CB7CC84SupportedWindows 10, version 1709 (RS3, build 16299) or greater
Block Office communication application from creating child processes26190899-1602-49e8-8b27-eb1d0a1ce869SupportedWindows 10, version 1709 (RS3, build 16299) or greater
Block persistence through WMI event subscriptione6db77e5-3df2-4cf1-b95a-636979351e5bNot supportedWindows 10, version 1903 (build 18362) or greater
Block process creations originating from PSExec and WMI commandsd1e49aac-8f56-4280-b9ba-993a6d77406cSupportedWindows 10, version 1709 (RS3, build 16299) or greater
Block untrusted and unsigned processes that run from USBb2b3f03d-6a65-4f7b-a9c7-1c7ef74a9ba4SupportedWindows 10, version 1709 (RS3, build 16299) or greater
Block Win32 API calls from Office macros92E97FA1-2EDF-4476-BDD6-9DD0B4DDDC7BSupportedWindows 10, version 1709 (RS3, build 16299) or greater
Use advanced protection against ransomwarec1db55ab-c21a-4637-bb3f-a12568109d35SupportedWindows 10, version 1709 (RS3, build 16299) or greater

Reference:

 https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-exploit-guard/attack-surface-reduction-exploit-guard

Create Active Directory Forest

These commands will create a forest and show how to populate it with users and groups.

The first cmdlet establishes the server as a domain controller and installs the AD domain service tools.

Install-WindowsFeature AD-Domain-Services

 

 Next, setup and configure the AD forest.

Install-ADDSForest -DomainName powershellengineering.com

 

You can now begin using AD functions like creating users.

New-ADUser -SamAccountName User1 -AccountPassword (read-host "Set user password" -asecurestring) -name "User1" -enabled $true -ChangePasswordAtLogon $true

This simple cmdlet will create a user named “User1” and ask for a password. The user will then be prompted to change password when they login for the first time. In a previous post I cover other methods to add users.

 

Lastly we can run a cmdlet that will establish group membership for the new user.

Add-ADPrincipalGroupMembership -Identity "CN=User1,CN=Users,DC=powershell,DC=engineering,DC=com", -MemberOf"CN=Enterprise Admins,CN=Users=DC=powershell,DC=engineering,DC=com",

 

And lastly we can run the Get-ADPrincipalGroupMembership cmdlet to confirm that the membership was granted.

Get-ADPrincipalGroupMembership User1

 

Useful parameters:

-Parameter

 

-Parameter

  

Reference:

Associate DNS with TCP/IP

This command associates DNS information with TCP/IP addresses on to an interface.

Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses ("127.0.0.1,"127.0.0.2")

 

Useful parameters:

-InterfaceAlias

            Specifies the name of the interface.

 

-ServerAdresses

            Specifies a list of DNS server IP addresses to use.

  

Reference:

https://docs.microsoft.com/en-us/powershell/module/dnsclient/Set-DnsClientServerAddress?view=win10-ps

Create a new A record

This command adds a host address (A) record to your DNS zone.

Add-DnsServerResourceRecordA -Name "hostname" -ZoneName "powershellengineering.com" -IPv4Address "127.0.0.1" 

 

Useful parameters:

-CreatePtr

            Creates a corresponding PTR record.

 

-AllowUpdateAny

            Gives any authenticated user rights to update the record.

  

Reference:

https://docs.microsoft.com/en-us/powershell/module/dnsserver/add-dnsserverresourcerecorda?view=win10-ps

Add Users in Active Directory

There are many ways to add users in AD with PowerShell. You can add them with a one liner like:

New-ADUser -Name "User1"

Or you can use a script to import and add many users all at once.