Merge branch 'master' into Playbook-instructions

This commit is contained in:
dicolanl 2020-01-13 15:33:29 -05:00
Родитель 591a81ccce cd164bb6ba
Коммит 56b1ac734c
192 изменённых файлов: 4809 добавлений и 2299 удалений

Просмотреть файл

@ -0,0 +1,11 @@
jobs:
- job: "YamlFileValidation"
pool:
vmImage: 'Ubuntu 16.04'
steps:
- task: Npm@1
displayName: 'npm install'
inputs:
verbose: false
- script: 'npm run tsc && node .script/yamlFileValidator.js'
displayName: 'Yaml File Syntax Validation'

16
.script/utils.ts Normal file
Просмотреть файл

@ -0,0 +1,16 @@
import { devOps, cli } from '@azure/avocado'; //TODO: use different way to get PR diff files
export async function getPullRequestDiffFiles() {
const config = cli.defaultConfig();
const pr = await devOps.createPullRequestProperties(config);
if (pr === undefined) {
console.log(`pr is undefined`);
return null;
}
const changedFiles = await pr.structuralDiff().toArray();
console.log(`diff files in current PR: ${changedFiles.length}`);
return changedFiles;
}

Просмотреть файл

@ -0,0 +1,47 @@
import { getPullRequestDiffFiles } from './utils';
import yaml from 'js-yaml';
import fs from 'fs';
export function IsValidYamlFile(filePath:string): Boolean {
try {
yaml.safeLoad(fs.readFileSync(filePath, 'utf8'));
return true;
}
catch (e) {
console.error(`Incorrect yaml file. Tile path: ${filePath}. Error message: ${e.message}`);
return false
}
}
const main = async () => {
console.log("start - yamlFileValidetor script");
const pullRequestDiffFiles = await getPullRequestDiffFiles();
if(pullRequestDiffFiles === null){
console.log("No changes files in PR");
return 0;
}
const changedYamlFiles = pullRequestDiffFiles.filter(filePath => filePath.endsWith('.yaml') || filePath.endsWith('.yml'));
if (changedYamlFiles.length === 0) {
console.log("No changes in yaml file");
return 0;
}
let retCode = 0;
changedYamlFiles.forEach(filePath => {
if(!IsValidYamlFile(filePath)){
retCode = -1;
}
});
return retCode;
}
main().then(retCode => {
if (retCode !== 0) {
console.error(`ERROR: incorrect yaml files`);
}
process.exit(retCode);
});

Просмотреть файл

@ -3,8 +3,7 @@
# the last matching pattern has the most precendence.
# Core team members
* @liemilyg @mgladi @orco365 @KobyKoren @lizamash @morshabi @shainw @ianhelle @timbMSFT @juliango2100 @dicolanl @Amitbergman
* @liemilyg @mgladi @orco365 @shalinoid @KobyKoren @lizamash @morshabi @shainw @ianhelle @timbMSFT @juliango2100 @dicolanl @Amitbergman @sagamzu @YaronFruchtmann
# This is copied from here: https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners

Просмотреть файл

@ -0,0 +1,4 @@
.git*
.vscode
local.settings.json
test

6
DataConnectors/O365 Data/O365APItoAS-Template/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,6 @@
# Azure Functions artifacts
bin
obj
appsettings.json
local.settings.json

Просмотреть файл

@ -0,0 +1,6 @@
{
"recommendations": [
"ms-azuretools.vscode-azurefunctions",
"ms-vscode.PowerShell"
]
}

Просмотреть файл

@ -0,0 +1,13 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Attach to PowerShell Functions",
"type": "PowerShell",
"request": "attach",
"customPipeName": "AzureFunctionsPSWorker",
"runspaceId": 1,
"preLaunchTask": "func: host start"
}
]
}

Просмотреть файл

@ -0,0 +1,6 @@
{
"azureFunctions.deploySubpath": ".",
"azureFunctions.projectLanguage": "PowerShell",
"azureFunctions.projectRuntime": "~2",
"debug.internalConsoleOptions": "neverOpen"
}

Просмотреть файл

@ -0,0 +1,11 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "func",
"command": "host start",
"problemMatcher": "$func-watch",
"isBackground": true
}
]
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"bindings": [
{
"name": "Timer",
"type": "timerTrigger",
"direction": "in",
"schedule": "0 */5 * * * *"
}
]
}

Просмотреть файл

@ -0,0 +1,135 @@
###################################################################################
# API Log to OMS Log Analytics Workspace
###################################################################################
#Credit: https://github.com/tsrob50/LogAnalyticsAPIFunction
function Write-OMSLogfile {
<#
.SYNOPSIS
Inputs a hashtable, date and workspace type and writes it to a Log Analytics Workspace.
.DESCRIPTION
Given a value pair hash table, this function will write the data to an OMS Log Analytics workspace.
Certain variables, such as Customer ID and Shared Key are specific to the OMS workspace data is being written to.
This function will not write to multiple OMS workspaces. Build-signature and post-analytics function from Microsoft documentation
at https://docs.microsoft.com/en-us/azure/log-analytics/log-analytics-data-collector-api
.PARAMETER DateTime
date and time for the log. DateTime value
.PARAMETER Type
Name of the logfile or Log Analytics "Type". Log Analytics will append _CL at the end of custom logs String Value
.PARAMETER LogData
A series of key, value pairs that will be written to the log. Log file are unstructured but the key should be consistent
withing each source.
.INPUTS
The parameters of data and time, type and logdata. Logdata is converted to JSON to submit to Log Analytics.
.OUTPUTS
The Function will return the HTTP status code from the Post method. Status code 200 indicates the request was received.
.NOTES
Version: 2.0
Author: Travis Roberts
Creation Date: 7/9/2018
Purpose/Change: Crating a stand alone function.
.EXAMPLE
This Example will log data to the "LoggingTest" Log Analytics table
$type = 'LoggingTest'
$dateTime = Get-Date
$data = @{
ErrorText = 'This is a test message'
ErrorNumber = 1985
}
$returnCode = Write-OMSLogfile $dateTime $type $data -Verbose
write-output $returnCode
#>
[cmdletbinding()]
Param(
[Parameter(Mandatory = $true, Position = 0)]
[datetime]$dateTime,
[parameter(Mandatory = $true, Position = 1)]
[string]$type,
[Parameter(Mandatory = $true, Position = 2)]
[psobject]$logdata,
[Parameter(Mandatory = $true, Position = 3)]
[string]$CustomerID,
[Parameter(Mandatory = $true, Position = 4)]
[string]$SharedKey
)
Write-Verbose -Message "DateTime: $dateTime"
Write-Verbose -Message ('DateTimeKind:' + $dateTime.kind)
Write-Verbose -Message "Type: $type"
write-Verbose -Message "LogData: $logdata"
#region Workspace ID and Key
# Workspace ID for the workspace
#$CustomerID = 'ENTER WORKSPACE ID HERE'
# Shared key needs to be set for environment
# Below uses an encrypted variable from Azure Automation
# Uncomment the next two lines if using Azure Automation Variable and comment the last
# $automationVarName = 'Enter Variable Name Here'
# $sharedKey = Get-AutomationVariable -name $automationVarName
# Key Vault is another secure option for storing the value
# Less secure option is to put the key in the code
#$SharedKey = 'ENTER WORKSPACE KEY HERE'
#endregion
# Supporting Functions
# Function to create the auth signature
function Build-signature ($CustomerID, $SharedKey, $Date, $ContentLength, $method, $ContentType, $resource) {
$xheaders = 'x-ms-date:' + $Date
$stringToHash = $method + "`n" + $contentLength + "`n" + $contentType + "`n" + $xHeaders + "`n" + $resource
$bytesToHash = [text.Encoding]::UTF8.GetBytes($stringToHash)
$keyBytes = [Convert]::FromBase64String($SharedKey)
$sha256 = New-Object System.Security.Cryptography.HMACSHA256
$sha256.key = $keyBytes
$calculateHash = $sha256.ComputeHash($bytesToHash)
$encodeHash = [convert]::ToBase64String($calculateHash)
$authorization = 'SharedKey {0}:{1}' -f $CustomerID,$encodeHash
return $authorization
}
# Function to create and post the request
Function Post-LogAnalyticsData ($CustomerID, $SharedKey, $Body, $Type) {
$method = "POST"
$ContentType = 'application/json'
$resource = '/api/logs'
$rfc1123date = ($dateTime).ToString('r')
$ContentLength = $Body.Length
$signature = Build-signature `
-customerId $CustomerID `
-sharedKey $SharedKey `
-date $rfc1123date `
-contentLength $ContentLength `
-method $method `
-contentType $ContentType `
-resource $resource
$uri = "https://" + $customerId + ".ods.opinsights.azure.com" + $resource + "?api-version=2016-04-01"
$headers = @{
"Authorization" = $signature;
"Log-Type" = $type;
"x-ms-date" = $rfc1123date
"time-generated-field" = $dateTime
}
$response = Invoke-WebRequest -Uri $uri -Method $method -ContentType $ContentType -Headers $headers -Body $body -UseBasicParsing
Write-Verbose -message ('Post Function Return Code ' + $response.statuscode)
return $response.statuscode
}
# Check if time is UTC, Convert to UTC if not.
# $dateTime = (Get-Date)
if ($dateTime.kind.tostring() -ne 'Utc'){
$dateTime = $dateTime.ToUniversalTime()
Write-Verbose -Message $dateTime
}
# Add DateTime to hashtable
#$logdata.add("DateTime", $dateTime)
$logdata | Add-Member -MemberType NoteProperty -Name "DateTime" -Value $dateTime
#Build the JSON file
$logMessage = ConvertTo-Json $logdata -Depth 20
Write-Verbose -Message $logMessage
#Submit the data
$returnCode = Post-LogAnalyticsData -CustomerID $CustomerID -SharedKey $SharedKey -Body ([System.Text.Encoding]::UTF8.GetBytes($logMessage)) -Type $type
Write-Verbose -Message "Post Statement Return Code $returnCode"
return $returnCode
}

Просмотреть файл

@ -0,0 +1,11 @@
# TimerTrigger - PowerShell
The `TimerTrigger` makes it incredibly easy to have your functions executed on a schedule. This sample demonstrates a simple use case of calling your function every 5 minutes.
## How it works
For a `TimerTrigger` to work, you provide a schedule in the form of a [cron expression](https://en.wikipedia.org/wiki/Cron#CRON_expression)(See the link for full details). A cron expression is a string with 6 separate expressions which represent a given schedule via patterns. The pattern we use to represent every 5 minutes is `0 */5 * * * *`. This, in plain text, means: "When seconds is equal to 0, minutes is divisible by 5, for any hour, day of the month, month, day of the week, or year".
## Learn more
<TODO> Documentation

Просмотреть файл

@ -0,0 +1,265 @@
# Input bindings are passed in via param block.
param($Timer)
#Import-module .\TimerTrigger\modules\Write-OMSLogfile.ps1
###################################################################################
# API Log to OMS Log Analytics Workspace
###################################################################################
#Credit: https://github.com/tsrob50/LogAnalyticsAPIFunction
function Write-OMSLogfile {
<#
.SYNOPSIS
Inputs a hashtable, date and workspace type and writes it to a Log Analytics Workspace.
.DESCRIPTION
Given a value pair hash table, this function will write the data to an OMS Log Analytics workspace.
Certain variables, such as Customer ID and Shared Key are specific to the OMS workspace data is being written to.
This function will not write to multiple OMS workspaces. Build-signature and post-analytics function from Microsoft documentation
at https://docs.microsoft.com/en-us/azure/log-analytics/log-analytics-data-collector-api
.PARAMETER DateTime
date and time for the log. DateTime value
.PARAMETER Type
Name of the logfile or Log Analytics "Type". Log Analytics will append _CL at the end of custom logs String Value
.PARAMETER LogData
A series of key, value pairs that will be written to the log. Log file are unstructured but the key should be consistent
withing each source.
.INPUTS
The parameters of data and time, type and logdata. Logdata is converted to JSON to submit to Log Analytics.
.OUTPUTS
The Function will return the HTTP status code from the Post method. Status code 200 indicates the request was received.
.NOTES
Version: 2.0
Author: Travis Roberts
Creation Date: 7/9/2018
Purpose/Change: Crating a stand alone function.
.EXAMPLE
This Example will log data to the "LoggingTest" Log Analytics table
$type = 'LoggingTest'
$dateTime = Get-Date
$data = @{
ErrorText = 'This is a test message'
ErrorNumber = 1985
}
$returnCode = Write-OMSLogfile $dateTime $type $data -Verbose
write-output $returnCode
#>
[cmdletbinding()]
Param(
[Parameter(Mandatory = $true, Position = 0)]
[datetime]$dateTime,
[parameter(Mandatory = $true, Position = 1)]
[string]$type,
[Parameter(Mandatory = $true, Position = 2)]
[psobject]$logdata,
[Parameter(Mandatory = $true, Position = 3)]
[string]$CustomerID,
[Parameter(Mandatory = $true, Position = 4)]
[string]$SharedKey
)
Write-Verbose -Message "DateTime: $dateTime"
Write-Verbose -Message ('DateTimeKind:' + $dateTime.kind)
Write-Verbose -Message "Type: $type"
write-Verbose -Message "LogData: $logdata"
#region Workspace ID and Key
# Workspace ID for the workspace
#$CustomerID = 'ENTER WORKSPACE ID HERE'
# Shared key needs to be set for environment
# Below uses an encrypted variable from Azure Automation
# Uncomment the next two lines if using Azure Automation Variable and comment the last
# $automationVarName = 'Enter Variable Name Here'
# $sharedKey = Get-AutomationVariable -name $automationVarName
# Key Vault is another secure option for storing the value
# Less secure option is to put the key in the code
#$SharedKey = 'ENTER WORKSPACE KEY HERE'
#endregion
# Supporting Functions
# Function to create the auth signature
function Build-signature ($CustomerID, $SharedKey, $Date, $ContentLength, $method, $ContentType, $resource) {
$xheaders = 'x-ms-date:' + $Date
$stringToHash = $method + "`n" + $contentLength + "`n" + $contentType + "`n" + $xHeaders + "`n" + $resource
$bytesToHash = [text.Encoding]::UTF8.GetBytes($stringToHash)
$keyBytes = [Convert]::FromBase64String($SharedKey)
$sha256 = New-Object System.Security.Cryptography.HMACSHA256
$sha256.key = $keyBytes
$calculateHash = $sha256.ComputeHash($bytesToHash)
$encodeHash = [convert]::ToBase64String($calculateHash)
$authorization = 'SharedKey {0}:{1}' -f $CustomerID,$encodeHash
return $authorization
}
# Function to create and post the request
Function Post-LogAnalyticsData ($CustomerID, $SharedKey, $Body, $Type) {
$method = "POST"
$ContentType = 'application/json'
$resource = '/api/logs'
$rfc1123date = ($dateTime).ToString('r')
$ContentLength = $Body.Length
$signature = Build-signature `
-customerId $CustomerID `
-sharedKey $SharedKey `
-date $rfc1123date `
-contentLength $ContentLength `
-method $method `
-contentType $ContentType `
-resource $resource
$uri = "https://" + $customerId + ".ods.opinsights.azure.com" + $resource + "?api-version=2016-04-01"
$headers = @{
"Authorization" = $signature;
"Log-Type" = $type;
"x-ms-date" = $rfc1123date
"time-generated-field" = $dateTime
}
$response = Invoke-WebRequest -Uri $uri -Method $method -ContentType $ContentType -Headers $headers -Body $body -UseBasicParsing
Write-Verbose -message ('Post Function Return Code ' + $response.statuscode)
return $response.statuscode
}
# Check if time is UTC, Convert to UTC if not.
# $dateTime = (Get-Date)
if ($dateTime.kind.tostring() -ne 'Utc'){
$dateTime = $dateTime.ToUniversalTime()
Write-Verbose -Message $dateTime
}
# Add DateTime to hashtable
#$logdata.add("DateTime", $dateTime)
$logdata | Add-Member -MemberType NoteProperty -Name "DateTime" -Value $dateTime
#Build the JSON file
$logMessage = ConvertTo-Json $logdata -Depth 20
Write-Verbose -Message $logMessage
#Submit the data
$returnCode = Post-LogAnalyticsData -CustomerID $CustomerID -SharedKey $SharedKey -Body ([System.Text.Encoding]::UTF8.GetBytes($logMessage)) -Type $type
Write-Verbose -Message "Post Statement Return Code $returnCode"
return $returnCode
}
function Get-AuthToken{
[cmdletbinding()]
Param(
[Parameter(Mandatory = $true, Position = 0)]
[string]$ClientID,
[parameter(Mandatory = $true, Position = 1)]
[string]$ClientSecret,
[Parameter(Mandatory = $true, Position = 2)]
[string]$tenantdomain,
[Parameter(Mandatory = $true, Position = 3)]
[string]$TenantGUID
)
# Create app of type Web app / API in Azure AD, generate a Client Secret, and update the client id and client secret here
$loginURL = "https://login.microsoftonline.com/"
# Get the tenant GUID from Properties | Directory ID under the Azure Active Directory section
$resource = "https://manage.office.com"
# auth
$body = @{grant_type="client_credentials";resource=$resource;client_id=$ClientID;client_secret=$ClientSecret}
$oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenantdomain/oauth2/token?api-version=1.0 -Body $body
$headerParams = @{'Authorization'="$($oauth.token_type) $($oauth.access_token)"}
return $headerParams
}
function Get-O365Data{
[cmdletbinding()]
Param(
[Parameter(Mandatory = $true, Position = 0)]
[string]$startTime,
[parameter(Mandatory = $true, Position = 1)]
[string]$endTime,
[Parameter(Mandatory = $true, Position = 2)]
[psobject]$headerParams,
[parameter(Mandatory = $true, Position = 3)]
[string]$tenantGuid
)
#List Available Content
$contentTypes = $env:contentTypes.split(",")
#Loop for each content Type like Audit.General
foreach($contentType in $contentTypes){
$listAvailableContentUri = "https://manage.office.com/api/v1.0/$tenantGUID/activity/feed/subscriptions/content?contentType=$contentType&PublisherIdentifier=$env:publisher&startTime=$startTime&endTime=$endTime"
do {
#List Available Content
$contentResult = Invoke-RestMethod -Method GET -Headers $headerParams -Uri $listAvailableContentUri
$contentResult.Count
#Loop for each Content
foreach($obj in $contentResult){
#Retrieve Content
$data = Invoke-RestMethod -Method GET -Headers $headerParams -Uri ($obj.contentUri)
$data.Count
#Loop through each Record in the Content
foreach($event in $data){
#Filtering for Recrord types
#Get all Record Types
if($env:recordTypes -eq "0"){
#We dont need Cloud App Security Alerts due to MCAS connector
if(($event.Source) -ne "Cloud App Security"){
#Write each event to Log A
#$writeResult = Write-OMSLogfile (Get-Date) $env:customLogName $event $env:workspaceId $env:workspaceKey
#$writeResult
}
}
else{
#Get only certain record types
$types = ($env:recordTypes).split(",")
if(($event.RecordType) -in $types){
#We dont need Cloud App Security Alerts due to MCAS connector
if(($event.Source) -ne "Cloud App Security"){
#write each event to Log A
#$writeResult = Write-OMSLogfile (Get-Date) $env:customLogName $event $env:workspaceId $env:workspaceKey
#$writeResult
}
}
}
}
}
#Handles Pagination
$nextPageResult = Invoke-WebRequest -Method GET -Headers $headerParams -Uri $listAvailableContentUri
If(($nextPageResult.Headers.NextPageUrl) -ne $null){
$nextPage = $true
$listAvailableContentUri = $nextPageResult.Headers.NextPageUrl
}
Else{$nextPage = $false}
} until ($nextPage -eq $false)
}
}
# Get the current universal time in the default string format
$currentUTCtime = (Get-Date).ToUniversalTime()
# The 'IsPastDue' porperty is 'true' when the current function invocation is later than scheduled.
if ($Timer.IsPastDue) {
Write-Host "PowerShell timer is running late!"
}
#add last run time to blob file to ensure no missed packages
$endTime = $currentUTCtime | Get-Date -Format yyyy-MM-ddThh:mm:ss
$azstoragestring = $Env:WEBSITE_CONTENTAZUREFILECONNECTIONSTRING
$Context = New-AzStorageContext -ConnectionString $azstoragestring
if((Get-AzStorageContainer -Context $Context).Name -contains "lastlog"){
#Set Container
$Blob = Get-AzStorageBlob -Context $Context -Container (Get-AzStorageContainer -Name "lastlog" -Context $Context).Name -Blob "lastlog.log"
$lastlogTime = $blob.ICloudBlob.DownloadText()
$startTime = $lastlogTime | Get-Date -Format yyyy-MM-ddThh:mm:ss
$endTime | Out-File "$env:TEMP\lastlog.log"
Set-AzStorageBlobContent -file "$env:TEMP\lastlog.log" -Container (Get-AzStorageContainer -Name "lastlog" -Context $Context).Name -Context $Context -Force
}
else {
#create container
$azStorageContainer = New-AzStorageContainer -Name "lastlog" -Context $Context
$endTime | Out-File "$env:TEMP\lastlog.log"
Set-AzStorageBlobContent -file "$env:TEMP\lastlog.log" -Container $azStorageContainer.name -Context $Context -Force
$startTime = $currentUTCtime.AddSeconds(-300) | Get-Date -Format yyyy-MM-ddThh:mm:ss
}
$startTime
$endTime
$lastlogTime
$domain = $domain+".onmicrosoft.com"
$headerParams = Get-AuthToken $env:clientID $env:clientSecret $domain $env:tenantGuid
Get-O365Data $startTime $endTime $headerParams $tenantGuid
# Write an information log with the current time.
Write-Host "PowerShell timer trigger function ran! TIME: $currentUTCtime"

Просмотреть файл

Просмотреть файл

@ -0,0 +1,6 @@
{
"version": "2.0",
"managedDependency": {
"enabled": true
}
}

Просмотреть файл

@ -0,0 +1,21 @@
# Azure Functions profile.ps1
#
# This profile.ps1 will get executed every "cold start" of your Function App.
# "cold start" occurs when:
#
# * A Function App starts up for the very first time
# * A Function App starts up after being de-allocated due to inactivity
#
# You can define helper functions, run commands, or specify environment variables
# NOTE: any variables defined that are not environment variables will get reset after the first execution
# Authenticate with Azure PowerShell using MSI.
# Remove this if you are not planning on using MSI or Azure PowerShell.
if ($env:MSI_SECRET -and (Get-Module -ListAvailable Az.Accounts)) {
Connect-AzAccount -Identity
}
# Uncomment the next line to enable legacy AzureRm alias in Azure PowerShell.
# Enable-AzureRmAlias
# You can also define functions or aliases that can be referenced in any of your PowerShell functions.

Просмотреть файл

@ -0,0 +1,4 @@
{
"$schema": "http://json.schemastore.org/proxies",
"proxies": {}
}

Просмотреть файл

@ -0,0 +1,6 @@
# This file enables modules to be automatically managed by the Functions service.
# See https://aka.ms/functionsmanageddependency for additional information.
#
@{
'Az' = '2.*'
}

Просмотреть файл

@ -0,0 +1,127 @@
# Deploy Function App for getting Office 365 Management API data into Azure Sentinel
This function app will poll O365 Activity Managment API every 5 mins for logs. It does support multi-tenant and is designed to get Audit.General and DLP.All events.
For multi-tenant, you will need to add 4 properties to the app config. You can do this locally in local.settings.json before deploying the app or after in the Azure Portal.
To add a tenant:
1. Update numberOfTenants to the number of tenants you want to poll.
2. Add each of the following items replacing tenant1_ with tenant2_ and 3 and so on.
* "tenant1_clientID": "<GUID>",
* "tenant1_clientSecret": "@Microsoft.KeyVault(SecretUri=https://<name>.vault.azure.net/secrets/<secret>/<version>)",
* "tenant1_domain": "<domain>",
* "tenant1_tenantGuid": "<GUID>",
## Deployment and Configuration
### Add AAD App Permissions
1. Go to Azure Active Directory / App Registrations
2. Create +New Registration
3. Call it "O365APItoAzureSentinel". Click Register.
4. Click API Permissions Blade.
5. Click Add a Permission.
6. Click Office 365 Management APIs.
7. Click Appplication Permissions
8. Check all permissions for each category. Click Add permissions.
9. Click grant admin consent for domain.com
10. Click Certificates and Secrets
11. Click New Client Secret
12. Enter a description, select never. Click Add.
13. IMPORTANT. Click copy next to the new secret and paste it somewhere temporaily. You can not come back to get the secret once you leave the blade.
14. Copy the client Id from the application properties and paste it somewhere.
15. Also copy the tenant Id from the AAD directory properties blade.
### Create O365 API Subscription
1. Open Powershell
2. Run the following commands
$ClientID = "<GUID> from AAD App Registration"
$ClientSecret = "<clientSecret> from AAD App Registrtion"
$loginURL = "https://login.microsoftonline.com/"
$tenantdomain = "<domain>.onmicrosoft.com"
$TenantGUID = "<tenantguid> from AAD"
$resource = "https://manage.office.com"
$body = @{grant_type="client_credentials";resource=$resource;client_id=$ClientID;client_secret=$ClientSecret}
$oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenantdomain/oauth2/token?api-version=1.0 -Body $body
$headerParams = @{'Authorization'="$($oauth.token_type) $($oauth.access_token)"}
$publisher = "<randomGuid>" Get a guid from https://guidgenerator.com/
* Run this command to enable Audit.General Subscription.
Invoke-WebRequest -Method Post -Headers $headerParams -Uri https://manage.office.com/api/v1.0/$tenantGuid/subscriptions/start?contentType=Audit.General&PublisherIdentifier=$Publisher
* Run this command to enable DLP.ALL subscription
Invoke-WebRequest -Method Post -Headers $headerParams -Uri https://manage.office.com/api/v1.0/$tenantGuid/subscriptions/start?contentType=DLP.ALL&PublisherIdentifier=$Publisher
### Deploy the Function App
Note: You will need to prepare VS code for Azure function development. See https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-function-powershell#prerequisites
1. Download the Zip file of the Azure Funciton app from Github.
2. Extract to location on your machine.
3. Open VS Code
4. Click File -> Open Folder
5. Select the Top folder from extracted files.
6. Type Crtl+Shift+P.
7. Click Azure Functions: Deploy to function app. You maybe asked to sign in to azure.
8. Click Create New function app in Azure (advanced)
9. Provide a unique name like "O365APItoAS". Press Enter
10. Click Windows
11. Click Consumption
12. Click PowerShell
13. Click Create new Resource Group
14. Press enter to accept the name
15. Click Create a new storage Account
16. Press enter to accept the name
17. Click Create new Application Insights resource
18. Press enter to accept the name
19. Pick a location
20. Deployment will begin.
21. Wait for the deployment to complete, then click upload settings in the bottom right
22. Click yes to all to upload.
23. Go to the Azure Portal.
24. Go to the resource group that was created. Click the Function.
25. Click Stop.
26. Click Platform Features Tab.
27. Click Identity
28. Click On under system assigned. Click Save. Click Yes.
### Create a Key Vault
1. Go to the Azure Portal.
2. Go to the resource group that was created. Click Add.
3. Type Key Vault.
4. Create a Key vault.
5. Go to the resource created.
6. Click Access Policies.
7. Click Add Access Policy
8. Select Secret Management from Configure from template
9. Click Select Principal
10. Search for the name of the function app. Click Select.
11. Click Add.
12. Click Save
13. Click Secrets
14. Click Generate
15. Enter O365Tenant1_clientsecret. Paste the AAD app secret. Click Create.
16. Click Generate
17. Enter O365workspaceKey. Paste the Azure Sentinel Workspace Key. Click Create.
18. Click O365clientsecret and copy the current version string to a temp location.
19. Click O365workspaceKey and copy the current version stringto a temp location.
20. Go to the Overiew blade. Copy the DNS Name to a temp location.
Note: you will need to create additional keys if you have multiple tenants.
### Confiugure Settings for the Function
1. Go to the Azure Portal.
2. Go to the resource group that was created. Click the Function.
3. Click Platform Features Tab.
4. Click Configuration under General.
5. click edit next to clientSecret.
6. Update the value using your copied properties.
* @Microsoft.KeyVault(SecretUri=https://<dnsname>/secrets/O365Tenant1_clientSecret/<versionstring>)
7. Click Ok.
8. click edit next to workspaceKey.
9. Update the value using your copied properties
* @Microsoft.KeyVault(SecretUri=https://<dnsname>/secrets/O365workspacekey/<versionstring>)
10. Click Ok.
11. Update each setting
* clientID = AAD app registration id
* contentTypes = Audit.General or Audit.General,DLP.All or DLP.All
* domain = <domain> from <domain>.onmicrosoft.com
* publisher is a random guid for throttling that we used in steps to create subscription.
* recordTypes This can be 0 or a list of record types comma seperated like 28,40,41 (see https://docs.microsoft.com/en-us/office/office-365-management-api/office-365-management-activity-api-schema#auditlogrecordtype)
* tenantGuid is your AAD tenant guid.
* workspaceId is your Azure Sentinel workspace id
12. Click Save
13. Go back to the function and click start under the overview blade.

Просмотреть файл

@ -19,20 +19,35 @@ relevantTechniques:
- T1496
query: |
let szOperationNames = dynamic(["Create or Update Virtual Machine", "Create Deployment"]);
let starttime = 7d;
let endtime = 1d;
AzureActivity
| where TimeGenerated >= startofday(ago(7d))
| where OperationName == "Create or Update Virtual Machine" or OperationName == "Create Deployment"
| where ActivityStatus == "Succeeded"
| make-series dResourceCount=dcount(ResourceId) default=0 on EventSubmissionTimestamp in range(startofday(ago(7d)), now(), 1d) by Caller
| where TimeGenerated between (startofday(ago(starttime)) .. startofday(ago(endtime)))
| where OperationName in~ (szOperationNames)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), ActivityTimeStamp = makelist(TimeGenerated), ActivityStatus = makelist(ActivityStatus),
OperationIds = makelist(OperationId), CallerIpAddress = makelist(CallerIpAddress), CorrelationId = makelist(CorrelationId)
by ResourceId, Caller, OperationName, Resource, ResourceGroup
| mvexpand CallerIpAddress
| where isnotempty(CallerIpAddress)
| make-series dResourceCount=dcount(ResourceId) default=0 on StartTimeUtc in range(startofday(ago(7d)), now(), 1d)
by Caller, tostring(ActivityTimeStamp), tostring(ActivityStatus), tostring(OperationIds), tostring(CallerIpAddress), tostring(CorrelationId), ResourceId, OperationName, Resource, ResourceGroup
| extend (RSquare,Slope,Variance,RVariance,Interception,LineFit)=series_fit_line(dResourceCount)
| where Slope > 0.2
| join kind=leftsemi (
// Last day's activity is anomalous
AzureActivity
| where TimeGenerated >= startofday(ago(1d))
| where OperationName == "Create or Update Virtual Machine" or OperationName == "Create Deployment"
| where ActivityStatus == "Succeeded"
| make-series dResourceCount=dcount(ResourceId) default=0 on EventSubmissionTimestamp in range(startofday(ago(1d)), now(), 1d) by Caller
| where TimeGenerated >= startofday(ago(endtime))
| where OperationName in~ (szOperationNames)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), ActivityTimeStamp = makelist(TimeGenerated), ActivityStatus = makelist(ActivityStatus),
OperationIds = makelist(OperationId), CallerIpAddress = makelist(CallerIpAddress), CorrelationId = makelist(CorrelationId)
by ResourceId, Caller, OperationName, Resource, ResourceGroup
| mvexpand CallerIpAddress
| where isnotempty(CallerIpAddress)
| make-series dResourceCount=dcount(ResourceId) default=0 on StartTimeUtc in range(startofday(ago(1d)), now(), 1d)
by Caller, tostring(ActivityTimeStamp), tostring(ActivityStatus), tostring(OperationIds), tostring(CallerIpAddress), tostring(CorrelationId), ResourceId, OperationName, Resource, ResourceGroup
| extend (RSquare,Slope,Variance,RVariance,Interception,LineFit)=series_fit_line(dResourceCount)
| where Slope >0.2
) on Caller
| where Slope > 0.2
) on Caller, CallerIpAddress
| mvexpand todynamic(ActivityTimeStamp), todynamic(ActivityStatus), todynamic(OperationIds), todynamic(CorrelationId)
| extend timestamp = ActivityTimeStamp, AccountCustomEntity = Caller, IPCustomEntity = CallerIpAddress

Просмотреть файл

@ -18,17 +18,21 @@ relevantTechniques:
- T1098
query: |
let createRoleAssignmentActivity = AzureActivity
| where OperationName == "Create role assignment"
| where ActivityStatus == "Succeeded"
| project TimeGenerated, EventSubmissionTimestamp, Caller, CallerIpAddress, SubscriptionId, ResourceId, OperationName;
let starttime = 14d;
let endtime = 1d;
// The number of operations below which an IP address is considered an unusual source of role assignment operations
let alertOperationThreshold = 5;
let createRoleAssignmentActivity = AzureActivity
| where OperationName == "Create role assignment";
createRoleAssignmentActivity
| where TimeGenerated >= ago(14d)
| summarize count() by CallerIpAddress
| where count_ <= alertOperationThreshold
| join kind = rightsemi ( createRoleAssignmentActivity
| where TimeGenerated >= ago(1d)
) on CallerIpAddress
| extend timestamp = TimeGenerated, AccountCustomEntity = Caller, IPCustomEntity = CallerIpAddress
| where TimeGenerated between (ago(starttime) .. ago(endtime))
| summarize count() by CallerIpAddress, Caller
| where count_ >= alertOperationThreshold
| join kind = rightanti (
createRoleAssignmentActivity
| where TimeGenerated > ago(endtime)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), ActivityTimeStamp = makelist(TimeGenerated), ActivityStatus = makelist(ActivityStatus),
OperationIds = makelist(OperationId), CorrelationId = makelist(CorrelationId), ActivityCountByCallerIPAddress = count()
by ResourceId, CallerIpAddress, Caller, OperationName, Resource, ResourceGroup
) on CallerIpAddress, Caller
| extend timestamp = StartTimeUtc, AccountCustomEntity = Caller, IPCustomEntity = CallerIpAddress

Просмотреть файл

@ -8,7 +8,7 @@ requiredDataConnectors:
dataTypes:
- AzureActivity
queryFrequency: 1d
queryPeriod: 7d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
@ -17,19 +17,23 @@ relevantTechniques:
- T1496
query: |
AzureActivity
| where TimeGenerated between (ago(7d) .. ago(1d))
| where OperationName =~ "Create or Update Virtual Machine" or OperationName =~ "Create Deployment"
| where ActivityStatus =~ "Succeeded"
| project Resource, ResourceGroup, Caller, OperationName, CallerIpAddress
| evaluate basket()
let szOperationNames = dynamic(["Create or Update Virtual Machine", "Create Deployment"]);
let starttime = 14d;
let endtime = 1d;
let RareCaller = AzureActivity
| where TimeGenerated between (ago(starttime) .. ago(endtime))
| where OperationName in~ (szOperationNames)
| project ResourceGroup, Caller, OperationName, CallerIpAddress
| join kind=rightantisemi (
AzureActivity
| where TimeGenerated >ago(1d)
| where OperationName =~ "Create or Update Virtual Machine" or OperationName =~ "Create Deployment"
| where ActivityStatus =~ "Succeeded"
| project TimeGenerated, Resource, ResourceGroup, Caller, OperationName, CallerIpAddress
) on ResourceGroup
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), CreateCount = count() by Caller, CallerIpAddress, Resource, ResourceGroup, OperationName
| where TimeGenerated > ago(endtime)
| where OperationName in~ (szOperationNames)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), ActivityStatus = makeset(ActivityStatus), OperationIds = makeset(OperationId), CallerIpAddress = makeset(CallerIpAddress)
by ResourceId, Caller, OperationName, Resource, ResourceGroup
) on Caller, ResourceGroup
| mvexpand CallerIpAddress
| where isnotempty(CallerIpAddress);
let Counts = RareCaller | summarize ActivityCountByCaller = count() by Caller;
RareCaller | join kind= inner (Counts) on Caller | project-away Caller1
| extend timestamp = StartTimeUtc, AccountCustomEntity = Caller, IPCustomEntity = CallerIpAddress
| sort by CreateCount desc nulls last
| sort by ActivityCountByCaller desc nulls last

Просмотреть файл

@ -9,7 +9,7 @@ requiredDataConnectors:
- connectorId: AzureActivity
dataTypes:
- AzureActivity
queryFrequency: 1d
queryFrequency: 14d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
@ -21,12 +21,24 @@ relevantTechniques:
- T1098
query: |
let timeframe = 1d;
let starttime = 14d;
let endtime = 1d;
// The number of operations below which an IP address is considered an unusual source of role assignment operations
let alertOperationThreshold = 5;
let SensitiveOperationList = dynamic(
["List keys", "List Storage Account Keys", "Register Subscription", "Create or Update Snapshot", "Create or Update Network Security Group"]);
AzureActivity
| where TimeGenerated >= ago(timeframe)
| where ActivityStatus =~ "Succeeded"
let SensitiveActivity = AzureActivity
| where OperationName in~ (SensitiveOperationList)
| project TimeGenerated, OperationName, ActivityStatus, Resource, Caller, CallerIpAddress, ResourceGroup, SubscriptionId, Authorization
| extend timestamp = TimeGenerated, AccountCustomEntity = Caller, IPCustomEntity = CallerIpAddress
| where ActivityStatus == "Succeeded";
SensitiveActivity
| where TimeGenerated between (ago(starttime) .. ago(endtime))
| summarize count() by CallerIpAddress, Caller
| where count_ >= alertOperationThreshold
| join kind = rightanti (
SensitiveActivity
| where TimeGenerated > ago(endtime)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), ActivityTimeStamp = makelist(TimeGenerated), ActivityStatus = makelist(ActivityStatus),
OperationIds = makelist(OperationId), CorrelationIds = makelist(CorrelationId), Resources = makelist(Resource), ResourceGroups = makelist(ResourceGroup), ResourceIds = makelist(ResourceId), ActivityCountByCallerIPAddress = count()
by CallerIpAddress, Caller, OperationName
) on CallerIpAddress, Caller
| extend timestamp = StartTimeUtc, AccountCustomEntity = Caller, IPCustomEntity = CallerIpAddress

Просмотреть файл

@ -20,6 +20,9 @@ relevantTechniques:
- T1008
query: |
let starttime = 10d;
let endtime = 1d;
let threshold = 100;
let nxDomainDnsEvents = DnsEvents
| where ResultCode == "3"
| where QueryType in ("A", "AAAA")
@ -27,16 +30,16 @@ query: |
| where Name !contains "/"
| where Name contains ".";
nxDomainDnsEvents
| where TimeGenerated > ago(1d)
| where TimeGenerated > ago(endtime)
| extend sld = tostring(split(Name, ".")[-2])
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), dcount(sld) by ClientIP
| where dcount_sld > 100
| where dcount_sld > threshold
// Filter out previously seen IPs
| join kind=leftanti (nxDomainDnsEvents
| where TimeGenerated between(ago(10d)..ago(1d))
| where TimeGenerated between(ago(starttime)..ago(endtime))
| extend sld = tostring(split(Name, ".")[-2])
| summarize dcount(sld) by ClientIP
| where dcount_sld > 100 ) on ClientIP
| where dcount_sld > threshold ) on ClientIP
// Pull out sample NXDomain responses for those remaining potentially infected IPs
| join kind = inner (nxDomainDnsEvents | summarize by Name, ClientIP) on ClientIP
| summarize StartTimeUtc = min(StartTimeUtc), EndTimeUtc = max(EndTimeUtc), sampleNXDomainList=make_list(Name, 100) by ClientIP, dcount_sld

Просмотреть файл

@ -19,19 +19,20 @@ relevantTechniques:
- T1046
query: |
let starttime = 8d;
let endtime = 1d;
let threshold = 10;
DnsEvents
| where TimeGenerated > ago(1d)
| where TimeGenerated > ago(endtime)
| where Name contains "in-addr.arpa"
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), dcount(Name) by ClientIP
| where dcount_Name >10
| where dcount_Name > threshold
| project StartTimeUtc, EndTimeUtc, ClientIP , dcount_Name
| join kind=leftanti (DnsEvents
| where TimeGenerated between(ago(8d)..ago(1d))
| where TimeGenerated between(ago(starttime)..ago(endtime))
| where Name contains "in-addr.arpa"
| summarize dcount(Name) by ClientIP, bin(TimeGenerated, 1d)
| where dcount_Name >10
| where dcount_Name > threshold
| project ClientIP , dcount_Name
) on ClientIP
| extend timestamp = StartTimeUtc, IPCustomEntity = ClientIP

Просмотреть файл

@ -19,80 +19,17 @@ relevantTechniques:
- T1496
query: |
let timeframe = 1d;
DnsEvents
| where TimeGenerated > ago(1d)
| where TimeGenerated > ago(timeframe)
| where Name contains "."
| where Name contains "monerohash.com"
or Name contains "do-dear.com"
or Name contains "xmrminerpro.com"
or Name contains "secumine.net"
or Name contains "xmrpool.com"
or Name contains "minexmr.org"
or Name contains "hashanywhere.com"
or Name contains "xmrget.com"
or Name contains "mininglottery.eu"
or Name contains "minergate.com"
or Name contains "moriaxmr.com"
or Name contains "multipooler.com"
or Name contains "moneropools.com"
or Name contains "xmrpool.eu"
or Name contains "coolmining.club"
or Name contains "supportxmr.com"
or Name contains "minexmr.com"
or Name contains "hashvault.pro"
or Name contains "xmrpool.net"
or Name contains "crypto-pool.fr"
or Name contains "xmr.pt"
or Name contains "miner.rocks"
or Name contains "walpool.com"
or Name contains "herominers.com"
or Name contains "gntl.co.uk"
or Name contains "semipool.com"
or Name contains "coinfoundry.org"
or Name contains "cryptoknight.cc"
or Name contains "fairhash.org"
or Name contains "baikalmine.com"
or Name contains "tubepool.xyz"
or Name contains "fairpool.xyz"
or Name contains "asiapool.io"
or Name contains "coinpoolit.webhop.me"
or Name contains "nanopool.org"
or Name contains "moneropool.com"
or Name contains "miner.center"
or Name contains "prohash.net"
or Name contains "poolto.be"
or Name contains "cryptoescrow.eu"
or Name contains "monerominers.net"
or Name contains "cryptonotepool.org"
or Name contains "extrmepool.org"
or Name contains "webcoin.me"
or Name contains "kippo.eu"
or Name contains "hashinvest.ws"
or Name contains "monero.farm"
or Name contains "supportxmr.com"
or Name contains "xmrpool.eu"
or Name contains "linux-repository-updates.com"
or Name contains "1gh.com"
or Name contains "dwarfpool.com"
or Name contains "hash-to-coins.com"
or Name contains "hashvault.pro"
or Name contains "pool-proxy.com"
or Name contains "hashfor.cash"
or Name contains "fairpool.cloud"
or Name contains "litecoinpool.org"
or Name contains "mineshaft.ml"
or Name contains "abcxyz.stream"
or Name contains "moneropool.ru"
or Name contains "cryptonotepool.org.uk"
or Name contains "extremepool.org"
or Name contains "extremehash.com"
or Name contains "hashinvest.net"
or Name contains "unipool.pro"
or Name contains "crypto-pools.org"
or Name contains "monero.net"
or Name contains "backup-pool.com"
or Name contains "mooo.com"
or Name contains "freeyy.me"
or Name contains "cryptonight.net"
or Name contains "shscrypto.net"
| where Name has_any ("monerohash.com", "do-dear.com", "xmrminerpro.com", "secumine.net", "xmrpool.com", "minexmr.org", "hashanywhere.com",
"xmrget.com", "mininglottery.eu", "minergate.com", "moriaxmr.com", "multipooler.com", "moneropools.com", "xmrpool.eu", "coolmining.club",
"supportxmr.com", "minexmr.com", "hashvault.pro", "xmrpool.net", "crypto-pool.fr", "xmr.pt", "miner.rocks", "walpool.com", "herominers.com",
"gntl.co.uk", "semipool.com", "coinfoundry.org", "cryptoknight.cc", "fairhash.org", "baikalmine.com", "tubepool.xyz", "fairpool.xyz", "asiapool.io",
"coinpoolit.webhop.me", "nanopool.org", "moneropool.com", "miner.center", "prohash.net", "poolto.be", "cryptoescrow.eu", "monerominers.net", "cryptonotepool.org",
"extrmepool.org", "webcoin.me", "kippo.eu", "hashinvest.ws", "monero.farm", "supportxmr.com", "xmrpool.eu", "linux-repository-updates.com", "1gh.com",
"dwarfpool.com", "hash-to-coins.com", "hashvault.pro", "pool-proxy.com", "hashfor.cash", "fairpool.cloud", "litecoinpool.org", "mineshaft.ml", "abcxyz.stream",
"moneropool.ru", "cryptonotepool.org.uk", "extremepool.org", "extremehash.com", "hashinvest.net", "unipool.pro", "crypto-pools.org", "monero.net",
"backup-pool.com", "mooo.com", "freeyy.me", "cryptonight.net", "shscrypto.net")
| extend timestamp = TimeGenerated, IPCustomEntity = ClientIP, HostCustomEntity = Computer

Просмотреть файл

@ -17,39 +17,12 @@ relevantTechniques:
- T1048
query: |
let timeframe = 1d;
DnsEvents
| where TimeGenerated > ago(1d)
| where TimeGenerated > ago(timeframe)
| where Name contains "."
| where Name contains "tor2web.org"
or Name contains "tor2web.com"
or Name contains "torlink.co"
or Name contains "onion.to"
or Name contains "onion.ink"
or Name contains "onion.cab"
or Name contains "onion.nu"
or Name contains "onion.link"
or Name contains "onion.it"
or Name contains "onion.city"
or Name contains "onion.direct"
or Name contains "onion.top"
or Name contains "onion.casa"
or Name contains "onion.plus"
or Name contains "onion.rip"
or Name contains "onion.dog"
or Name contains "tor2web.fi"
or Name contains "tor2web.blutmagie.de"
or Name contains "onion.sh"
or Name contains "onion.lu"
or Name contains "onion.pet"
or Name contains "t2w.pw"
or Name contains "tor2web.ae.org"
or Name contains "tor2web.io"
or Name contains "tor2web.xyz"
or Name contains "onion.lt"
or Name contains "s1.tor-gateways.de"
or Name contains "s2.tor-gateways.de"
or Name contains "s3.tor-gateways.de"
or Name contains "s4.tor-gateways.de"
or Name contains "s5.tor-gateways.de"
or Name contains "hiddenservice.net"
| where Name has_any ("tor2web.org", "tor2web.com", "torlink.co", "onion.to", "onion.ink", "onion.cab", "onion.nu", "onion.link",
"onion.it", "onion.city", "onion.direct", "onion.top", "onion.casa", "onion.plus", "onion.rip", "onion.dog", "tor2web.fi",
"tor2web.blutmagie.de", "onion.sh", "onion.lu", "onion.pet", "t2w.pw", "tor2web.ae.org", "tor2web.io", "tor2web.xyz", "onion.lt",
"s1.tor-gateways.de", "s2.tor-gateways.de", "s3.tor-gateways.de", "s4.tor-gateways.de", "s5.tor-gateways.de", "hiddenservice.net")
| extend timestamp = TimeGenerated, IPCustomEntity = ClientIP, HostCustomEntity = Computer

Просмотреть файл

@ -1,7 +1,7 @@
id: 8ee967a2-a645-4832-85f4-72b635bcb3a6
name: Failed AzureAD logons but success logon to host
description: |
'Identifies a list of IP addresses with a minimum number (defualt of 5) of failed logon attempts to Azure Active Directory.
'Identifies a list of IP addresses with a minimum number (default of 5) of failed logon attempts to Azure Active Directory.
Uses that list to identify any successful remote logons to hosts from these IPs within the same timeframe.'
severity: Medium
requiredDataConnectors:
@ -24,36 +24,36 @@ tactics:
relevantTechniques:
- T1078
- T1110
query: |
let timeframe = 1d;
//Adjust this threshold to fit the environment
let signin_threshold = 5;
//Make a list of all IPs with failed signins to AAD above our threshold
let suspicious_signins =
SigninLogs
| where TimeGenerated >= ago(1d)
| where TimeGenerated >= ago(timeframe)
| where ResultType !in ("0", "50125", "50140")
| where IPAddress != "127.0.0.1"
| summarize count() by IPAddress
| where count_ > signin_threshold
| summarize make_list(IPAddress);
//See if any of these IPs have sucessfull logged into *nix hosts
//See if any of these IPs have sucessfully logged into *nix hosts
let linux_logons =
Syslog
| where TimeGenerated >= ago(1d)
| where TimeGenerated >= ago(timeframe)
| where Facility contains "auth" and ProcessName != "sudo"
| where SyslogMessage has "Accepted"
| extend SourceIP = extract("(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.(([0-9]{1,3})))",1,SyslogMessage)
| where SourceIP in (suspicious_signins)
| extend Reason = "Multiple failed AAD logins from IP address"
| project TimeGenerated, Computer, HostIP, IpAddress = SourceIP, SyslogMessage, Facility, ProcessName, Reason;
//See if any of these IPs have sucessfull logged into Windows hosts
//See if any of these IPs have sucessfully logged into Windows hosts
let win_logons =
SecurityEvent
| where TimeGenerated >= ago(1d)
| where TimeGenerated >= ago(timeframe)
| where EventID == 4624
| where LogonType == 10 or LogonType == 7 or LogonType == 3
| where LogonType in (10, 7, 3)
| where IpAddress != "-"
| where IpAddress in (suspicious_signins)
| extend Reason = "Multiple failed AAD logins from IP address"

Просмотреть файл

@ -2,13 +2,15 @@ id: 26a3b261-b997-4374-94ea-6c37f67f4f39
name: Known GALLIUM domains and hashes
description: |
'GALLIUM command and control domains and hash values for tools and malware used by GALLIUM.
Matches domain name IOCs related to the GALLIUM activity group with CommonSecurityLog, DnsEvents, VMConnection and SecurityEvents dataTypes.'
Matches domain name IOCs related to the GALLIUM activity group with CommonSecurityLog, DnsEvents, VMConnection and SecurityEvents dataTypes.
References: https://www.microsoft.com/security/blog/2019/12/12/gallium-targeting-global-telecom/ '
severity: High
requiredDataConnectors:
- connectorId: DNS
dataTypes:
- DnsEvents
- dataTypes:
- connectorId: AzureMonitor(VMInsights)
dataTypes:
- VMConnection
- connectorId: CiscoASA
dataTypes:
@ -31,24 +33,28 @@ query: |
let DomainNames = dynamic(["asyspy256.ddns.net","hotkillmail9sddcc.ddns.net","rosaf112.ddns.net","cvdfhjh1231.myftp.biz","sz2016rose.ddns.net","dffwescwer4325.myftp.biz","cvdfhjh1231.ddns.net"]);
let SHA1Hash = dynamic (["53a44c2396d15c3a03723fa5e5db54cafd527635", "9c5e496921e3bc882dc40694f1dcc3746a75db19", "aeb573accfd95758550cf30bf04f389a92922844", "79ef78a797403a4ed1a616c68e07fff868a8650a", "4f6f38b4cec35e895d91c052b1f5a83d665c2196", "1e8c2cac2e4ce7cbd33c3858eb2e24531cb8a84d", "e841a63e47361a572db9a7334af459ddca11347a", "c28f606df28a9bc8df75a4d5e5837fc5522dd34d", "2e94b305d6812a9f96e6781c888e48c7fb157b6b", "dd44133716b8a241957b912fa6a02efde3ce3025", "8793bf166cb89eb55f0593404e4e933ab605e803", "a39b57032dbb2335499a51e13470a7cd5d86b138", "41cc2b15c662bc001c0eb92f6cc222934f0beeea", "d209430d6af54792371174e70e27dd11d3def7a7", "1c6452026c56efd2c94cea7e0f671eb55515edb0", "c6b41d3afdcdcaf9f442bbe772f5da871801fd5a", "4923d460e22fbbf165bbbaba168e5a46b8157d9f", "f201504bd96e81d0d350c3a8332593ee1c9e09de", "ddd2db1127632a2a52943a2fe516a2e7d05d70d2"]);
let SHA256Hash = dynamic (["9ae7c4a4e1cfe9b505c3a47e66551eb1357affee65bfefb0109d02f4e97c06dd", "7772d624e1aed327abcd24ce2068063da0e31bb1d5d3bf2841fc977e198c6c5b", "657fc7e6447e0065d488a7db2caab13071e44741875044f9024ca843fe4e86b5", "2ef157a97e28574356e1d871abf75deca7d7a1ea662f38b577a06dd039dbae29", "52fd7b90d7144ac448af4008be639d4d45c252e51823f4311011af3207a5fc77", "a370e47cb97b35f1ae6590d14ada7561d22b4a73be0cb6df7e851d85054b1ac3", "5bf80b871278a29f356bd42af1e35428aead20cd90b0c7642247afcaaa95b022", "6f690ccfd54c2b02f0c3cb89c938162c10cbeee693286e809579c540b07ed883", "3c884f776fbd16597c072afd81029e8764dd57ee79d798829ca111f5e170bd8e", "1922a419f57afb351b58330ed456143cc8de8b3ebcbd236d26a219b03b3464d7", "fe0e4ef832b62d49b43433e10c47dc51072959af93963c790892efc20ec422f1", "7ce9e1c5562c8a5c93878629a47fe6071a35d604ed57a8f918f3eadf82c11a9c", "178d5ee8c04401d332af331087a80fb4e5e2937edfba7266f9be34a5029b6945", "51f70956fa8c487784fd21ab795f6ba2199b5c2d346acdeef1de0318a4c729d9", "889bca95f1a69e94aaade1e959ed0d3620531dc0fc563be9a8decf41899b4d79", "332ddaa00e2eb862742cb8d7e24ce52a5d38ffb22f6c8bd51162bd35e84d7ddf", "44bcf82fa536318622798504e8369e9dcdb32686b95fcb44579f0b4efa79df08", "63552772fdd8c947712a2cff00dfe25c7a34133716784b6d486227384f8cf3ef", "056744a3c371b5938d63c396fe094afce8fb153796a65afa5103e1bffd7ca070"]);
let SigNames = dynamic(["TrojanDropper:Win32/BlackMould.A!dha", "Trojan:Win32/BlackMould.B!dha", "Trojan:Win32/QuarkBandit.A!dha", "Trojan:Win32/Sidelod.A!dha"]);
(union isfuzzy=true
(CommonSecurityLog
| where TimeGenerated >= ago(timeframe)
| parse Message with * '(' DNSName ')' *
| where isnotempty(FileHash)
| where FileHash in (SHA256Hash) or DNSName in~ (DomainNames)
| extend Account = SourceUserID, Host = DeviceName, IPAddress = SourceIP),
| extend Account = SourceUserID, Computer = DeviceName, IPAddress = SourceIP
),
(DnsEvents
| where TimeGenerated >= ago(timeframe)
| extend DNSName = Name
| where isnotempty(DNSName)
| where DNSName in~ (DomainNames)
| extend timestamp = TimeGenerated, IPAddress = ClientIP, DNSName = Name, Host = Computer),
| extend IPAddress = ClientIP
),
(VMConnection
| where TimeGenerated >= ago(timeframe)
| parse RemoteDnsCanonicalNames with * '["' DNSName '"]' *
| where isnotempty(DNSName)
| where DNSName in~ (DomainNames)
| extend timestamp = TimeGenerated, IPAddress = RemoteIp, Host = Computer
| extend IPAddress = RemoteIp
),
(Event
//This query uses sysmon data depending on table name used this may need updataing
@ -60,5 +66,12 @@ query: |
| parse Hashes with * 'SHA1=' SHA1 ',' *
| where isnotempty(Hashes)
| where Hashes in (SHA1Hash)
| extend timestamp = TimeGenerated, AccountCustomEntity = UserName, HostCustomEntity = Computer )
| extend Account = UserName
),
(SecurityAlert
| where TimeGenerated >= ago(timeframe)
| where Entities has_any (SigNames)
| extend Computer = tostring(parse_json(Entities)[0].HostName)
)
)
| extend timestamp = TimeGenerated, AccountCustomEntity = Account, HostCustomEntity = Computer, IPCustomEntity = IPAddress

Просмотреть файл

@ -1,7 +1,7 @@
id: 1ce5e766-26ab-4616-b7c8-3b33ae321e80
name: Failed host logons but success logon to AzureAD
description: |
'Identifies a list of IP addresses with a minimum number(defualt of 5) of failed logon attempts to remote hosts.
'Identifies a list of IP addresses with a minimum number(default of 5) of failed logon attempts to remote hosts.
Uses that list to identify any successful logons to Azure Active Directory from these IPs within the same timeframe.'
severity: Medium
requiredDataConnectors:
@ -27,14 +27,15 @@ relevantTechniques:
query: |
let timeframe = 1d;
//Adjust this threshold to fit environment
let signin_threshold = 5;
//Make a list of IPs with failed Windows host logins above threshold
let win_fails =
SecurityEvent
| where TimeGenerated >= ago(1d)
| where TimeGenerated >= ago(timeframe)
| where EventID == 4625
| where LogonType == 10 or LogonType == 7 or LogonType == 3
| where LogonType in (10, 7, 3)
| where IpAddress != "-"
| summarize count() by IpAddress
| where count_ > signin_threshold
@ -42,7 +43,7 @@ query: |
//Make a list of IPs with failed *nix host logins above threshold
let nix_fails =
Syslog
| where TimeGenerated > ago(1d)
| where TimeGenerated > ago(timeframe)
| where Facility contains 'auth' and ProcessName != 'sudo'
| extend SourceIP = extract("(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.(([0-9]{1,3})))",1,SyslogMessage)
| where SourceIP != "" and SourceIP != "127.0.0.1"
@ -51,8 +52,8 @@ query: |
| summarize make_list(SourceIP);
//See if any of the IPs with failed host logins hve had a sucessful Azure AD login
SigninLogs
| where TimeGenerated > ago(1d)
| where TimeGenerated > ago(timeframe)
| where ResultType !in ("0", "50125", "50140")
| where IPAddress in (win_fails) or IPAddress in (nix_fails)
| extend Reason= "Multiple failed host logins from IP address"
| extend Reason= "Multiple failed host logins from IP address with successful Azure AD login"
| extend timstamp = TimeGenerated, AccountCustomEntity = UserPrincipalName, IPCustomEntity = IPAddress

Просмотреть файл

@ -0,0 +1,120 @@
id: 7ee72a9e-2e54-459c-bc8a-8c08a6532a63
name: Known IRIDIUM IP
description: |
'IRIDIUM command and control IP. Identifies a match across various data feeds for IP IOCs related to the IRIDIUM activity group.'
severity: High
requiredDataConnectors:
- connectorId: Office365
dataTypes:
- OfficeActivity
- connectorId: DNS
dataTypes:
- DnsEvents
- connectorId: AzureMonitor(VMInsights)
dataTypes:
- VMConnection
- connectorId: CiscoASA
dataTypes:
- CommonSecurityLog
- connectorId: PaloAltoNetworks
dataTypes:
- CommonSecurityLog
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
- connectorId: AzureMonitor(WireData)
dataTypes:
- WireData
- connectorId: AzureMonitor(IIS)
dataTypes:
- W3CIISLog
- connectorId: AzureActivity
dataTypes:
- AzureActivity
- connectorId: AWS
dataTypes:
- AWSCloudTrail
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
query: |
let timeframe = 1d;
let IPList = dynamic(["154.223.45.38","185.141.207.140","185.234.73.19","216.245.210.106","51.91.48.210","46.255.230.229"]);
(union isfuzzy=true
(CommonSecurityLog
| where TimeGenerated >= ago(timeframe)
| where isnotempty(SourceIP) or isnotempty(DestinationIP)
| where SourceIP in (IPList) or DestinationIP in (IPList) or Message has_any (IPList)
| extend IPMatch = case(SourceIP in (IPList), "SourceIP", DestinationIP in (IPList), "DestinationIP", "Message")
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by SourceIP, DestinationIP, DeviceProduct, DeviceAction, Message, Protocol, SourcePort, DestinationPort, DeviceAddress, DeviceName, IPMatch
| extend timestamp = StartTimeUtc, IPCustomEntity = case(IPMatch == "SourceIP", SourceIP, IPMatch == "DestinationIP", DestinationIP, "IP in Message Field")
),
(OfficeActivity
| where TimeGenerated >= ago(timeframe)
|extend SourceIPAddress = ClientIP, Account = UserId
| where SourceIPAddress in (IPList)
| extend timestamp = TimeGenerated , IPCustomEntity = SourceIPAddress , AccountCustomEntity = Account
),
(DnsEvents
| where TimeGenerated >= ago(timeframe)
| extend DestinationIPAddress = IPAddresses, Host = Computer
| where DestinationIPAddress has_any (IPList)
| extend timestamp = TimeGenerated, IPCustomEntity = DestinationIPAddress, HostCustomEntity = Host
),
(VMConnection
| where TimeGenerated >= ago(timeframe)
| where isnotempty(SourceIp) or isnotempty(DestinationIp)
| where SourceIp in (IPList) or DestinationIp in (IPList)
| extend IPMatch = case( SourceIp in (IPList), "SourceIP", DestinationIp in (IPList), "DestinationIP", "None")
| extend timestamp = TimeGenerated , IPCustomEntity = case(IPMatch == "SourceIP", SourceIp, IPMatch == "DestinationIP", DestinationIp, "None"), Host = Computer
),
(Event
| where TimeGenerated >= ago(timeframe)
| where Source == "Microsoft-Windows-Sysmon"
| where EventID == 3
| extend EvData = parse_xml(EventData)
| extend EventDetail = EvData.DataItem.EventData.Data
| extend SourceIP = EventDetail.[9].["#text"], DestinationIP = EventDetail.[14].["#text"]
| where SourceIP in (IPList) or DestinationIP in (IPList)
| extend IPMatch = case( SourceIP in (IPList), "SourceIP", DestinationIP in (IPList), "DestinationIP", "None")
| extend timestamp = TimeGenerated, AccountCustomEntity = UserName, HostCustomEntity = Computer , IPCustomEntity = case(IPMatch == "SourceIP", SourceIP, IPMatch == "DestinationIP", DestinationIP, "None")
),
(WireData
| where TimeGenerated >= ago(timeframe)
| where isnotempty(RemoteIP)
| where RemoteIP in (IPList)
| extend timestamp = TimeGenerated, IPCustomEntity = RemoteIP, HostCustomEntity = Computer
),
(SigninLogs
| where TimeGenerated >= ago(timeframe)
| where isnotempty(IPAddress)
| where IPAddress in (IPList)
| extend timestamp = TimeGenerated, AccountCustomEntity = UserPrincipalName, IPCustomEntity = IPAddress
),
(W3CIISLog
| where TimeGenerated >= ago(timeframe)
| where isnotempty(cIP)
| where cIP in (IPList)
| extend timestamp = TimeGenerated, IPCustomEntity = cIP, HostCustomEntity = Computer, AccountCustomEntity = csUserName
),
(AzureActivity
| where TimeGenerated >= ago(timeframe)
| where isnotempty(CallerIpAddress)
| where CallerIpAddress in (IPList)
| extend timestamp = TimeGenerated, IPCustomEntity = CallerIpAddress, AccountCustomEntity = Caller
),
(
AWSCloudTrail
| where TimeGenerated >= ago(timeframe)
| where isnotempty(SourceIpAddress)
| where SourceIpAddress in (IPList)
| extend timestamp = TimeGenerated, IPCustomEntity = SourceIpAddress, AccountCustomEntity = UserIdentityUserName
)
)

Просмотреть файл

@ -0,0 +1,43 @@
id: 65c78944-930b-4cae-bd79-c3664ae30ba7
name: MFA disabled for a user
description: |
'Multi-Factor Authentication (MFA) helps prevent credential compromise. This alert identifies when an attempt has been made to diable MFA for a user '
severity: Medium
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
- connectorId: AWS
dataTypes:
- AWSCloudTrail
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
relevantTechniques:
- T1098
query: |
let timeframe = 1d;
(union isfuzzy=true
(AuditLogs
| where TimeGenerated >= ago(timeframe)
| where OperationName =~ "Disable Strong Authentication"
| extend IPAddress = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)
| extend InitiatedByUser = iff(isnotempty(tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)),
tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName), tostring(parse_json(tostring(InitiatedBy.app)).displayName))
| extend Targetprop = todynamic(TargetResources)
| extend TargetUser = tostring(Targetprop[0].userPrincipalName)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by User = TargetUser, InitiatedByUser , Operation = OperationName , CorrelationId, IPAddress, Category, Source = SourceSystem , AADTenantId, Type
),
(AWSCloudTrail
| where TimeGenerated >= ago(timeframe)
| where EventName in~ ("DeactivateMFADevice", "DeleteVirtualMFADevice")
| extend InstanceProfileName = tostring(parse_json(RequestParameters).InstanceProfileName)
| extend TargetUser = tostring(parse_json(RequestParameters).userName)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by User = TargetUser, Source = EventSource , Operation = EventName , TenantorInstance_Detail = InstanceProfileName, IPAddress = SourceIpAddress
)
)
| extend timestamp = StartTimeUtc, AccountCustomEntity = User, IPCustomEntity = IPAddress

Просмотреть файл

@ -5,8 +5,9 @@ description: |
to identify potential instances of executables of the same name having been recently run.'
severity: Medium
requiredDataConnectors:
- dataTypes:
- CommonSecurityLog
- connectorId: TrendMicro
dataTypes:
- CommonSecurityLog
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
@ -30,10 +31,10 @@ query: |
// Correlate suspect executables seen in TrendMicro rule updates with similar activity on endpoints
CommonSecurityLog
| where TimeGenerated >= ago(timeframe)
| where DeviceVendor == "Trend Micro"
| where Activity == "Deny List updated"
| where DeviceVendor =~ "Trend Micro"
| where Activity =~ "Deny List updated"
| where RequestURL endswith ".exe"
| project TimeGenerated, Activity , RequestURL , SourceIP, DestinationIP
| extend suspectExeName = tolower(tostring(split(RequestURL, '/')[-1]))
| join (endpointData) on $left.suspectExeName == $right.shortFileName
| extend timestamp = TimeGenerated, IPCustomEntity = DestinationIP, AccountCustomEntity = TargetUserName, HostCustomEntity = Computer
| extend timestamp = TimeGenerated, IPCustomEntity = DestinationIP, AccountCustomEntity = TargetUserName, HostCustomEntity = Computer, URLCustomEntity = RequestURL

Просмотреть файл

@ -10,12 +10,10 @@ requiredDataConnectors:
- AWSCloudTrail
- connectorId: Office365
dataTypes:
- OfficeActivity
- connectorId: WAF
- OfficeActivity
- connectorId: AzureMonitor(IIS)
dataTypes:
- AzureDiagnostics
- dataTypes:
- W3CIISLog
- W3CIISLog
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
@ -35,37 +33,32 @@ query: |
let UserAgentAll =
(union isfuzzy=true
(OfficeActivity
| where TimeGenerated >= ago(starttime)
| where TimeGenerated >= ago(starttime)
| where isnotempty(ExtendedProperties)
| where ExtendedProperties has "useragent"
| extend UserAgent = extractjson("$[0].Value", ExtendedProperties, typeof(string))
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by SourceIP = ClientIP, Account = UserId, Type
),
(
AzureDiagnostics
| where TimeGenerated >= ago(starttime)
| extend UserAgent = replace(@'\+', @' ', userAgent_s)
| where isnotempty(UserAgent)
| parse msg_s with * 'HTTPS request from' SourceIP ':' *
| where isnotempty(SourceIP)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by UserAgent, SourceIP, Type
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by UserAgent, SourceIP = ClientIP, Account = UserId, Type, RecordType, Operation
),
(
W3CIISLog
| where TimeGenerated >= ago(starttime)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by UserAgent = csUserAgent, SourceIP = cIP, Account = csUserName, Type
| where TimeGenerated >= ago(starttime)
| where isnotempty(csUserAgent)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by UserAgent = csUserAgent, SourceIP = cIP, Account = csUserName, Type, sSiteName, csMethod, csUriStem
),
(
AWSCloudTrail
| where TimeGenerated >= ago(starttime)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by UserAgent, SourceIP = SourceIpAddress, Account = UserIdentityUserName, Type
| where TimeGenerated >= ago(starttime)
| where isnotempty(UserAgent)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by UserAgent, SourceIP = SourceIpAddress, Account = UserIdentityUserName, Type, EventSource, EventName
));
UserAgentAll
| where StartTimeUtc < ago(endtime)
| summarize IP = dcount(SourceIP) by tostring(UserAgent), Account, Type
| summarize by tostring(UserAgent), SourceIP, Account, Type, RecordType, Operation, EventSource, EventName, sSiteName, csMethod, csUriStem
| join kind=rightanti
(
UserAgentAll
| where StartTimeUtc >= ago(endtime)
| summarize StartTimeUtc = min(StartTimeUtc), EndTimeUtc = max(EndTimeUtc), IP = dcount(SourceIP) by tostring(UserAgent), SourceIP, Account, Type
) on UserAgent, Account, Type
//| project UserAgent, SourceIP
| summarize StartTimeUtc = min(StartTimeUtc), EndTimeUtc = max(EndTimeUtc), count() by tostring(UserAgent), SourceIP, Account, Type, RecordType, Operation, EventSource, EventName, sSiteName, csMethod, csUriStem
) on UserAgent, SourceIP, Account, Type, RecordType, Operation, EventSource, EventName, sSiteName, csMethod, csUriStem
| extend timestamp = StartTimeUtc, IPCustomEntity = SourceIP, AccountCustomEntity = Account

Просмотреть файл

@ -8,7 +8,8 @@ requiredDataConnectors:
- connectorId: DNS
dataTypes:
- DnsEvents
- dataTypes:
- connectorId: AzureMonitor(VMInsights)
dataTypes:
- VMConnection
- connectorId: CiscoASA
dataTypes:

Просмотреть файл

@ -6,7 +6,8 @@ description: |
and could indicate credential compromise for the user account.'
severity: Medium
requiredDataConnectors:
- dataTypes:
- connectorId: CiscoASA
dataTypes:
- CommonSecurityLog
- connectorId: AzureActiveDirectory
dataTypes:

Просмотреть файл

@ -8,7 +8,8 @@ requiredDataConnectors:
- connectorId: DNS
dataTypes:
- DnsEvents
- dataTypes:
- connectorId: AzureMonitor(VMInsights)
dataTypes:
- VMConnection
- connectorId: CiscoASA
dataTypes:

Просмотреть файл

@ -0,0 +1,54 @@
id: 70b12a3b-4896-42cb-910c-5ffaf8d7987d
name: THALLIUM domains included in DCU takedown
description: |
'THALLIUM spearphishing and command and control domains included in December 2019 DCU/MSTIC takedown.
Matches domain name IOCs related to the THALLIUM activity group with CommonSecurityLog, DnsEvents, VMConnection and SecurityEvents dataTypes.
References: https://blogs.microsoft.com/on-the-issues/2019/12/30/microsoft-court-action-against-nation-state-cybercrime/ '
severity: High
requiredDataConnectors:
- connectorId: DNS
dataTypes:
- DnsEvents
- connectorId: AzureMonitor(VMInsights)
dataTypes:
- VMConnection
- connectorId: CiscoASA
dataTypes:
- CommonSecurityLog
- connectorId: PaloAltoNetworks
dataTypes:
- CommonSecurityLog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
- CredentialAccess
query: |
let timeframe = 1d;
let DomainNames = dynamic(["seoulhobi.biz", "reader.cash", "pieceview.club", "app-wallet.com", "bigwnet.com", "bitwoll.com", "cexrout.com", "change-pw.com", "checkprofie.com", "cloudwebappservice.com", "ctquast.com", "dataviewering.com", "day-post.com", "dialy-post.com", "documentviewingcom.com", "dovvn-mail.com", "down-error.com", "drivecheckingcom.com", "drog-service.com", "encodingmail.com", "filinvestment.com", "foldershareing.com", "golangapis.com", "hotrnall.com", "lh-logins.com", "login-use.com", "mail-down.com", "matmiho.com", "mihomat.com", "natwpersonal-online.com", "nidlogin.com", "nid-login.com", "nidlogon.com", "pw-change.com", "rnaii.com", "rnailm.com", "sec-live.com", "secrityprocessing.com", "securitedmode.com", "securytingmail.com", "set-login.com", "usrchecking.com", "com-serviceround.info", "mai1.info", "reviewer.mobi", "files-download.net", "fixcool.net", "hanrnaii.net", "office356-us.org", "smtper.org"]);
(union isfuzzy=true
(CommonSecurityLog
| where TimeGenerated >= ago(timeframe)
| parse Message with * '(' DNSName ')' *
| where isnotempty(FileHash)
| where DNSName in~ (DomainNames)
| extend Account = SourceUserID, Computer = DeviceName, IPAddress = SourceIP
),
(DnsEvents
| where TimeGenerated >= ago(timeframe)
| extend DNSName = Name
| where isnotempty(DNSName)
| where DNSName in~ (DomainNames)
| extend IPAddress = ClientIP
),
(VMConnection
| where TimeGenerated >= ago(timeframe)
| parse RemoteDnsCanonicalNames with * '["' DNSName '"]' *
| where isnotempty(DNSName)
| where DNSName in~ (DomainNames)
| extend IPAddress = RemoteIp
)
)
| extend timestamp = TimeGenerated, AccountCustomEntity = Account, HostCustomEntity = Computer, IPCustomEntity = IPAddress

Просмотреть файл

@ -11,7 +11,8 @@ requiredDataConnectors:
- connectorId: PaloAltoNetworks
dataTypes:
- CommonSecurityLog
- dataTypes:
- connectorId: AzureMonitor(VMInsights)
dataTypes:
- VMConnection
queryFrequency: 1h
queryPeriod: 14d

Просмотреть файл

@ -1,7 +1,7 @@
id: 871ba14c-88ef-48aa-ad38-810f26760ca3
name: Multiple users email forwarded to same destination
description: |
'Identifies when multiple user mailboxes are configured to forward to the same destination.
'Identifies when multiple (more than one) users mailboxes are configured to forward to the same destination.
This could be an attacker-controlled destination mailbox configured to collect mail from multiple compromised user accounts.'
severity: Medium
requiredDataConnectors:
@ -28,10 +28,21 @@ query: |
| extend parsed = parse_json(Parameters)
| extend parameterName = parsed[1].Name, fwdingDestination = tostring(parsed[1].Value)
| where isnotempty(fwdingDestination)
| summarize TimeGenerated=max(TimeGenerated) by UserId, ClientIP, fwdingDestination
| summarize TimeGenerated=max(TimeGenerated), userCount = dcount(UserId), UserId = make_list(UserId), ClientIP = makeset(ClientIP) by fwdingDestination
| where userCount > 1
| mvexpand UserId, ClientIP
| extend UserId = tostring(UserId), ClientIP = tostring(ClientIP)
| extend timestamp = TimeGenerated, AccountCustomEntity = UserId, IPCustomEntity = ClientIP
| extend ClientIPOnly = case(
ClientIP has ".", tostring(split(ClientIP,":")[0]),
ClientIP has "[", tostring(trim_start(@'[[]',tostring(split(ClientIP,"]")[0]))),
ClientIP
)
| extend Port = case(
ClientIP has ".", (split(ClientIP,":")[1]),
ClientIP has "[", tostring(split(ClientIP,"]:")[1]),
ClientIP
)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), DistinctUserCount = dcount(UserId), UserId = makeset(UserId),
Ports = makeset(Port), EventCount = count() by fwdingDestination, ClientIP = ClientIPOnly
| where DistinctUserCount > 1
| mvexpand UserId
| extend UserId = tostring(UserId), Ports = tostring(Ports)
| distinct StartTimeUtc, EndTimeUtc, UserId, DistinctUserCount, ClientIP, Ports, fwdingDestination, EventCount
| extend timestamp = StartTimeUtc, AccountCustomEntity = UserId, IPCustomEntity = ClientIP

Просмотреть файл

@ -22,6 +22,7 @@ query: |
let timeframe = 1d;
OfficeActivity
| where TimeGenerated >= ago(timeframe)
| where Operation in~ ( "AddMailbox-Permission", "Add-MailboxFolderPermission", "Set-Mailbox", "New-ManagementRoleAssignment")
| where Operation in~ ( "Add-MailboxPermission", "Add-MailboxFolderPermission", "Set-Mailbox", "New-ManagementRoleAssignment")
and not(UserId has_any ('NT AUTHORITY\\SYSTEM (Microsoft.Exchange.ServiceHost)','devilfish-applicationaccount') and Operation in~ ( "Add-MailboxPermission", "Set-Mailbox"))
| extend timestamp = TimeGenerated, AccountCustomEntity = UserId, IPCustomIdentity = ClientIP

Просмотреть файл

@ -2,7 +2,7 @@
name: SharePointFileOperation via previously unseen IPs
description: |
'Identifies when the volume of documents uploaded to or downloaded from Sharepoint by new IP addresses
exceeds a threshold (default is 100).'
exceeds a threshold (default is 50).'
severity: Medium
requiredDataConnectors:
- connectorId: Office365
@ -18,31 +18,32 @@ relevantTechniques:
- T1030
query: |
let threshold = 100;
let threshold = 50;
let szSharePointFileOperation = "SharePointFileOperation";
let szOperations = dynamic(["FileDownloaded", "FileUploaded"]);
let starttime = 14d;
let endtime = 1d;
let historicalActivity =
OfficeActivity
| where TimeGenerated between(ago(14d)..ago(1d))
| where TimeGenerated between(ago(starttime)..ago(endtime))
| where RecordType =~ szSharePointFileOperation
| where Operation in~ (szOperations)
| summarize historicalCount = count() by ClientIP;
| summarize historicalCount = count() by ClientIP, RecordType, Operation;
let recentActivity = OfficeActivity
| where TimeGenerated > ago(1d)
| where TimeGenerated > ago(endtime)
| where RecordType =~ szSharePointFileOperation
| where Operation in~ (szOperations)
| summarize recentCount = count() by ClientIP;
recentActivity | join kind= leftanti ( historicalActivity ) on ClientIP
// More than 100 downloads/uploads from a new IP
| where recentCount > threshold
| join kind = rightsemi
(OfficeActivity
| where TimeGenerated >= ago(1d)
| summarize min(Start_Time), max(Start_Time), recentCount = count() by ClientIP, RecordType, Operation;
let RareIP = recentActivity | join kind= leftanti ( historicalActivity ) on ClientIP, RecordType, Operation
// More than 50 downloads/uploads from a new IP
| where recentCount > threshold;
OfficeActivity
| where TimeGenerated >= ago(endtime)
| where RecordType =~ szSharePointFileOperation
| where Operation in~ (szOperations)
)
on ClientIP
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), count() by RecordType, Operation, UserType, UserId, ClientIP, OfficeWorkload, Site_Url
| extend timestamp = StartTimeUtc, AccountCustomEntity = UserId, IPCustomEntity = ClientIP
| order by Operation, UserId asc
| join kind= inner (RareIP) on ClientIP, RecordType, Operation
| where Start_Time between(min_Start_Time .. max_Start_Time)
| summarize StartTimeUtc = min(min_Start_Time), EndTimeUtc = max(max_Start_Time) by RecordType, Operation, UserType, UserId, ClientIP, OfficeWorkload, Site_Url, OfficeObjectId, UserAgent, IPSeenCount = recentCount
| extend timestamp = StartTimeUtc, AccountCustomEntity = UserId, IPCustomEntity = ClientIP, URLCustomEntity = Site_Url
| order by IPSeenCount desc, ClientIP asc, Operation asc, UserId asc

Просмотреть файл

@ -1,8 +1,8 @@
id: 5dd76a87-9f87-4576-bab3-268b0e2b338b
name: SharePointFileOperation via devices with previously unseen user agents
description: |
'Identifies if the number of documents downloaded from device(s) associated
with a previously unseen user agent exceeds a threshold (default is 10).'
'Identifies if the number of documents uploaded or downloaded from device(s) associated
with a previously unseen user agent exceeds a threshold (default is 5).'
severity: Medium
requiredDataConnectors:
- connectorId: Office365
@ -18,33 +18,37 @@ relevantTechniques:
- T1030
query: |
let threshold = 10;
let threshold = 5;
let szSharePointFileOperation = "SharePointFileOperation";
let szOperations = dynamic(["FileDownloaded", "FileUploaded"]);
let starttime = 14d;
let endtime = 1d;
let historicalActivity =
OfficeActivity
| where TimeGenerated between(ago(14d)..ago(1d))
| where TimeGenerated between(ago(starttime)..ago(endtime))
| where RecordType =~ szSharePointFileOperation
| where Operation in~ (szOperations)
| summarize historicalCount = count() by UserAgent;
| where isnotempty(UserAgent)
| summarize historicalCount = count() by UserAgent, RecordType, Operation;
let recentActivity = OfficeActivity
| where RecordType =~ szSharePointFileOperation
| where Operation in~ (szOperations)
| where TimeGenerated > ago(1d)
| summarize recentCount = count() by UserAgent;
recentActivity | join kind = leftanti (historicalActivity) on UserAgent
| project RecordType = szSharePointFileOperation, UserAgent, recentCount
| order by recentCount asc, UserAgent
// More than 10 downloads/uploads from a new user agent
| where recentCount > threshold
| join kind = rightsemi
(OfficeActivity
| where TimeGenerated >= ago(1d)
| where TimeGenerated > ago(endtime)
| where isnotempty(UserAgent)
| summarize min(Start_Time), max(Start_Time), recentCount = count() by UserAgent, RecordType, Operation;
let RareUserAgent = recentActivity | join kind = leftanti (historicalActivity) on UserAgent
| order by recentCount desc, UserAgent
// More than 3 downloads/uploads from a new user agent today
| where recentCount > threshold;
OfficeActivity
| where TimeGenerated > ago(endtime)
| where RecordType =~ szSharePointFileOperation
| where Operation in~ (szOperations)
)
on UserAgent
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), count() by RecordType, Operation, UserAgent, UserType, UserId, ClientIP, OfficeWorkload, Site_Url
| extend timestamp = StartTimeUtc, AccountCustomEntity = UserId, IPCustomEntity = ClientIP
| order by UserAgent asc, Operation asc, UserId asc
| where isnotempty(UserAgent)
| join kind= inner (RareUserAgent)
on UserAgent, RecordType, Operation
| where Start_Time between(min_Start_Time .. max_Start_Time)
| summarize StartTimeUtc = min(min_Start_Time), EndTimeUtc = max(max_Start_Time) by RecordType, Operation, UserAgent, UserType, UserId, ClientIP, OfficeWorkload, Site_Url, OfficeObjectId, UserAgentSeenCount = recentCount
| extend timestamp = StartTimeUtc, AccountCustomEntity = UserId, IPCustomEntity = ClientIP, URLCustomEntity = Site_Url
| order by UserAgentSeenCount desc, UserAgent asc, Operation asc, UserId asc

Просмотреть файл

@ -28,7 +28,16 @@ query: |
| where UserType in~ ("Admin","DcAdmin")
// Only admin or global-admin can disable/remove policy
| where Operation startswith "Remove-" or Operation startswith "Disable-"
| where Operation contains "AntiPhish" or Operation contains "SafeAttachment"
or Operation contains "SafeLinks" or Operation contains "Dlp" or Operation contains "Audit"
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), OperationCount = count() by Operation, UserType, UserId, ClientIP, ResultStatus, Parameters
| where Operation has_any ("AntiPhish", "SafeAttachment", "SafeLinks", "Dlp", "Audit")
| extend ClientIPOnly = case(
ClientIP has ".", tostring(split(ClientIP,":")[0]),
ClientIP has "[", tostring(trim_start(@'[[]',tostring(split(ClientIP,"]")[0]))),
ClientIP
)
| extend Port = case(
ClientIP has ".", (split(ClientIP,":")[1]),
ClientIP has "[", tostring(split(ClientIP,"]:")[1]),
ClientIP
)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), OperationCount = count() by Operation, UserType, UserId, ClientIP = ClientIPOnly, Port, ResultStatus, Parameters
| extend timestamp = StartTimeUtc, AccountCustomEntity = UserId, IPCustomEntity = ClientIP

Просмотреть файл

@ -47,5 +47,5 @@ query: |
| extend DNS_TimeGenerated = TimeGenerated
) on $left.DomainName==$right.Name
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, DNS_TimeGenerated, Computer, ClientIP, Name, QueryType
| extend timestamp = DNS_TimeGenerated, HostCustomEntity = Computer, IPCustomEntity = ClientIP
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, Url, DNS_TimeGenerated, Computer, ClientIP, Name, QueryType
| extend timestamp = DNS_TimeGenerated, HostCustomEntity = Computer, IPCustomEntity = ClientIP, URLCustomEntity = Url

Просмотреть файл

@ -57,5 +57,5 @@ query: |
| extend Alert_Description = Description
) on $left.DomainName==$right.domain
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, Alert_TimeGenerated, AlertName, Alert_Description, ProviderName, AlertSeverity, ConfidenceLevel, HostName, IP_addr
| extend timestamp = Alert_TimeGenerated, HostCustomEntity = HostName, IPCustomEntity = IP_addr
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, Alert_TimeGenerated, AlertName, Alert_Description, ProviderName, AlertSeverity, ConfidenceLevel, HostName, IP_addr, Url
| extend timestamp = Alert_TimeGenerated, HostCustomEntity = HostName, IPCustomEntity = IP_addr, URLCustomEntity = Url

Просмотреть файл

@ -47,5 +47,5 @@ query: |
| extend Syslog_TimeGenerated = TimeGenerated
) on $left.DomainName==$right.domain
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, Syslog_TimeGenerated, SyslogMessage, Computer, ProcessName, domain, HostIP
| extend timestamp = Syslog_TimeGenerated, HostCustomEntity = Computer, IPCustomEntity = HostIP
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, Syslog_TimeGenerated, SyslogMessage, Computer, ProcessName, domain, HostIP, Url
| extend timestamp = Syslog_TimeGenerated, HostCustomEntity = Computer, IPCustomEntity = HostIP, URLCustomEntity = Url

Просмотреть файл

@ -34,7 +34,7 @@ query: |
)
on $left.EmailRecipient == $right.Caller
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, AzureActivity_TimeGenerated,
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, Url, AzureActivity_TimeGenerated,
EmailSenderName, EmailSourceDomain, EmailSourceIpAddress, EmailSubject, FileHashValue, FileHashType, Caller, Level, CallerIpAddress, Category, OperationName,
OperationNameValue, ActivityStatus, ResourceGroup, SubscriptionId
| extend timestamp = AzureActivity_TimeGenerated, AccountCustomEntity = Caller, IPCustomEntity = CallerIpAddress
| extend timestamp = AzureActivity_TimeGenerated, AccountCustomEntity = Caller, IPCustomEntity = CallerIpAddress, URLCustomEntity = Url

Просмотреть файл

@ -33,6 +33,6 @@ query: |
)
on $left.EmailRecipient == $right.UserId
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, OfficeActivity_TimeGenerated,
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore, OfficeActivity_TimeGenerated,
EmailSenderName, EmailSourceDomain, EmailSourceIpAddress, EmailSubject, FileHashValue, FileHashType, UserId, ClientIP, Operation, UserType, RecordType, OfficeWorkload, Parameters
| extend timestamp = OfficeActivity_TimeGenerated, AccountCustomEntity = UserId, IPCustomEntity = ClientIP
| extend timestamp = OfficeActivity_TimeGenerated, AccountCustomEntity = UserId, IPCustomEntity = ClientIP, URLCustomEntity = Url

Просмотреть файл

@ -36,7 +36,7 @@ query: |
)
on $left.EmailRecipient == $right.DestinationUserID
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, CommonSecurityLog_TimeGenerated,
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore, CommonSecurityLog_TimeGenerated,
EmailSenderName, EmailSourceDomain, EmailSourceIpAddress, EmailSubject, FileHashValue, FileHashType, DestinationUserID, DeviceEventClassID, LogSeverity, DeviceAction,
SourceIP, SourcePort, DestinationIP, DestinationPort, Protocol, ApplicationProtocol
| extend timestamp = CommonSecurityLog_TimeGenerated, AccountCustomEntity = DestinationUserID, IPCustomEntity = SourceIP
| extend timestamp = CommonSecurityLog_TimeGenerated, AccountCustomEntity = DestinationUserID, IPCustomEntity = SourceIP, URLCustomEntity = Url

Просмотреть файл

@ -31,7 +31,8 @@ query: |
// Converting Entities into dynamic data type and use mv-expand to unpack the array
| extend EntitiesDynamicArray = parse_json(Entities) | mv-expand EntitiesDynamicArray
// Parsing relevant entity column to filter type account and creating new column by combining account and UPNSuffix
| extend Entitytype = tostring(parse_json(EntitiesDynamicArray).Type), EntityName = tostring(parse_json(EntitiesDynamicArray).Name), EntityUPNSuffix = tostring(parse_json(EntitiesDynamicArray).UPNSuffix)
| extend Entitytype = tostring(parse_json(EntitiesDynamicArray).Type), EntityName = tostring(parse_json(EntitiesDynamicArray).Name),
EntityUPNSuffix = tostring(parse_json(EntitiesDynamicArray).UPNSuffix)
| where Entitytype =~ "account"
| extend EntityEmail = tolower(strcat(EntityName, "@", EntityUPNSuffix))
| where EntityEmail matches regex emailregex
@ -39,6 +40,7 @@ query: |
)
on $left.EmailRecipient == $right.EntityEmail
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, SecurityAlert_TimeGenerated,
EmailSenderName, EmailSourceDomain, EmailSourceIpAddress, EmailSubject, FileHashValue, FileHashType, EntityEmail, AlertName, AlertType, AlertSeverity, Entities, ProviderName, VendorName
| extend timestamp = SecurityAlert_TimeGenerated, AccountCustomEntity = EntityEmail
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore, SecurityAlert_TimeGenerated,
EmailSenderName, EmailSourceDomain, EmailSourceIpAddress, EmailSubject, FileHashValue, FileHashType, EntityEmail, AlertName, AlertType,
AlertSeverity, Entities, ProviderName, VendorName
| extend timestamp = SecurityAlert_TimeGenerated, AccountCustomEntity = EntityEmail, URLCustomEntity = Url

Просмотреть файл

@ -35,7 +35,7 @@ query: |
)
on $left.EmailRecipient == $right.TargetUserName
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, SecurityEvent_TimeGenerated,
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore, SecurityEvent_TimeGenerated,
EmailSenderName, EmailSourceDomain, EmailSourceIpAddress, EmailSubject, FileHashValue, FileHashType, Computer, EventID, TargetUserName, Activity, IpAddress, AccountType,
LogonTypeName, LogonProcessName, Status, SubStatus
| extend timestamp = SecurityEvent_TimeGenerated, AccountCustomEntity = TargetUserName, IPCustomEntity = IpAddress, HostCustomEntity = Computer
| extend timestamp = SecurityEvent_TimeGenerated, AccountCustomEntity = TargetUserName, IPCustomEntity = IpAddress, HostCustomEntity = Computer, URLCustomEntity = Url

Просмотреть файл

@ -38,7 +38,7 @@ query: |
)
on $left.EmailRecipient == $right.UserPrincipalName
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, SigninLogs_TimeGenerated,
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore, SigninLogs_TimeGenerated,
EmailSenderName, EmailSourceDomain, EmailSourceIpAddress, EmailSubject, FileHashValue, FileHashType, IPAddress, UserPrincipalName, AppDisplayName,
StatusCode, StatusDetails, NetworkIP, NetworkDestinationIP, NetworkSourceIP
| extend timestamp = SigninLogs_TimeGenerated, AccountCustomEntity = UserPrincipalName, IPCustomEntity = IPAddress
| extend timestamp = SigninLogs_TimeGenerated, AccountCustomEntity = UserPrincipalName, IPCustomEntity = IPAddress, URLCustomEntity = Url

Просмотреть файл

@ -31,5 +31,7 @@ query: |
)
on $left.FileHashValue == $right.FileHash
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, CommonSecurityLog_TimeGenerated, SourceIP, SourcePort, DestinationIP, DestinationPort, SourceUserID, SourceUserName, DeviceName, DeviceAction, RequestURL, DestinationUserName, DestinationUserID, ApplicationProtocol, Activity
| extend timestamp = CommonSecurityLog_TimeGenerated, IPCustomEntity = SourceIP, HostCustomEntity = DeviceName, AccountCustomEntity = SourceUserName
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore,
CommonSecurityLog_TimeGenerated, SourceIP, SourcePort, DestinationIP, DestinationPort, SourceUserID, SourceUserName, DeviceName, DeviceAction,
RequestURL, DestinationUserName, DestinationUserID, ApplicationProtocol, Activity
| extend timestamp = CommonSecurityLog_TimeGenerated, IPCustomEntity = SourceIP, HostCustomEntity = DeviceName, AccountCustomEntity = SourceUserName, URLCustomEntity = Url

Просмотреть файл

@ -32,5 +32,6 @@ query: |
)
on $left.FileHashValue == $right.FileHash
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, SecurityEvent_TimeGenerated, Process, FileHash, Computer, Account, Event
| extend timestamp = SecurityEvent_TimeGenerated, AccountCustomEntity = Account, HostCustomEntity = Computer
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore,
SecurityEvent_TimeGenerated, Process, FileHash, Computer, Account, Event
| extend timestamp = SecurityEvent_TimeGenerated, AccountCustomEntity = Account, HostCustomEntity = Computer, URLCustomEntity = Url

Просмотреть файл

@ -37,7 +37,7 @@ query: |
)
on $left.TI_ipEntity == $right.SourceIpAddress
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, AWSCloudTrail_TimeGenerated,
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore, AWSCloudTrail_TimeGenerated,
TI_ipEntity, EventName, EventTypeName, UserIdentityAccountId, UserIdentityPrincipalid, UserIdentityUserName, SourceIpAddress,
NetworkIP, NetworkDestinationIP, NetworkSourceIP, EmailSourceIpAddress
| extend timestamp = AWSCloudTrail_TimeGenerated, IPCustomEntity = SourceIpAddress, AccountCustomEntity = UserIdentityUserName
| extend timestamp = AWSCloudTrail_TimeGenerated, IPCustomEntity = SourceIpAddress, AccountCustomEntity = UserIdentityUserName, URLCustomEntity = Url

Просмотреть файл

@ -37,6 +37,6 @@ query: |
)
on $left.TI_ipEntity == $right.CallerIpAddress
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, AzureActivity_TimeGenerated,
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore, AzureActivity_TimeGenerated,
TI_ipEntity, CallerIpAddress, Caller, OperationName, ActivityStatus, Category, ResourceId, NetworkIP, NetworkDestinationIP, NetworkSourceIP, EmailSourceIpAddress
| extend timestamp = AzureActivity_TimeGenerated, IPCustomEntity = CallerIpAddress, AccountCustomEntity = Caller
| extend timestamp = AzureActivity_TimeGenerated, IPCustomEntity = CallerIpAddress, AccountCustomEntity = Caller, URLCustomEntity = Url

Просмотреть файл

@ -41,6 +41,6 @@ query: |
)
on $left.TI_ipEntity == $right.SingleIP
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, DNS_TimeGenerated,
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore, DNS_TimeGenerated,
TI_ipEntity, Computer, EventId, SubType, ClientIP, Name, IPAddresses, NetworkIP, NetworkDestinationIP, NetworkSourceIP, EmailSourceIpAddress
| extend timestamp = DNS_TimeGenerated, IPCustomEntity = ClientIP, HostCustomEntity = Computer
| extend timestamp = DNS_TimeGenerated, IPCustomEntity = ClientIP, HostCustomEntity = Computer, URLCustomEntity = Url

Просмотреть файл

@ -37,6 +37,6 @@ query: |
)
on $left.TI_ipEntity == $right.ClientIP
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, OfficeActivity_TimeGenerated,
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore, OfficeActivity_TimeGenerated,
TI_ipEntity, ClientIP, UserId, Operation, ResultStatus, RecordType, OfficeObjectId, NetworkIP, NetworkDestinationIP, NetworkSourceIP, EmailSourceIpAddress
| extend timestamp = OfficeActivity_TimeGenerated, IPCustomEntity = ClientIP, AccountCustomEntity = UserId
| extend timestamp = OfficeActivity_TimeGenerated, IPCustomEntity = ClientIP, AccountCustomEntity = UserId, URLCustomEntity = Url

Просмотреть файл

@ -7,7 +7,8 @@ requiredDataConnectors:
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
- dataTypes:
- connectorId: AzureMonitor(VMInsights)
dataTypes:
- VMConnection
queryFrequency: 1h
queryPeriod: 14d
@ -37,6 +38,6 @@ query: |
)
on $left.TI_ipEntity == $right.RemoteIp
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, VMConnection_TimeGenerated,
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore, VMConnection_TimeGenerated,
TI_ipEntity, Computer, Direction, ProcessName, SourceIp, DestinationIp, RemoteIp, Protocol, DestinationPort, NetworkIP, NetworkDestinationIP, NetworkSourceIP, EmailSourceIpAddress
| extend timestamp = VMConnection_TimeGenerated, IPCustomEntity = RemoteIp, HostCustomEntity = Computer
| extend timestamp = VMConnection_TimeGenerated, IPCustomEntity = RemoteIp, HostCustomEntity = Computer, URLCustomEntity = Url

Просмотреть файл

@ -7,7 +7,8 @@ requiredDataConnectors:
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
- dataTypes:
- connectorId: AzureMonitor(IIS)
dataTypes:
- W3CIISLog
queryFrequency: 1h
queryPeriod: 14d
@ -38,7 +39,7 @@ query: |
)
on $left.TI_ipEntity == $right.cIP
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore,
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore,
W3CIISLog_TimeGenerated, TI_ipEntity, Computer, sSiteName, cIP, sIP, sPort, csMethod, csUserName, scStatus, scSubStatus, scWin32Status,
NetworkIP, NetworkDestinationIP, NetworkSourceIP, EmailSourceIpAddress
| extend timestamp = W3CIISLog_TimeGenerated, IPCustomEntity = cIP, HostCustomEntity = Computer, AccountCustomEntity = csUserName
| extend timestamp = W3CIISLog_TimeGenerated, IPCustomEntity = cIP, HostCustomEntity = Computer, AccountCustomEntity = csUserName, URLCustomEntity = Url

Просмотреть файл

@ -7,7 +7,8 @@ requiredDataConnectors:
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
- dataTypes:
- connectorId: AzureMonitor(WireData)
dataTypes:
- WireData
queryFrequency: 1h
queryPeriod: 14d
@ -37,6 +38,6 @@ query: |
)
on $left.TI_ipEntity == $right.RemoteIP
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, WireData_TimeGenerated,
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore, WireData_TimeGenerated,
TI_ipEntity, Computer, LocalIP, RemoteIP, ProcessName, ApplicationProtocol, LocalPortNumber, NetworkIP, NetworkDestinationIP, NetworkSourceIP, EmailSourceIpAddress
| extend timestamp = WireData_TimeGenerated, IPCustomEntity = RemoteIP, HostCustomEntity = Computer
| extend timestamp = WireData_TimeGenerated, IPCustomEntity = RemoteIP, HostCustomEntity = Computer, URLCustomEntity = Url

Просмотреть файл

@ -39,6 +39,6 @@ query: |
)
on $left.TI_ipEntity == $right.IPAddress
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, SigninLogs_TimeGenerated,
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore, SigninLogs_TimeGenerated,
TI_ipEntity, IPAddress, UserPrincipalName, AppDisplayName, StatusCode, StatusDetails, NetworkIP, NetworkDestinationIP, NetworkSourceIP, EmailSourceIpAddress
| extend timestamp = SigninLogs_TimeGenerated, AccountCustomEntity = UserPrincipalName, IPCustomEntity = IPAddress
| extend timestamp = SigninLogs_TimeGenerated, AccountCustomEntity = UserPrincipalName, IPCustomEntity = IPAddress, URLCustomEntity = Url

Просмотреть файл

@ -37,5 +37,5 @@ query: |
) on Url
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore,
Audit_TimeGenerated, OperationName, Identity, userPrincipalName, TargetResourceDisplayName
| extend timestamp = Audit_TimeGenerated, AccountCustomEntity = userPrincipalName, HostCustomEntity = TargetResourceDisplayName
Audit_TimeGenerated, OperationName, Identity, userPrincipalName, TargetResourceDisplayName, Url
| extend timestamp = Audit_TimeGenerated, AccountCustomEntity = userPrincipalName, HostCustomEntity = TargetResourceDisplayName, URLCustomEntity = Url

Просмотреть файл

@ -38,5 +38,6 @@ query: |
| extend User = iif(isnotempty(UserId), UserId, iif(isnotempty(Actor), tostring(parse_json(Actor)[0].ID), tostring(parse_json(Parameters)[0].Vlaue)))
) on Url
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, Operation, UserType, OfficeWorkload, Parameters, Office_TimeGenerated, Url, User
| extend timestamp = Office_TimeGenerated, AccountCustomEntity = User
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, Operation,
UserType, OfficeWorkload, Parameters, Office_TimeGenerated, Url, User
| extend timestamp = Office_TimeGenerated, AccountCustomEntity = User, URLCustomEntity = Url

Просмотреть файл

@ -43,4 +43,4 @@ query: |
| extend CSL_TimeGenerated = TimeGenerated
) on $left.Url == $right.PA_Url
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, DeviceAction, SourceIP, CSL_TimeGenerated, PA_Url, DeviceName
| extend timestamp = CSL_TimeGenerated, IPCustomEntity = SourceIP, HostCustomEntity = DeviceName
| extend timestamp = CSL_TimeGenerated, IPCustomEntity = SourceIP, HostCustomEntity = DeviceName, URLCustomEntity = PA_Url

Просмотреть файл

@ -40,5 +40,6 @@ query: |
| extend Alert_TimeGenerated = TimeGenerated
) on Url
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, Alert_TimeGenerated, AlertName, AlertSeverity, Description, Url, Compromised_Host
| extend timestamp = Alert_TimeGenerated, HostCustomEntity = Compromised_Host
| project LatestIndicatorTime, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, Alert_TimeGenerated,
AlertName, AlertSeverity, Description, Url, Compromised_Host
| extend timestamp = Alert_TimeGenerated, HostCustomEntity = Compromised_Host, URLCustomEntity = Url

Просмотреть файл

@ -35,4 +35,4 @@ query: |
) on Url
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, Syslog_TimeGenerated, SyslogMessage, Computer, ProcessName, Url, HostIP
| extend timestamp = Syslog_TimeGenerated, HostCustomEntity = Computer, IPCustomEntity = HostIP
| extend timestamp = Syslog_TimeGenerated, HostCustomEntity = Computer, IPCustomEntity = HostIP, URLCustomEntity = Url

Просмотреть файл

@ -4,7 +4,8 @@ description: |
'Identifies connection attempts (success or fail) from clients with very short or very long User Agent strings and with less than 100 connection attempts.'
severity: Low
requiredDataConnectors:
- dataTypes:
- connectorId: AzureMonitor(IIS)
dataTypes:
- W3CIISLog
queryFrequency: 1d
queryPeriod: 1d

Просмотреть файл

@ -12,7 +12,8 @@ description: |
Win32 Status code mapping: https://msdn.microsoft.com/en-us/library/cc231199.aspx'
severity: Medium
requiredDataConnectors:
- dataTypes:
- connectorId: AzureMonitor(IIS)
dataTypes:
- W3CIISLog
queryFrequency: 1d
queryPeriod: 1d

Просмотреть файл

@ -9,7 +9,8 @@ description: |
Win32 Status code mapping - https://msdn.microsoft.com/en-us/library/cc231199.aspx'
severity: Medium
requiredDataConnectors:
- dataTypes:
- connectorId: AzureMonitor(IIS)
dataTypes:
- W3CIISLog
queryFrequency: 1d
queryPeriod: 1d

Просмотреть файл

@ -9,7 +9,8 @@ description: |
Win32 Status code mapping - https://msdn.microsoft.com/en-us/library/cc231199.aspx'
severity: Medium
requiredDataConnectors:
- dataTypes:
- connectorId: AzureMonitor(IIS)
dataTypes:
- W3CIISLog
queryFrequency: 1d
queryPeriod: 1d

Просмотреть файл

@ -1,23 +0,0 @@
// Id: 801bacb0-612a-4195-a84f-7939cca63b92
// DisplayName: DNS Name Lookup Query from least prevalent ClientIP to remote IP Address
// Description: Summary of Bottom 10 Client IP and Domain Names for a given remote IPAddress from DnsEvent Lookup Query data (set time range to +-3h when running the query)
// InputEntityType: Ip
// InputFields: [Address]
// OutputEntityTypes: [Ip]
// QueryPeriodBefore: 3h
// QueryPeriodAfter: 3h
// DataSource: #DnsEvents
// Tactics: #CommandAndControl, #Exfiltration
let GetAllIPByClientIP = (v_IP_Address:string){
DnsEvents
| where SubType == 'LookupQuery'
| where IPAddresses contains v_IP_Address
| summarize min(TimeGenerated), max(TimeGenerated), makeset(Name), count() by ClientIP, IPAddresses
| project StartTimeUtc = min_TimeGenerated, EndTimeUtc = max_TimeGenerated, ClientIP, DomainNames = set_Name, IPAddresses, count_
| top 10 by count_ asc nulls last
| project StartTimeUtc, EndTimeUtc, ClientIP, IPAddresses, DomainNames
| project-rename IP_Address=ClientIP
};
// change <Address> value below
GetAllIPByClientIP('<Address>')

Просмотреть файл

@ -0,0 +1,30 @@
Id: 801bacb0-612a-4195-a84f-7939cca63b92
DisplayName: DNS Name Lookup Query from least prevalent ClientIP to remote IP Address
Description: Summary of Bottom 10 Client IP and Domain Names for a given remote IPAddress from DnsEvent Lookup Query data (set time range to +-3h when running the query)
InputEntityType: Ip
InputFields:
- Address
OutputEntityTypes:
- Ip
QueryPeriodBefore: 3h
QueryPeriodAfter: 3h
DataSource:
- DnsEvents
Tactics:
- CommandAndControl
- Exfiltration
query: |
let GetAllIPByClientIP = (v_IP_Address:string){
DnsEvents
| where SubType == 'LookupQuery'
| where IPAddresses contains v_IP_Address
| summarize min(TimeGenerated), max(TimeGenerated), makeset(Name), count() by ClientIP, IPAddresses
| project StartTimeUtc = min_TimeGenerated, EndTimeUtc = max_TimeGenerated, ClientIP, DomainNames = set_Name, IPAddresses, count_
| top 10 by count_ asc nulls last
| project StartTimeUtc, EndTimeUtc, ClientIP, IPAddresses, DomainNames
| project-rename IP_Address=ClientIP
};
// change <Address> value below
GetAllIPByClientIP('<Address>')

Просмотреть файл

@ -1,23 +0,0 @@
// Id: 0cb64e03-8534-47b6-9094-7de2d018fd7a
// DisplayName: DNS Name Lookup Query from most prevalent ClientIP to remote IP Address
// Description: Summary of Top 10 Client IP and Domain Names for a given remote IPAddress from DnsEvent Lookup Query data (set time range to +-3h when running the query)
// InputEntityType: Ip
// InputFields: [Address]
// OutputEntityTypes: [Ip]
// QueryPeriodBefore: 3h
// QueryPeriodAfter: 3h
// DataSource: #DnsEvents
// Tactics: #CommandAndControl, #Exfiltration
let GetAllIPByClientIP = (v_IP_Address:string){
DnsEvents
| where SubType == 'LookupQuery'
| where IPAddresses contains v_IP_Address
| summarize min(TimeGenerated), max(TimeGenerated), makeset(Name), count() by ClientIP, IPAddresses
| project StartTimeUtc = min_TimeGenerated, EndTimeUtc = max_TimeGenerated, ClientIP, DomainNames = set_Name, IPAddresses, count_
| top 10 by count_ desc nulls last
| project StartTimeUtc, EndTimeUtc, ClientIP, IPAddresses, DomainNames
| project-rename IP_Address=ClientIP
};
// change <Address> value below
GetAllIPByClientIP('<Address>')

Просмотреть файл

@ -0,0 +1,29 @@
Id: 0cb64e03-8534-47b6-9094-7de2d018fd7a
DisplayName: DNS Name Lookup Query from most prevalent ClientIP to remote IP Address
Description: Summary of Top 10 Client IP and Domain Names for a given remote IPAddress from DnsEvent Lookup Query data (set time range to +-3h when running the query)
InputEntityType: Ip
InputFields:
- Address
OutputEntityTypes:
- Ip
QueryPeriodBefore: 3h
QueryPeriodAfter: 3h
DataSource:
- DnsEvents
Tactics:
- CommandAndControl
- Exfiltration
query: |
let GetAllIPByClientIP = (v_IP_Address:string){
DnsEvents
| where SubType == 'LookupQuery'
| where IPAddresses contains v_IP_Address
| summarize min(TimeGenerated), max(TimeGenerated), makeset(Name), count() by ClientIP, IPAddresses
| project StartTimeUtc = min_TimeGenerated, EndTimeUtc = max_TimeGenerated, ClientIP, DomainNames = set_Name, IPAddresses, count_
| top 10 by count_ desc nulls last
| project StartTimeUtc, EndTimeUtc, ClientIP, IPAddresses, DomainNames
| project-rename IP_Address=ClientIP
};
// change <Address> value below
GetAllIPByClientIP('<Address>')

Просмотреть файл

@ -1,35 +0,0 @@
// Id: 4c541df8-a680-4da5-96c9-74456927213f
// DisplayName: Hosts The Account Failed to Log-in to Most
// Description: Hosts The Account Failed to Log-in to Most during the range of -1d and +1d
// InputEntityType: Account
// InputFields: [Name, UPNSuffix, AadUserId]
// OutputEntityTypes: [Host]
// QueryPeriodBefore: 1d
// QueryPeriodAfter: 1d
// DataConnector: #SecurityEvents; DataTypes: #SecurityEvent;
// Tactics : #Persistence, #Discovery, #LateralMovement, #Collection
let SuccessfulLoginEventId = 4624;
let FailedLoginEventId = 4625;
let FailedLoginWithCorrectUser = '0xc000006a';
let MostFailedLogins = (v_Account_Name:string, v_Account_NTDomain:string, v_Account_UPNSuffix:string){
SecurityEvent
| where Account contains v_Account_Name // Basic sanity for performance
// parse Account sections
| extend acc = split(Account,"@")
| extend usr = split(tostring(acc[0]),"\\")
| extend Account_Name = usr[1], Account_NTDomain = usr[0], Account_UPNSuffix = acc[1]
// filter by account
| where (Account_Name != "" and tolower(Account_Name) == tolower(v_Account_Name))
and ((Account_NTDomain != "" and tolower(v_Account_NTDomain) == tolower(Account_NTDomain) ) or
(Account_UPNSuffix != "" and tolower(v_Account_UPNSuffix) == tolower(Account_UPNSuffix))
)
| summarize Success = sum(EventID==SuccessfulLoginEventId), Host_Aux_NumberOfFailedLoginsByAccount = sum((EventID==FailedLoginEventId and
tolower(SubStatus)==FailedLoginWithCorrectUser))
by Computer, Account
| top 10 by Host_Aux_NumberOfFailedLoginsByAccount
| parse Computer with Host_NTDomain "\\" *
| extend Host_HostName = tostring(split(Computer,'.')[0]),
Host_DnsDomain = strcat_array(array_slice(split(Computer,'.'),1,256),'.'), Host_Aux_SuccessfulLoginExists = (Success > 0)
};
MostFailedLogins('<Name>', '<NTDomain>','<UPNSuffix>')

Просмотреть файл

@ -0,0 +1,48 @@
Id: 4c541df8-a680-4da5-96c9-74456927213f
DisplayName: Hosts The Account Failed to Log-in to Most
Description: Hosts The Account Failed to Log-in to Most during the range of -1d and +1d
InputEntityType: Account
InputFields:
- Name + UPNSuffix
- Name + NTDomain
OutputEntityTypes:
- Host
QueryPeriodBefore: 1d
QueryPeriodAfter: 1d
DataConnector:
ConnectorId: SecurityEvents
DataTypes:
- SecurityEvent
Tactics:
- Persistence
- Discovery
- LateralMovement
- Collection
query: |
let SuccessfulLoginEventId = 4624;
let FailedLoginEventId = 4625;
let FailedLoginWithCorrectUser = '0xc000006a';
let MostFailedLogins = (v_Account_Name:string, v_Account_NTDomain:string, v_Account_UPNSuffix:string){
SecurityEvent
| where Account contains v_Account_Name // Basic sanity for performance
// parse Account sections
| extend NameAndUPNSuffix = split(Account,'@')
| extend NameAndNtDomain = split(Account,'\\')
| extend Account_UPNSuffix = iff(array_length(NameAndUPNSuffix) == 2, NameAndUPNSuffix[1], '')
| extend Account_NTDomain = iff(array_length(NameAndNtDomain) == 2, NameAndNtDomain[0], '')
| extend Account_Name = iff(array_length(NameAndNtDomain) == 2, NameAndNtDomain[1], iff(array_length(NameAndUPNSuffix) == 2, NameAndUPNSuffix[0], Account))
// filter by account
| where (Account_Name != '' and tolower(Account_Name) == tolower(v_Account_Name))
and ((Account_NTDomain != '' and tolower(v_Account_NTDomain) == tolower(Account_NTDomain) ) or
(Account_UPNSuffix != '' and tolower(v_Account_UPNSuffix) == tolower(Account_UPNSuffix))
)
| summarize Success = sum(EventID==SuccessfulLoginEventId), Host_Aux_NumberOfFailedLoginsByAccount = sum((EventID==FailedLoginEventId and
tolower(SubStatus)==FailedLoginWithCorrectUser))
by Computer, Account
| top 10 by Host_Aux_NumberOfFailedLoginsByAccount
| parse Computer with Host_NTDomain '\\' *
| extend Host_HostName = tostring(split(Computer,'.')[0]),
Host_DnsDomain = strcat_array(array_slice(split(Computer,'.'),1,256),'.'), Host_Aux_SuccessfulLoginExists = (Success > 0)
};
MostFailedLogins('<Name>', '<NTDomain>','<UPNSuffix>')

Просмотреть файл

@ -1,41 +0,0 @@
// Id: 2db8cac9-d2ce-4494-93bf-4678cd872ce4
// Name: IPs From Rare Locations Used By Account
// Description: IPs with rare location (less than 10 per cent of user's activity) during the range of +1w and -1w
// InputEntityType: Account
// InputFields: [Name, UPNSuffix, AadUserId]
// OutputEntityTypes: [IP]
// QueryPeriodBefore: 1w
// QueryPeriodAfter: 1w
// DataConnector: #AzureActiveDirectory; DataTypes: #SigninLogs
// Tactics: #CredentialAccess, #Collection
let IPsFromRareLocations = (v_Account_Name:string, v_Account_AadUserId:string){
let LocationPrevalence =
SigninLogs
| extend v_Account_Name = case(
v_Account_Name has "@", tostring(split(v_Account_Name, "@")[0]),
v_Account_Name has "\\", tostring(split(v_Account_Name, "\\")[1]),
v_Account_Name
)
| parse UserPrincipalName with Account_Name "@" Account_UPNSuffix
| project-rename Account_AadUserId = UserId
| where ((isnotempty(Account_Name) and Account_Name == v_Account_Name) or
(isnotempty(Account_AadUserId) and Account_AadUserId == v_Account_AadUserId)
)
| summarize LocationCount = count() by Location, UserPrincipalName, IPAddress
;
LocationPrevalence
| summarize makeset(IPAddress), makeset(Location), makeset(LocationCount) , totalActivity = sum(LocationCount) by UserPrincipalName
| mvexpand Location = set_Location, LocationCount = set_LocationCount, IPAddress = set_IPAddress
| extend Location = tostring(Location), LocationCount = toint(LocationCount), IPAddress = tostring(IPAddress)
| extend percentOfActivity = 100*LocationCount/totalActivity
| where percentOfActivity < 10
| project UserPrincipalName, IPAddress, Location, LocationCount, percentOfActivity
| top 10 by LocationCount asc nulls last
| extend Account_Aux_info = pack("LocationCount", LocationCount, "PercentOfActivity", percentOfActivity)
| parse UserPrincipalName with Account_NTDomain "\\" *
| extend Account_Name = extract(@"^([^\\]*\\)?([^@]+)(@.*)?$", 2, UserPrincipalName),
Account_UPNSuffix = extract(@"^([^\\]*\\)?([^@]+)(@(.*))?$", 4, UserPrincipalName)
| project Account_Name, Account_NTDomain, Account_UPNSuffix, IP_Address = IPAddress, IP_Location = Location, Account_Aux_info
};
IPsFromRareLocations('<Name>', '<AadUserId>')

Просмотреть файл

@ -0,0 +1,49 @@
Id: 2db8cac9-d2ce-4494-93bf-4678cd872ce4
DisplayName: IPs From Rare Locations Used By Account
Description: IPs with rare location (less than 10 percent of users activity) during the range of +1w and -1w
InputEntityType: Account
InputFields:
- Name
- AadUserId
OutputEntityTypes:
- IP
QueryPeriodBefore: 1w
QueryPeriodAfter: 1w
DataConnector:
ConnectorId: AzureActiveDirectory
DataTypes:
- SigninLogs
Tactics:
- CredentialAccess
- Collection
query: |
let IPsFromRareLocations = (v_Account_Name:string, v_Account_AadUserId:string){
let LocationPrevalence =
SigninLogs
| extend v_Account_Name = case(
v_Account_Name has '@', tostring(split(v_Account_Name, '@')[0]),
v_Account_Name has '\\', tostring(split(v_Account_Name, '\\')[1]),
v_Account_Name
)
| parse UserPrincipalName with Account_Name '@' Account_UPNSuffix
| project-rename Account_AadUserId = UserId
| where ((isnotempty(Account_Name) and Account_Name =~ v_Account_Name) or
(isnotempty(Account_AadUserId) and Account_AadUserId == v_Account_AadUserId)
)
| summarize LocationCount = count() by Location, UserPrincipalName, IPAddress;
LocationPrevalence
| summarize makeset(IPAddress), makeset(Location), makeset(LocationCount) , totalActivity = sum(LocationCount) by UserPrincipalName
| mvexpand Location = set_Location, LocationCount = set_LocationCount, IPAddress = set_IPAddress
| extend Location = tostring(Location), LocationCount = toint(LocationCount), IPAddress = tostring(IPAddress)
| extend percentOfActivity = 100*LocationCount/totalActivity
| where percentOfActivity < 10
| project UserPrincipalName, IPAddress, Location, LocationCount, percentOfActivity
| top 10 by LocationCount asc nulls last
| extend Account_Aux_info = pack('LocationCount', LocationCount, 'PercentOfActivity', percentOfActivity)
| parse UserPrincipalName with Account_NTDomain '\\' *
| extend Account_Name = extract(@'^([^\\]*\\)?([^@]+)(@.*)?$', 2, UserPrincipalName),
Account_UPNSuffix = extract(@'^([^\\]*\\)?([^@]+)(@(.*))?$', 4, UserPrincipalName)
| project Account_Name, Account_NTDomain, Account_UPNSuffix, IP_Address = IPAddress, IP_Location = Location, Account_Aux_info
};
IPsFromRareLocations('<Name>', '<AadUserId>')

Просмотреть файл

@ -1,35 +0,0 @@
// Id: 98b2ce21-167d-43bd-a496-9f2c85c5f95b
// DisplayName: Accounts With Several Failed Logins Immediately Followed By A Successful Login
// Description: Accounts With Several Failed Logins immediately Followed By A Successful Login during the range of -1w and +1w
// InputEntityType: Host
// InputFields: [HostName, NTDomain, DnsDomain]
// OutputEntityTypes: [Account]
// QueryPeriodBefore: 1w
// QueryPeriodAfter: 1w
// DataConnector: #SecurityEvents; DataTypes: #SecurityEvent
// Tactics: #LateralMovement, #CredentialAccess
let BRUTEFORCE_THRESHOLD = 10;
let SuccessfulLoginEventId = 4624;
let FailedLoginEventId = 4625;
let AccountsPossibleSuccessfulBruteForce = (v_Host_HostName:string, v_Host_NTDomain:string, v_Host_DnsDomain:string){
SecurityEvent
| where AccountType == "User"
and tolower(Computer) startswith tolower(v_Host_HostName)
and (
tolower(Computer) contains tolower(v_Host_NTDomain) or
tolower(Computer) endswith tolower(v_Host_DnsDomain)
)
| extend Fails = (EventID == FailedLoginEventId), Success = (EventID == SuccessfulLoginEventId)
| extend Account = tolower(Account)
| summarize Account_Aux_SuccessPerMin = sum(Success), Account_Aux_FailPerMin = sum(Fails) by Account, bin(TimeGenerated, 1m)
| where Account_Aux_FailPerMin > BRUTEFORCE_THRESHOLD and Account_Aux_SuccessPerMin > 0
| extend EventData = pack("FailPerMin",Account_Aux_FailPerMin, "SuccessPerMin", Account_Aux_SuccessPerMin, "Time", TimeGenerated )
| summarize Max = max(Account_Aux_FailPerMin), Account_Aux_EventsData=makeset(EventData) by Account
| top 10 by Max
| parse Account with Account_NTDomain "\\" *
| extend Account_Name = extract(@"^([^\\]*\\)?([^@]+)(@.*)?$",2,Account),
Account_UPNSuffix = extract(@"^([^\\]*\\)?([^@]+)(@(.*))?$",4,Account)
| project Account_Name, Account_NTDomain, Account_UPNSuffix, Account_Aux_EventsData
};
AccountsPossibleSuccessfulBruteForce('<HostName>','<NTDomain>','<DnsDomain>')

Просмотреть файл

@ -0,0 +1,44 @@
Id: 98b2ce21-167d-43bd-a496-9f2c85c5f95b
DisplayName: Accounts With Several Failed Logins Immediately Followed By A Successful Login
Description: Accounts With Several Failed Logins immediately Followed By A Successful Login during the range of -1w and +1w
InputEntityType: Host
InputFields:
- HostName + NTDomain
- HostName + DnsDomain
OutputEntityTypes:
- Account
QueryPeriodBefore: 1w
QueryPeriodAfter: 1w
DataConnector:
ConnectorId: SecurityEvents
DataTypes:
- SecurityEvent
Tactics:
- LateralMovement
- CredentialAccess
query: |
let BRUTEFORCE_THRESHOLD = 10;
let SuccessfulLoginEventId = 4624;
let FailedLoginEventId = 4625;
let AccountsPossibleSuccessfulBruteForce = (v_Host_HostName:string, v_Host_NTDomain:string, v_Host_DnsDomain:string){
SecurityEvent
| where AccountType == 'User'
and tolower(Computer) startswith tolower(v_Host_HostName)
and (
(isnotempty(v_Host_NTDomain) and tolower(Computer) contains tolower(v_Host_NTDomain)) or
(isnotempty(v_Host_DnsDomain) and tolower(Computer) endswith tolower(v_Host_DnsDomain))
)
| extend Fails = (EventID == FailedLoginEventId), Success = (EventID == SuccessfulLoginEventId)
| extend Account = tolower(Account)
| summarize Account_Aux_SuccessPerMin = sum(Success), Account_Aux_FailPerMin = sum(Fails) by Account, bin(TimeGenerated, 1m)
| where Account_Aux_FailPerMin > BRUTEFORCE_THRESHOLD and Account_Aux_SuccessPerMin > 0
| extend EventData = pack('FailPerMin',Account_Aux_FailPerMin, 'SuccessPerMin', Account_Aux_SuccessPerMin, 'Time', TimeGenerated )
| summarize Max = max(Account_Aux_FailPerMin), Account_Aux_EventsData=makeset(EventData) by Account
| top 10 by Max
| parse Account with Account_NTDomain '\\' *
| extend Account_Name = extract(@'^([^\\]*\\)?([^@]+)(@.*)?$',2,Account),
Account_UPNSuffix = extract(@'^([^\\]*\\)?([^@]+)(@(.*))?$',4,Account)
| project Account_Name, Account_NTDomain, Account_UPNSuffix, Account_Aux_EventsData
};
AccountsPossibleSuccessfulBruteForce('<HostName>','<NTDomain>','<DnsDomain>')

Просмотреть файл

@ -1,24 +0,0 @@
// Id: 37ca3555-c135-4a73-a65e-9c1d00323f5d
// DisplayName: Least Active Accounts On Azure From IP
// Description: Least active accounts on azure from IP during the range of -12h and +12h
// InputEntityType: IP
// InputFields: [Address]
// OutputEntityTypes: [Account]
// QueryPeriodBefore: 12h
// QueryPeriodAfter: 12h
// DataConnector: #AzureActivity; DataTypes: #AzureActivity
// Tactics: #Exfiltration, #CommandAndControl, #Collection
let AccountActivity_byIP = (v_IP_Address:string){
AzureActivity
| where Caller != '' and CallerIpAddress == v_IP_Address
| summarize Account_Aux_StartTime = min(TimeGenerated),
Account_Aux_EndTime = max(TimeGenerated),
Count = count() by
Caller, TenantId
| top 10 by Count asc nulls last
| extend UPN = iff(Caller contains '@', Caller, ''), Account_AadUserId = iff(Caller !contains '@', Caller,'')
| extend Account_Name = split(UPN,'@')[0] , Account_UPNSuffix = split(UPN,'@')[1]
| project Account_Name, Account_UPNSuffix, Account_AadUserId, Account_AadTenantId=TenantId, Account_Aux_StartTime , Account_Aux_EndTime
};
AccountActivity_byIP('<Address>')

Просмотреть файл

@ -0,0 +1,33 @@
Id: 37ca3555-c135-4a73-a65e-9c1d00323f5d
DisplayName: Least Active Accounts On Azure From IP
Description: Least active accounts on azure from IP during the range of -12h and +12h
InputEntityType: IP
InputFields:
- Address
OutputEntityTypes:
- Account
QueryPeriodBefore: 12h
QueryPeriodAfter: 12h
DataConnector:
ConnectorId: AzureActivity
DataTypes:
- AzureActivity
Tactics:
- Exfiltration
- CommandAndControl
- Collection
query: |
let AccountActivity_byIP = (v_IP_Address:string){
AzureActivity
| where Caller != '' and CallerIpAddress == v_IP_Address
| summarize Account_Aux_StartTime = min(TimeGenerated),
Account_Aux_EndTime = max(TimeGenerated),
Count = count() by
Caller, TenantId
| top 10 by Count asc nulls last
| extend UPN = iff(Caller contains '@', Caller, ''), Account_AadUserId = iff(Caller !contains '@', Caller,'')
| extend Account_Name = split(UPN,'@')[0] , Account_UPNSuffix = split(UPN,'@')[1]
| project Account_Name, Account_UPNSuffix, Account_AadUserId, Account_AadTenantId=TenantId, Account_Aux_StartTime , Account_Aux_EndTime
};
AccountActivity_byIP('<Address>')

Просмотреть файл

@ -1,24 +0,0 @@
// Id: 97a1d515-abf2-4231-9a35-985f9de0bb91
// DisplayName: Most Active Accounts On Azure From IP
// Description: Most active accounts on azure from IP during the range of -12h and +12h
// InputEntityType: IP
// InputFields: [Address]
// OutputEntityTypes: [Account]
// QueryPeriodBefore: 12h
// QueryPeriodAfter: 12h
// DataConnector: #AzureActivity; DataTypes: #AzureActivity
// Tactics: #Exfiltration, #CommandAndControl, #Collection
let AccountActivity_byIP = (v_IP_Address:string){
AzureActivity
| where Caller != '' and CallerIpAddress == v_IP_Address
| summarize Account_Aux_StartTime = min(TimeGenerated),
Account_Aux_EndTime = max(TimeGenerated),
Count = count() by
Caller, TenantId
| top 10 by Count desc nulls last
| extend UPN = iff(Caller contains '@', Caller, ''), Account_AadUserId = iff(Caller !contains '@', Caller,'')
| extend Account_Name = split(UPN,'@')[0] , Account_UPNSuffix = split(UPN,'@')[1]
| project Account_Name, Account_UPNSuffix, Account_AadUserId, Account_AadTenantId=TenantId, Account_Aux_StartTime , Account_Aux_EndTime
};
AccountActivity_byIP('<Address>')

Просмотреть файл

@ -0,0 +1,33 @@
Id: 97a1d515-abf2-4231-9a35-985f9de0bb91
DisplayName: Most Active Accounts On Azure From IP
Description: Most active accounts on azure from IP during the range of -12h and +12h
InputEntityType: IP
InputFields:
- Address
OutputEntityTypes:
- Account
QueryPeriodBefore: 12h
QueryPeriodAfter: 12h
DataConnector:
ConnectorId: AzureActivity
DataTypes:
- AzureActivity
Tactics:
- Exfiltration
- CommandAndControl
- Collection
query: |
let AccountActivity_byIP = (v_IP_Address:string){
AzureActivity
| where Caller != '' and CallerIpAddress == v_IP_Address
| summarize Account_Aux_StartTime = min(TimeGenerated),
Account_Aux_EndTime = max(TimeGenerated),
Count = count() by
Caller, TenantId
| top 10 by Count desc nulls last
| extend UPN = iff(Caller contains '@', Caller, ''), Account_AadUserId = iff(Caller !contains '@', Caller,'')
| extend Account_Name = split(UPN,'@')[0] , Account_UPNSuffix = split(UPN,'@')[1]
| project Account_Name, Account_UPNSuffix, Account_AadUserId, Account_AadTenantId=TenantId, Account_Aux_StartTime , Account_Aux_EndTime
};
AccountActivity_byIP('<Address>')

Просмотреть файл

@ -1,21 +0,0 @@
// Id: aa497951-c779-4ea2-be2a-127ea66c5fba
// DisplayName: Hosts Receiving Least Amount of Data from IP
// Description: Hosts receiving least Amount of data from IP during the range of -1d and +1d
// DataConnector: ; DataTypes: #WireData
// InputEntityType: IP
// InputFields: [Address]
// OutputEntityTypes: [Host]
// QueryPeriodBefore: 1d
// QueryPeriodAfter: 1d
// Tactics: #Exfiltration, #CommandAndControl, #Collection
let HostsReceivingDatafromIP = (v_IP_Address:string){
WireData
| parse Computer with HostName "." Host_DnsDomain
| where SessionState == "Disconnected"
| where RemoteIP == v_IP_Address
| extend Host_HostName = iff(Computer has ".", HostName, Computer)
| summarize Host_Aux_BytesReceived = sum(ReceivedBytes), make_set(LocalIP) by Host_HostName, Host_DnsDomain
| top 10 by Host_Aux_BytesReceived asc nulls last
};
HostsReceivingDatafromIP('<Address>')

Просмотреть файл

@ -0,0 +1,30 @@
Id: aa497951-c779-4ea2-be2a-127ea66c5fba
DisplayName: Hosts Receiving Least Amount of Data from IP
Description: Hosts receiving least Amount of data from IP during the range of -1d and +1d
InputEntityType: IP
InputFields:
- Address
OutputEntityTypes:
- Host
QueryPeriodBefore: 1d
QueryPeriodAfter: 1d
DataConnector:
ConnectorId: ''
DataTypes:
- WireData
Tactics:
- Exfiltration
- CommandAndControl
- Collection
query: |
let HostsReceivingDatafromIP = (v_IP_Address:string){
WireData
| parse Computer with HostName '.' Host_DnsDomain
| where SessionState == 'Disconnected'
| where RemoteIP == v_IP_Address
| extend Host_HostName = iff(Computer has '.', HostName, Computer)
| summarize Host_Aux_BytesReceived = sum(ReceivedBytes), make_set(LocalIP) by Host_HostName, Host_DnsDomain
| top 10 by Host_Aux_BytesReceived asc nulls last
};
HostsReceivingDatafromIP('<Address>')

Просмотреть файл

@ -1,21 +0,0 @@
// Id: 73fb9b8d-fd13-4c43-8136-6d693cafaa23
// DisplayName: Hosts Receiving Most Amount of Data from IP
// Description: Hosts Receiving Most Amount of Data from IP during the range of -1d and +1d
// DataConnector: ; DataTypes: #WireData
// InputEntityType: IP
// InputFields: [Address]
// OutputEntityTypes: [Host]
// QueryPeriodBefore: 1d
// QueryPeriodAfter: 1d
// Tactics: #Exfiltration, #CommandAndControl, #Collection
let HostsReceivingDatafromIP = (v_IP_Address:string){
WireData
| parse Computer with HostName "." Host_DnsDomain
| where SessionState == "Disconnected"
| where RemoteIP == v_IP_Address
| extend Host_HostName = iff(Computer has ".", HostName, Computer)
| summarize Host_Aux_BytesReceived = sum(ReceivedBytes), make_set(LocalIP) by Host_HostName, Host_DnsDomain
| top 10 by Host_Aux_BytesReceived desc nulls last
};
HostsReceivingDatafromIP('<Address>')

Просмотреть файл

@ -0,0 +1,30 @@
Id: 73fb9b8d-fd13-4c43-8136-6d693cafaa23
DisplayName: Hosts Receiving Most Amount of Data from IP
Description: Hosts Receiving Most Amount of Data from IP during the range of -1d and +1d
InputEntityType: IP
InputFields:
- Address
OutputEntityTypes:
- Host
QueryPeriodBefore: 1d
QueryPeriodAfter: 1d
DataConnector:
ConnectorId: ''
DataTypes:
- WireData
Tactics:
- Exfiltration
- CommandAndControl
- Collection
query: |
let HostsReceivingDatafromIP = (v_IP_Address:string){
WireData
| parse Computer with HostName '.' Host_DnsDomain
| where SessionState == "Disconnected"
| where RemoteIP == v_IP_Address
| extend Host_HostName = iff(Computer has '.', HostName, Computer)
| summarize Host_Aux_BytesReceived = sum(ReceivedBytes), make_set(LocalIP) by Host_HostName, Host_DnsDomain
| top 10 by Host_Aux_BytesReceived desc nulls last
};
HostsReceivingDatafromIP('<Address>')

Просмотреть файл

@ -1,21 +0,0 @@
// Id: ab597a67-352e-4914-b2e6-d64919a910a8
// DisplayName: Hosts Sending Least Amount of Data to IP
// Description: Hosts sending least amount of data to IP during the range of -1d and +1d
// InputEntityType: IP
// InputFields: [Address]
// OutputEntityTypes: [Host]
// QueryPeriodBefore: 1d
// QueryPeriodAfter: 1d
// DataConnector: ; DataTypes: #WireData
// Tactics: #Exfiltration, #CommandAndControl, #Collection
let HostsSendingDatatoIP = (v_IP_Address:string){
WireData
| where SessionState == "Disconnected"
| where RemoteIP == v_IP_Address
| summarize Host_Aux_BytesSent = sum(SentBytes) by Computer, LocalIP
| parse Computer with HostName "." Host_DnsName
| extend Host_HostName = iff(Computer has ".", HostName, Computer)
| top 10 by Host_Aux_BytesSent asc nulls last
};
HostsSendingDatatoIP('<Address>')

Просмотреть файл

@ -0,0 +1,30 @@
Id: ab597a67-352e-4914-b2e6-d64919a910a8
DisplayName: Hosts Sending Least Amount of Data to IP
Description: Hosts sending least amount of data to IP during the range of -1d and +1d
InputEntityType: IP
InputFields:
- Address
OutputEntityTypes:
- Host
QueryPeriodBefore: 1d
QueryPeriodAfter: 1d
DataConnector:
ConnectorId: ''
DataTypes:
- WireData
Tactics:
- Exfiltration
- CommandAndControl
- Collection
query: |
let HostsSendingDatatoIP = (v_IP_Address:string){
WireData
| where SessionState == 'Disconnected'
| where RemoteIP == v_IP_Address
| summarize Host_Aux_BytesSent = sum(SentBytes) by Computer, LocalIP
| parse Computer with HostName '.' Host_DnsName
| extend Host_HostName = iff(Computer has '.', HostName, Computer)
| top 10 by Host_Aux_BytesSent asc nulls last
};
HostsSendingDatatoIP('<Address>')

Просмотреть файл

@ -1,21 +0,0 @@
// Id: 5b57680b-d60a-42a5-9cd5-17e499834f8e
// DisplayName: Hosts Sending Most Amount of Data to IP
// Description: Hosts Sending Most Amount of Data to IP
// InputEntityType: IP
// InputFields: [Address]
// OutputEntityTypes: [Host]
// QueryPeriodBefore: 1d
// QueryPeriodAfter: 1d
// DataConnector: ; DataTypes: #WireData
// Tactics: #Exfiltration, #CommandAndControl, #Collection
let HostsSendingDatatoIP = (v_IP_Address:string){
WireData
| where SessionState == "Disconnected"
| where RemoteIP == v_IP_Address
| summarize Host_Aux_BytesSent = sum(SentBytes) by Computer, LocalIP
| parse Computer with HostName "." Host_DnsName
| extend Host_HostName = iff(Computer has ".", HostName, Computer)
| top 10 by Host_Aux_BytesSent desc nulls last
};
HostsSendingDatatoIP('<Address>')

Просмотреть файл

@ -0,0 +1,30 @@
Id: 5b57680b-d60a-42a5-9cd5-17e499834f8e
DisplayName: Hosts Sending Most Amount of Data to IP
Description: Hosts Sending Most Amount of Data to IP
InputEntityType: IP
InputFields:
- Address
OutputEntityTypes:
- Host
QueryPeriodBefore: 1d
QueryPeriodAfter: 1d
DataConnector:
ConnectorId: ''
DataTypes:
- WireData
Tactics:
- Exfiltration
- CommandAndControl
- Collection
query: |
let HostsSendingDatatoIP = (v_IP_Address:string){
WireData
| where SessionState == 'Disconnected'
| where RemoteIP == v_IP_Address
| summarize Host_Aux_BytesSent = sum(SentBytes) by Computer, LocalIP
| parse Computer with HostName '.' Host_DnsName
| extend Host_HostName = iff(Computer has '.', HostName, Computer)
| top 10 by Host_Aux_BytesSent desc nulls last
};
HostsSendingDatatoIP('<Address>')

Просмотреть файл

@ -1,22 +0,0 @@
// Id: 980762f8-014e-4439-8840-5f0a90285dce
// DisplayName: Destination IPs with Most Dropped Sessions
// Description: Destination IPs with most dropped sessions, sorted by number of ports and number of drops
// DataConnector: #WindowsFirewall; DataTypes: #WindowsFirewall
// InputEntityType: IP
// InputFields: [Address]
// OutputEntityTypes: IP
// QueryPeriodBefore: 6h
// QueryPeriodAfter: 6h
// Tactics : #Exfiltration, #CommandAndControl, #Collection, #WindowsFirewall
let MostDroppedDestIP = (v_IP_Address:string){
WindowsFirewall
| where FirewallAction == "DROP"
and SourceIP == v_IP_Address
| summarize DropCount = count(), Ports = makeset(DestinationPort) by DestinationIP
| sort by array_length(Ports), DropCount
| serialize rn=row_number()
| top 10 by rn asc nulls last
| project-rename IP_Address = DestinationIP, IP_Aux_DropCount = DropCount, IP_Aux_DroppedSessionPorts = Ports
};
MostDroppedDestIP('<Address>')

Просмотреть файл

@ -0,0 +1,34 @@
Id: 980762f8-014e-4439-8840-5f0a90285dce
DisplayName: Destination IPs with Most Dropped Sessions
Description: |
Destination IPs with most dropped sessions, sorted by number of ports and number of drops
InputEntityType: IP
InputFields:
- Address
OutputEntityTypes:
- IP
QueryPeriodBefore: 6h
QueryPeriodAfter: 6h
DataConnector:
ConnectorId: WindowsFirewall
DataTypes:
- WindowsFirewall
Tactics:
- Exfiltration
- CommandAndControl
- Collection
- WindowsFirewall
query: |
let MostDroppedDestIP = (v_IP_Address:string){
WindowsFirewall
| where FirewallAction == 'DROP'
and SourceIP == v_IP_Address
| summarize DropCount = count(), Ports = makeset(DestinationPort) by DestinationIP
| sort by array_length(Ports), DropCount
| serialize rn=row_number()
| top 10 by rn asc nulls last
| project-rename IP_Address = DestinationIP, IP_Aux_DropCount = DropCount, IP_Aux_DroppedSessionPorts = Ports
};
MostDroppedDestIP('<Address>')

Просмотреть файл

@ -1,22 +0,0 @@
// Id: 935ab312-cb52-42a5-b296-548f21786102
// DisplayName: Source IPs with Most Dropped Sessions
// Description: Source IPs with most dropped sessions
// DataConnector: #WindowsFirewall; DataTypes: #WindowsFirewall
// InputEntityType: IP
// InputFields: [Address]
// OutputEntityTypes: [IP]
// QueryPeriodBefore: 6h
// QueryPeriodAfter: 6h
// Tactics: #Exfiltration, #CommandAndControl, #Collection
let MostDroppedSourceIP = (v_IP_Address:string){
WindowsFirewall
| where FirewallAction == "DROP"
and DestinationIP == v_IP_Address
| summarize IP_Aux_DropCount = count(), DestPorts = makeset(DestinationPort) by SourceIP
| sort by IP_Aux_DropCount
| serialize rn=row_number()
| top 10 by rn asc nulls last
| project-rename IP_Address = SourceIP
};
MostDroppedSourceIP('<Address>')

Просмотреть файл

@ -0,0 +1,32 @@
Id: 935ab312-cb52-42a5-b296-548f21786102
DisplayName: Source IPs with Most Dropped Sessions
Description: Source IPs with most dropped sessions
InputEntityType: IP
InputFields:
- Address
OutputEntityTypes:
- IP
QueryPeriodBefore: 6h
QueryPeriodAfter: 6h
DataConnector:
ConnectorId: WindowsFirewall
DataTypes:
- WindowsFirewall
Tactics:
- Exfiltration
- CommandAndControl
- Collection
query: |
let MostDroppedSourceIP = (v_IP_Address:string){
WindowsFirewall
| where FirewallAction == 'DROP'
and DestinationIP == v_IP_Address
| summarize IP_Aux_DropCount = count(), DestPorts = makeset(DestinationPort) by SourceIP
| sort by IP_Aux_DropCount
| serialize rn=row_number()
| top 10 by rn asc nulls last
| project-rename IP_Address = SourceIP
};
MostDroppedSourceIP('<Address>')

Просмотреть файл

@ -1,22 +0,0 @@
// Id: 588f5d9f-3380-4eff-9983-e61d62fdd172
// DisplayName: Office Activity IP Address by Account
// Description: Summary of Accounts for a given ClientIP on Office Activity data (set time range to +-12h when running the query)
// InputEntityType: Ip
// InputFields: [Address]
// OutputEntityTypes: [Account]
// QueryPeriodBefore: 12h
// QueryPeriodAfter: 12h
// DataSource: #OfficeActivity
// Tactics: #Persistence, #Discovery, #LateralMovement, #Collection
let GetAllAccountByIP = (v_IP_Address:string){
OfficeActivity
| where ClientIP contains v_IP_Address
| extend info = pack("ClientIP", ClientIP, "UserType", UserType, "Operation", Operation, "OfficeWorkload", OfficeWorkload, "ResultStatus", ResultStatus)
| summarize min(TimeGenerated), max(TimeGenerated), count(), Account_Aux_info = makeset(info) by UserId
| project StartTimeUtc = min_TimeGenerated, EndTimeUtc = max_TimeGenerated, UserId, count_, Account_Aux_info
| project-rename Account_UnstructuredName=UserId
| top 10 by count_ desc nulls last
};
// change <Address> value below
GetAllAccountByIP('<Address>')

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше