I have an upcoming session where I’ll be talking about the various Generative AI infused tools to help security analysts and engineers. I’ll be showing demos of the differences between the tools and making recommendations on which to use in specific scenarios. If you’re interested in attending this session, stay tuned to this blog for details to come later.
To get started preparing, I started with a simple prompt to get a very specific KQL query (or queries). Here you can see what I saw and check out the differences.
You can use the following list to click directly to the tool you’re most interested in:
Come back here to let me know which one you find is the most helpful and thorough:
The Prompt
How can I use a KQL to query to identify the following suspicious behavior?
Unusual login attempts or failed logins across devices and patterns that indicate potential data exfiltration or unauthorized file access.
Copilot
To identify suspicious behaviors like unusual login attempts, failed logins, and patterns of data exfiltration using KQL, you can construct queries that analyze and filter logs for specific indicators. Here’s how you can approach each case:
1. Unusual Login Attempts or Failed Logins Across Devices
Focus on event logs that capture authentication activities, such as
SigninLogs
or similar datasets.Use filters for failed logins (
resultType: "Failure"
) and group data by user accounts or devices to spot anomalies.
Here's an example query:
SigninLogs
| where ResultType == "Failure"
| summarize FailureCount = count() by UserPrincipalName, DeviceDisplayName, bin(TimeGenerated, 1h)
| where FailureCount > 5
| order by FailureCount desc
This identifies users or devices with multiple failed login attempts within a short timeframe.
2. Patterns Indicating Potential Data Exfiltration or Unauthorized File Access
Use logs that track file activities or access attempts, like
FileAuditLogs
or endpoint activity logs.Look for excessive file access or unusual data transfer patterns (e.g., large data volumes, rare access times, or specific file types).
Here's an example query:
FileAuditLogs
| where ActionType == "FileAccess"
| summarize FileAccessCount = count(), DataVolume = sum(FileSize) by UserPrincipalName, bin(TimeGenerated, 1h)
| where FileAccessCount > 10 or DataVolume > 10485760 // Thresholds for anomaly detection
| order by DataVolume desc
This flags users with unusually high file access or data transfer activity.
Enhancements
For both scenarios, you can:
Correlate data across multiple logs (e.g., authentication and file access logs).
Add IP address and geolocation filters to detect activity from uncommon or flagged locations.
Include time-based comparisons to identify deviations from typical behavior.
Would you like to refine these queries further based on specific thresholds or datasets you're working with? Let’s elevate this investigation together!
Grok
To identify suspicious behavior like unusual login attempts, failed logins, or patterns indicating potential data exfiltration or unauthorized file access using KQL (Kusto Query Language), you can craft queries targeting relevant data sources such as Azure Active Directory (AAD) logs, Microsoft 365 audit logs, or endpoint security logs (e.g., Microsoft Defender for Endpoint). Below are example KQL queries tailored to these scenarios, assuming you're querying in a Microsoft Sentinel or Azure Data Explorer environment.
1. Unusual Login Attempts or Failed Logins
This query looks for failed login attempts, multiple failed logins from the same user across devices, or logins from unusual locations/IPs.
kql
SigninLogs
| where TimeGenerated > ago(7d)
| where ResultType != 0 // Non-successful logins (failed attempts)
| summarize FailedLoginCount = count(), DistinctDevices = dcount(DeviceDetail.deviceId), DistinctIPs = dcount(IPAddress), Locations = make_set(Location)
by UserPrincipalName, AppDisplayName
| where FailedLoginCount > 5 or DistinctIPs > 2 or array_length(Locations) > 2
| project UserPrincipalName, FailedLoginCount, DistinctDevices, DistinctIPs, Locations, AppDisplayName
| order by FailedLoginCount desc
Explanation:
SigninLogs: Contains Azure AD sign-in events.
ResultType != 0: Filters for failed logins (success is 0).
summarize: Aggregates failed login counts, devices, IPs, and locations per user.
where FailedLoginCount > 5 or DistinctIPs > 2 or array_length(Locations) > 2: Flags users with high failure rates, multiple IPs, or diverse locations.
Adjust thresholds (5, 2) based on your environment.
2. Suspicious Patterns Across Devices
This query detects users logging in from multiple devices in a short time frame, which could indicate compromised credentials.
kql
SigninLogs
| where TimeGenerated > ago(1d)
| where ResultType == 0 // Successful logins
| summarize DeviceCount = dcount(DeviceDetail.deviceId), Devices = make_set(DeviceDetail.displayName)
by UserPrincipalName, bin(TimeGenerated, 1h)
| where DeviceCount > 2
| project TimeGenerated, UserPrincipalName, DeviceCount, Devices
| order by DeviceCount desc
Explanation:
Filters for successful logins (ResultType == 0).
bin(TimeGenerated, 1h): Groups events into 1-hour windows.
dcount(DeviceDetail.deviceId): Counts unique devices per user.
where DeviceCount > 2: Flags users accessing from more than 2 devices in an hour (adjust as needed).
3. Potential Data Exfiltration
This query looks for patterns like large file downloads, unusual file sharing, or access to sensitive files, using audit logs (e.g., Office 365 or SharePoint activity).
kql
OfficeActivity
| where TimeGenerated > ago(7d)
| where Operation in ("FileDownloaded", "FileSyncDownloadedFull", "FileShared", "FileAccessed")
| summarize FileCount = count(), TotalBytes = sum(iff(isnotempty(FileSize), tolong(FileSize), 0)), DistinctFiles = dcount(SourceFileName)
by UserId, ClientIP, Operation
| where FileCount > 50 or TotalBytes > 100000000 // Approx 100MB
| project UserId, ClientIP, FileCount, TotalBytes, DistinctFiles, Operation
| order by TotalBytes desc
Explanation:
OfficeActivity: Contains file-related activities in Office 365 (SharePoint, OneDrive, etc.).
Operation in (...): Targets file download, sync, sharing, or access events.
sum(iff(...)): Aggregates total bytes downloaded (handles missing FileSize).
where FileCount > 50 or TotalBytes > 100000000: Flags users downloading many files or large data volumes (adjust thresholds).
4. Unauthorized File Access
This query detects repeated failed attempts to access files or access to sensitive files by users without prior history.
kql
OfficeActivity
| where TimeGenerated > ago(7d)
| where Operation == "FileAccessFailed" or (Operation == "FileAccessed" and SourceFileName contains "sensitive")
| summarize AccessAttempts = count(), DistinctFiles = dcount(SourceFileName)
by UserId, Operation, ClientIP
| where AccessAttempts > 10
| project UserId, Operation, AccessAttempts, DistinctFiles, ClientIP
| order by AccessAttempts desc
Explanation:
Operation == "FileAccessFailed": Captures failed file access attempts.
SourceFileName contains "sensitive": Flags access to files with sensitive keywords (customize as needed).
where AccessAttempts > 10: Highlights users with excessive failed attempts.
5. Combining Indicators for Suspicious Behavior
To correlate failed logins with potential exfiltration, you can join SigninLogs and OfficeActivity:
kql
let SuspiciousUsers = SigninLogs
| where TimeGenerated > ago(1d)
| where ResultType != 0
| summarize FailedLogins = count() by UserPrincipalName
| where FailedLogins > 5;
OfficeActivity
| where TimeGenerated > ago(1d)
| where Operation in ("FileDownloaded", "FileShared")
| join kind=inner SuspiciousUsers on $left.UserId == $right.UserPrincipalName
| summarize FileCount = count(), TotalBytes = sum(iff(isnotempty(FileSize), tolong(FileSize), 0))
by UserId, ClientIP
| where FileCount > 20 or TotalBytes > 50000000
| project UserId, ClientIP, FileCount, TotalBytes
Explanation:
SuspiciousUsers: Identifies users with many failed logins.
join: Correlates with file activity from those users.
Flags users who failed logins and then downloaded/shared files.
Tips for Refining Queries
Adjust Time Windows: Use ago(1h), ago(1d), or ago(7d) based on your monitoring needs.
Tune Thresholds: Modify counts (> 5, > 50) to reduce false positives.
Filter by Environment: Add conditions like AppDisplayName == "SharePoint Online" or DeviceDetail.operatingSystem to focus on specific platforms.
Enrich with Context: Include UserAgent, Location, or RiskLevel from SigninLogs for deeper analysis.
Schedule Alerts: Use Microsoft Sentinel to run these queries periodically and trigger alerts.
If you have specific data sources or scenarios in mind, let me know, and I can tailor the queries further!
ChatGPT
To identify unusual login attempts, failed logins across multiple devices, and patterns that suggest data exfiltration or unauthorized access using Kusto Query Language (KQL) in environments like Microsoft Sentinel or Defender, you'd typically correlate logs from SigninLogs, AuditLogs, and DeviceFileEvents or DeviceNetworkEvents (if Defender for Endpoint is enabled).
Here's a breakdown of how to approach each indicator and sample queries for them:
🔐 1. Unusual Login Attempts
Detect sign-ins from new countries, rare devices, or impossible travel scenarios.
SigninLogs
| extend DeviceDetail = tostring(DeviceDetail.deviceDisplayName)
| extend Location = tostring(LocationDetails.countryOrRegion)
| summarize Count = count(),
Devices = dcount(DeviceDetail),
Locations = dcount(Location)
by UserPrincipalName, bin(TimeGenerated, 1d)
| where Devices > 3 or Locations > 2
Optional: Cross-check against previous behavior to detect anomalies.
🚫 2. Multiple Failed Login Attempts Across Devices
Helps detect potential brute-force or credential stuffing attempts.
SigninLogs
| where ResultType != 0 // 0 = success
| summarize FailedAttempts = count(), UniqueDevices = dcount(DeviceDetail.deviceDisplayName)
by UserPrincipalName, bin(TimeGenerated, 1h)
| where FailedAttempts >= 5 and UniqueDevices >= 3
📤 3. Potential Data Exfiltration
Look for large outbound file transfers or access to many files in a short time (if Defender or Purview logs are available).
Option A: DeviceFileEvents (Defender for Endpoint)
DeviceFileEvents
| where ActionType == "FileCopied" or ActionType == "FileMoved"
| where FileName endswith ".docx" or FileName endswith ".xlsx" or FileName endswith ".pdf"
| summarize FileAccesses = count(), UniqueDestinations = dcount(RemoteDeviceName)
by DeviceName, InitiatingProcessAccountName, bin(Timestamp, 1h)
| where FileAccesses > 50
Option B: SharePoint or OneDrive access via AuditLogs
AuditLogs
| where Operation in ("FileAccessed", "FileDownloaded", "FileViewed", "FileAccessedExtended")
| where Site_Url has_any ("sharepoint.com", "onedrive.com")
| summarize AccessCount = count(), UniqueFiles = dcount(ObjectId)
by UserId, bin(TimeGenerated, 1h)
| where AccessCount > 100 or UniqueFiles > 50
🔄 4. Correlating Logins with File Access
Suspicious behavior might involve a failed login flood followed by successful login and file access.
let FailedLogins = SigninLogs
| where ResultType != 0
| summarize FailedAttempts = count() by UserPrincipalName, bin(TimeGenerated, 1h);
let FileAccess = DeviceFileEvents
| where ActionType == "FileRead"
| summarize FileReads = count() by InitiatingProcessAccountName, bin(Timestamp, 1h);
FailedLogins
| join kind=inner (
FileAccess
| project UserPrincipalName = InitiatingProcessAccountName, FileReads, Timestamp
) on UserPrincipalName
| where FileReads > 50 and FailedAttempts > 5
✅ Tips
Replace
DeviceFileEvents
andDeviceNetworkEvents
with actual data sources available in your tenant (e.g.,OfficeActivity
for SharePoint/OneDrive).Add user risk signals from IdentityInfo or Microsoft Defender XDR if enabled.
Use
join
operations to correlate across time and behavior.
Let me know your exact data sources (Sentinel, Defender for Endpoint, Purview, etc.), and I can tailor these further.
Microsoft Security Copilot
Microsoft 365 Copilot
Certainly! Here are some KQL queries to help you identify suspicious behavior:
Unusual Login Attempts
To detect unusual login attempts, you can use the following query to identify multiple failed login attempts within a short time frame:
let failed_threshold = 5; // threshold for failed login times
let failed_count = 2; // threshold for number of failed logins
let start_time = startofday(datetime(2025-04-01)); // start date
let end_time = endofday(datetime(2025-04-15)); // end date
SecurityEvent
| where TimeGenerated between (start_time .. end_time)
| where EventID == 4625 // Event ID for failed login
| project Account, TimeGenerated, Computer
| sort by TimeGenerated asc, Account
| serialize
| extend nextAccount = next(Account, 1), nextTimeGenerated = next(TimeGenerated, 1)
| where Account == nextAccount
| extend TimeDeltaInSeconds = datetime_diff("second", nextTimeGenerated, TimeGenerated)
| where TimeDeltaInSeconds <= failed_threshold
| project TimeGenerated, TimeDeltaInSeconds, Account, Computer
| summarize Failed_Logins = count(), avg(TimeDeltaInSeconds), first_failed = min(TimeGenerated), last_failed = max(TimeGenerated), standarddev = stdev(TimeDeltaInSeconds), variance = variance(TimeDeltaInSeconds), TimeDeltaList = make_list(TimeDeltaInSeconds) by Account
| where standarddev < 1
| where Failed_Logins >= failed_count
| sort by Failed_Logins desc
Data Exfiltration
To detect potential data exfiltration, you can use the following query to identify large file transfers:
let TermAccount = 'departing.employee'; // Enter the departing employee's username
let ReleaseTime = datetime("2025-04-01 00:00:00"); // Enter the date the resignation or termination was announced
DeviceNetworkEvents
| where InitiatingProcessAccountName =~ TermAccount
| where Timestamp > ReleaseTime
| join kind=inner (DeviceFileEvents) on InitiatingProcessAccountName
| where FileName endswith ".docx" or FileName endswith ".pptx" or FileName endswith ".xlsx" or FileName endswith ".pdf"
| join DeviceNetworkInfo on DeviceId
| where ConnectedNetworks !contains '\"Category\":\"Domain\"' // Looking for remote, non-domain networks
| summarize TotalFiles = count() by bin(Timestamp, 5m), InitiatingProcessAccountName
| where TotalFiles > 1000 // adjust accordingly
| project TotalFiles, Timestamp, InitiatingProcessAccountName
Unauthorized File Access
To detect unauthorized file access, you can use the following query:
DeviceFileEvents
| where ActionType == "FileAccessed"
| where FileName endswith ".docx" or FileName endswith ".pptx" or FileName endswith ".xlsx" or FileName endswith ".pdf"
| where AccountName != "authorized_user" // Replace with the authorized user's account name
| summarize FileAccessCount = count() by AccountName, FileName
| where FileAccessCount > 10 // adjust threshold as needed
| project AccountName, FileName, FileAccessCount
These queries should help you monitor and identify suspicious activities related to login attempts, data exfiltration, and unauthorized file access[1] [2] [3] [4].
Do you need any further assistance with these queries?
References
[1] function - Assistance with Kusto Query Language (KQL) for Identifying ...
[2] KQL Query for failed logins · GitHub
[3] KQL-Queries/FailedLoginAttempts.kql at Main · EEN421/KQL-Queries - GitHub
[4] KQL-Scripts/Detect unauthorized access attempts to Azure ... - GitHub
Gemini
You can use Kusto Query Language (KQL) to identify unusual login attempts, failed logins across devices, and patterns indicative of potential data exfiltration or unauthorized file access by analyzing various security logs within Microsoft Defender for Cloud, Microsoft Sentinel, and other relevant data sources.
Here's a breakdown of how you can construct KQL queries for each of these scenarios, along with explanations and considerations:
1. Unusual Login Attempts and Failed Logins Across Devices:
This involves looking for login patterns that deviate from a user's normal behavior, such as logins from new or multiple unusual locations, at unusual times, or a high number of failed attempts followed by a successful login.
Code snippet
// Look for successful logins from new or unusual locations for a user
SigninLogs
| where UserId == "<UserPrincipalName>" // Replace with the specific user or remove for all users
| summarize arg_min(TimeGenerated, Location) by UserId, Location
| where count() > <Threshold> // Set a threshold for the number of distinct locations
| extend UnusualLocation = iff(Location !in (<FrequentLocations>), "Unusual", "Frequent") // Optional: Compare against known frequent locations
| where UnusualLocation == "Unusual"
| project TimeGenerated, UserId, Location, IPAddress, UserAgent
// Look for a high number of failed logins followed by a successful login for a user
SigninLogs
| where UserId == "<UserPrincipalName>" // Replace with the specific user or remove for all users
| make-series FailedCount = countif(ResultType != 0) default=0 on TimeGenerated in range(ago(1d), now(), 1h)
| mvexpand TimeGenerated, FailedCount
| where FailedCount > <FailedLoginThreshold> // Set a threshold for the number of failed attempts in an hour
| join kind=inner (
SigninLogs
| where UserId == "<UserPrincipalName>" and ResultType == 0 // Successful logins
| project SuccessfulLoginTime = TimeGenerated
) on $left.TimeGenerated == $right.SuccessfulLoginTime
| project FailedLoginTime = TimeGenerated, UserId, IPAddress, UserAgent, SuccessfulLoginTime
// Look for logins from multiple devices within a short timeframe for a user
SigninLogs
| where UserId == "<UserPrincipalName>" // Replace with the specific user or remove for all users
| summarize dcount(DeviceId) by UserId, bin(TimeGenerated, 1h)
| where dcount_DeviceId > <MultipleDeviceThreshold> // Set a threshold for the number of devices in an hour
| join kind=inner (
SigninLogs
| where UserId == "<UserPrincipalName>"
| project TimeGenerated, UserId, DeviceId, IPAddress, UserAgent, Location
) on $left.UserId == $right.UserId and $left.TimeGenerated == bin($right.TimeGenerated, 1h)
| project TimeGenerated, UserId, DeviceId, IPAddress, UserAgent, Location, DeviceCount = dcount_DeviceId
Key Considerations:
SigninLogs
table: This table in Azure Monitor/Sentinel contains information about Azure Active Directory sign-ins.<UserPrincipalName>
: Replace this with a specific user if you are investigating a particular account.<Threshold>
: Define a reasonable threshold for the number of distinct locations to consider unusual. This will depend on your environment.<FrequentLocations>
: (Optional) Replace this with a comma-separated list of locations that are considered normal for your users.<FailedLoginThreshold>
: Set a threshold for the number of failed login attempts within a specific timeframe (e.g., per hour) that you consider suspicious.<MultipleDeviceThreshold>
: Set a threshold for the number of distinct devices a user logs in from within a short timeframe.Time Window: Adjust the time range (
ago(1d)
,1h
) as needed for your analysis.Baselines: Establishing baselines for user behavior (e.g., typical login locations, devices) can significantly improve the accuracy of these queries. You might need to create separate queries to build these baselines over time.
Noise Reduction: Expect some false positives. You'll need to fine-tune your thresholds and potentially incorporate more contextual information to reduce noise.
2. Patterns Indicating Potential Data Exfiltration:
Identifying data exfiltration through logs can be challenging as it often involves legitimate actions performed in a malicious way. However, you can look for patterns that might suggest unusual data access or transfer.
Code snippet
// Look for a high volume of file downloads by a single user within a short timeframe
AuditLogs
| where OperationName contains "Download" and UserId == "<UserPrincipalName>" // Adjust OperationName based on your data source
| summarize DownloadCount = count() by UserId, bin(TimeGenerated, 1h)
| where DownloadCount > <HighDownloadThreshold> // Set a threshold for the number of downloads per hour
| project TimeGenerated, UserId, DownloadCount
// Look for access to an unusually large number of files or folders by a user
StorageFileLogs // Replace with the relevant table for file access logs
| where UserId == "<UserPrincipalName>" and AccessType in ("Read", "List") // Adjust AccessType as needed
| summarize AccessedResourceCount = dcount(ObjectName) by UserId, bin(TimeGenerated, 1d)
| where AccessedResourceCount > <HighResourceAccessThreshold> // Set a threshold for the number of unique resources accessed per day
| project TimeGenerated, UserId, AccessedResourceCount
// Look for external sharing of files or folders (if your logs capture this)
AuditLogs
| where OperationName contains "Share" and SharingType == "External" and UserId == "<UserPrincipalName>" // Adjust OperationName and SharingType
| project TimeGenerated, UserId, TargetResource, SharedWith
// Look for unusual network traffic patterns (requires Network Security Group logs or similar)
AzureNetworkAnalytics_CL
| where SourceObjectId == "<UserObjectId>" or DestinationObjectId == "<UserObjectId>" // Filter by user's associated objects
| summarize TotalBytesTransferred = sum(TotalBytesTransferred) by SourceIP, DestinationIP, DestinationPort, Protocol
| order by TotalBytesTransferred desc
| take <TopN> // Look for unusually high traffic volumes to specific external IPs or ports
// Look for multiple successful logins followed by a large number of file access/downloads
let suspiciousLogins = SigninLogs
| where UserId == "<UserPrincipalName>" and ResultType == 0
| project SuccessfulLoginTime = TimeGenerated;
StorageFileLogs // Or relevant file access logs
| where UserId == "<UserPrincipalName>" and AccessType in ("Read", "Download")
| join kind=inner suspiciousLogins on $left.TimeGenerated > $right.SuccessfulLoginTime and $left.TimeGenerated < $right.SuccessfulLoginTime + 1h // Look for activity within 1 hour of login
| summarize FileActionCount = count() by UserId, bin(TimeGenerated, 1h)
| where FileActionCount > <HighFileActionThresholdAfterLogin>
| project TimeGenerated, UserId, FileActionCount
Key Considerations:
Data Sources: The specific tables and fields will vary depending on the Microsoft services you are using (e.g., Azure Storage, SharePoint, OneDrive) and whether you have enabled diagnostic logging. You might need to explore tables like
AuditLogs
,StorageFileLogs
,SharePointActivity
,OfficeActivity
,AzureNetworkAnalytics_CL
, etc.<HighDownloadThreshold>
,<HighResourceAccessThreshold>
,<HighFileActionThresholdAfterLogin>
: Set appropriate thresholds based on your understanding of normal user activity.<TopN>
: When analyzing network traffic, look at the top N entries with the highest traffic volume.Correlation: Combining login patterns with file access patterns (as shown in the last example) can provide stronger indicators of suspicious activity.
Context is Crucial: Data exfiltration attempts can often mimic legitimate activity. Understanding the user's role, typical workflows, and the sensitivity of the accessed data is essential for accurate identification.
Alerting and Automation: Once you identify relevant patterns, you can create alerts in Microsoft Sentinel to be notified of such activity in the future. You can also automate responses using playbooks.
3. Unauthorized File Access:
This focuses on identifying access to files or folders that a user should not have permissions to.
Code snippet
// Look for "Access Denied" or similar events in file access logs
StorageFileLogs // Replace with the relevant table
| where UserId == "<UserPrincipalName>" and AccessResult == "Denied" // Adjust AccessResult based on your logs
| summarize DeniedAccessCount = count() by UserId, ObjectName
| where DeniedAccessCount > <DeniedAccessThreshold> // Set a threshold for the number of denied access attempts to the same object
| project TimeGenerated, UserId, ObjectName, DeniedAccessCount
// Look for successful access to sensitive or restricted files/folders by users who don't normally access them
StorageFileLogs // Replace with the relevant table
| where ObjectName startswith "<SensitiveFilePathPrefix>" and AccessType in ("Read", "Write") // Filter for specific sensitive paths
| summarize AccessCount = count() by UserId, ObjectName
| where UserId !in (<ListOfAuthorizedUsers>) // Compare against a list of users who are normally authorized
| project TimeGenerated, UserId, ObjectName, AccessType
// Look for privilege escalation followed by access to sensitive resources (requires correlating different log types)
let elevatedPrincipals = AuditLogs
| where OperationName contains "AssignRoleToPrincipal" and TargetResources contains "<SensitiveResource>" // Identify potential privilege escalation
| project ElevatedUserId = InitiatedBy.user.userPrincipalName, ElevationTime = TimeGenerated;
StorageFileLogs // Or relevant resource access logs
| join kind=inner elevatedPrincipals on $left.UserId == $right.ElevatedUserId and $left.TimeGenerated > $right.ElevationTime and $left.TimeGenerated < $right.ElevationTime + 1h // Look for access shortly after elevation
| where ObjectName startswith "<SensitiveFilePathPrefix>" and AccessType in ("Read", "Write")
| project TimeGenerated, UserId, ObjectName, AccessType, ElevationTime
Key Considerations:
<DeniedAccessThreshold>
: Set a threshold for the number of denied access attempts to the same resource by a user.<SensitiveFilePathPrefix>
: Replace this with the starting path of sensitive file shares or folders.<ListOfAuthorizedUsers>
: Maintain a list of users who are normally authorized to access specific sensitive resources.Privilege Escalation Detection: Identifying privilege escalation attempts is crucial for preventing unauthorized access. Correlate audit logs related to role assignments with subsequent access attempts.
General Best Practices for KQL Queries:
Be Specific: The more specific your criteria, the fewer false positives you will encounter.
Use Time Windows: Analyze activity within relevant timeframes (e.g., last hour, last day).
Normalize Data: Ensure your data sources are consistent and that user and resource identifiers are properly normalized.
Parameterize Queries: Use variables or parameters for thresholds, user lists, and file paths to make your queries more flexible.
Test and Tune: Thoroughly test your queries in a non-production environment and adjust thresholds as needed to minimize noise and maximize detection accuracy.
Combine Indicators: Correlating multiple suspicious behaviors can significantly increase the confidence in your findings.
Leverage Anomaly Detection: Microsoft Sentinel provides built-in anomaly detection rules that can help identify unusual behavior without requiring you to write complex KQL queries from scratch. Review and enable relevant anomaly detection rules.
By combining these KQL query techniques and tailoring them to your specific environment and security concerns, you can effectively identify unusual login attempts, failed logins, and patterns indicative of potential data exfiltration or unauthorized file access. Remember that continuous monitoring, analysis, and tuning of your queries are essential for maintaining a strong security posture.
Github Copilot has been extremely helpful in these types of scenarios in my experience. Vs. other Copilots, especially Security Co-pilot. Making some presumptions, especially for custom plugins you've created or have access to. Output from Github Copilot is much richer and an option I can't select above.