Repository: rod-trent/Sentinel-SOC-101
Branch: main
Commit: 580741663b0e
Files: 41
Total size: 29.4 KB
Directory structure:
gitextract__ibqyia7/
├── Code-and-Queries/
│ ├── Alert_Signin_After_180_Days.kql
│ ├── All-legacy-auth.kql
│ ├── BruteForcePorts.kql
│ ├── DNSLookup.kql
│ ├── DetectZeroDay.kql
│ ├── Detect_Multiple_Teams_Delete_IP.kql
│ ├── Detect_Multiple_Teams_Delete_User.kql
│ ├── DoS-IoT.kql
│ ├── DoSWebApp.kql
│ ├── DomainEntropy.kql
│ ├── ElevatedAccounts.ps1
│ ├── Emails-from-malicious-IPs.kql
│ ├── FailedLogins.kql
│ ├── GitEvents.kql
│ ├── Last_Signin_Date.kql
│ ├── Legacy-auth-IP.kql
│ ├── Legacy-auth-timeframe.kql
│ ├── Legacy-auth-top10users.kql
│ ├── Legacy-auth-user.kql
│ ├── LegacyAuth30.kql
│ ├── LegacyAuthGraphAPI.kql
│ ├── LegacyAuthTimes.kql
│ ├── MalwareUploaded.kql
│ ├── MissingUpdates.kql
│ ├── No_Signing_Last_90_Days.kql
│ ├── Phishing-campaigns.kql
│ ├── Quishing.kql
│ ├── RareDomains.kql
│ ├── Readme.md
│ ├── SessionStealing.kql
│ ├── Spear_Phishing.kql
│ ├── TILookupforDNSEvents.kql
│ ├── TopThreatEmails.kql
│ ├── Unusual-logins.kql
│ └── XSS-Attack.kql
├── Images/
│ └── Readme.md
├── LICENSE
├── README.md
├── Videos/
│ └── Readme.md
└── eBook/
├── Collateral/
│ └── Readme.md
└── Readme.md
================================================
FILE CONTENTS
================================================
================================================
FILE: Code-and-Queries/Alert_Signin_After_180_Days.kql
================================================
//Create an alert when a user who has not signed in for more than 180 days signs in again
SigninLogs
| where TimeGenerated > ago(30d)
| summarize arg_max(TimeGenerated, *) by UserPrincipalName
| project UserPrincipalName, TimeGenerated
| join kind=leftouter (
externaldata(displayName:string,lastSignInDateTime:datetime)
[@"https://graph.microsoft.com/v1.0/users?$select=displayName,signInActivity"]
with(format="json", ingestionMapping=[{"column":"displayName","path":"displayName"},{"column":"lastSignInDateTime","path":"signInActivity/lastSignInDateTime"}])
on $left.UserPrincipalName == $right.displayName
)
on UserPrincipalName
| project UserPrincipalName, TimeGenerated, lastSignInDateTime
| where lastSignInDateTime < ago(180d)
| extend AccountCustomEntity = UserPrincipalName
================================================
FILE: Code-and-Queries/All-legacy-auth.kql
================================================
view all legacy authentication events:
SecurityEvent
| where EventID == 4624 and TargetLogonId != 0x0
| extend LegacyAuthentication = iif((LogonProcessName =~ "Advapi" or LogonProcessName =~ "Ssp"), "True", "False")
| where LegacyAuthentication == "True"
================================================
FILE: Code-and-Queries/BruteForcePorts.kql
================================================
detect brute force attacks targeting RDP or SSH management ports could be:
SecurityEvent
| where EventID == 4625
| where (SubStatus == "0xc000006A" or SubStatus == "0xc0000064")
| project TimeGenerated, EventID, WorkstationName, Computer, Account, LogonTypeName, LogonType, LogonProcessName, SubStatus, Activity
================================================
FILE: Code-and-Queries/DNSLookup.kql
================================================
//Identifies IP addresses performing DNS lookups associated with common ToR proxies
DnsEvents
| where Name contains "."
| 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
================================================
FILE: Code-and-Queries/DetectZeroDay.kql
================================================
//Based on available IOCs, the following KQL query example detects CVE-2023-23397
DeviceProcessEvents
| where InitiatingProcessFileName == "svchost.exe"
| where FileName == "rundll32.exe" and ProcessCommandLine contains "davclnt.dll" and ProcessCommandLine contains "DavSetCookie"
| where ProcessCommandLine !contains "http://10."
| where ProcessCommandLine !contains "http://192.168."
| extend url = split(ProcessCommandLine, "http://")[1]
| extend domain = split(url, "/")[0]
| where domain contains "." and domain !endswith ".local"
| summarize count() by tostring(domain)
================================================
FILE: Code-and-Queries/Detect_Multiple_Teams_Delete_IP.kql
================================================
//Detecting multiple Teams deletion from a single IP address
OfficeActivity
| where TimeGenerated > ago(1h)
| where Operation =~ \"TeamDeleted\"
| summarize count() by ClientIP
| where count_ > 5
================================================
FILE: Code-and-Queries/Detect_Multiple_Teams_Delete_User.kql
================================================
//Detecting multiple Teams deletion by a single user
OfficeActivity
| where TimeGenerated > ago(1h)
| where Operation =~ \"TeamDeleted\"
| summarize count() by UserId
| where count_ > 5
================================================
FILE: Code-and-Queries/DoS-IoT.kql
================================================
//KQL Example of DoS attack against an IoT device
SecurityAlert
| where ProductName == "Azure Security Center for IoT"
| where AlertName == "Suspicion of Denial Of Service Attack"
| where TimeGenerated <= ProcessingEndTime + 60m
| extend DeviceId = tostring(parse_json(ExtendedProperties).DeviceId)
| extend SourceDeviceAddress = tostring(parse_json(ExtendedProperties).SourceDeviceAddress)
| extend DestDeviceAddress = tostring(parse_json(ExtendedProperties).DestinationDeviceAddress)
| extend RemediationSteps = tostring(parse_json(RemediationSteps)[0])
| extend Protocol = tostring(parse_json(ExtendedProperties).Protocol)
| extend AlertManagementUri = tostring(parse_json(ExtendedProperties).AlertManagementUri)
| project TimeGenerated, DeviceId, ProductName, ProductComponentName, AlertSeverity, AlertName, Description, Protocol, SourceDeviceAddress, DestDeviceAddress, RemediationSteps, Tactics, Entities, VendorOriginalId, AlertLink, AlertManagementUri
================================================
FILE: Code-and-Queries/DoSWebApp.kql
================================================
//KQL Example of DoS attack against a web application
//This query will return a table with the following columns:
//TimeGenerated: The timestamp of the web request
//clientIP_s: The IP address of the client that made the web request
//requestUri_s: The URI of the web request
//httpStatus_d: The HTTP status code of the web response
//Requests: The number of requests made by the client IP address in the time range
// Set the time range to look for potential DoS attacks
let timeRange = 1h;
// Set the threshold for the number of requests per IP address that indicates a DoS attack
let threshold = 1000;
// Get the web requests from the AzureDiagnostics table
let webRequests = AzureDiagnostics
| where TimeGenerated > ago(timeRange)
| where Category == "ApplicationGatewayAccessLog"
| project TimeGenerated, clientIP_s, requestUri_s, httpStatus_d;
// Group the web requests by IP address and count the number of requests per IP
let ipCounts = webRequests
| summarize Requests = count() by clientIP_s
| where Requests > threshold;
// Join the ipCounts with the webRequests to get the details of the requests from the potential attackers
ipCounts
| join kind=inner webRequests on clientIP_s
| project TimeGenerated, clientIP_s, requestUri_s, httpStatus_d, Requests
| order by TimeGenerated desc
================================================
FILE: Code-and-Queries/DomainEntropy.kql
================================================
//Detect rare domains seen in the Office 365 audit logs
OfficeActivity
| where TimeGenerated > ago(30d)
| where Operation in ("Send", "Receive", "Download", "Upload", "Accessed", "Modified", "Renamed", "Deleted", "Shared", "Unshared")
| summarize Users=dcount(UserId), UserAgents=dcount(UserAgent), Records=count() by OrganizationName
| extend DomainLength = strlen(OrganizationName)
| extend Entropy = DomainLength * 1.0 / array_length(split(OrganizationName, ""))
| where Entropy > 3.5
| order by Users asc
| take 10
================================================
FILE: Code-and-Queries/ElevatedAccounts.ps1
================================================
# Get list of elevated accounts in Entra ID (AzureAD)
Get-AzureADDirectoryRoleMember -ObjectId (Get-AzureADDirectoryRole | Where-Object {$_.displayName -eq "Global Administrator"}).ObjectId
================================================
FILE: Code-and-Queries/Emails-from-malicious-IPs.kql
================================================
This query determines emails sent by top malicious/bad IP addresses.
let cutoff = 5;
EmailEvents
| where ThreatTypes has "Malware" or ThreatTypes has "Phish"
| summarize count() by SenderIPv4
| where count_ > cutoff // Arbitrary cutoff, increase or decrease as needed
| join EmailEvents on SenderIPv4
| where DeliveryAction =~ "Delivered"
================================================
FILE: Code-and-Queries/FailedLogins.kql
================================================
SigninLogs
| where ResultType == "50126" // Failed sign-in due to bad username or password
| where UserPrincipalName in ((Get-AzureADDirectoryRoleMember -ObjectId (Get-AzureADDirectoryRole | Where-Object {$_.displayName -eq "Global Administrator"}).ObjectId).UserPrincipalName) // Filter by global administrators
| summarize count() by UserPrincipalName // Count the number of failed sign-in attempts by user
| where count_ > 6 // Filter by users who have more than 6 failed sign-in attempts
| join kind=inner ( // Join with the original sign-in logs table
SigninLogs
| where ResultType == "50126"
| where UserPrincipalName in ((Get-AzureADDirectoryRoleMember -ObjectId (Get-AzureADDirectoryRole | Where-Object {$_.displayName -eq "Global Administrator"}).ObjectId).UserPrincipalName)
| summarize min(TimeGenerated), max(TimeGenerated) by UserPrincipalName // Get the first and last failed sign-in time by user
) on UserPrincipalName
| where (max_TimeGenerated - min_TimeGenerated) < 10m // Filter by users who have failed sign-in attempts within 10 minutes
| project UserPrincipalName, count_, min_TimeGenerated, max_TimeGenerated // Select the relevant columns
================================================
FILE: Code-and-Queries/GitEvents.kql
================================================
//This rule monitors `GitEvents` for modifications to the repository by users other than the ones specified, potentially highlighting unauthorized changes. Uses a custom table.
GitEvents
| where OperationName == 'RepoModified'
| where User notin ('KnownDev1', 'KnownDev2')
================================================
FILE: Code-and-Queries/Last_Signin_Date.kql
================================================
//Get the last sign-in date and time for all users
SigninLogs
| where TimeGenerated > ago(30d)
| summarize arg_max(TimeGenerated, *) by UserPrincipalName
| project UserPrincipalName, TimeGenerated
| join kind=leftouter (
externaldata(displayName:string,lastSignInDateTime:datetime)
[@"https://graph.microsoft.com/v1.0/users?$select=displayName,signInActivity"]
with(format="json", ingestionMapping=[{"column":"displayName","path":"displayName"},{"column":"lastSignInDateTime","path":"signInActivity/lastSignInDateTime"}])
on $left.UserPrincipalName == $right.displayName
)
on UserPrincipalName
| project UserPrincipalName, TimeGenerated, lastSignInDateTime
================================================
FILE: Code-and-Queries/Legacy-auth-IP.kql
================================================
//View all legacy authentication events from a specific source IP address
SecurityEvent
| where EventID == 4624 and TargetLogonId != 0x0 and IpAddress == "<source IP address>"
| extend LegacyAuthentication = iif((LogonProcessName =~ "Advapi" or LogonProcessName =~ "Ssp"), "True", "False")
| where LegacyAuthentication == "True"
================================================
FILE: Code-and-Queries/Legacy-auth-timeframe.kql
================================================
//View all legacy authentication events within a specific time frame
SecurityEvent
| where TimeGenerated > ago(1d) and EventID == 4624 and TargetLogonId != 0x0
| extend LegacyAuthentication = iif((LogonProcessName =~ "Advapi" or LogonProcessName =~ "Ssp"), "True", "False")
| where LegacyAuthentication == "True"
================================================
FILE: Code-and-Queries/Legacy-auth-top10users.kql
================================================
//View the top 10 users with the most legacy authentication events
SecurityEvent
| where EventID == 4624 and TargetLogonId != 0x0
| extend LegacyAuthentication = iif((LogonProcessName =~ "Advapi" or LogonProcessName =~ "Ssp"), "True", "False")
| where LegacyAuthentication == "True"
| summarize count() by AccountName
| top 10 by count_
================================================
FILE: Code-and-Queries/Legacy-auth-user.kql
================================================
view all legacy authentication events from a specific user:
SecurityEvent
| where EventID == 4624 and TargetLogonId != 0x0 and AccountName == "<username>"
| extend LegacyAuthentication = iif((LogonProcessName =~ "Advapi" or LogonProcessName =~ "Ssp"), "True", "False")
| where LegacyAuthentication == "True"
================================================
FILE: Code-and-Queries/LegacyAuth30.kql
================================================
//This query returns all sign-in attempts using legacy authentication protocols in the last 30 days.
SigninLogs
| where TimeGenerated > ago(30d)
| where ClientAppUsed in ("Browser", "Exchange ActiveSync", "IMAP4", "Mobile Apps and Desktop clients", "Other clients", "POP3", "SMTP")
================================================
FILE: Code-and-Queries/LegacyAuthGraphAPI.kql
================================================
//This query joins the SigninLogs table with an external data source from the Microsoft Graph API, which returns the displayName and lastSignInDateTime properties for all users
SigninLogs
| where TimeGenerated > ago(30d)
| where ClientAppUsed in ("Browser", "Exchange ActiveSync", "IMAP4", "Mobile Apps and Desktop clients", "Other clients", "POP3", "SMTP")
| summarize arg_max(TimeGenerated, *) by UserPrincipalName
| project UserPrincipalName, TimeGenerated
| join kind=leftouter (
externaldata(displayName:string,lastSignInDateTime:datetime)
[@"https://graph.microsoft.com/v1.0/users?$select=displayName,signInActivity"]
with(format="json", ingestionMapping=[{"column":"displayName","path":"displayName"},{"column":"lastSignInDateTime","path":"signInActivity/lastSignInDateTime"}])
on $left.UserPrincipalName == $right.displayName
)
on UserPrincipalName
| project UserPrincipalName, TimeGenerated, lastSignInDateTime
| where lastSignInDateTime < ago(90d)
| extend AccountCustomEntity = UserPrincipalName
================================================
FILE: Code-and-Queries/LegacyAuthTimes.kql
================================================
//This query groups and counts the sign-in attempts by user principal name (UPN), which is usually the same as the user’s email address, and orders them by descending order
SigninLogs
| where TimeGenerated > ago(30d)
| where ClientAppUsed in ("Browser", "Exchange ActiveSync", "IMAP4", "Mobile Apps and Desktop clients", "Other clients", "POP3", "SMTP")
| summarize count() by UserPrincipalName
| order by count_ desc
================================================
FILE: Code-and-Queries/MalwareUploaded.kql
================================================
//Malware uploaded to SharePoint or OneDrive
OfficeActivity
| where TimeGenerated > ago(30d)
| where Operation == "FileMalwareDetected"
| project
TimeGenerated,
OfficeWorkload,
['File Name']=SourceFileName,
['File Location']=OfficeObjectId,
['Relative File URL']=SourceRelativeUrl,
ClientIP
================================================
FILE: Code-and-Queries/MissingUpdates.kql
================================================
// Missing updates summary
// Get a summary of missing updates by category.
Update
| where TimeGenerated>ago(5h) and OSType=="Linux" and SourceComputerId in ((Heartbeat
| where TimeGenerated>ago(12h) and OSType=="Linux" and notempty(Computer)
| summarize arg_max(TimeGenerated, Solutions) by SourceComputerId
| where Solutions has "updates"
| distinct SourceComputerId))
| summarize hint.strategy=partitioned arg_max(TimeGenerated, UpdateState, Classification) by Computer, SourceComputerId, Product, ProductArch
| where UpdateState=~"Needed"
| summarize by Product, ProductArch, Classification
| union (Update
| where TimeGenerated>ago(14h) and OSType!="Linux" and (Optional==false or Classification has "Critical" or Classification has "Security") and SourceComputerId in ((Heartbeat
| where TimeGenerated>ago(12h) and OSType=~"Windows" and notempty(Computer)
| summarize arg_max(TimeGenerated, Solutions) by SourceComputerId
| where Solutions has "updates"
| distinct SourceComputerId))
| summarize hint.strategy=partitioned arg_max(TimeGenerated, UpdateState, Classification, Approved) by Computer, SourceComputerId, UpdateID
| where UpdateState=~"Needed" and Approved!=false
| summarize by UpdateID, Classification )
| summarize allUpdatesCount=count(), criticalUpdatesCount=countif(Classification has "Critical"), securityUpdatesCount=countif(Classification has "Security"), otherUpdatesCount=countif(Classification !has "Critical" and Classification !has "Security")
================================================
FILE: Code-and-Queries/No_Signing_Last_90_Days.kql
================================================
//Get a list of users who have not signed in for more than 90 days
SigninLogs
| where TimeGenerated > ago(30d)
| summarize arg_max(TimeGenerated, *) by UserPrincipalName
| project UserPrincipalName, TimeGenerated
| join kind=leftouter (
externaldata(displayName:string,lastSignInDateTime:datetime)
[@"https://graph.microsoft.com/v1.0/users?$select=displayName,signInActivity"]
with(format="json", ingestionMapping=[{"column":"displayName","path":"displayName"},{"column":"lastSignInDateTime","path":"signInActivity/lastSignInDateTime"}])
on $left.UserPrincipalName == $right.displayName
)
on UserPrincipalName
| project UserPrincipalName, TimeGenerated, lastSignInDateTime
| where lastSignInDateTime < ago(90d)
================================================
FILE: Code-and-Queries/Phishing-campaigns.kql
================================================
//This query helps surface phishing campaigns associated with Appspot abuse. These emails frequently contain phishing links that utilize the recipients' own email address as a unique identifier in the URI.
EmailUrlInfo
// Detect URLs with a subdomain on appspot.com
| where UrlDomain matches regex @'\b[\w\-]+-dot-[\w\-\.]+\.appspot\.com\b'
// Enrich results with sender and recipient data
| join kind=inner EmailEvents on $left.NetworkMessageId==$right.NetworkMessageId
// Phishing attempts from Appspot related campaigns typically contain the recipient's email address in the URI
// Example 1: https://example-dot-example.appspot.com/#recipient@domain.com
// Example 2: https://example-dot-example.appspot.com/index.html?user=recipient@domain.com
| where Url has RecipientEmailAddress
// Some phishing campaigns pass recipient email as a Base64 encoded string in the URI
or Url has base64_encode_tostring(RecipientEmailAddress)
| project-away Timestamp1, NetworkMessageId1, ReportId1
================================================
FILE: Code-and-Queries/Quishing.kql
================================================
//Detect QR Codes in emails
let image_extensions = dynamic(["jpg", "jpeg", "png", "bmp", "gif"]);
EmailAttachmentInfo
| where FileType in (image_extensions)
| where FileName matches regex "[A-Z0-9]{9,10}.[A-Za-z0-9]+$"
| join EmailUrlInfo on TenantId
| where UrlLocation == "Attachment"
| distinct FileName, FileType, SenderFromAddress, RecipientEmailAddress, UrlDomain, Url
================================================
FILE: Code-and-Queries/RareDomains.kql
================================================
//Rare Domains Seen in Cloud Logs
// Define the time range to look for OfficeActivity events
let Lookback = ago(7d);
// Get the OfficeActivity events and filter by OperationName
let OfficeEvents = OfficeActivity
| where TimeGenerated > Lookback
| where Operation in ("FileDownloaded", "FileUploaded")
| extend Domain = tostring(split(SourceRelativeUrl, "/")[2]) // extract the domain name from the file URL
| project TimeGenerated, UserId, Operation, SourceFileName, Domain;
// Get the ThreatIntelligenceIndicator records and filter by ThreatType
let TIRecords = ThreatIntelligenceIndicator
| where TimeGenerated > Lookback
| where ThreatType == "DomainName"
| project Domain = NetworkDestinationAsn, ThreatSeverity;
// Join the OfficeEvents and TIRecords tables on Domain
let JoinedEvents = OfficeEvents
| join kind=leftouter (
TIRecords
) on Domain;
// Calculate the rarity score of each domain based on its frequency and threat level
// The rarity score is defined as log10(Count) * (ThreatLevel + 1), where Count is the number of events for each domain and ThreatLevel is a numeric value from 0 to 3
// The higher the rarity score, the more rare and potentially malicious the domain is
let RarityScore = JoinedEvents
| summarize Count = count() by Domain, ThreatSeverity // count the number of events for each domain and threat level combination
| extend RarityScore = log10(Count) * (ThreatSeverity + 1) // calculate the rarity score
| order by RarityScore desc; // order by rarity score in descending order
// Display the results
RarityScore
================================================
FILE: Code-and-Queries/Readme.md
================================================
================================================
FILE: Code-and-Queries/SessionStealing.kql
================================================
// Define the time range and the threshold for the number of sessions per user
let starttime = 7d;
let endtime = now();
let session_threshold = 10;
// Get the sign-in events from Azure Active Directory
let signin_events = SigninLogs
| where TimeGenerated between (starttime .. endtime)
| where ResultType == 0 // successful sign-ins only
| project TimeGenerated, UserPrincipalName, IPAddress, SessionId;
// Get the cloud app events from Microsoft Cloud App Security
let cloudapp_events = CloudAppEvents
| where TimeGenerated between (starttime .. endtime)
| project TimeGenerated, UserPrincipalName, IPAddress, SessionId;
// Join the sign-in events and the cloud app events by user principal name and session id
let joined_events = signin_events
| join kind=inner cloudapp_events on UserPrincipalName, SessionId
| project TimeGenerated, UserPrincipalName, IPAddress, SessionId;
// Group the events by user principal name and session id, and count the number of distinct IP addresses per session
let session_stats = joined_events
| summarize IPCount = dcount(IPAddress) by UserPrincipalName, SessionId
| project UserPrincipalName, SessionId, IPCount;
// Find the sessions that have more than one IP address associated with them
let multi_ip_sessions = session_stats
| where IPCount > 1
| project UserPrincipalName, SessionId;
// Find the users that have more than the threshold number of sessions with multiple IP addresses
let suspicious_users = multi_ip_sessions
| summarize SessionCount = count() by UserPrincipalName
| where SessionCount > session_threshold
| project UserPrincipalName;
// Return the suspicious users and their sessions with multiple IP addresses
suspicious_users
| join kind=inner multi_ip_sessions on UserPrincipalName
| join kind=inner session_stats on UserPrincipalName, SessionId
| project UserPrincipalName, SessionId, IPCount;
================================================
FILE: Code-and-Queries/Spear_Phishing.kql
================================================
//This query looks for email events in the last seven days where the subject contains certain keywords, the sender’s domain is different from the recipients, and the count of such emails exceeds a threshold.
let lookbackTime = 7d;
let threshold = 5;
EmailEvents
| where TimeGenerated >= ago(lookbackTime)
| where Subject has_any ("urgent", "payment", "required", "request")
| where SenderFromDomain != RecipientDomain
| summarize Count = count() by SenderFromDomain, RecipientEmailAddress
| where Count > threshold
| project SenderFromDomain, RecipientEmailAddress, Count
================================================
FILE: Code-and-Queries/TILookupforDNSEvents.kql
================================================
//TI Lookup that match DNS events
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
ThreatIntelligenceIndicator
| where TimeGenerated >= ago(ioc_lookBack) and ExpirationDateTime > now()
| where Active == true
// Picking up only IOC's that contain the entities we want
| where isnotempty(NetworkIP) or isnotempty(EmailSourceIpAddress) or isnotempty(NetworkDestinationIP) or isnotempty(NetworkSourceIP)
// As there is potentially more than 1 indicator type for matching IP, taking NetworkIP first, then others if that is empty.
// Taking the first non-empty value based on potential IOC match availability
| extend TI_ipEntity = iff(isnotempty(NetworkIP), NetworkIP, NetworkDestinationIP)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(EmailSourceIpAddress), EmailSourceIpAddress, TI_ipEntity)
| join (
DnsEvents | where TimeGenerated >= ago(dt_lookBack)
| where SubType =~ "LookupQuery" and isnotempty(IPAddresses)
| extend SingleIP = split(IPAddresses, ",")
| mvexpand SingleIP
| extend SingleIP = tostring(SingleIP)
// renaming time column so it is clear the log this came from
| extend DNS_TimeGenerated = TimeGenerated
)
on $left.TI_ipEntity == $right.SingleIP
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| 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, URLCustomEntity = Url
================================================
FILE: Code-and-Queries/TopThreatEmails.kql
================================================
//This query determines emails sent by top malicious/bad IP addresses.
let cutoff = 5;
EmailEvents
| where ThreatTypes has "Malware" or ThreatTypes has "Phish"
| summarize count() by SenderIPv4
| where count_ > cutoff // Arbitrary cutoff, increase or decrease as needed
| join EmailEvents on SenderIPv4
| where DeliveryAction =~ "Delivered"
================================================
FILE: Code-and-Queries/Unusual-logins.kql
================================================
identify unusual login attempts from regions where your suppliers are based, you might use:
SigninLogs
| where Location in ('SupplierRegion1', 'SupplierRegion2')
| summarize Count=count() by UserPrincipalName, Location
| where Count > 5
================================================
FILE: Code-and-Queries/XSS-Attack.kql
================================================
Leverage Microsoft Sentinel for XSS attack detection, create an analytics rule with the following KQL query:
AzureDiagnostics
| where ResourceType == "APPLICATIONGATEWAYS"
| where Category == "ApplicationGatewayFirewallLog"
| where Message contains "XSS Attack"
| project Message, details_message_s, details_data_s, clientIp_s, action_s
================================================
FILE: Images/Readme.md
================================================
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2023 Rod Trent
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Microsoft Sentinel SOC 101<br>
## Content and collateral for the Microsoft Sentinel SOC 101 series.
Announcing the Microsoft Sentinel 101 series on the <a href="https://rodtrent.substack.com/podcast" target="_blank">After the Blog</a> podcast: <a href="https://rodtrent.substack.com/p/episode-5-announcing-the-microsoft#details" target="_blank">Episode 5: Announcing the Microsoft Sentinel SOC 101 Blog Series</a>
Get the eBook: https://github.com/rod-trent/Sentinel-SOC-101/tree/main/eBook
## TOC
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Brute Force Attacks with Microsoft Sentinel</a> - <i>Posted SEP 18, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-443" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Phishing Attacks with Microsoft Sentinel</a> - <i>Posted SEP 19, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-321" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Malware Attacks with Microsoft Sentinel</a> - <i>Posted SEP 20, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-658" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Cross-Site Scripting (XSS) Attacks with Microsoft Sentinel</a> - <i>Posted SEP 21, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-leveraging" target="_blank">Microsoft Sentinel SOC 101: Leveraging MITRE ATT&CK Techniques with Microsoft Sentinel</a> - <i>Posted SEP 22, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-f83" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Supply Chain Attacks with Microsoft Sentinel</a> - <i>Posted SEP 25, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-5ab" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Credential Reuse Attacks with Microsoft Sentinel</a> - <i>Posted SEP 26, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-28a" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate SQL Injection Attacks with Microsoft Sentinel</a> - <i>Posted SEP 27, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-8be" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Denial of Service Attacks with Microsoft Sentinel</a> - <i>Posted SEP 28, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-ae5" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Man-in-the-Middle (MitM) Attacks with Microsoft Sentinel</a> - <i>Posted SEP 29, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-980" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Keylogger Attacks with Microsoft Sentinel</a> - <i>Posted OCT 2, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-8d0" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Cryptojacking Attacks with Microsoft Sentinel</a> - <i>Posted OCT 3, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-2a3" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Drive-by Download Attacks with Microsoft Sentinel</a> - <i>Posted OCT 4, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-b94" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Quishing Attacks with Microsoft Sentinel</a> - <i>Posted OCT 5, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-214" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Session Token Stealing Attacks with Microsoft Sentinel</a> - <i>Posted OCT 6, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-15e" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Fileless Malware Attacks with Microsoft Sentinel</a> - <i>Posted OCT 9, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-018" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Zero-day Exploits with Microsoft Sentinel</a> - <i>Posted OCT 10, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-40b" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate a DNS Spoofing Attack with Microsoft Sentinel</a> - <i>Posted OCT 11, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-07c" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Advanced Persistent Threats (APTs) with Microsoft Sentinel</a> - <i>Posted OCT 12, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-513" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate a VIP Account that has Multiple Failed Logons within a Threshold with Microsoft Sentinel</a> - <i>Posted OCT 17, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-940" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Rare Domains Seen in Cloud Logs</a> - <i>Posted OCT 19, 2023</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-448" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Inactive Account Sign-ins with Microsoft Sentinel
Where have you been?</a> - <i>Posted FEB 5, 2024</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-ebc" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Social Engineering Attacks with Microsoft Sentinel</a> - <i>Posted FEB 6, 2024</i>
* <a href="https://rodtrent.substack.com/p/microsoft-sentinel-soc-101-how-to-9b0" target="_blank">Microsoft Sentinel SOC 101: How to Detect and Mitigate Multiple Microsoft Teams Deleted by a Single User with Microsoft Sentinel</a> - <i>Posted FEB 7, 2024</i>
<p align="center"><img src="https://github.com/rod-trent/Sentinel-SOC-101/blob/main/Images/sentinelsocsmall.jpeg?raw=true" alt="Microsoft Sentinel SOC 101"></center></p>
================================================
FILE: Videos/Readme.md
================================================
================================================
FILE: eBook/Collateral/Readme.md
================================================
================================================
FILE: eBook/Readme.md
================================================
# Microsoft Sentinel SOC 101 - the Book
This is part of an ongoing series to educate about using Microsoft Security products in real world scenario.
The full series index (including code, queries, and detections) is located here: https://aka.ms/SentinelSOC101
The book will be updated when each new part in this series is released.
This book is updated every time a new part of this series is chaptered. The most current edition of this book will always be located at: https://github.com/rod-trent/Sentinel-SOC-101/tree/main/eBook
*Book release ver. 0.070, February 12, 2024 8:00pm EST*
gitextract__ibqyia7/
├── Code-and-Queries/
│ ├── Alert_Signin_After_180_Days.kql
│ ├── All-legacy-auth.kql
│ ├── BruteForcePorts.kql
│ ├── DNSLookup.kql
│ ├── DetectZeroDay.kql
│ ├── Detect_Multiple_Teams_Delete_IP.kql
│ ├── Detect_Multiple_Teams_Delete_User.kql
│ ├── DoS-IoT.kql
│ ├── DoSWebApp.kql
│ ├── DomainEntropy.kql
│ ├── ElevatedAccounts.ps1
│ ├── Emails-from-malicious-IPs.kql
│ ├── FailedLogins.kql
│ ├── GitEvents.kql
│ ├── Last_Signin_Date.kql
│ ├── Legacy-auth-IP.kql
│ ├── Legacy-auth-timeframe.kql
│ ├── Legacy-auth-top10users.kql
│ ├── Legacy-auth-user.kql
│ ├── LegacyAuth30.kql
│ ├── LegacyAuthGraphAPI.kql
│ ├── LegacyAuthTimes.kql
│ ├── MalwareUploaded.kql
│ ├── MissingUpdates.kql
│ ├── No_Signing_Last_90_Days.kql
│ ├── Phishing-campaigns.kql
│ ├── Quishing.kql
│ ├── RareDomains.kql
│ ├── Readme.md
│ ├── SessionStealing.kql
│ ├── Spear_Phishing.kql
│ ├── TILookupforDNSEvents.kql
│ ├── TopThreatEmails.kql
│ ├── Unusual-logins.kql
│ └── XSS-Attack.kql
├── Images/
│ └── Readme.md
├── LICENSE
├── README.md
├── Videos/
│ └── Readme.md
└── eBook/
├── Collateral/
│ └── Readme.md
└── Readme.md
Condensed preview — 41 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (34K chars).
[
{
"path": "Code-and-Queries/Alert_Signin_After_180_Days.kql",
"chars": 801,
"preview": "//Create an alert when a user who has not signed in for more than 180 days signs in again\n\nSigninLogs\n| where TimeGenera"
},
{
"path": "Code-and-Queries/All-legacy-auth.kql",
"chars": 262,
"preview": "view all legacy authentication events:\n\nSecurityEvent\n| where EventID == 4624 and TargetLogonId != 0x0 \n| extend Legacy"
},
{
"path": "Code-and-Queries/BruteForcePorts.kql",
"chars": 313,
"preview": "detect brute force attacks targeting RDP or SSH management ports could be:\n\nSecurityEvent\n| where EventID == 4625\n| wher"
},
{
"path": "Code-and-Queries/DNSLookup.kql",
"chars": 725,
"preview": "//Identifies IP addresses performing DNS lookups associated with common ToR proxies\nDnsEvents\n| where Name contains \".\"\n"
},
{
"path": "Code-and-Queries/DetectZeroDay.kql",
"chars": 576,
"preview": "//Based on available IOCs, the following KQL query example detects CVE-2023-23397\nDeviceProcessEvents\n| where Initiating"
},
{
"path": "Code-and-Queries/Detect_Multiple_Teams_Delete_IP.kql",
"chars": 197,
"preview": "//Detecting multiple Teams deletion from a single IP address\n\nOfficeActivity\n| where TimeGenerated > ago(1h)\n| where Ope"
},
{
"path": "Code-and-Queries/Detect_Multiple_Teams_Delete_User.kql",
"chars": 187,
"preview": "//Detecting multiple Teams deletion by a single user\n\nOfficeActivity\n| where TimeGenerated > ago(1h)\n| where Operation ="
},
{
"path": "Code-and-Queries/DoS-IoT.kql",
"chars": 961,
"preview": "//KQL Example of DoS attack against an IoT device\n\nSecurityAlert\n| where ProductName == \"Azure Security Center for IoT\"\n"
},
{
"path": "Code-and-Queries/DoSWebApp.kql",
"chars": 1298,
"preview": "//KQL Example of DoS attack against a web application\n\n//This query will return a table with the following columns:\n//Ti"
},
{
"path": "Code-and-Queries/DomainEntropy.kql",
"chars": 520,
"preview": "//Detect rare domains seen in the Office 365 audit logs\n\nOfficeActivity\n| where TimeGenerated > ago(30d)\n| where Operati"
},
{
"path": "Code-and-Queries/ElevatedAccounts.ps1",
"chars": 191,
"preview": "# Get list of elevated accounts in Entra ID (AzureAD)\n\nGet-AzureADDirectoryRoleMember -ObjectId (Get-AzureADDirectoryRol"
},
{
"path": "Code-and-Queries/Emails-from-malicious-IPs.kql",
"chars": 342,
"preview": "This query determines emails sent by top malicious/bad IP addresses.\n\nlet cutoff = 5;\nEmailEvents\n| where ThreatTypes ha"
},
{
"path": "Code-and-Queries/FailedLogins.kql",
"chars": 1179,
"preview": "SigninLogs\n| where ResultType == \"50126\" // Failed sign-in due to bad username or password\n| where UserPrincipalName in "
},
{
"path": "Code-and-Queries/GitEvents.kql",
"chars": 274,
"preview": "//This rule monitors `GitEvents` for modifications to the repository by users other than the ones specified, potentially"
},
{
"path": "Code-and-Queries/Last_Signin_Date.kql",
"chars": 674,
"preview": "//Get the last sign-in date and time for all users\n\nSigninLogs\n| where TimeGenerated > ago(30d)\n| summarize arg_max(Time"
},
{
"path": "Code-and-Queries/Legacy-auth-IP.kql",
"chars": 336,
"preview": "//View all legacy authentication events from a specific source IP address\n\nSecurityEvent \n| where EventID == 4624 and T"
},
{
"path": "Code-and-Queries/Legacy-auth-timeframe.kql",
"chars": 322,
"preview": "//View all legacy authentication events within a specific time frame\n\nSecurityEvent \n| where TimeGenerated > ago(1d) an"
},
{
"path": "Code-and-Queries/Legacy-auth-top10users.kql",
"chars": 350,
"preview": "//View the top 10 users with the most legacy authentication events\n\nSecurityEvent \n| where EventID == 4624 and TargetLo"
},
{
"path": "Code-and-Queries/Legacy-auth-user.kql",
"chars": 317,
"preview": "view all legacy authentication events from a specific user:\n\nSecurityEvent \n| where EventID == 4624 and TargetLogonId !"
},
{
"path": "Code-and-Queries/LegacyAuth30.kql",
"chars": 284,
"preview": "//This query returns all sign-in attempts using legacy authentication protocols in the last 30 days. \n\nSigninLogs\n| wher"
},
{
"path": "Code-and-Queries/LegacyAuthGraphAPI.kql",
"chars": 1024,
"preview": "//This query joins the SigninLogs table with an external data source from the Microsoft Graph API, which returns the dis"
},
{
"path": "Code-and-Queries/LegacyAuthTimes.kql",
"chars": 419,
"preview": "//This query groups and counts the sign-in attempts by user principal name (UPN), which is usually the same as the user’"
},
{
"path": "Code-and-Queries/MalwareUploaded.kql",
"chars": 316,
"preview": "//Malware uploaded to SharePoint or OneDrive\n\nOfficeActivity\n| where TimeGenerated > ago(30d)\n| where Operation == \"File"
},
{
"path": "Code-and-Queries/MissingUpdates.kql",
"chars": 1476,
"preview": "// Missing updates summary \n// Get a summary of missing updates by category. \nUpdate\n| where TimeGenerated>ago(5h) and O"
},
{
"path": "Code-and-Queries/No_Signing_Last_90_Days.kql",
"chars": 728,
"preview": "//Get a list of users who have not signed in for more than 90 days\n\nSigninLogs\n| where TimeGenerated > ago(30d)\n| summar"
},
{
"path": "Code-and-Queries/Phishing-campaigns.kql",
"chars": 995,
"preview": "//This query helps surface phishing campaigns associated with Appspot abuse. These emails frequently contain phishing li"
},
{
"path": "Code-and-Queries/Quishing.kql",
"chars": 376,
"preview": "//Detect QR Codes in emails\n\nlet image_extensions = dynamic([\"jpg\", \"jpeg\", \"png\", \"bmp\", \"gif\"]);\nEmailAttachmentInfo\n|"
},
{
"path": "Code-and-Queries/RareDomains.kql",
"chars": 1553,
"preview": "//Rare Domains Seen in Cloud Logs\n\n// Define the time range to look for OfficeActivity events\nlet Lookback = ago(7d);\n//"
},
{
"path": "Code-and-Queries/Readme.md",
"chars": 1,
"preview": "\n"
},
{
"path": "Code-and-Queries/SessionStealing.kql",
"chars": 1854,
"preview": "// Define the time range and the threshold for the number of sessions per user\nlet starttime = 7d;\nlet endtime = now();\n"
},
{
"path": "Code-and-Queries/Spear_Phishing.kql",
"chars": 573,
"preview": "//This query looks for email events in the last seven days where the subject contains certain keywords, the sender’s dom"
},
{
"path": "Code-and-Queries/TILookupforDNSEvents.kql",
"chars": 1801,
"preview": "//TI Lookup that match DNS events\nlet dt_lookBack = 1h;\nlet ioc_lookBack = 14d;\nThreatIntelligenceIndicator\n| where Time"
},
{
"path": "Code-and-Queries/TopThreatEmails.kql",
"chars": 344,
"preview": "//This query determines emails sent by top malicious/bad IP addresses.\n\nlet cutoff = 5;\nEmailEvents\n| where ThreatTypes "
},
{
"path": "Code-and-Queries/Unusual-logins.kql",
"chars": 238,
"preview": "identify unusual login attempts from regions where your suppliers are based, you might use:\n\nSigninLogs\n| where Location"
},
{
"path": "Code-and-Queries/XSS-Attack.kql",
"chars": 338,
"preview": "Leverage Microsoft Sentinel for XSS attack detection, create an analytics rule with the following KQL query:\n\nAzureDiagn"
},
{
"path": "Images/Readme.md",
"chars": 1,
"preview": "\n"
},
{
"path": "LICENSE",
"chars": 1066,
"preview": "MIT License\n\nCopyright (c) 2023 Rod Trent\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\n"
},
{
"path": "README.md",
"chars": 6337,
"preview": "# Microsoft Sentinel SOC 101<br>\n## Content and collateral for the Microsoft Sentinel SOC 101 series.\n\nAnnouncing the Mi"
},
{
"path": "Videos/Readme.md",
"chars": 1,
"preview": "\n"
},
{
"path": "eBook/Collateral/Readme.md",
"chars": 1,
"preview": "\n"
},
{
"path": "eBook/Readme.md",
"chars": 595,
"preview": "# Microsoft Sentinel SOC 101 - the Book\n\nThis is part of an ongoing series to educate about using Microsoft Security pro"
}
]
About this extraction
This page contains the full source code of the rod-trent/Sentinel-SOC-101 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 41 files (29.4 KB), approximately 8.8k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.