Merge branch 'master' into Playbook-instructions
This commit is contained in:
Коммит
56b1ac734c
|
@ -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'
|
|
@ -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
|
|
@ -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>')
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче