├── Sentinel-KQL-Sorguları ├── Diagrams │ ├── parse.png │ ├── parse1.png │ ├── parse2.png │ ├── parse3.png │ ├── parse4.png │ ├── parse5.png │ ├── split1.png │ ├── split2.png │ ├── split3.png │ ├── kql-pipe.png │ ├── querypack1.png │ ├── querypack2.png │ ├── querypack3.png │ ├── querypack4.png │ ├── querypack5.png │ ├── deploytoazure.png │ ├── render-areachart.png │ ├── render-barchart.png │ ├── render-piechart.png │ ├── render-timechart.png │ ├── render-columnchart.png │ ├── render-timebarchart.png │ ├── SentinelTableMapping.png │ ├── render-timecolumnchart.png │ ├── ConditionalAccess-LogicApp.png │ ├── render-timecolumnchartnames.png │ ├── render-timecolumnchartstacked.png │ ├── render-timecolumnchartunstacked.png │ └── render-timecolumn-outlookonedrivesharepoint.png ├── Functions │ └── ReadmeImages │ │ ├── function1.png │ │ ├── function2.png │ │ ├── function3.png │ │ ├── function4.png │ │ └── function5.png ├── Workbooks │ └── ReadmeImages │ │ ├── workbook1.png │ │ ├── workbook2.png │ │ ├── workbook3.png │ │ └── workbook4.png ├── Data Management │ ├── 365-Visualize365DaysofKql.kql │ ├── Data-TableSizePerMDEDevice.kql │ ├── Data-NewTablesFound.kql │ └── Data-CalculatePercentageperTable.kql ├── Office 365 │ ├── OfficeActivity-SharedTeamsChannelCreated.kql │ ├── EmailEvents-VisualizePostDeliveryActions.kql │ ├── OfficeActivity-DetectNewExchangeAdminRole.kql │ ├── OfficeActivity-MalwareDetected.kql │ ├── OfficeActivity-VisualizeFilesSharedtoGuests.kql │ ├── OfficeActivity-VisualizeTopGuestDownloads.kql │ ├── OfficeActivity-FindNewOperations.kql │ ├── OfficeActivity-SummarizeDownloadActivitybyGuests.kql │ ├── OfficeActivity-InboxRuleParse.kql │ ├── OfficeActivity-FilesSharedtoGuestsfromOnedrive.kql │ ├── OfficeActivity-FindUserswhoDownloadedMalware.kql │ ├── OfficeActivity-GuestDomainsHighestDownloads.kql │ ├── EmailEvents-VisualizeBlockedEmailPercentage.kql │ ├── OfficeActivity-ExchangeScopingPolicyApplied.kql │ ├── OfficeActivity-SummarizeGuestsAddedtoTeams.kql │ ├── OfficeActivity-SummarizeTeamsAppInstalls.kql │ ├── OfficeActivity-NewTeamsAppInstalled.kql │ ├── OfficeActivity-SummarizeTeamsCreatedDeleted.kql │ ├── OfficeActivity-TeamsRoleChanges.kql │ ├── Audit-DailySummaryofO365AdminActivity.kql │ ├── OfficeActivity-GuestAddedtoMultipleTeams.kql │ ├── OfficeActivity-VisualizeFileShareTopGuestDomains.kql │ ├── OfficeActivity-VisualizeDownloadsvsUploads.kql │ ├── OfficeActivity-VisualizeGuestDownloadsfromO365withTrend.kql │ ├── OfficeActivity-CalculatePercentageofDownloadsperDomain.kql │ ├── OfficeActivity-MultipleFilesSharedtoGuests.kql │ ├── OfficeActivity-DetectEmailsReadbyAdmins.kql │ ├── OfficeActivity-DetectFullMailboxAccess.kql │ ├── OfficeActivity-VisualizeGuestsAddedRemovedfromTeams.kql │ ├── OfficeActivity-DetectUsermadeOwneronmultipleTeams.kql │ ├── OfficeActivity-SummaryofExternalActivity.kql │ ├── EmailEvents-VisualizeBlockedEmailDeviation.kql │ └── EmailEvents-MostBlockedDomains.kql ├── UEBA │ ├── IdentityInfo-FindGuestswithHighBlastRadius.kql │ ├── IdentityInfo-FindAtRiskandHighBlastRadiusUsers.kql │ ├── IdentityInfo-FindAccountsPasswordNotRequired.kql │ ├── IdentityInfo-FindPrivAccountsHighBlastRadius.kql │ ├── IdentityInfo-VisualizeBlastRadius.kql │ ├── IdentityInfo-FindAccountswithsameEmployeeId.kql │ └── IdentityInfo-FindUserswithmanyGroups.kql ├── Azure Active Directory │ ├── MSGraph-GuestLogonQuery │ ├── Identity-VisualizeRiskEventsoverTime.kql │ ├── Identity-ServicePrincipalSigninsbyIP.kql │ ├── Audit-SummarizePIMRolesActivated.kql │ ├── Identity-YourUsersSigningIntoOtherTenantsAsGuests.kql │ ├── Identity-AuthenticationStrengthsParser │ ├── Audit-BitLockerKeyRetrieved.kql │ ├── Audit-UserAddedtoRoleOutsidePIM.kql │ ├── Identity-DetectMultipleDistinctRiskEvents.kql │ ├── OAuth-DelegatedPermissionsGrant.kql │ ├── Identity-FindAppswithNoSignins.kql │ ├── Identity-SummarizeGuestInactivity.kql │ ├── OAuth-SummarizeServicePrincipalInactivity.kql │ ├── Identity-ConditionalAccessPivotTable.kql │ ├── Audit-NewDomainAdded.kql │ ├── Identity-VisualizeSigninsbyDeviceTrust.kql │ ├── Identity-ServicePrincipalSummaryofResources.kql │ ├── Identity-GuestAddedtoAADRole.kql │ ├── Identity-UserTryingtoAccessMultipleApps.kql │ ├── Identity-ManagedIdentitySummaryofResources.kql │ ├── Identity-SummarizeUnknownLocationnoMFA.kql │ ├── Identity-VisualizeGuestDomains.kql │ ├── Identity-VisualizeDistinctInboundGuests.kql │ ├── Audit-AccessPackageCreated.kql │ ├── Identity-CAPoliciesNotinUse.kql │ ├── Identity-ServicePrincipalCreatedbyManagedIdentity.kql │ ├── Identity-VisualizeGuestRedemptionswithTrend.kql │ ├── Identity-VisualizeExternalAADGuestsvsExternalGuests.kql │ ├── Audit-DetectActivePIMAssignment.kql │ ├── Identity-ServicePrincipalSigninfromnewIP.kql │ ├── Identity-FindMultipleCASuccesses.kql │ ├── Identity-SummarizeMFAFailures.kql │ ├── Audit-NewTenantCreated.kql │ ├── Identity-VisualizeMFAMethods.kql │ ├── Audit-DetectCredentialAddedtoApp.kql │ ├── Audit-SummarizeWeeklyPIM.kql │ ├── Identity-VisualizeKnownvsUnknownLocation.kql │ ├── Identity-SummarizeAccountInactivity.kql │ ├── Audit-GroupAddedtoPIM.kql │ ├── Audit-UsersAddedtoDynamicGroups.kql │ ├── Identity-VisualizeGuestAppAccess.kql │ ├── Identity-VisualizeInboundvsOutboundGuests.kql │ ├── Identity-GuestTypeParser.kql │ ├── Identity-VisualizePasswordvsPasswordless.kql │ ├── Identity-GuestInvitesSentvsRedeemed.kql │ ├── Identity-SummarizeOutboundGuestActivity.kql │ ├── Identity-VisualizeMFAMethodsovertime.kql │ ├── Identity-SummarizeAppUsageMonthonMonth.kql │ ├── Identity-VisualizeWorldMap.kql │ ├── Audit-NewOperations.kql │ ├── Identity-FindNewEnterpriseApps.kql │ ├── OAuth-ApplicationPermissionsGrant.kql │ ├── Identity-VisualizeConditionalAccessFailures.kql │ ├── Audit-AllowedBlockedDomainListChanges.kql │ ├── Identity-ApplicationAccessReview.kql │ ├── Identity-CalculateRiskyApps.kql │ ├── Identity-AppAccessMembersvsGuests.kql │ ├── Audit-AppProxySettoPassThrough.kql │ ├── Identity-AdminUpdatingSecurityInfo.kql │ ├── Audit-FindUsersFailingSSPR.kql │ ├── Identity-DailySummaryofUsersAddedtoAADGroups.kql │ ├── Identity-SummarizeConditionalAccessPoliciesfailures.kql │ ├── SSPR-PasswordResetInitiatedviaMSGraph.kql │ ├── Identity-VisualizeSSPR.kql │ ├── Identity-RoleAddedtoServicePrincipal.kql │ ├── Audit-GuestAddedtoPIM.kql │ ├── Identity-InactiveGuestAccounts.kql │ ├── PIM-UserAssignedRolebutHasntActivated.kql │ ├── Audit-DetectPIMActivationsOutsideWorkingHours.kql │ ├── Audit-MultipleUsersSameMFANumber.kql │ ├── Audit-DailySummaryofAdminActivity.kql │ ├── Audit-FindUsersFailingNewPasswordSSPR.kql │ ├── Identity-MFAPercentageperapp.kql │ ├── Audit-ListBulkActivities.kql │ ├── Identity-MFACountPerUser.kql │ ├── Identity-ParseIPInfofromSecurityAlert.kql │ ├── Audit-DetectFirstTimeServicePrincipalCreation.kql │ ├── Audit-DetectSSPRAfterHours.kql │ ├── Audit-RedirectURIChanged.kql │ ├── Identity-ConditionalAccessMostFailures.kql │ ├── Identity-VisualizeControlsvsNoControls.kql │ ├── Identity-CalculateRiskyUsers.kql │ ├── Audit-UserswithPrivRolesbutnoActivity.kql │ ├── Identity-VisualizeMFAChallengevsPreviouslySatisfied.kql │ ├── Audit-UsersWhoHaventElevatedPIM.kql │ ├── Identity-AppswithmostSFAPrivUsers.kql │ ├── Identity-FirstPartyApps.kql │ ├── Audit-CustomSecurityAttributeSet.kql │ ├── Audit-DetectNewPrivilegedGroupAdded.kql │ ├── Identity-UserReportedSuspiciousMFA.kql │ ├── Identity-ConditionalAccessPoliciesNotinUse.kql │ ├── Identity-LegacyAuthPivotTable.kql │ ├── Identity-SummarizeGuestDomainbyType.kql │ ├── Audit-DetectConditionalAccessChangesAfterHours.kql │ ├── Identity-AppsWithMoreGuests.kql │ ├── Identity-InactivePrivilegedUsers.kql │ ├── Identity-SingleFactorSigninsFromPrivUsers.kql │ ├── Audit-PivotTableofPrivilegedUserActions.kql │ ├── Identity-GuestsInvitedbutnotRedeemed.kql │ ├── Identity-ParseUserAgent.kql │ ├── Identity-SummarizeGuestConditionalAccess.kql │ └── Identity-MultipleCAFailures.kql ├── Defender for Endpoint │ ├── Device-DetectInvalidCertificates.kql │ ├── Device-SummarizeLocalGroupAdditions.kql │ ├── Device-VisualizeMaliciousSmartScreenURLs.kql │ ├── Device-PowerShellExecutionModeChanged.kql │ ├── Device-SummarizeSmartScreenPhishingDomains.kql │ ├── Device-FindDevicesToOnboard.kql │ ├── Device-FindDevicesNoLongerSendingEvents.kql │ ├── Device-NewASREvents.kql │ ├── Device-DetectInboundPublicRDP.kql │ ├── Device-DetectSecurityLogCleared.kql │ ├── Device-VisualizeOSBuildspermonth.kql │ ├── Device-FindNewEvents.kql │ ├── Device-FindNewDevices.kql │ ├── Device-DetectInternaltoExternalTeamviewer.kql │ ├── Device-VisualizeASREventswithtrend.kql │ ├── Device-CreateSetofLocalAdminsperDevice.kql │ ├── Device-DetectRDPRecon.kql │ ├── Firewall Queries │ │ ├── Devices-NoRDP.kql │ │ ├── Devices-NoSSH.kql │ │ ├── Devices-NoSMB.kql │ │ └── Devices-NoHTTP.kql │ ├── Device-DetectCredentialBackup.kql │ ├── Device-FirstTimeWhoAmI.kql │ ├── Device-ASRAudit.kql │ ├── Device-ParseURL.kql │ ├── Device-SummarizeSmartScreenUntrustedFiles.kql │ ├── Device-ASRSummary.kql │ ├── Device-Windows11DevicesandUsers.kql │ ├── Device-WindowsVersionPivotTable.kql │ ├── Device-DetectRegistryTampering.kql │ └── Device-LocalUserswithAdmin.kql ├── Log Analytics │ ├── LAQuery-VisualizeQueriesRun.kql │ ├── LAQuery-UsersvsAutomationQueryStats.kql │ ├── LAQuery-FindQueryStats.kql │ └── LAQuery-NewUsersQueryingData.kql ├── Heartbeat │ ├── Heartbeat-VisualizeDistinctComputersperMonth.kql │ └── Heartbeat-NoHeartbeatinTimeframe.kql ├── Active Directory │ ├── AADPasswordProtection-AllEvents.kql │ ├── SecurityEvent-AccountSetPasswordNotRequired.kql │ ├── SecurityEvent-UnconstrainedDelegationtoUser.kql │ ├── SecurityEvent-UnconstrainedDelegationEnabled.kql │ ├── SecurityEvent-AccountPreAuthChanges.kql │ ├── SecurityEvent-DailySummaryofGroupAdditions.kql │ ├── SecurityEvent-AccountSensitivityChanged.kql │ ├── SecurityEvent-SummarizeRDPActivity.kql │ ├── SecurityEvent-LogonToDeviceListChanged.kql │ └── SecurityEvent-VisualizeAccountsCreatedDisabledDeleted.kql ├── Security Alert │ ├── SecurityAlert-VisualizeAlertsbyProduct.kql │ ├── SecurityAlert-VisualizeMDEAlertSeverity.kql │ ├── SecurityAlert-MultipleLowSeverityAlertsTriggered.kql │ ├── SecurityAlert-ForecastIdentityProtection.kql │ ├── SecurityAlert-DeviceAlertwithLateralMovement.kql │ ├── SecurityAlert-SuspectedGoldenTicket.kql │ ├── SecurityAlert-PercentageofAlertsHighorCritical.kql │ ├── SecurityAlert-MultipleAlertsTriggered.kql │ ├── SecurityAlert-FindMostPhishedUsers.kql │ ├── SecurityAlert-VisualizeTotalAlertsvsUniqueAlerts.kql │ ├── SecurityAlert-VisualizeAlertsbyMITRE.kql │ ├── SecurityAlert-VisualizeTopPhishingDomains.kql │ ├── SecurityAlert-DefenderforIDRecon.kql │ ├── SecurityAlert-PossibleDNSDataTransfer.kql │ ├── SecurityAlert-DetectNewAlerts.kql │ ├── SecurityAlert-FindSigninsforAnomalousToken.kql │ ├── SecurityAlert-WhichTablesAreInUse.kql │ ├── SecurityAlert-MalwareDetectedinISO.kql │ └── SecurityAlert-RetrieveEmailforSuspiciousEmailPatterns.kql ├── Azure Bastion │ ├── Bastion-AuditUsage.kql │ └── Bastion-SummarizeAccountAccess.kql ├── Azure Sentinel Incidents │ ├── SecurityIncident-VisualizeIncidentSeverity.kql │ ├── SecurityIncident-PlaybookActivities.kql │ ├── SecurityIncident-VisualizeMitreAtt&ck.kql │ ├── SecurityIncident-DaysSinceLastIncident.kql │ └── SecurityIncident-VisualizeIncidentswithTrend.kql ├── Azure Diagnostics │ ├── AppGateway-VisualizeWAFTraffic.kql │ ├── AppGateway-MostAttackedHostName.kql │ └── CVE-2021-44228.kql ├── Windows Security Events │ ├── SecEvents-FindDevicesNoLongerSendingLogs.kql │ ├── SecEvents-PotentialRDPRecon.kql │ └── SecEvents-FindLateralMovementUsers.kql ├── Defender for Cloud Apps │ ├── DCA-PaidTrialStarted.kql │ ├── DCA-PivotTableAdminOperations.kql │ └── DCA-FindAzureADAdminActions.kql ├── Azure Activity │ ├── AzureVM-DiskImageURLGenerated.kql │ ├── AzureLogAnalytics-DetectwhenWorkspaceKeysareRead.kql │ └── AzureStorage-FirstTimeStorageKeyEnumeration.kql ├── Azure Key Vault │ ├── KeyVault-IPAddedtoFirewall.kql │ └── KeyVault-PotentiallySensitiveOperations.kql ├── Intune │ ├── IntuneDevices-VisualizeMatchingDeviceIds.kql │ ├── IntuneDevices-VisualizeDeviceComplianceovertime.kql │ ├── IntuneDevices-FindDetailsofNonCompliantDevices.kql │ ├── IntuneDevices-VisualizeLastContact.kql │ ├── IntuneDevices-VisualizeDeviceJoinTypebyWeek.kql │ └── IntuneDevices-RetrieveDeviceInfoAfterWipe.kql ├── DNS │ ├── DNS-FindDevicesThatHaveQueriedSuspiciousDomains.kql │ └── DnsEvents-FindStaleDomains.kql └── Defender for Identity │ ├── IdentityDirectoryEvents-EncryptionChange.kql │ └── IdentityDirectoryEvents-AccountDelegationChanged.kql └── 05-00-Analytics-Rule-Olusturma.md /Sentinel-KQL-Sorguları/Diagrams/parse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/parse.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/parse1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/parse1.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/parse2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/parse2.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/parse3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/parse3.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/parse4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/parse4.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/parse5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/parse5.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/split1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/split1.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/split2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/split2.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/split3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/split3.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/kql-pipe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/kql-pipe.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/querypack1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/querypack1.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/querypack2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/querypack2.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/querypack3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/querypack3.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/querypack4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/querypack4.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/querypack5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/querypack5.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/deploytoazure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/deploytoazure.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/render-areachart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/render-areachart.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/render-barchart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/render-barchart.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/render-piechart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/render-piechart.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/render-timechart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/render-timechart.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/render-columnchart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/render-columnchart.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/render-timebarchart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/render-timebarchart.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/SentinelTableMapping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/SentinelTableMapping.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/render-timecolumnchart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/render-timecolumnchart.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Functions/ReadmeImages/function1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Functions/ReadmeImages/function1.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Functions/ReadmeImages/function2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Functions/ReadmeImages/function2.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Functions/ReadmeImages/function3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Functions/ReadmeImages/function3.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Functions/ReadmeImages/function4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Functions/ReadmeImages/function4.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Functions/ReadmeImages/function5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Functions/ReadmeImages/function5.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Workbooks/ReadmeImages/workbook1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Workbooks/ReadmeImages/workbook1.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Workbooks/ReadmeImages/workbook2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Workbooks/ReadmeImages/workbook2.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Workbooks/ReadmeImages/workbook3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Workbooks/ReadmeImages/workbook3.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Workbooks/ReadmeImages/workbook4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Workbooks/ReadmeImages/workbook4.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/ConditionalAccess-LogicApp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/ConditionalAccess-LogicApp.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/render-timecolumnchartnames.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/render-timecolumnchartnames.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/render-timecolumnchartstacked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/render-timecolumnchartstacked.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/render-timecolumnchartunstacked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/render-timecolumnchartunstacked.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Diagrams/render-timecolumn-outlookonedrivesharepoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmreOzanMemis/AzureSentinel/HEAD/Sentinel-KQL-Sorguları/Diagrams/render-timecolumn-outlookonedrivesharepoint.png -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Data Management/365-Visualize365DaysofKql.kql: -------------------------------------------------------------------------------- 1 | //Visualize the breakdown of 365 days of KQL 2 | externaldata(Category: string, Count: int) 3 | [ 4 | h@'https://gist.githubusercontent.com/reprise99/12487ffefee2c2c417e2706150e25b8e/raw/0679cbb29e43e370c7304bb9b3d0007042a8ad52/365daysofkql.csv' 5 | ] | sort by Count desc | render piechart 6 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-SharedTeamsChannelCreated.kql: -------------------------------------------------------------------------------- 1 | //Detect when a shared Teams channel is created 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | OfficeActivity 6 | | where Operation == "ChannelAdded" 7 | | where ChannelType == "Shared" 8 | | project TimeGenerated, Actor=UserId, TeamName, ChannelType, ChannelName -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/UEBA/IdentityInfo-FindGuestswithHighBlastRadius.kql: -------------------------------------------------------------------------------- 1 | //Find Azure AD guest accounts that are considered to have a high blast radius 2 | 3 | //Data connector required for this query - Microsoft Sentinel UEBA 4 | 5 | IdentityInfo 6 | | where TimeGenerated > ago(30d) 7 | | summarize arg_max(TimeGenerated, *) by AccountUPN 8 | | where UserType == "Guest" and BlastRadius == "High" -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/MSGraph-GuestLogonQuery: -------------------------------------------------------------------------------- 1 | //Microsoft Graph query to find idle guest accounts. Change le date as required. Query will find all users with last sign in activity prior to that date. 2 | 3 | https://graph.microsoft.com/beta/users?$filter=UserType eq 'Guest'&$filter=signInActivity/lastSignInDateTime le 2021-08-01T00:00:00Z&$select=displayName,UserType,signInActivity -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/UEBA/IdentityInfo-FindAtRiskandHighBlastRadiusUsers.kql: -------------------------------------------------------------------------------- 1 | //Find accounts that are considered to have a high blast radius and currently at risk 2 | 3 | //Data connector required for this query - Microsoft Sentinel UEBA 4 | 5 | IdentityInfo 6 | | where TimeGenerated > ago(30d) 7 | | summarize arg_max(TimeGenerated, *) by AccountUPN 8 | | where BlastRadius == "High" 9 | | where RiskState == "AtRisk" -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-DetectInvalidCertificates.kql: -------------------------------------------------------------------------------- 1 | //Detect invalid certificates 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | DeviceEvents 6 | | where ActionType == "AntivirusReport" 7 | | extend ParsedAdditionalFields = parse_json(AdditionalFields) 8 | | evaluate bag_unpack(ParsedAdditionalFields) 9 | | where Signer startswith "INVALID" 10 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Log Analytics/LAQuery-VisualizeQueriesRun.kql: -------------------------------------------------------------------------------- 1 | //Visualize how many queries you have run in your Sentinel workspace over the last year 2 | 3 | LAQueryLogs 4 | | where TimeGenerated > ago (365d) 5 | | where AADEmail == "username@domain.com" 6 | | make-series Count=count() default=0 on TimeGenerated from ago(365d) to now() step 1d 7 | | render timechart with (title="#365daysofKQL queries run per day", ytitle="Count") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/UEBA/IdentityInfo-FindAccountsPasswordNotRequired.kql: -------------------------------------------------------------------------------- 1 | //Find user accounts with the 'password not required' flag set in Active Directory 2 | 3 | //Data connector required for this query - Microsoft Sentinel UEBA 4 | 5 | IdentityInfo 6 | | where TimeGenerated > ago(30d) 7 | | summarize arg_max(TimeGenerated, *) by AccountUPN 8 | | extend UACFlags = tostring(UserAccountControl[0]) 9 | | where UACFlags == "PasswordNotRequired" -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/UEBA/IdentityInfo-FindPrivAccountsHighBlastRadius.kql: -------------------------------------------------------------------------------- 1 | //Find user accounts that hold an Azure AD privileged role and are considered to have a high blast radius 2 | 3 | //Data connector required for this query - Microsoft Sentinel UEBA 4 | 5 | IdentityInfo 6 | | where TimeGenerated > ago(30d) 7 | | summarize arg_max(TimeGenerated, *) by AccountUPN 8 | | where isnotempty(AssignedRoles) and AssignedRoles != "[]" 9 | | where BlastRadius == "High" -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Heartbeat/Heartbeat-VisualizeDistinctComputersperMonth.kql: -------------------------------------------------------------------------------- 1 | //Visualize distinct computers per month sending data 2 | 3 | //Data connector required for this query - Heartbeat (created automatically when you onboard machines to Sentinel) 4 | 5 | Heartbeat 6 | | where TimeGenerated > ago(365d) 7 | | summarize Count=dcount(Computer)by Month=startofmonth(TimeGenerated) 8 | | render columnchart with (title="Distinct monthly computers sending data to Microsoft Sentinel") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/UEBA/IdentityInfo-VisualizeBlastRadius.kql: -------------------------------------------------------------------------------- 1 | //Visualize accounts by blast radius level 2 | 3 | //Data connector required for this query - Microsoft Sentinel UEBA 4 | 5 | IdentityInfo 6 | | where TimeGenerated > ago(30d) 7 | | summarize arg_max(TimeGenerated, *) by AccountUPN 8 | | where isnotempty(BlastRadius) 9 | | summarize Count=count()by BlastRadius 10 | | order by Count 11 | | render piechart with (title="Accounts by Microsoft Sentinel EUBA blast radius") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Active Directory/AADPasswordProtection-AllEvents.kql: -------------------------------------------------------------------------------- 1 | //If you add "Microsoft-AzureADPasswordProtection-DCAgent/Admin" as a log source to Sentinel/Log Analytics you can query Azure AD Password Protection events 2 | Event 3 | | where Source == "Microsoft-AzureADPasswordProtection-DCAgent" 4 | | where EventID in ("10014", "10015", "10016", "30002", "30004", "30026", "10024", "30008", "30010", "30028", "30024", "30003", "30005", "30027", "30022", "30007", "10025", "30009", "30029", "30023") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-SummarizeLocalGroupAdditions.kql: -------------------------------------------------------------------------------- 1 | //Summarize the total count of all local group additions by group name 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | DeviceEvents 6 | | where TimeGenerated > ago (30d) 7 | | where ActionType == "UserAccountAddedToLocalGroup" 8 | | summarize ['Local Group Addition Count']=count() by ['Local Group Name']=tostring(AdditionalFields.GroupName) 9 | | sort by ['Local Group Addition Count'] -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/EmailEvents-VisualizePostDeliveryActions.kql: -------------------------------------------------------------------------------- 1 | //Visualize any post delivery actions such as email being quarantined or deleted by admins 2 | 3 | //Data connector required for this query - M365 Defender - Email* tables 4 | 5 | EmailPostDeliveryEvents 6 | | where TimeGenerated > ago (90d) 7 | | where Action !in ("None", "Unknown") 8 | | make-series Count=count() on TimeGenerated from ago(45d) to now() step 1d by Action 9 | | render timechart with (title="Email post delivery actions over time") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-VisualizeMaliciousSmartScreenURLs.kql: -------------------------------------------------------------------------------- 1 | //Visualize the most common domains triggering Microsoft Defender SmartScreen warnings 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | DeviceEvents 6 | | where TimeGenerated > ago (30d) 7 | | where ActionType == "SmartScreenUrlWarning" 8 | | parse RemoteUrl with * '://' Domain '/' * 9 | | where isnotempty(Domain) 10 | | summarize Count=count()by Domain 11 | | sort by Count 12 | | render barchart -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-VisualizeRiskEventsoverTime.kql: -------------------------------------------------------------------------------- 1 | //Visualize the different risk types (e.g password spray, unlikely travel) per month 2 | 3 | //Data connector required for this query - Azure Active Directory - AAD User Risk Events 4 | 5 | AADUserRiskEvents 6 | | where TimeGenerated > ago (180d) 7 | | where isnotempty(RiskEventType) 8 | | summarize Count=count()by RiskEventType, startofmonth(TimeGenerated) 9 | | render columnchart with (kind=unstacked, title="Risk event types per month", xtitle="Month") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Security Alert/SecurityAlert-VisualizeAlertsbyProduct.kql: -------------------------------------------------------------------------------- 1 | //Visualize the number of alerts generated per day by each Defender product 2 | 3 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 4 | 5 | SecurityAlert 6 | | where TimeGenerated > ago(14d) 7 | | summarize Count=count() by ProductName, bin(TimeGenerated, 1d) 8 | | where ProductName != "Azure Sentinel" 9 | | render columnchart with (kind=unstacked, title="Alerts by Defender product per day") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-DetectNewExchangeAdminRole.kql: -------------------------------------------------------------------------------- 1 | //Detect when a new Exchange admin role is created and parse the permissions 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | OfficeActivity 6 | | where Operation == "New-RoleGroup" 7 | | where RecordType == "ExchangeAdmin" 8 | | parse Parameters with * 'Name","Value":"' ['Role Name'] '"' * 9 | | parse Parameters with * 'Roles","Value":"' ['Permissions Added'] '"' * 10 | | project TimeGenerated, Actor=UserId, ['Role Name'], ['Permissions Added'] -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Bastion/Bastion-AuditUsage.kql: -------------------------------------------------------------------------------- 1 | //Find which users have attempted to connect to virtual machines using RDP or SSH in Azure Bastion 2 | 3 | //Data connector required for this query - Azure Bastion 4 | 5 | MicrosoftAzureBastionAuditLogs 6 | | parse TargetResourceId with * 'VIRTUALMACHINES/' ['Virtual Machine Name'] 7 | | project 8 | TimeGenerated, 9 | Message, 10 | UserName, 11 | Protocol, 12 | ['Virtual Machine Name'], 13 | ['Virtual Machine IP']=TargetVMIPAddress 14 | | sort by TimeGenerated desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Sentinel Incidents/SecurityIncident-VisualizeIncidentSeverity.kql: -------------------------------------------------------------------------------- 1 | //Visualize the severity of your Microsoft Sentinel incidents per month 2 | 3 | //Data connector required for this query - Microsoft Sentinel Incidents (generated automatically if you create incidents in Sentinel) 4 | 5 | SecurityIncident 6 | | where TimeGenerated > ago(365d) 7 | | summarize Count=dcount(IncidentNumber)by Severity, startofmonth(TimeGenerated) 8 | | render columnchart with (kind=unstacked, title="Microsoft Sentinel Incident Severity", xtitle="Month") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Diagnostics/AppGateway-VisualizeWAFTraffic.kql: -------------------------------------------------------------------------------- 1 | //Visualize WAF rule actions such as allowed, blocked, detected and matched over time 2 | 3 | //Data connector required for this query - Azure Diagnostics (Application Gateways) 4 | 5 | AzureDiagnostics 6 | | where TimeGenerated > ago(30d) 7 | | where ResourceType == "APPLICATIONGATEWAYS" 8 | | summarize count()by action_s, bin(TimeGenerated, 1h) 9 | | where isnotempty(action_s) 10 | | render timechart with (ytitle="WAF Hit Count", title="Web application firewall traffic over time") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-MalwareDetected.kql: -------------------------------------------------------------------------------- 1 | //Alerts when a file believed to be malware is uploaded to your Office 365 tenant in SharePoint or OneDrive 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | OfficeActivity 6 | | where TimeGenerated > ago(30d) 7 | | where Operation == "FileMalwareDetected" 8 | | project 9 | TimeGenerated, 10 | OfficeWorkload, 11 | ['File Name']=SourceFileName, 12 | ['File Location']=OfficeObjectId, 13 | ['Relative File URL']=SourceRelativeUrl, 14 | ClientIP 15 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-VisualizeFilesSharedtoGuests.kql: -------------------------------------------------------------------------------- 1 | //Visualize the files shared to guests from Office 365 over time 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | let timerange=90d; 6 | OfficeActivity 7 | | where TimeGenerated > ago(timerange) 8 | | where Operation in ("SecureLinkCreated", "AddedToSecureLink") 9 | | where TargetUserOrGroupType == "Guest" 10 | | summarize Count=count()by bin(TimeGenerated, 1d) 11 | | render timechart with (ytitle="File Count", title="Files shared with guests over time") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/UEBA/IdentityInfo-FindAccountswithsameEmployeeId.kql: -------------------------------------------------------------------------------- 1 | //Summarize accounts in our environment that have the same employee id (i.e regular and admin accounts) 2 | 3 | //Data connector required for this query - Microsoft Sentinel UEBA 4 | 5 | IdentityInfo 6 | | where TimeGenerated > ago(30d) 7 | | summarize arg_max(TimeGenerated, *) by AccountUPN 8 | | where isnotempty(EmployeeId) 9 | | summarize ['Count of accounts']=dcount(AccountUPN), ['List of accounts']=make_set(AccountUPN) by EmployeeId 10 | | sort by ['Count of accounts'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-PowerShellExecutionModeChanged.kql: -------------------------------------------------------------------------------- 1 | //Detect when a non system account changes the PowerShell execution policy on a device 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | DeviceEvents 6 | | where ActionType == "PowerShellCommand" 7 | | where InitiatingProcessFileName == "powershell.exe" 8 | | where InitiatingProcessAccountName != "system" 9 | | where AdditionalFields.Command == "Set-ExecutionPolicy" 10 | | project TimeGenerated, DeviceName, InitiatingProcessAccountName -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-ServicePrincipalSigninsbyIP.kql: -------------------------------------------------------------------------------- 1 | //Make a set of IP addresses that your Azure AD service principals have signed in from 2 | 3 | //Data connector required for this query - Azure Active Directory - Service Principal Signin Logs 4 | 5 | AADServicePrincipalSignInLogs 6 | | where TimeGenerated > ago(90d) 7 | | where ResultType == "0" 8 | | summarize ['List of IP Addresses']=make_set(IPAddress), ['Count of Unique IP Addresses']=dcount(IPAddress) by ServicePrincipalName, AppId 9 | | sort by ['Count of Unique IP Addresses'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-VisualizeTopGuestDownloads.kql: -------------------------------------------------------------------------------- 1 | //Visualize the top 20 files downloaded by Azure AD guests over the last month 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | OfficeActivity 6 | | where TimeGenerated > ago (30d) 7 | | where Operation in ("FileSyncDownloadedFull", "FileDownloaded") 8 | | where UserId contains "#ext#" 9 | | summarize Count=count()by FileName=SourceFileName 10 | | sort by Count desc 11 | | take 20 12 | | render barchart with (title="Top files downloaded by guests over the last month") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Windows Security Events/SecEvents-FindDevicesNoLongerSendingLogs.kql: -------------------------------------------------------------------------------- 1 | //Find computers that have not sent any security events for over an hour 2 | 3 | //Data connector required for this query - Windows Security Events via AMA or Security Events via Legacy Agent 4 | 5 | SecurityEvent 6 | | where TimeGenerated > ago (1d) 7 | | summarize ['Last Record Received'] = datetime_diff("minute", now(), max(TimeGenerated)) by Computer 8 | | project Computer, ['Last Record Received'] 9 | | where ['Last Record Received'] >= 60 10 | | order by ['Last Record Received'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Security Alert/SecurityAlert-VisualizeMDEAlertSeverity.kql: -------------------------------------------------------------------------------- 1 | //Visualize the severity of your MDE alerts (Informational, Low, Medium, High) per day 2 | 3 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 4 | 5 | SecurityAlert 6 | | where TimeGenerated > ago (14d) 7 | | where ProviderName == "MDATP" 8 | | summarize Count=count()by AlertSeverity, startofday(TimeGenerated) 9 | | render columnchart with (kind=unstacked, ytitle="Alert Count", xtitle="Day", title="Defender for Endpoint alert severity per day") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/UEBA/IdentityInfo-FindUserswithmanyGroups.kql: -------------------------------------------------------------------------------- 1 | //Find user accounts that are members of over 150 groups. These can cause issues with SAML claims. 2 | //See https://docs.microsoft.com/en-us/azure/active-directory/hybrid/how-to-connect-fed-group-claims 3 | 4 | //Data connector required for this query - Microsoft Sentinel UEBA 5 | 6 | IdentityInfo 7 | | where TimeGenerated > ago(30d) 8 | | summarize arg_max(TimeGenerated, *) by AccountUPN 9 | | extend ['Group Count']=array_length(GroupMembership) 10 | | sort by ['Group Count'] desc 11 | | where ['Group Count'] > 150 -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Diagnostics/AppGateway-MostAttackedHostName.kql: -------------------------------------------------------------------------------- 1 | //Visualize the most attacked hostname behind an Azure App Gateway/WAF 2 | 3 | //Data connector required for this query - Azure Diagnostics (Application Gateways) 4 | 5 | AzureDiagnostics 6 | | where TimeGenerated > ago(30d) 7 | | where ResourceType == "APPLICATIONGATEWAYS" 8 | | where isnotempty(ruleId_s) 9 | | summarize ['WAF Hit Count']=count() by hostname_s 10 | | where isnotempty(hostname_s) 11 | | sort by ['WAF Hit Count'] desc 12 | | render barchart with (title="Most WAF Hits by Hostname", xtitle="Hostname") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Cloud Apps/DCA-PaidTrialStarted.kql: -------------------------------------------------------------------------------- 1 | //Alert when a user starts a paid trial of a M365 product 2 | 3 | //Data connector required for this query - M365 Defender - CloudAppEvents 4 | 5 | CloudAppEvents 6 | | extend Operation = tostring(RawEventData.Operation) 7 | | where Operation == "StartAPaidTrial" 8 | | extend UserId = tostring(RawEventData.UserId) 9 | | extend LicenseDisplayName = tostring(RawEventData.LicenseDisplayName) 10 | | extend Workload = tostring(RawEventData.Workload) 11 | | project TimeGenerated, Operation, UserId, LicenseDisplayName, Workload -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-SummarizePIMRolesActivated.kql: -------------------------------------------------------------------------------- 1 | //Summarize and visualize the roles being activated in Azure AD PIM 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where TimeGenerated > ago(30d) 7 | | where OperationName == "Add member to role completed (PIM activation)" 8 | | extend ['Azure AD Role Name'] = tostring(TargetResources[0].displayName) 9 | | summarize Count=count()by ['Azure AD Role Name'] 10 | | sort by Count 11 | | render barchart with (title="Count of Azure AD PIM activations by role") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-YourUsersSigningIntoOtherTenantsAsGuests.kql: -------------------------------------------------------------------------------- 1 | //Find sign ins where your users signed into other Azure AD tenants as outbound guests 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where AADTenantId == HomeTenantId 7 | | where ResourceTenantId != AADTenantId 8 | | where UserType == "Guest" 9 | | project 10 | TimeGenerated, 11 | AppDisplayName, 12 | UserPrincipalName, 13 | ResultType, 14 | Location, 15 | IPAddress, 16 | ['Guest Tenant Id']=ResourceTenantId -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Log Analytics/LAQuery-UsersvsAutomationQueryStats.kql: -------------------------------------------------------------------------------- 1 | //Visualizes queries against your log analytics workspace categorized by users and service principals 2 | 3 | //Data connector required for this query - Log Analytics diagnostic settings enabled on your Sentinel workspace 4 | 5 | let timeframe=45d; 6 | LAQueryLogs 7 | | where TimeGenerated > ago (timeframe) 8 | | summarize Users=countif(isnotempty(AADEmail)), Playbooks=countif(isempty(AADEmail)) by bin(TimeGenerated, 1d) 9 | | render columnchart with (kind=unstacked, ytitle="Queries Run", title="Queries Run - Users vs Playbooks") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-AuthenticationStrengthsParser: -------------------------------------------------------------------------------- 1 | //mv-apply parser to retrieve the Authentication requirements for an Azure AD Sign in 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | mv-apply arp = todynamic(AuthenticationRequirementPolicies) on 7 | (extend requirementProvider = tostring(arp.requirementProvider) 8 | ) 9 | | project 10 | TimeGenerated, 11 | UserPrincipalName, 12 | AppDisplayName, 13 | AuthenticationRequirement, 14 | requirementProvider, 15 | CorrelationId -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-SummarizeSmartScreenPhishingDomains.kql: -------------------------------------------------------------------------------- 1 | //Summarize the domains that Smartscreen is blocking as phishing attempts 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | DeviceEvents 6 | | where TimeGenerated > ago (30d) 7 | | where ActionType startswith "SmartScreen" 8 | | extend SmartScreenExperience = tostring(AdditionalFields.Experience) 9 | | where AdditionalFields.Experience == "Phishing" 10 | | parse-where RemoteUrl with * '://' RemoteDomain '/' * 11 | | summarize Count=count()by RemoteDomain 12 | | sort by Count -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Log Analytics/LAQuery-FindQueryStats.kql: -------------------------------------------------------------------------------- 1 | //Create a list of all tables in Sentinel, then iterate through the list to audit the LAQuery log table to see which are being actively used 2 | 3 | //Data connector required for this query - Log Analytics diagnostic settings enabled on your Sentinel workspace 4 | 5 | let tablenames = search * 6 | | summarize make_set($table); 7 | LAQueryLogs 8 | | mv-apply table=toscalar(tablenames) to typeof(string) on (where QueryText contains ['table']) 9 | | summarize QueryCount = count()by ['table'] 10 | | order by QueryCount 11 | | render piechart -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-BitLockerKeyRetrieved.kql: -------------------------------------------------------------------------------- 1 | //Detects when a BitLocker key is read in Azure AD and retrieves the device and key ids 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where OperationName == "Read BitLocker key" 7 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 8 | | extend s = tostring(AdditionalDetails[0].value) 9 | | parse s with * "ID: '" KeyId "'" * 10 | | parse s with * "device: '" DeviceId "'" 11 | | project TimeGenerated, OperationName, Actor, KeyId, DeviceId -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-UserAddedtoRoleOutsidePIM.kql: -------------------------------------------------------------------------------- 1 | //Alert when a user is added directly to an Azure AD role, bypassing PIM 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where OperationName has "Add member to role outside of PIM" 7 | | extend RoleName = tostring(TargetResources[0].displayName) 8 | | extend UserAdded = tostring(TargetResources[2].displayName) 9 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 10 | | project TimeGenerated, OperationName, RoleName, UserAdded, Actor -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-DetectMultipleDistinctRiskEvents.kql: -------------------------------------------------------------------------------- 1 | //Detect when a user flags 3 or more distinct Azure AD risk events within a single day 2 | 3 | //Data connector required for this query - Azure Active Directory - AAD User Risk Events 4 | 5 | AADUserRiskEvents 6 | | where TimeGenerated > ago(7d) 7 | | where RiskState != "dismissed" 8 | | summarize 9 | ['Distinct count of risk events']=dcount(RiskEventType), 10 | ['List of risk events']=make_set(RiskEventType) 11 | by UserPrincipalName, bin(TimeGenerated, 1d) 12 | | where ['Distinct count of risk events'] >= 3 -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-FindDevicesToOnboard.kql: -------------------------------------------------------------------------------- 1 | //Find the information of any devices found by Defender network device discovery that are able to onboarded to Defender 2 | 3 | //Data connector required for this query - Advanced Hunting license 4 | 5 | //This query only works in Advanced Hunting 6 | DeviceInfo 7 | | summarize arg_max(Timestamp, *) by DeviceId 8 | | where OnboardingStatus == "Can be onboarded" 9 | | where isempty(MergedToDeviceId) 10 | | project ['Time last seen']=Timestamp, DeviceName, DeviceId, OSDistribution, OSVersion, DeviceCategory, IsAzureADJoined, JoinType -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-FindNewOperations.kql: -------------------------------------------------------------------------------- 1 | //Find any new operations audited in Office 365 in the last 14 days vs the previous 180 days 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | let existingoperations= 6 | OfficeActivity 7 | | where TimeGenerated > ago(180d) and TimeGenerated < ago(14d) 8 | | distinct Operation; 9 | OfficeActivity 10 | | where TimeGenerated > ago(14d) 11 | | summarize arg_min(TimeGenerated, *) by Operation 12 | | where Operation !in (existingoperations) 13 | | project ['Time First Seen']=TimeGenerated, Operation, OfficeWorkload -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Log Analytics/LAQuery-NewUsersQueryingData.kql: -------------------------------------------------------------------------------- 1 | //Find users querying your Log Analytics/Sentinel data for the first time 2 | 3 | //Data connector required for this query - Log Analytics diagnostic settings enabled on your Sentinel workspace 4 | 5 | let knownusers= 6 | LAQueryLogs 7 | | where TimeGenerated > ago(180d) and TimeGenerated < ago(1d) 8 | | distinct AADEmail 9 | | where isnotempty(AADEmail); 10 | LAQueryLogs 11 | | where TimeGenerated > ago(1d) 12 | | where AADEmail !in (knownusers) 13 | | where isnotempty(AADEmail) 14 | | project TimeGenerated, AADEmail, QueryText -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-SummarizeDownloadActivitybyGuests.kql: -------------------------------------------------------------------------------- 1 | //Summarize the total count and the list of files downloaded by guests in your Office 365 tenant 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | let timeframe=7d; 6 | OfficeActivity 7 | | where TimeGenerated > ago(timeframe) 8 | | where Operation in ("FileSyncDownloadedFull", "FileDownloaded") 9 | | where UserId contains "#EXT#" 10 | | summarize 11 | ['Count of Downloads']=count(), 12 | ['List of Files Downloaded']=make_set(OfficeObjectId) 13 | by UserId 14 | | sort by ['Count of Downloads'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Data Management/Data-TableSizePerMDEDevice.kql: -------------------------------------------------------------------------------- 1 | //Calculate the size of the combined Device* tables from Defender for Endpoint by device name 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | union withsource=_TableName Device* 6 | | where TimeGenerated > ago(7d) 7 | | summarize 8 | Entries = count(), Size = sum(_BilledSize) 9 | by DeviceName 10 | | project 11 | ['Device Name'] = DeviceName, 12 | ['Table Size'] = Size, 13 | ['Table Entries'] = Entries, 14 | ['Size per Entry'] = 1.0 * Size / Entries 15 | | order by ['Table Size'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Heartbeat/Heartbeat-NoHeartbeatinTimeframe.kql: -------------------------------------------------------------------------------- 1 | //Finds computers that haven't sent a heartbeat in the last 30 days 2 | 3 | //Data connector required for this query - Heartbeat (created automatically when you onboard machines to Sentinel) 4 | 5 | Heartbeat 6 | | where TimeGenerated > ago(365d) 7 | | summarize arg_max(TimeGenerated, *) by Computer 8 | | project 9 | Computer, 10 | ['Last Heartbeat']=TimeGenerated, 11 | ['Days Since Last Heartbeat']=datetime_diff("day", now(), TimeGenerated) 12 | | where ['Days Since Last Heartbeat'] > 30 13 | | sort by ['Days Since Last Heartbeat'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Active Directory/SecurityEvent-AccountSetPasswordNotRequired.kql: -------------------------------------------------------------------------------- 1 | //Alert when an Active Directory account is set to password not required 2 | 3 | //Data connector required for this query - Windows Security Events via AMA or Security Events via Legacy Agent 4 | 5 | 6 | SecurityEvent 7 | | project TimeGenerated, EventID, TargetAccount, SubjectAccount, UserAccountControl 8 | | where EventID == "4738" 9 | | where UserAccountControl has ("2082") 10 | | extend Activity = strcat("Account set to password not required") 11 | | project TimeGenerated, Target=TargetAccount, Actor=SubjectAccount, Activity 12 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Active Directory/SecurityEvent-UnconstrainedDelegationtoUser.kql: -------------------------------------------------------------------------------- 1 | //Detects when unconstrained kerberos delegation is enabled on a user object 2 | 3 | //Data connector required for this query - Windows Security Events via AMA or Security Events via Legacy Agent 4 | 5 | SecurityEvent 6 | | where EventID == "4738" 7 | | parse EventData with * 'NewUacValue">' NewUacValue '' * 8 | | parse EventData with * 'TargetUserName">' UserName '' * 9 | | parse EventData with * 'SubjectUserName">' Actor '' * 10 | | where NewUacValue == "0x2010" 11 | | project TimeGenerated, Activity, UserName, Actor -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/OAuth-DelegatedPermissionsGrant.kql: -------------------------------------------------------------------------------- 1 | //Query to find applications that have had delegated permissions granted to them 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where Category == "ApplicationManagement" 7 | | where OperationName has "Add delegated permission grant" 8 | | extend UpdatedPermissions = tostring(parse_json(tostring(parse_json(tostring(TargetResources[0].modifiedProperties))[0].newValue))) 9 | | extend AppId = tostring(TargetResources[1].id) 10 | | project TimeGenerated, UpdatedPermissions, OperationName, AppId -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Active Directory/SecurityEvent-UnconstrainedDelegationEnabled.kql: -------------------------------------------------------------------------------- 1 | //Detects when unconstrained kerberos delegation is enabled on a computer object 2 | 3 | //Data connector required for this query - Windows Security Events via AMA or Security Events via Legacy Agent 4 | 5 | SecurityEvent 6 | | where EventID == "4742" 7 | | parse EventData with * 'NewUacValue">' NewUacValue '' * 8 | | parse EventData with * 'TargetUserName">' ComputerName '' * 9 | | parse EventData with * 'SubjectUserName">' Actor '' * 10 | | where NewUacValue == "0x2080" 11 | | project TimeGenerated, Activity, ComputerName, Actor -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-FindAppswithNoSignins.kql: -------------------------------------------------------------------------------- 1 | //Find Azure AD applications that have had no signins for over 30 days. May be a sign of an app no longer in use or users bypassing SSO. 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago (365d) 7 | | where ResultType == 0 8 | | summarize arg_max(TimeGenerated, *) by AppId 9 | | project 10 | AppDisplayName, 11 | ['Last Logon Time']=TimeGenerated, 12 | ['Days Since Last Logon']=datetime_diff("day", now(), TimeGenerated) 13 | | where ['Days Since Last Logon'] > 30 -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-SummarizeGuestInactivity.kql: -------------------------------------------------------------------------------- 1 | //Group your Azure AD guest accounts into the month they last signed in 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago (360d) 7 | | where UserType == "Guest" or UserPrincipalName contains "#ext#" 8 | | where ResultType == 0 9 | | summarize arg_max(TimeGenerated, *) by UserPrincipalName 10 | | project TimeGenerated, UserPrincipalName 11 | | summarize ['Inactive Guest Accounts']=make_set(UserPrincipalName) by Month=startofmonth(TimeGenerated) 12 | | sort by Month desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Activity/AzureVM-DiskImageURLGenerated.kql: -------------------------------------------------------------------------------- 1 | //Detect when a download URL is generated for an Azure virtual machine disk 2 | 3 | //Data connector required for this query - Azure Activity 4 | 5 | AzureActivity 6 | | where OperationNameValue == "MICROSOFT.COMPUTE/DISKS/BEGINGETACCESS/ACTION" 7 | | where ActivityStatusValue == "Success" 8 | | extend DiskName = tostring(Properties_d.resource) 9 | | project 10 | TimeGenerated, 11 | Actor=Caller, 12 | ['Actor IP Address']=CallerIpAddress, 13 | ['Azure Subscription Id']=SubscriptionId, 14 | ['Azure Resource Group']=ResourceGroup, 15 | DiskName -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/OAuth-SummarizeServicePrincipalInactivity.kql: -------------------------------------------------------------------------------- 1 | //Summarize your Azure AD service principals by the last time they signed in, grouped by month 2 | 3 | //Data connector required for this query - Azure Active Directory - Service Principal Signin Logs 4 | 5 | AADServicePrincipalSignInLogs 6 | | project TimeGenerated, AppId, ResultType, ServicePrincipalName 7 | | where TimeGenerated > ago (360d) 8 | | where ResultType == 0 9 | | summarize arg_max(TimeGenerated, *) by AppId 10 | | summarize ['Application List']=make_set(ServicePrincipalName) by Month=startofmonth(TimeGenerated) 11 | | sort by Month asc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Security Alert/SecurityAlert-MultipleLowSeverityAlertsTriggered.kql: -------------------------------------------------------------------------------- 1 | //Detect when the same user or device triggers 3 or more low severity alerts in the space of a day 2 | 3 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 4 | 5 | SecurityAlert 6 | | where TimeGenerated > ago (7d) 7 | | where AlertSeverity == "Low" 8 | | summarize 9 | ['Count of low severity alerts']=dcount(AlertName), 10 | ['List of low severity alerts']=make_set(AlertName) 11 | by CompromisedEntity, bin(TimeGenerated, 1d) 12 | | where ['Count of low severity alerts'] >= 3 -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-ConditionalAccessPivotTable.kql: -------------------------------------------------------------------------------- 1 | //Create a pivot table showing all conditional access policy outcomes over the last 30 days 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago(30d) 7 | | extend CA = parse_json(ConditionalAccessPolicies) 8 | | mv-expand bagexpansion=array CA 9 | | evaluate bag_unpack(CA) 10 | | extend 11 | ['CA Outcome']=tostring(column_ifexists('result', "")), 12 | ['CA Policy Name'] = column_ifexists('displayName', "") 13 | | evaluate pivot(['CA Outcome'], count(), ['CA Policy Name']) -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Cloud Apps/DCA-PivotTableAdminOperations.kql: -------------------------------------------------------------------------------- 1 | //Defender for Cloud Apps tracks administrative actions under the 'isAdminOperation' flag. This query will build a pivot table of all admin operations completed by your users 2 | //Works in both Sentinel and Advanced Hunting 3 | 4 | //Data connector required for this query - Advanced Hunting license or M365 Defender - CloudAppEvents for Sentinel 5 | 6 | CloudAppEvents 7 | | where IsAdminOperation == "true" 8 | | where AccountType == "Regular" 9 | | extend UserPrincipalName = tostring(RawEventData.UserId) 10 | | evaluate pivot(ActionType, count(), UserPrincipalName) 11 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-InboxRuleParse.kql: -------------------------------------------------------------------------------- 1 | //Query to retrieve the name of inbox rules created via mv-apply 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | OfficeActivity 6 | | where TimeGenerated > ago (30d) 7 | | where Operation == "New-InboxRule" 8 | | mv-apply p=todynamic(Parameters) on ( 9 | where p.Name == "Name" 10 | | extend RuleName = tostring(p.Value) 11 | ) 12 | | project TimeGenerated, UserId, ClientIP, RuleName 13 | //Additionally search for inbox rule names that have no alphanumeric characters, can be a sign of threat actor activity 14 | | where RuleName matches regex @"^[^a-zA-Z0-9]*$" -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Sentinel Incidents/SecurityIncident-PlaybookActivities.kql: -------------------------------------------------------------------------------- 1 | //Visualize which playbooks are interacting with security incidents 2 | 3 | //Data connector required for this query - Microsoft Sentinel Incidents (generated automatically if you create incidents in Sentinel) 4 | 5 | let timeframe=45d; 6 | SecurityIncident 7 | | where TimeGenerated > ago (timeframe) 8 | | where ModifiedBy startswith "Playbook" 9 | | summarize Count=count() by ModifiedBy 10 | | sort by Count desc 11 | | render barchart 12 | with ( 13 | title="Count of playbooks interacting with Microsoft Sentinel incidents", 14 | ytitle="Playbook Name") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Security Alert/SecurityAlert-ForecastIdentityProtection.kql: -------------------------------------------------------------------------------- 1 | //Forecast the count of Azure AD Identity Protection Events events for the next 14 days based on the previous 30 days 2 | 3 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 4 | 5 | SecurityAlert 6 | | where ProviderName == "IPC" 7 | | make-series ["Azure AD Identity Protection Events"]=count() on TimeGenerated from ago(30d) to now() + 14d step 1d 8 | | extend ["Azure AD Identity Protection Events Forecast"] = series_decompose_forecast(['Azure AD Identity Protection Events'], toint(14d / 1d)) 9 | | render timechart -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-NewDomainAdded.kql: -------------------------------------------------------------------------------- 1 | //Detect when an admin adds a new unverified or verified domain into your Azure AD tenant 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where OperationName in ("Add verified domain", "Add unverified domain") 7 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 8 | | extend ['Actor IP Address'] = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress) 9 | | extend Domain = tostring(TargetResources[0].displayName) 10 | | project TimeGenerated, OperationName, Actor, ['Actor IP Address'], Domain -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-VisualizeSigninsbyDeviceTrust.kql: -------------------------------------------------------------------------------- 1 | //Visualize sign in attempts to your Azure AD tenant by device trust type 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago(30d) 7 | | extend DeviceTrustType = tostring(DeviceDetail.trustType) 8 | | extend ['Trust Type']=case(isnotempty(DeviceTrustType), strcat=DeviceTrustType, 9 | isempty(DeviceTrustType), strcat="Untrusted", 10 | "unknown") 11 | | summarize Count=count()by ['Trust Type'], bin(TimeGenerated, 1d) 12 | | render timechart with (title="Signins to Azure AD by trust type") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Key Vault/KeyVault-IPAddedtoFirewall.kql: -------------------------------------------------------------------------------- 1 | // Detects when an IP address has been added to an Azure Key Vault firewall allow list 2 | 3 | //Data connector required for this query - Azure Key Vault 4 | 5 | AzureDiagnostics 6 | | where ResourceType == "VAULTS" 7 | | where OperationName == "VaultPatch" 8 | | where ResultType == "Success" 9 | | where isnotempty(addedIpRule_Value_s) 10 | | project 11 | TimeGenerated, 12 | VaultName=Resource, 13 | SubscriptionId, 14 | IPAddressofActor=CallerIPAddress, 15 | Actor=identity_claim_http_schemas_xmlsoap_org_ws_2005_05_identity_claims_upn_s, 16 | IPRangeAdded=addedIpRule_Value_s -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-FilesSharedtoGuestsfromOnedrive.kql: -------------------------------------------------------------------------------- 1 | //Find when files are shared from OneDrive to third party guests 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | OfficeActivity 6 | | where TimeGenerated > ago(7d) 7 | | where OfficeWorkload == "OneDrive" 8 | | where Operation in ("SecureLinkCreated", "AddedToSecureLink") 9 | | where TargetUserOrGroupType == "Guest" or TargetUserOrGroupName contains "#EXT#" 10 | | project 11 | TimeGenerated, 12 | ['User Who Shared']=UserId, 13 | ['Guest Granted Access']=TargetUserOrGroupName, 14 | ['File Shared']=OfficeObjectId 15 | | sort by TimeGenerated desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-ServicePrincipalSummaryofResources.kql: -------------------------------------------------------------------------------- 1 | //Create a summary of which resources each of your service principals are connecting to. The higher the count the potential higher blast radius. 2 | 3 | //Data connector required for this query - Azure Active Directory - Service Principal Signin Logs 4 | 5 | AADServicePrincipalSignInLogs 6 | | where TimeGenerated > ago(30d) 7 | | where ResultType == "0" 8 | | summarize 9 | ['List of Azure Resources']=make_set(ResourceDisplayName), 10 | ['Count of Azure Resources']=dcount(ResourceDisplayName) 11 | by ServicePrincipalName 12 | | sort by ['Count of Azure Resources'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-FindUserswhoDownloadedMalware.kql: -------------------------------------------------------------------------------- 1 | //When Office 365 detects malware in OneDrive or SharePoint find any users that downloaded the same file 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | let malware= 6 | OfficeActivity 7 | | where TimeGenerated > ago(1d) 8 | | where Operation == "FileMalwareDetected" 9 | | distinct OfficeObjectId; 10 | OfficeActivity 11 | | where TimeGenerated > ago (1d) 12 | | where Operation in ("FileSyncDownloadedFull", "FileDownloaded") 13 | | where OfficeObjectId in (malware) 14 | | summarize ['Users who Downloaded']=make_set(UserId) by ['File Name']=OfficeObjectId -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-GuestAddedtoAADRole.kql: -------------------------------------------------------------------------------- 1 | //Detects when an Azure AD guest is added to an Azure AD role 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where OperationName == "Add member to role" 7 | | extend Target = tostring(TargetResources[0].userPrincipalName) 8 | | extend RoleAdded = tostring(parse_json(tostring(parse_json(tostring(TargetResources[0].modifiedProperties))[1].newValue))) 9 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 10 | | where Target contains "#ext#" 11 | | project TimeGenerated, OperationName, Actor, Target, RoleAdded -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-UserTryingtoAccessMultipleApps.kql: -------------------------------------------------------------------------------- 1 | //Detect users trying to access multiple applications they haven't been granted access to over a short period of time 2 | //In this example alert when a user attempts to access 2 or more unique applications in 30 minutes 3 | 4 | //Data connector required for this query - Azure Active Directory - Signin Logs 5 | 6 | SigninLogs 7 | | where ResultType == "50105" 8 | | summarize 9 | ['Denied Application List']=make_set(AppDisplayName), 10 | ['Count of Applications']=dcount(AppDisplayName) 11 | by UserPrincipalName, bin(TimeGenerated, 30m) 12 | | where ['Count of Applications'] >= 2 -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-ManagedIdentitySummaryofResources.kql: -------------------------------------------------------------------------------- 1 | //Summarize the Azure resources that each of your managed identities are accessing. The higher the count the higher the potential blast radius. 2 | 3 | //Data connector required for this query - Azure Active Directory - Managed Identity Signin Logs 4 | 5 | AADManagedIdentitySignInLogs 6 | | where TimeGenerated > ago(30d) 7 | | where ResultType == 0 8 | | summarize 9 | ['List of Azure Resources Accessed']=make_set(ResourceDisplayName), 10 | ['Distinct Resources Accessed']=dcount(ResourceDisplayName) 11 | by ServicePrincipalName 12 | | sort by ['Distinct Resources Accessed'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-SummarizeUnknownLocationnoMFA.kql: -------------------------------------------------------------------------------- 1 | //Find the applications with the most signins coming from unknown locations (not defined in Conditional Access) and only requiring single factor authentication 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago(30d) 7 | | where ResultType == 0 8 | | where NetworkLocationDetails == '[]' 9 | | where AuthenticationRequirement == "singleFactorAuthentication" 10 | | summarize ['Count of signins']=count(), ['Distinct user count']=dcount(UserPrincipalName) by AppDisplayName 11 | | sort by ['Distinct user count'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-VisualizeGuestDomains.kql: -------------------------------------------------------------------------------- 1 | //Visualize the most popular domains that have redeemed invites to your Azure AD tenant over the last 30 days. 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where TimeGenerated > ago(30d) 7 | | where OperationName == "Redeem external user invite" 8 | | extend GuestEmail = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 9 | | extend ['User Domain'] = tostring(split(GuestEmail, "@")[1]) 10 | | where isnotempty(['User Domain']) 11 | | summarize Count=count()by ['User Domain'] 12 | | sort by Count desc 13 | | render barchart -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Intune/IntuneDevices-VisualizeMatchingDeviceIds.kql: -------------------------------------------------------------------------------- 1 | //Visualize devices in intune with the same intune and Azure AD Id per week by join type 2 | 3 | //Data connector required for this query - Intune data sent to Sentinel workspace 4 | 5 | IntuneDevices 6 | | where TimeGenerated > ago (180d) 7 | | summarize arg_max(TimeGenerated, *) by DeviceName, startofweek(TimeGenerated) 8 | | where DeviceId == ReferenceId 9 | | where OS == 'Windows' 10 | | summarize count()by startofweek(TimeGenerated), JoinType 11 | | where isnotempty( JoinType) 12 | | render columnchart with (kind=unstacked, title="Devices with the same Azure AD and Intune device Id per week by join type") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-GuestDomainsHighestDownloads.kql: -------------------------------------------------------------------------------- 1 | //Summarize the total count of downloads from Office 365 for each of your guest domains 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | OfficeActivity 6 | | where TimeGenerated > ago(30d) 7 | | where Operation in ("FileSyncDownloadedFull", "FileDownloaded") 8 | | where UserId contains "#EXT#" 9 | | extend ['Guest UserPrincipalName'] = tostring(split(UserId,"#")[0]) 10 | | extend ['Guest Domain'] = tostring(split(['Guest UserPrincipalName'],"_")[-1]) 11 | | project ['Guest Domain'] 12 | | summarize ['Download Count']=count()by ['Guest Domain'] 13 | | sort by ['Download Count'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-VisualizeDistinctInboundGuests.kql: -------------------------------------------------------------------------------- 1 | //Visualize distinct inbound guests (guest accounts accessing your tenant) per month 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago(365d) 7 | | where UserType == "Guest" 8 | | where ResultType == 0 9 | //Find only inbound guests, i.e those guests accessing your tenant. 10 | | summarize Count=dcountif(UserPrincipalName, AADTenantId != HomeTenantId and HomeTenantId != ResourceTenantId) by startofmonth(TimeGenerated) 11 | | render columnchart with (title="Distinct guest accounts accessing your tenant per month") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Data Management/Data-NewTablesFound.kql: -------------------------------------------------------------------------------- 1 | //Detect when new tables have been written to in the last week compared to the last 90 days 2 | 3 | //Data connector required for this query - query will automatically union any data you have 4 | 5 | let existingtables= 6 | union withsource=_TableName * 7 | | where TimeGenerated > ago(90d) and TimeGenerated < ago(7d) 8 | | distinct Type; 9 | let newtables= 10 | union withsource=_TableName * 11 | | where TimeGenerated > ago(7d) 12 | | summarize ['First Log Received'] = min(TimeGenerated) by Type 13 | | project Type, ['First Log Received']; 14 | existingtables 15 | | join kind=rightanti newtables on Type -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-AccessPackageCreated.kql: -------------------------------------------------------------------------------- 1 | //Detect when an Azure AD Entitlement Package is created. You may want to review to see what resources and roles have been included in the package. 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where TimeGenerated > ago(1d) 7 | | where OperationName == "Create access package" 8 | | where TargetResources[0].type == "AccessPackage" 9 | | extend AccessPackageName = tostring(TargetResources[0].displayName) 10 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 11 | | project TimeGenerated, OperationName, AccessPackageName, Actor -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Data Management/Data-CalculatePercentageperTable.kql: -------------------------------------------------------------------------------- 1 | //Calculate the percentage that each table in your Sentinel workspace is contributing to total ingestion for billable data 2 | 3 | //Data connector required for this query - Usage (generated automatically on a log analytics workspace) 4 | 5 | Usage 6 | | where TimeGenerated > ago(30d) 7 | | where IsBillable == "true" 8 | | summarize ['Table size in GB']=sum(Quantity / 1024) by DataType 9 | | as T 10 | | extend Percentage = round(100.0 * ['Table size in GB'] / toscalar (T 11 | | summarize sum(['Table size in GB'])), 2) 12 | | project-reorder DataType, ['Table size in GB'], Percentage 13 | | sort by Percentage desc 14 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-CAPoliciesNotinUse.kql: -------------------------------------------------------------------------------- 1 | //Find any CA policies that are not actively in use (no success or failure events) 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | //Microsoft Sentinel query 6 | SigninLogs 7 | | where TimeGenerated > ago(180d) 8 | | where UserType == "Member" 9 | | mv-expand todynamic(ConditionalAccessPolicies) 10 | | extend CAResult=tostring(ConditionalAccessPolicies.result), CAName=tostring(ConditionalAccessPolicies.displayName) 11 | | summarize TotalCount=count(),ResultSet=make_set(CAResult) by CAName 12 | | where not(ResultSet has_any ("success","failure")) 13 | | sort by CAName asc 14 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-FindDevicesNoLongerSendingEvents.kql: -------------------------------------------------------------------------------- 1 | //Find devices that have stopped sending network events over the last 30 days, retrieve last event time and calculate the days since last event 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | DeviceNetworkEvents 6 | | project TimeGenerated, DeviceName 7 | | where TimeGenerated > ago(365d) 8 | | summarize arg_max(TimeGenerated, DeviceName) by DeviceName 9 | | project DeviceName, ['Days Since Last Event'] = datetime_diff('day', now(), TimeGenerated), ['Last Event Time']=TimeGenerated 10 | | where ['Days Since Last Event'] > 30 11 | | sort by ['Days Since Last Event'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/EmailEvents-VisualizeBlockedEmailPercentage.kql: -------------------------------------------------------------------------------- 1 | //Visualize how much is email is being blocked as a percentage of total email over time 2 | 3 | //Data connector required for this query - M365 Defender - Email* tables 4 | 5 | EmailEvents 6 | | where TimeGenerated > ago (30d) 7 | | where EmailDirection == "Inbound" 8 | | summarize 9 | TotalCount=count(), 10 | BlockedCount=countif(DeliveryAction in ("Blocked", "Junked")) 11 | by bin(TimeGenerated, 6h) 12 | | extend Percentage=(todouble(BlockedCount) * 100 / todouble(TotalCount)) 13 | | project-away TotalCount, BlockedCount 14 | | render timechart with (title="Percentage of email blocked over time", ymax=100) -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Security Alert/SecurityAlert-DeviceAlertwithLateralMovement.kql: -------------------------------------------------------------------------------- 1 | //Detect when a device triggers a Defender for Endpoint alert where Defender for Identity has also detected a lateral movement path 2 | 3 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 4 | 5 | SecurityAlert 6 | | where ProviderName == "MDATP" 7 | | project TimeGenerated, AlertName, CompromisedEntity 8 | | join kind=inner ( 9 | IdentityDirectoryEvents 10 | | where ActionType == "Potential lateral movement path identified") 11 | on $left.CompromisedEntity == $right.DeviceName 12 | | distinct DeviceName, AlertName, AccountName, ReportId -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Active Directory/SecurityEvent-AccountPreAuthChanges.kql: -------------------------------------------------------------------------------- 1 | //Detect when Kerberos preauthentication is enabled or disabled for a user 2 | 3 | //Data connector required for this query - Windows Security Events via AMA or Security Events via Legacy Agent 4 | 5 | SecurityEvent 6 | | where EventID == 4738 7 | | where AccountType == "User" 8 | | where UserAccountControl has_any ("2064", "2096") 9 | | extend Action = case(UserAccountControl has "2096", strcat("Kerberos preauthentication disabled"), 10 | UserAccountControl has "2064", strcat("Kerberos preauthentication enabled"), 11 | "unknown") 12 | | project TimeGenerated, Actor=SubjectAccount, User=TargetAccount, Action 13 | 14 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-ServicePrincipalCreatedbyManagedIdentity.kql: -------------------------------------------------------------------------------- 1 | //Detect when an Azure AD service principal is created for a managed identity 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where OperationName == "Add service principal" 7 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.app)).displayName) 8 | | extend ['Service Principal DisplayName'] = tostring(TargetResources[0].displayName) 9 | | extend ['Service Principal Id'] = tostring(TargetResources[0].id) 10 | | where Actor == "Managed Service Identity" 11 | | project TimeGenerated, ['Service Principal DisplayName'], ['Service Principal Id'] -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Sentinel Incidents/SecurityIncident-VisualizeMitreAtt&ck.kql: -------------------------------------------------------------------------------- 1 | //Visualize the incidents generated in Microsoft Sentinel by MITRE ATT&CK tactics 2 | 3 | //Data connector required for this query - Microsoft Sentinel Incidents (generated automatically if you create incidents in Sentinel) 4 | 5 | SecurityIncident 6 | | where TimeGenerated > ago(30d) 7 | | summarize arg_min(TimeGenerated, *) by IncidentNumber 8 | | extend Tactics = tostring(AdditionalData.tactics) 9 | | where Tactics != "[]" 10 | | mv-expand todynamic(Tactics) 11 | | summarize Count=count()by tostring(Tactics) 12 | | sort by Count 13 | | render barchart with (title="Microsoft Sentinel incidents by MITRE ATT&CK tactic") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-ExchangeScopingPolicyApplied.kql: -------------------------------------------------------------------------------- 1 | //Detect when a new scoping policy is applied, scoping policies are used to limit permissions to Exchange mailboxes being accessed via OAuth. They should be configured with least privilege 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | OfficeActivity 6 | | where Operation == "New-ApplicationAccessPolicy" 7 | | extend GroupPolicyAppliedTo = tostring(parse_json(Parameters)[1].Value) 8 | | extend AppId = tostring(parse_json(Parameters)[0].Value) 9 | | extend AccessRight = tostring(parse_json(Parameters)[2].Value) 10 | | project TimeGenerated, Actor=UserId, Operation, AccessRight, GroupPolicyAppliedTo, AppId -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-SummarizeGuestsAddedtoTeams.kql: -------------------------------------------------------------------------------- 1 | //Find any of your Teams that have had guests added to them in the last week and arrange by the Teams with the most guests added. 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | OfficeActivity 6 | | where TimeGenerated > ago(7d) 7 | | where Operation == "MemberAdded" 8 | | mv-expand Members 9 | | extend UserAdded = tostring(Members.UPN) 10 | | where UserAdded contains "#EXT#" 11 | | where CommunicationType == "Team" 12 | | summarize 13 | ['Number of Guests Added']=dcount(UserAdded), 14 | ['List of Guests Added']=make_set(UserAdded) 15 | by TeamName 16 | | sort by ['Number of Guests Added'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-VisualizeGuestRedemptionswithTrend.kql: -------------------------------------------------------------------------------- 1 | //Visualize how many guests are redeeming invites over the time period with trend 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | let StartDate = now(-180d); 6 | let EndDate = now(); 7 | AuditLogs 8 | | where OperationName == "Redeem external user invite" 9 | | make-series TotalInvites=count() on TimeGenerated in range(StartDate, EndDate, 1d) 10 | | extend (RSquare, SplitIdx, Variance, RVariance, TrendLine)=series_fit_2lines(TotalInvites) 11 | | project TimeGenerated, TotalInvites, TrendLine 12 | | render timechart with (title="Guest Invites redeemed over time with trend") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Sentinel Incidents/SecurityIncident-DaysSinceLastIncident.kql: -------------------------------------------------------------------------------- 1 | //Calculate how many days since each analytic rule last triggered, useful to determine if rules are still valid 2 | 3 | //Data connector required for this query - Microsoft Sentinel Incidents (generated automatically if you create incidents in Sentinel) 4 | 5 | SecurityIncident 6 | | where TimeGenerated > ago(180d) 7 | | where Status == "New" and ModifiedBy == "Incident created from alert" 8 | | summarize arg_max(TimeGenerated, *) by Title 9 | | extend ['Days Since Last Incident'] = datetime_diff("day", now(), TimeGenerated) 10 | | project Title, ['Days Since Last Incident'] 11 | | sort by ['Days Since Last Incident'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Intune/IntuneDevices-VisualizeDeviceComplianceovertime.kql: -------------------------------------------------------------------------------- 1 | //Visualize device compliance (compliant, non-compliant, managed by Config Manager, not evaluated or in grace period) per week over time 2 | 3 | //Data connector required for this query - Intune data sent to Sentinel workspace 4 | 5 | IntuneDevices 6 | | where TimeGenerated > ago (180d) 7 | | summarize arg_max(DeviceName, *) by DeviceName, startofweek(TimeGenerated) 8 | | where isnotempty(CompliantState) 9 | | summarize ComplianceCount=count()by CompliantState, startofweek(TimeGenerated) 10 | | render timechart 11 | with ( 12 | ytitle="Device Count", 13 | xtitle="Week", 14 | title="Device compliance per week over time") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-SummarizeTeamsAppInstalls.kql: -------------------------------------------------------------------------------- 1 | //Summarize the applications installed into Teams in the last month. Apps are grouped into the scope they were installed to - Team, Chat or User and by name and application id 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | OfficeActivity 6 | | where TimeGenerated > ago (30d) 7 | | where Operation == "AppInstalled" 8 | | summarize 9 | ['App Installed to Team Scope']=countif(OperationScope == "Team"), 10 | ['App Installed to Chat Scope']=countif(OperationScope == "Chat"), 11 | ['App Installed to User Scope']=countif(OperationScope == "User") 12 | by AddonName, AzureADAppId 13 | | sort by AddonName asc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Security Alert/SecurityAlert-SuspectedGoldenTicket.kql: -------------------------------------------------------------------------------- 1 | //When Defender for Identity detects suspected golden ticket usage, parse the relevant user accounts and host names 2 | 3 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 4 | 5 | SecurityAlert 6 | | where AlertName startswith "Suspected Golden Ticket usage" 7 | | mv-expand todynamic(Entities) 8 | | extend AccountName = tostring(Entities.Name) 9 | | extend HostName = tostring(Entities.HostName) 10 | | summarize 11 | Accounts=make_list_if(AccountName, isnotempty(AccountName)), 12 | Hosts=make_list_if(HostName, isnotempty(HostName)) 13 | by VendorOriginalId 14 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-VisualizeExternalAADGuestsvsExternalGuests.kql: -------------------------------------------------------------------------------- 1 | //Visualize signins from External Azure AD guests (those that belong to another Azure AD tenant) vs External Guests (such as Gmail) to your tenant 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago (45d) 7 | | where UserType == "Guest" 8 | | summarize 9 | ['External Guests']=countif(ResourceTenantId == HomeTenantId), 10 | ['External Azure AD Guests']=countif(ResourceTenantId != HomeTenantId) 11 | by bin(TimeGenerated, 1d) 12 | | render timechart with (title="External Azure AD Guests vs External Guests", ytitle="Count") 13 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Activity/AzureLogAnalytics-DetectwhenWorkspaceKeysareRead.kql: -------------------------------------------------------------------------------- 1 | //Detect when the workspace keys to an Azure log analytics workspace are read 2 | 3 | //Data connector required for this query - Azure Activity 4 | 5 | AzureActivity 6 | | where OperationNameValue == "MICROSOFT.OPERATIONALINSIGHTS/WORKSPACES/SHAREDKEYS/ACTION" 7 | | extend WorkspaceName = tostring(parse_json(Properties).resource) 8 | | where ActivityStatusValue == "Success" 9 | | project 10 | TimeGenerated, 11 | Actor=Caller, 12 | ['Log Analytics Workspace Name']=WorkspaceName, 13 | ['Actor IP Address']=CallerIpAddress, 14 | ['Azure Subscription Id']=SubscriptionId, 15 | ['Azure Resource Group']=ResourceGroup -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-NewASREvents.kql: -------------------------------------------------------------------------------- 1 | //Retrieve any new ASR alerts in your environment over the last week not previously seen in the prior 90 days and which devices have triggered them 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | let timerange=90d; 6 | let existingalerts= 7 | DeviceEvents 8 | | where TimeGenerated > ago (timerange) and TimeGenerated < ago(7d) 9 | | where ActionType startswith "Asr" 10 | | distinct ActionType; 11 | DeviceEvents 12 | | where TimeGenerated > ago(7d) 13 | | where ActionType startswith "Asr" 14 | | where ActionType !in (existingalerts) 15 | | summarize ['Device List']=make_set(DeviceName) by ActionType -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-NewTeamsAppInstalled.kql: -------------------------------------------------------------------------------- 1 | //Detect when an app is installed into Teams for the first time compared to the previous timerange 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | let knownapps= 6 | OfficeActivity 7 | | where TimeGenerated > ago(180d) and TimeGenerated < ago(7d) 8 | | where OfficeWorkload == "MicrosoftTeams" 9 | | where Operation == "AppInstalled" 10 | | distinct AzureADAppId; 11 | OfficeActivity 12 | | where TimeGenerated > ago (7d) 13 | | where OfficeWorkload == "MicrosoftTeams" 14 | | where Operation == "AppInstalled" 15 | | where AzureADAppId !in (knownapps) 16 | | project TimeGenerated, UserId, AddonName, AzureADAppId -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-DetectActivePIMAssignment.kql: -------------------------------------------------------------------------------- 1 | //Alert when a user is assigned to a permanent active Azure AD role 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where OperationName in ("Add member to role in PIM completed (permanent)","Add member to role in PIM completed (timebound)") 7 | | where TargetResources[2].type == "User" 8 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 9 | | extend User = tostring(TargetResources[2].userPrincipalName) 10 | | extend ['Azure AD Role Name'] = tostring(TargetResources[0].displayName) 11 | | project TimeGenerated, Actor, User, ['Azure AD Role Name'] 12 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-ServicePrincipalSigninfromnewIP.kql: -------------------------------------------------------------------------------- 1 | //Detect when an Azure AD service principal signs in from an IP previously not seen 2 | 3 | //Data connector required for this query - Azure Active Directory - Service Principal Signin Logs 4 | 5 | AADServicePrincipalSignInLogs 6 | | where TimeGenerated > ago(180d) and TimeGenerated < ago(7d) 7 | | distinct AppId, IPAddress 8 | | join kind=rightanti 9 | ( 10 | AADServicePrincipalSignInLogs 11 | | where TimeGenerated > ago(7d) 12 | | project TimeGenerated, AppId, IPAddress, ResultType, ServicePrincipalName 13 | ) 14 | on IPAddress 15 | | where ResultType == "0" 16 | | distinct ServicePrincipalName, AppId, IPAddress -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-FindMultipleCASuccesses.kql: -------------------------------------------------------------------------------- 1 | //Find sign ins that have triggered multiple unique conditional access policy successes - maybe a chance to rationalize policy 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago (30d) 7 | | mv-apply ca=todynamic(ConditionalAccessPolicies) on ( 8 | where ca.result == "success" 9 | | extend PolicyName = tostring(ca.displayName) 10 | ) 11 | | summarize 12 | ['Count of Poicies Applied']=dcount(PolicyName), 13 | ['List of Policies Applied']=make_set(PolicyName) 14 | by CorrelationId, UserPrincipalName 15 | | where ['Count of Poicies Applied'] >= 2 -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-SummarizeMFAFailures.kql: -------------------------------------------------------------------------------- 1 | //Summarize the count of the various types of MFA failures (such as user not responding, invalid codes, user declining the authentication) 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago (90d) 7 | | where ResultType == "500121" 8 | | mv-expand todynamic(AuthenticationDetails) 9 | | project AuthenticationDetails, ResultType 10 | | extend ['MFA Failure Type'] = tostring(parse_json(AuthenticationDetails).authenticationStepResultDetail) 11 | | where ['MFA Failure Type'] startswith "MFA denied" 12 | | summarize Count=count()by ['MFA Failure Type'] 13 | | sort by Count 14 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-DetectInboundPublicRDP.kql: -------------------------------------------------------------------------------- 1 | //Detects inbound RDP network connections from public IP addresses 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables or Advanced Hunting license 4 | 5 | //Works in both Microsoft Sentinel and Advanced Hunting 6 | DeviceNetworkEvents 7 | | where ActionType == "InboundConnectionAccepted" 8 | | where LocalIPType == "Private" and RemoteIPType == "Public" 9 | | where LocalPort == 3389 or InitiatingProcessCommandLine =~ "svchost.exe -k termsvcs -s TermService" 10 | | project 11 | TimeGenerated, 12 | DeviceName, 13 | DeviceId, 14 | LocalIP, 15 | LocalPort, 16 | RemoteIP, 17 | InitiatingProcessCommandLine -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Active Directory/SecurityEvent-DailySummaryofGroupAdditions.kql: -------------------------------------------------------------------------------- 1 | //Create a daily report of users being added to on premise Active Directory groups, summarized by group name 2 | 3 | //Data connector required for this query - Windows Security Events via AMA or Security Events via Legacy Agent 4 | 5 | SecurityEvent 6 | | where TimeGenerated > ago (7d) 7 | | where AccountType == "User" 8 | | where EventID in (4728, 4732, 4756, 4761, 4746, 4751) 9 | | project TimeGenerated, MemberName, ['Group Name']=TargetUserName, EventID 10 | | parse MemberName with * 'CN=' UserAdded ',' * 11 | | summarize UsersAdded=make_set(UserAdded) by ['Group Name'], startofday(TimeGenerated) 12 | | sort by ['Group Name'] asc, TimeGenerated desc 13 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-NewTenantCreated.kql: -------------------------------------------------------------------------------- 1 | //Find when a new Azure AD tenant is created by a user in your tenant 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where OperationName == "Create Company" 7 | | where Result == "success" 8 | | extend Type = tostring(TargetResources[0].type) 9 | | where Type == "Directory" 10 | | extend ['Actor IP Address'] = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress) 11 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 12 | | extend ['New Tenant Id'] = tostring(TargetResources[0].id) 13 | | project TimeGenerated, OperationName, Actor, ['Actor IP Address'], ['New Tenant Id'] -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-VisualizeMFAMethods.kql: -------------------------------------------------------------------------------- 1 | //Visualize the MFA types used by your users, i.e text message, mobile app notification, verification code 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago (30d) 7 | | where AuthenticationRequirement == "multiFactorAuthentication" 8 | | project AuthenticationDetails 9 | | extend ['MFA Method'] = tostring(parse_json(AuthenticationDetails)[1].authenticationMethod) 10 | | summarize Count=count()by ['MFA Method'] 11 | | where ['MFA Method'] != "Previously satisfied" and isnotempty(['MFA Method']) 12 | | sort by Count desc 13 | | render barchart with (title="Types of MFA Methods used") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-DetectCredentialAddedtoApp.kql: -------------------------------------------------------------------------------- 1 | //Detect when a new credential is added to an Azure AD application registration 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where OperationName has "Update application – Certificates and secrets management" 7 | | extend ApplicationName = tostring(TargetResources[0].displayName) 8 | | extend ApplicationObjectId = tostring(TargetResources[0].id) 9 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 10 | | extend ActorIPAddress = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress) 11 | | project TimeGenerated, ApplicationName, ApplicationObjectId, Actor, ActorIPAddress -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Sentinel Incidents/SecurityIncident-VisualizeIncidentswithTrend.kql: -------------------------------------------------------------------------------- 1 | //Create a visualization showing the total Sentinel incidents and the trend of incidents over time 2 | 3 | //Data connector required for this query - Microsoft Sentinel Incidents (generated automatically if you create incidents in Sentinel) 4 | 5 | SecurityIncident 6 | | summarize arg_min(TimeGenerated, *) by IncidentNumber 7 | | make-series TotalIncidents=count() default=0 on TimeGenerated in range(ago(90d), now(), 1d) 8 | | extend (RSquare, SplitIdx, Variance, RVariance, TrendLine)=series_fit_2lines(TotalIncidents) 9 | | project TimeGenerated, TotalIncidents, TrendLine 10 | | render timechart with (title="Microsoft Sentinel incidents over time with trend") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-DetectSecurityLogCleared.kql: -------------------------------------------------------------------------------- 1 | //Detect when a user clears the security event log on one of your devices 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | DeviceEvents 6 | | where ActionType == "SecurityLogCleared" 7 | | project 8 | TimeGenerated, 9 | DeviceName, 10 | InitiatingProcessAccountDomain, 11 | InitiatingProcessAccountName 12 | 13 | //Advanced Hunting query 14 | 15 | //Data connector required for this query - Advanced Hunting license 16 | 17 | DeviceEvents 18 | | where ActionType == "SecurityLogCleared" 19 | | project 20 | Timestamp, 21 | DeviceName, 22 | InitiatingProcessAccountDomain, 23 | InitiatingProcessAccountName -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Security Alert/SecurityAlert-PercentageofAlertsHighorCritical.kql: -------------------------------------------------------------------------------- 1 | //Calculate the percentage of alerts that are high or critical per product 2 | 3 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 4 | 5 | SecurityAlert 6 | | where TimeGenerated > ago(30d) 7 | | summarize 8 | ['Total Alert Count']=count(), 9 | ['Total High or Critical Count']=countif(AlertSeverity in ("Critical", "High")) 10 | by ProductName 11 | | extend Percentage=(todouble(['Total High or Critical Count']) * 100 / todouble(['Total Alert Count'])) 12 | | project-reorder ProductName, ['Total Alert Count'], ['Total High or Critical Count'], Percentage 13 | | sort by Percentage desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-SummarizeWeeklyPIM.kql: -------------------------------------------------------------------------------- 1 | //Create a summary of PIM activations for all your users per week 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where TimeGenerated > ago (30d) 7 | | where OperationName == "Add member to role completed (PIM activation)" 8 | | extend User = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 9 | | extend Role = tostring(TargetResources[0].displayName) 10 | | where isnotempty(User) 11 | | summarize 12 | ['Roles Activated']=make_list(Role), 13 | ['Times Activated']=make_list(TimeGenerated) 14 | by User, ['Week Starting']=startofweek(TimeGenerated) 15 | | sort by User asc, ['Week Starting'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-SummarizeTeamsCreatedDeleted.kql: -------------------------------------------------------------------------------- 1 | //Create a weekly summary of Teams created and deleted in your Office 365 tenant 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | OfficeActivity 6 | | where TimeGenerated > ago(30d) 7 | | where Operation in ("TeamCreated", "TeamDeleted") 8 | | summarize 9 | ['Count of Teams Created']=dcountif(TeamName, Operation == "TeamCreated"), 10 | ['List of Teams Created']=make_set_if(TeamName, Operation == "TeamCreated"), 11 | ['Count of Teams Deleted']=dcountif(TeamName, Operation == "TeamDeleted"), 12 | ['List of Teams Deleted']=make_set_if(TeamName, Operation == "TeamDeleted") 13 | by Week=startofweek(TimeGenerated) 14 | | sort by Week desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Security Alert/SecurityAlert-MultipleAlertsTriggered.kql: -------------------------------------------------------------------------------- 1 | //Detect when a user or device triggers 3 or more unique alerts within a short time frame. This example uses a period of 4 hours 2 | 3 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 4 | 5 | SecurityAlert 6 | | where TimeGenerated > ago(1d) 7 | | where isnotempty(CompromisedEntity) and CompromisedEntity != "CompromisedEntity" 8 | | project TimeGenerated, ProviderName, AlertName, CompromisedEntity 9 | | summarize 10 | ['Alert Names']=make_set(AlertName), 11 | ['Count of Unique Alerts']=dcount(AlertName) 12 | by CompromisedEntity, bin(TimeGenerated, 4h) 13 | | where ['Count of Unique Alerts'] >= 3 -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-VisualizeOSBuildspermonth.kql: -------------------------------------------------------------------------------- 1 | //Visualize the OS build numbers of your Windows 10 and 11 devices per month over the last year 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | DeviceInfo 6 | | where TimeGenerated > ago(365d) 7 | | where OSPlatform in ("Windows10", "Windows11") 8 | | extend OSBuildNumber=tostring(OSBuild) 9 | | summarize arg_max(TimeGenerated, *) by DeviceName, startofmonth(TimeGenerated) 10 | | summarize count()by OSBuildNumber, startofmonth(TimeGenerated) 11 | | where isnotempty(OSBuildNumber) 12 | | render areachart 13 | with ( 14 | ytitle="Device Count", 15 | xtitle="Month", 16 | title="Count of Windows 10 and 11 OS Builds per month") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-TeamsRoleChanges.kql: -------------------------------------------------------------------------------- 1 | //Detect when the role for a user changes to owner or back to standard member in your any of your Teams 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | OfficeActivity 6 | | where Operation == "MemberRoleChanged" 7 | | mv-expand Members 8 | | extend User = tostring(Members.UPN) 9 | | extend x = tostring(Members.Role) 10 | | extend Action = case(x == "1", strcat("User changed to member"), 11 | x == "2", strcat("User changed to owner"), "unknown") 12 | | where Action in ("User changed to member", "User changed to owner") 13 | | project 14 | TimeGenerated, 15 | TeamName, 16 | ActorType=UserType, 17 | Actor=UserId, 18 | UserAdded=User, 19 | Action -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-VisualizeKnownvsUnknownLocation.kql: -------------------------------------------------------------------------------- 1 | //Visualize sign in attempts to your Azure AD tenant by known or unknown network locations 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago(30d) 7 | //If a location it will be populated by its name, i.e 'Corporate Internet', if known the NetworkLocationDetails array is empty so we can find it by searching on '[]' 8 | | summarize 9 | ['Known Location']=countif(NetworkLocationDetails != '[]'), 10 | ['Unknown Location']=countif(NetworkLocationDetails == '[]') 11 | by bin (TimeGenerated, 1d) 12 | | render timechart with (title="Known vs Unknown signins to Azure AD", ytitle="Count") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Security Alert/SecurityAlert-FindMostPhishedUsers.kql: -------------------------------------------------------------------------------- 1 | //Find the most phished users from the last 365 days 2 | 3 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 4 | 5 | SecurityAlert 6 | | where TimeGenerated > ago (365d) 7 | | where ProviderName == "OATP" 8 | | where AlertName in ("Email messages containing malicious URL removed after delivery", "Email messages containing phish URLs removed after delivery") 9 | | mv-expand todynamic(Entities) 10 | | project Entities 11 | | extend User = tostring(Entities.MailboxPrimaryAddress) 12 | | where isnotempty(User) 13 | | summarize ['Count of Phishing Attempts']=count()by User 14 | | order by ['Count of Phishing Attempts'] 15 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-SummarizeAccountInactivity.kql: -------------------------------------------------------------------------------- 1 | //Summarize accounts (both members and guests) that haven't signed on for over 60 days, and summarize them into the month they last signed in 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago (365d) 7 | | where ResultType == 0 8 | | where isnotempty(UserType) 9 | | summarize arg_max(TimeGenerated, *) by UserPrincipalName 10 | | where TimeGenerated < ago(60d) 11 | | summarize 12 | ['Inactive Account List']=make_set(UserPrincipalName), 13 | ['Count of Inactive Accounts']=dcount(UserPrincipalName) 14 | by UserType, Month=startofmonth(TimeGenerated) 15 | | sort by Month desc, UserType asc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-FindNewEvents.kql: -------------------------------------------------------------------------------- 1 | //Find any new DeviceEvents found in your environment over the last week compared to the last 6 months 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | //First find existing action types over the six months 6 | let knownevents= 7 | DeviceEvents 8 | | where TimeGenerated > ago (180d) and TimeGenerated < ago(7d) 9 | | distinct ActionType; 10 | //Find new action types in the last week, the time they were first seen and how many counts seen this week 11 | DeviceEvents 12 | | where TimeGenerated > ago(7d) 13 | | where ActionType !in (knownevents) 14 | | summarize ['First Time Seen']=min(TimeGenerated), Count=count() by ActionType 15 | | sort by Count desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-GroupAddedtoPIM.kql: -------------------------------------------------------------------------------- 1 | //Find when an Azure AD group is assigned (either permanent or eligble) to an Azure AD PIM assignment 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where OperationName in ("Add eligible member to role in PIM completed (permanent)", "Add member to role in PIM completed (permanent)") 7 | | where TargetResources[2].type == "Group" 8 | | extend GroupName = tostring(TargetResources[2].displayName) 9 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 10 | | extend ['Azure AD Role Name'] = tostring(TargetResources[0].displayName) 11 | | project TimeGenerated, OperationName, Actor, GroupName, ['Azure AD Role Name'] -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Bastion/Bastion-SummarizeAccountAccess.kql: -------------------------------------------------------------------------------- 1 | //Summarize your Bastion usage by which users are connecting to which devices via which protocl (RDP or SSH) 2 | 3 | //Data connector required for this query - Azure Bastion 4 | 5 | MicrosoftAzureBastionAuditLogs 6 | | where TimeGenerated > ago (30d) 7 | | where Message == "Successfully Connected." 8 | | summarize 9 | ['Count of RDP Devices']=dcountif(TargetVMIPAddress, Protocol == "rdp"), 10 | ['List of RDP Devices']=make_set_if(TargetVMIPAddress, Protocol == "rdp"), 11 | ['Count of SSH Devices']=dcountif(TargetVMIPAddress, Protocol == "ssh"), 12 | ['List of SSH Devices']=make_set_if(TargetVMIPAddress, Protocol == "ssh") 13 | by UserName 14 | | sort by ['Count of RDP Devices'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-FindNewDevices.kql: -------------------------------------------------------------------------------- 1 | //Find new devices onboarded to Defender in the last month 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | let knowndevices= 6 | DeviceNetworkInfo 7 | | where TimeGenerated > ago (180d) and TimeGenerated < ago(30d) 8 | | distinct DeviceName; 9 | DeviceNetworkInfo 10 | | where TimeGenerated > ago (30d) 11 | | summarize arg_min(TimeGenerated, *) by DeviceName 12 | | where DeviceName !in (knowndevices) 13 | | project TimeGenerated, DeviceName 14 | | extend ['Days Since First Seen']=datetime_diff("day", now(), TimeGenerated) 15 | | project ['Time First Seen']=TimeGenerated, ['Days Since First Seen'], DeviceName 16 | | sort by ['Days Since First Seen'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-UsersAddedtoDynamicGroups.kql: -------------------------------------------------------------------------------- 1 | //Summarize all groups that have had users added to them via dynamic rules 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where TimeGenerated > ago(1d) 7 | | where OperationName == "Add member to group" 8 | | where Identity == "Microsoft Approval Management" 9 | | where TargetResources[0].type == "User" 10 | | extend GroupName = tostring(parse_json(tostring(parse_json(tostring(TargetResources[0].modifiedProperties))[1].newValue))) 11 | | extend User = tostring(TargetResources[0].userPrincipalName) 12 | | summarize ['Count of Users Added']=dcount(User), ['List of Users Added']=make_set(User) by GroupName 13 | | sort by GroupName asc 14 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-VisualizeGuestAppAccess.kql: -------------------------------------------------------------------------------- 1 | //Visualize the most popular enterprise applications accessed by guest accounts 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | let timeframe=45d; 6 | SigninLogs 7 | | where TimeGenerated > ago(timeframe) 8 | | where UserType == "Guest" or UserPrincipalName contains "#ext#" 9 | | where ResultType == 0 10 | // Exclude Microsoft management applications 11 | | where AppDisplayName !in ("Microsoft Invitation Acceptance Portal", "My Apps", "Microsoft App Access Panel", "Microsoft Authentication Broker") 12 | | summarize AppCount=count()by AppDisplayName 13 | | sort by AppCount desc 14 | | render piechart with (title="Most Popular Apps Accessed by Guests") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-VisualizeInboundvsOutboundGuests.kql: -------------------------------------------------------------------------------- 1 | //Visualize inbound Azure AD guests (other tenants connecting to your tenant) vs outbound (your users connecting to other tenants) 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago (30d) 7 | | where UserType == "Guest" 8 | | where ResultType == 0 9 | | summarize 10 | ['Inbound Guests']=countif(AADTenantId != HomeTenantId and HomeTenantId != ResourceTenantId), 11 | ['Outbound Guests']=countif(AADTenantId == HomeTenantId and ResourceTenantId != AADTenantId) 12 | by bin(TimeGenerated, 1d) 13 | | render timechart with (title="Inbound Guest vs Outbound Guest Access", ytitle="Number of connections") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Key Vault/KeyVault-PotentiallySensitiveOperations.kql: -------------------------------------------------------------------------------- 1 | // Detects Key Vault operations that could be malicious 2 | 3 | //Data connector required for this query - Azure Key Vault 4 | 5 | let operationlist = dynamic( 6 | ["VaultDelete", "KeyDelete", "SecretDelete", "SecretPurge", "KeyPurge", "SecretBackup", "KeyBackup", "SecretListDeleted", "CertificateCreate", "CertificatePurge"]); 7 | AzureDiagnostics 8 | | where ResourceType == "VAULTS" and ResultType == "Success" 9 | | where OperationName in (operationlist) 10 | | project TimeGenerated, 11 | ResourceGroup, 12 | SubscriptionId, 13 | KeyVaultName=Resource, 14 | KeyVaultTarget=id_s, 15 | Actor=identity_claim_upn_s, 16 | IPAddressofActor=CallerIPAddress, 17 | OperationName -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-DetectInternaltoExternalTeamviewer.kql: -------------------------------------------------------------------------------- 1 | //Detects successful TeamViewer connections from internal to external IP addresses 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables or Advanced Hunting license 4 | 5 | //If TeamViewer is a sanctioned app then you will get lots of hits. 6 | //Works in both Microsoft Sentinel and Advanced Hunting 7 | DeviceNetworkEvents 8 | | where InitiatingProcessFileName contains "teamviewer.exe" 9 | | where ActionType == "ConnectionSuccess" 10 | | where LocalIPType == "Private" 11 | | where RemoteIPType == "Public" 12 | | project TimeGenerated, 13 | DeviceName, 14 | InitiatingProcessAccountName, 15 | InitiatingProcessFileName, 16 | LocalIP, 17 | RemoteIP -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Security Alert/SecurityAlert-VisualizeTotalAlertsvsUniqueAlerts.kql: -------------------------------------------------------------------------------- 1 | //Visualize your total alerts vs distinct entity alerts per week 2 | 3 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 4 | 5 | SecurityAlert 6 | | where TimeGenerated > ago(180d) 7 | //Exclude alerts generated by Microsoft Sentinel itself if you don't wish to double count them 8 | | where ProductName != "ASI Scheduled Alerts" 9 | | where Status == "New" 10 | | summarize 11 | ['Total Security Alerts']=count(), 12 | ['Unique Entity Alerts']=dcountif(CompromisedEntity, isnotempty(CompromisedEntity)) 13 | by bin(TimeGenerated, 7d) 14 | | render timechart with (title="Total Security Alerts vs Unique Entity Alerts") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Active Directory/SecurityEvent-AccountSensitivityChanged.kql: -------------------------------------------------------------------------------- 1 | //Detect when the 'account is sensitive and cannot be delegated' flag on an account is changed 2 | 3 | //Data connector required for this query - Windows Security Events via AMA or Security Events via Legacy Agent 4 | 5 | SecurityEvent 6 | | project TimeGenerated, EventID, TargetAccount, SubjectAccount, UserAccountControl 7 | | where EventID == "4738" 8 | | where UserAccountControl has_any("2094", "2062") 9 | | extend Activity = case 10 | (UserAccountControl contains "2094", strcat("Account Sensitivity Enabled"), 11 | UserAccountControl contains "2062", strcat("Account Sensitivity Disabled"), 12 | "Unknown") 13 | | project TimeGenerated, Target=TargetAccount, Actor=SubjectAccount, Activity -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/DNS/DNS-FindDevicesThatHaveQueriedSuspiciousDomains.kql: -------------------------------------------------------------------------------- 1 | //When a domain is flagged by Defender for Cloud (Azure Security Center) as suspicious then find any other clients that have queried that domain in DNS events 2 | 3 | //Data connector required for this query - DNS 4 | 5 | let suspiciousurl= 6 | SecurityAlert 7 | | where AlertName startswith "Communication with suspicious random domain name" 8 | | mv-expand todynamic(Entities) 9 | | project Entities 10 | | extend SuspiciousURL = tostring(Entities.DomainName) 11 | | where isnotempty(SuspiciousURL) 12 | | distinct SuspiciousURL; 13 | DnsEvents 14 | | where QueryType == "A" 15 | | project Name, ClientIP 16 | | where Name in (suspiciousurl) 17 | | summarize ['Client IPs']=make_set(ClientIP) by Name -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/Audit-DailySummaryofO365AdminActivity.kql: -------------------------------------------------------------------------------- 1 | //Create a daily summary of activities completed by your O365 admins 2 | 3 | //Data connector required for this query - Office 365 4 | //Data connector required for this query - Microsoft Sentinel UEBA 5 | 6 | let timerange=14d; 7 | IdentityInfo 8 | | where TimeGenerated > ago(21d) 9 | | summarize arg_max(TimeGenerated, *) by AccountUPN 10 | | where AssignedRoles has_any ("Global Administrator", "Exchange Administrator", "Teams Administrator", "SharePoint Administrator") 11 | | project UserId=AccountUPN 12 | | join kind=inner ( 13 | OfficeActivity 14 | | where TimeGenerated > ago(timerange) 15 | ) 16 | on UserId 17 | | summarize AdminActivities=make_list(Operation)by UserId, startofday(TimeGenerated) -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-VisualizeASREventswithtrend.kql: -------------------------------------------------------------------------------- 1 | //Visualize your total attack surface reduction rule events over time with trend 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | let StartDate = now(-90d); 6 | let EndDate = now(); 7 | DeviceEvents 8 | | where ActionType startswith "Asr" 9 | | make-series ['Total ASR Events']=count() on TimeGenerated in range(StartDate, EndDate, 1d) 10 | | extend (RSquare, SplitIdx, Variance, RVariance, TrendLine)=series_fit_2lines(['Total ASR Events']) 11 | | project TimeGenerated, ['Total ASR Events'], Trend=TrendLine 12 | | render timechart 13 | with ( 14 | xtitle="Day", 15 | ytitle="Count of ASR Events", 16 | title="Attack surface reduction events over time with trend") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Active Directory/SecurityEvent-SummarizeRDPActivity.kql: -------------------------------------------------------------------------------- 1 | //Creates a list of computers that your users have connected to via RDP and the total count of distinct computers each user has connected to 2 | 3 | //Data connector required for this query - Windows Security Events via AMA or Security Events via Legacy Agent 4 | 5 | SecurityEvent 6 | | where TimeGenerated > ago(7d) 7 | | where EventID == "4624" 8 | | where LogonType == 10 9 | //Extend new column that drops Account to lower case so users are correctly summarized, i.e User123 and user123 are combined 10 | | extend AccountName=tolower(Account) 11 | | summarize 12 | ['Count of Computers']=dcount(Computer), 13 | ['List of Computers']=make_set(Computer) 14 | by AccountName 15 | | sort by ['Count of Computers'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-GuestAddedtoMultipleTeams.kql: -------------------------------------------------------------------------------- 1 | //Detect when a guest is added to multiple Teams in a short time frame. 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | //Define a time period to check and the threshold of how many Teams to alert on. 6 | let timeframe=15m; 7 | let threshold=2; 8 | OfficeActivity 9 | | where TimeGenerated > ago(1d) 10 | | where Operation == "MemberAdded" 11 | | mv-expand Members 12 | | extend UserAdded = tostring(Members.UPN) 13 | | where UserAdded contains "#EXT#" 14 | | where CommunicationType == "Team" 15 | | summarize 16 | ['Number of Teams Guest Added To']=dcount(TeamName), ['Team Names']=make_set(TeamName) by UserAdded, bin(TimeGenerated, timeframe) 17 | | where ['Number of Teams Guest Added To'] >= threshold -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-GuestTypeParser.kql: -------------------------------------------------------------------------------- 1 | //Adds logic to your SigninLogs to determine whether guest authentications are inbound (guests accessing your tenant) or outbound (your users accessing other tenants) 2 | 3 | //Data connector required for this query - Azure Active Directory - Sign in Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago (1d) 7 | | where UserType == "Guest" 8 | | project TimeGenerated, UserPrincipalName, AppDisplayName, ResultType, IPAddress, HomeTenantId, ResourceTenantId, AADTenantId 9 | | extend ['Guest Type']=case(AADTenantId != HomeTenantId and HomeTenantId != ResourceTenantId, strcat("Inbound Guest"), 10 | AADTenantId == HomeTenantId and ResourceTenantId != AADTenantId, strcat("Outbound Guest"), 11 | "unknown") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-VisualizePasswordvsPasswordless.kql: -------------------------------------------------------------------------------- 1 | //Visualize password vs passwordless signins per day 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago (180d) 7 | | mv-expand todynamic(AuthenticationDetails) 8 | | project TimeGenerated, AuthenticationDetails 9 | | extend AuthMethod = tostring(AuthenticationDetails.authenticationMethod) 10 | | summarize 11 | Passwordless=countif(AuthMethod in ("Windows Hello for Business", "Passwordless phone sign-in", "FIDO2 security key", "X.509 Certificate")), 12 | Password=countif(AuthMethod == "Password") 13 | by bin(TimeGenerated, 1d) 14 | | render timechart with (title="Passwordless vs Password Authentication", ytitle="Count") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Identity/IdentityDirectoryEvents-EncryptionChange.kql: -------------------------------------------------------------------------------- 1 | //Detect when the encryption types on a device are changed and parse the previous and current encryption types. 2 | 3 | //Data connector required for this query - M365 Defender - Identity* tables or Advanced Hunting license 4 | 5 | //If you don't send Defender for Id logs to Sentinel you can use the query in M365 Advanced Hunting directly 6 | IdentityDirectoryEvents 7 | | where ActionType == "Account Supported Encryption Types changed" 8 | | parse AdditionalFields with * 'FROM AccountSupportedEncryptionTypes":"' PreviousEncryption '"' * 9 | | parse AdditionalFields with * 'TO AccountSupportedEncryptionTypes":"' CurrentEncryption '"' * 10 | | project TimeGenerated, TargetDeviceName, PreviousEncryption, CurrentEncryption -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-VisualizeFileShareTopGuestDomains.kql: -------------------------------------------------------------------------------- 1 | //Visualize the guest domains that have had the most files shares to them from your Office 365 tenant 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | OfficeActivity 6 | | where TimeGenerated > ago(30d) 7 | | where Operation in~ ("AddedToSecureLink", "SecureLinkCreated", "SecureLinkUpdated") 8 | | where TargetUserOrGroupType == "Guest" and TargetUserOrGroupName contains "#ext#" 9 | | extend ['Guest UserPrincipalName'] = tostring(split(TargetUserOrGroupName, "#")[0]) 10 | | extend ['Guest Domain'] = tostring(split(['Guest UserPrincipalName'], "_")[-1]) 11 | | summarize Count=count() by ['Guest Domain'] 12 | | top 20 by Count 13 | | render barchart with (title="Top guest domains with files shared to") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Security Alert/SecurityAlert-VisualizeAlertsbyMITRE.kql: -------------------------------------------------------------------------------- 1 | //Visualize your security alerts by MITRE ATT&CK tactic 2 | 3 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 4 | 5 | SecurityAlert 6 | | where TimeGenerated > ago(30d) 7 | //Exclude Sentinel generated alerts if you want to. This may stop you double counting alerts, i.e generated by Azure AD Identity Protection and then again in Sentinel. 8 | | where ProviderName != "ASI Scheduled Alerts" 9 | // 10 | | where isnotempty(Tactics) and Tactics != "Unknown" 11 | | summarize arg_max(TimeGenerated, *) by VendorOriginalId 12 | | summarize Count=count()by Tactics 13 | | sort by Count desc 14 | | render barchart with (title="Security alerts by MITRE ATT&CK tactic") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-GuestInvitesSentvsRedeemed.kql: -------------------------------------------------------------------------------- 1 | //Visualizes the total guest invites sent from your Azure AD tenant vs those redeemed. Data is summarized per week. 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | let timerange=180d; 6 | AuditLogs 7 | | where TimeGenerated > ago (timerange) 8 | | where OperationName in ("Redeem external user invite", "Invite external user") 9 | | summarize 10 | InvitesSent=countif(OperationName == "Invite external user"), 11 | InvitesRedeemed=countif(OperationName == "Redeem external user invite") 12 | by startofweek(TimeGenerated) 13 | | render columnchart 14 | with ( 15 | title="Guest Invites Sent v Guest Invites Redeemed", 16 | xtitle="Invites", 17 | kind=unstacked) -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-SummarizeOutboundGuestActivity.kql: -------------------------------------------------------------------------------- 1 | //Summarize outbound (your users connecting to other tenants) activity by listing the users and which applications they are accessing in each remote tenant 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago(30d) 7 | | where UserType == "Guest" 8 | | where AADTenantId == HomeTenantId 9 | | where ResourceTenantId != AADTenantId 10 | | summarize 11 | ['Count of Applications']=dcount(AppDisplayName), 12 | ['List of Applications']=make_set(AppDisplayName), 13 | ['Count of Users']=dcount(UserPrincipalName), 14 | ['List of Users']=make_set(UserPrincipalName) 15 | by ResourceTenantId 16 | | sort by ['Count of Users'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-VisualizeMFAMethodsovertime.kql: -------------------------------------------------------------------------------- 1 | //Visualize the MFA types used by your users - phone sign in, mobile passcode, push or text message, over time 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago (180d) 7 | | where AuthenticationRequirement == "multiFactorAuthentication" 8 | | project TimeGenerated, AuthenticationDetails 9 | | extend ['MFA Method'] = tostring(parse_json(AuthenticationDetails)[1].authenticationMethod) 10 | | summarize Count=count()by ['MFA Method'], bin(TimeGenerated, 7d) 11 | | where ['MFA Method'] != "Previously satisfied" and isnotempty(['MFA Method']) 12 | | render timechart with (ytitle="Count", xtitle="Day", title="MFA methods per week over time") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-CreateSetofLocalAdminsperDevice.kql: -------------------------------------------------------------------------------- 1 | // Searches device info table for non server operating systems then return any users who have logged on interactively as an admin as a set per device. Can add exclusions for known IT admin accounts 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | let devices= 6 | DeviceInfo 7 | | where TimeGenerated > ago(30d) 8 | | where OSPlatform !contains "Server" 9 | | summarize arg_max(TimeGenerated, *) by DeviceName 10 | | project DeviceName; 11 | DeviceLogonEvents 12 | | where LogonType == "Interactive" 13 | | where IsLocalAdmin == true 14 | | join kind=inner devices on DeviceName 15 | | where AccountName !contains "admin" 16 | | summarize make_set(AccountName) by DeviceName -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-SummarizeAppUsageMonthonMonth.kql: -------------------------------------------------------------------------------- 1 | //Summarize the usage of all your applications from last month to this month. Usage is calculated by distinct users per application. 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago(60d) 7 | | where ResultType == "0" 8 | | summarize 9 | ['Last Month Signins']=dcountif(UserPrincipalName, TimeGenerated > ago(60d) and TimeGenerated < ago(30d)), 10 | ['This Month Signins']=dcountif(UserPrincipalName, TimeGenerated > ago(30d)) 11 | by AppId, AppDisplayName 12 | | extend ['Percentage Change']=(todouble(['This Month Signins'] - todouble(['Last Month Signins'])) / todouble(['Last Month Signins']) * 100) 13 | | sort by AppDisplayName asc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-VisualizeWorldMap.kql: -------------------------------------------------------------------------------- 1 | //Visualize your sign ins over a world map 2 | //This visualization is supported in the Kusto Explorer app or the Web UI 3 | //You can install the app here - https://learn.microsoft.com/en-us/azure/data-explorer/kusto/tools/kusto-explorer 4 | 5 | //Data connector required for this query - Azure Active Directory - Signin Logs 6 | 7 | SigninLogs 8 | | where TimeGenerated > ago (90d) 9 | | extend BeginLat = toreal(parse_json(tostring(LocationDetails.geoCoordinates)).latitude) 10 | | extend BeginLon = toreal(parse_json(tostring(LocationDetails.geoCoordinates)).longitude) 11 | | summarize Count=count() by BeginLon, BeginLat 12 | | project-reorder BeginLon, BeginLat, Count 13 | | where isnotempty(BeginLon) 14 | | render scatterchart with (kind=map) -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Intune/IntuneDevices-FindDetailsofNonCompliantDevices.kql: -------------------------------------------------------------------------------- 1 | //When Azure AD flags a device as non compliant, retrieve the details about the devices from Intune 2 | 3 | //Data connector required for this query - Intune data sent to Sentinel workspace 4 | 5 | //First find the device name from the 'device no longer compliant' action 6 | let devices= 7 | AuditLogs 8 | | where TimeGenerated > ago (1d) 9 | | where OperationName == "Device no longer compliant" 10 | | extend DeviceName = tostring(TargetResources[0].displayName) 11 | | distinct DeviceName; 12 | //Lookup those devices in the IntuneDevices table, and retrieve the latest record 13 | IntuneDevices 14 | | where TimeGenerated > ago (7d) 15 | | summarize arg_max(TimeGenerated, *) by DeviceName 16 | | where DeviceName in (devices) -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-VisualizeDownloadsvsUploads.kql: -------------------------------------------------------------------------------- 1 | //Visualize uploads vs downloads in your Office 365 tenant per day 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | OfficeActivity 6 | | where TimeGenerated > ago(30d) 7 | | project TimeGenerated, Operation 8 | | where Operation in ("FileSyncDownloadedFull", "FileSyncUploadedFull", "FileDownloaded", "FileUploaded") 9 | | summarize 10 | ['Files Downloaded']=countif(Operation in ("FileDownloaded", "FileSyncDownloadedFull")), 11 | ['Files Uploaded']=countif(Operation in ("FileSyncUploadedFull", "FileUploaded")) 12 | by startofday(TimeGenerated) 13 | | render columnchart 14 | with ( 15 | kind=unstacked, 16 | title="Downloads vs Uploads in Office 365", 17 | ytitle="Count", 18 | xtitle="Day") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-VisualizeGuestDownloadsfromO365withTrend.kql: -------------------------------------------------------------------------------- 1 | //Visualize how many files are being downloaded from your Office 365 tenant by guest accounts with trend 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | let StartDate = now(-90d); 6 | let EndDate = now(); 7 | OfficeActivity 8 | | where TimeGenerated > ago(90d) 9 | | where Operation in ("FileSyncDownloadedFull", "FileDownloaded") 10 | | where UserId contains "#ext#" 11 | | make-series TotalDownloads=count() on TimeGenerated in range(StartDate, EndDate, 1d) 12 | | extend (RSquare, SplitIdx, Variance, RVariance, TrendLine)=series_fit_2lines(TotalDownloads) 13 | | project TimeGenerated, TotalDownloads, TrendLine 14 | | render timechart with (title="Guest downloads from Office 365 per day over time with trend") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-NewOperations.kql: -------------------------------------------------------------------------------- 1 | //Find any new operations generated in the Azure AD audit table in the last two weeks compared to the last 180 days, you can adjust the time periods 2 | //e.g. change 180d to 90d and 14d to 7d would find new events in the last week not seen in the 90 prior to that 3 | 4 | //Data connector required for this query - Azure Active Directory - Audit Logs 5 | 6 | AuditLogs 7 | | where TimeGenerated > ago (180d) and TimeGenerated < ago(14d) 8 | | distinct OperationName, LoggedByService 9 | | join kind=rightanti( 10 | AuditLogs 11 | | where TimeGenerated > ago(14d) 12 | | summarize TotalCount=count(), FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated) by OperationName, LoggedByService 13 | ) 14 | on OperationName, LoggedByService 15 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-FindNewEnterpriseApps.kql: -------------------------------------------------------------------------------- 1 | //Find new applications your users are signing into in the last month vs the previous 6 months. For each find the first time the app was used, how many total signins and distinct users accessing each one 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | let knownapps= 6 | SigninLogs 7 | | where TimeGenerated > ago(180d) and TimeGenerated < ago (30d) 8 | | distinct AppId; 9 | SigninLogs 10 | | where TimeGenerated > ago(30d) 11 | | where AppId !in (knownapps) 12 | | where isnotempty(AppDisplayName) 13 | | summarize 14 | ['First Time Seen']=min(TimeGenerated), 15 | Count=count(), 16 | ['User Count']=dcount(UserPrincipalName) 17 | by AppDisplayName 18 | | sort by Count desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/OAuth-ApplicationPermissionsGrant.kql: -------------------------------------------------------------------------------- 1 | //Query to find applications that have had application permissions granted to them 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where OperationName has "Add app role assignment to service principal" 7 | | extend UpdatedPermission = tostring(parse_json(tostring(parse_json(tostring(TargetResources[0].modifiedProperties))[1].newValue))) 8 | | extend AppName = tostring(parse_json(tostring(parse_json(tostring(TargetResources[0].modifiedProperties))[4].newValue))) 9 | | extend User = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 10 | | extend AppId = tostring(TargetResources[1].id) 11 | | project TimeGenerated, OperationName, UpdatedPermission, AppName, AppId, User -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-VisualizeConditionalAccessFailures.kql: -------------------------------------------------------------------------------- 1 | //Visualizes conditional access policy failures over time by policy name 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | let start = now(-90d); 6 | let end = now(); 7 | let timeframe= 12h; 8 | SigninLogs 9 | | project TimeGenerated, ResultType, ConditionalAccessPolicies 10 | | where ResultType == 53003 11 | | mv-expand ConditionalAccessPolicies 12 | | where ConditionalAccessPolicies.result == "failure" 13 | | extend ['CA Policy Name'] = tostring(ConditionalAccessPolicies.displayName) 14 | | make-series ['Failure Count'] = count() default=0 on TimeGenerated in range(start, end, timeframe) by ['CA Policy Name'] 15 | | render timechart with (title="Conditional access policy failure over time") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-AllowedBlockedDomainListChanges.kql: -------------------------------------------------------------------------------- 1 | //Detect when a domain is added or removed to either the allow or block list in Azure AD external identities 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where OperationName == "Update policy" 7 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 8 | | mv-expand TargetResources 9 | | extend modifiedProperties = parse_json(TargetResources).modifiedProperties 10 | | mv-expand modifiedProperties 11 | | extend newValue = parse_json(modifiedProperties).newValue 12 | | mv-expand todynamic(newValue) 13 | | where newValue has "InvitationsAllowedAndBlockedDomainsPolicy" 14 | | project TimeGenerated, OperationName, Actor, ['New Domain Policy']=newValue 15 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-ApplicationAccessReview.kql: -------------------------------------------------------------------------------- 1 | //Query to find users who have access to an application but haven't signed in for 90 days 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | //Data connector required for this query - Microsoft Sentinel UEBA 5 | 6 | let signins= 7 | SigninLogs 8 | | where TimeGenerated > ago (90d) 9 | | where AppDisplayName has "Application Name" 10 | | project TimeGenerated, UserPrincipalName, AppDisplayName; 11 | IdentityInfo 12 | | where TimeGenerated > ago (21d) 13 | | summarize arg_max(TimeGenerated, *) by AccountUPN 14 | | extend UserPrincipalName = AccountUPN 15 | | where GroupMembership contains "Group that gives access to Application" 16 | | join kind=leftanti signins on UserPrincipalName 17 | | project UserPrincipalName -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-CalculateRiskyApps.kql: -------------------------------------------------------------------------------- 1 | //Calculate the percentage of signins to all your Azure AD apps considered risky. Those requiring single factor authentication, coming from an unknown location and from an unknown device 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago (30d) 7 | | where ResultType == 0 8 | | extend DeviceTrustType = tostring(DeviceDetail.trustType) 9 | | summarize 10 | ['Total Signins']=count(), 11 | ['At Risk Signins']=countif(NetworkLocationDetails == '[]' and isempty(DeviceTrustType) and AuthenticationRequirement == "singleFactorAuthentication") 12 | by AppDisplayName 13 | | extend ['At Risk Percentage']=(todouble(['At Risk Signins']) * 100 / todouble(['Total Signins'])) -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-CalculatePercentageofDownloadsperDomain.kql: -------------------------------------------------------------------------------- 1 | //Calculate the percentage that each guest domain is contributing to total downloads from your Office 365 tenant 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | OfficeActivity 6 | | where TimeGenerated > ago(30d) 7 | | where Operation in ("FileSyncDownloadedFull", "FileDownloaded") 8 | | where UserId contains "#EXT#" 9 | | extend ['Guest UserPrincipalName'] = tostring(split(UserId, "#")[0]) 10 | | extend ['Guest Domain'] = tostring(split(['Guest UserPrincipalName'], "_")[-1]) 11 | | summarize Count=count() by ['Guest Domain'] 12 | | as T 13 | | extend Percentage = round(100.0 * Count / toscalar (T 14 | | summarize sum(Count)), 2) 15 | | project-reorder ['Guest Domain'], Count, Percentage 16 | | sort by Percentage desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-AppAccessMembersvsGuests.kql: -------------------------------------------------------------------------------- 1 | //Creates a list of your applications and summarizes successful signins by members vs guests separated to total and distinct signins 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago(30d) 7 | | project TimeGenerated, UserType, ResultType, AppDisplayName, UserPrincipalName 8 | | where ResultType == 0 9 | | summarize 10 | ['Total Member Signins']=countif(UserType == "Member"), 11 | ['Distinct Member Signins']=dcountif(UserPrincipalName, UserType == "Member"), 12 | ['Total Guest Signins']=countif(UserType == "Guest"), 13 | ['Distinct Guest Signins']=dcountif(UserPrincipalName, UserType == "Guest") 14 | by AppDisplayName 15 | | sort by AppDisplayName asc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Security Alert/SecurityAlert-VisualizeTopPhishingDomains.kql: -------------------------------------------------------------------------------- 1 | //Visualize the most popular weaponized domains in the phishing emails your users receive 2 | 3 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 4 | 5 | SecurityAlert 6 | | where TimeGenerated > ago(365d) 7 | | where ProviderName == "OATP" 8 | | where AlertName in ("Email messages containing malicious URL removed after delivery​","Email messages containing phish URLs removed after delivery") 9 | | mv-expand todynamic(Entities) 10 | | extend MaliciousURL = tostring(Entities.Url) 11 | | project MaliciousURL 12 | | parse-where MaliciousURL with * "//" ['Malicious Domain'] "/" * 13 | | summarize Count=count() by ['Malicious Domain'] 14 | | sort by Count desc 15 | | render barchart 16 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-AppProxySettoPassThrough.kql: -------------------------------------------------------------------------------- 1 | //Alert when an application using Azure AD app proxy is set to pass through as it's pre-auth setting 2 | AuditLogs 3 | | where LoggedByService == "Application Proxy" 4 | | where OperationName == "Update application" 5 | | where Result == "success" 6 | | extend PreAuthSetting = tostring(parse_json(tostring(parse_json(tostring(TargetResources[0].modifiedProperties))[0].newValue))) 7 | | where PreAuthSetting == "Passthru" 8 | | extend ['App Display Name'] = tostring(TargetResources[0].displayName) 9 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 10 | | extend ['Actor IP Address'] = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress) 11 | | project TimeGenerated, PreAuthSetting, ['App Display Name'], Actor, ['Actor IP Address'] -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-AdminUpdatingSecurityInfo.kql: -------------------------------------------------------------------------------- 1 | //Detects when an admin changes the authentication phone details for another user 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where OperationName == "Admin updated security info" 7 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 8 | | extend Target = tostring(TargetResources[0].userPrincipalName) 9 | | extend ['New Phone Number'] = tostring(parse_json(tostring(parse_json(tostring(TargetResources[0].modifiedProperties))[2].newValue))) 10 | | extend ['Old Phone Number'] = tostring(parse_json(tostring(parse_json(tostring(TargetResources[0].modifiedProperties))[2].oldValue))) 11 | | project TimeGenerated, Actor, Target, ['New Phone Number'], ['Old Phone Number'] -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-FindUsersFailingSSPR.kql: -------------------------------------------------------------------------------- 1 | //Detect users who are trying to use self service password reset but failing as they don't have any authentication methods listed 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where LoggedByService == "Self-service Password Management" 7 | | extend User = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 8 | | extend ['User IP Address'] = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress) 9 | | sort by TimeGenerated asc 10 | | summarize ['SSPR Actions']=make_list(ResultReason) by CorrelationId, User, ['User IP Address'] 11 | | where ['SSPR Actions'] has "User's account has insufficient authentication methods defined. Add authentication info to resolve this" 12 | | sort by User desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-DailySummaryofUsersAddedtoAADGroups.kql: -------------------------------------------------------------------------------- 1 | //Create a daily summary of Azure Active Directory group additions 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | let timerange=7d; 6 | AuditLogs 7 | | where TimeGenerated > ago (timerange) 8 | | where OperationName == "Add member to group" 9 | | extend Type = tostring(TargetResources[0].type) 10 | | where Type == "User" 11 | | extend ['Group Name'] = tostring(parse_json(tostring(parse_json(tostring(TargetResources[0].modifiedProperties))[1].newValue))) 12 | | extend UserAdded = tostring(TargetResources[0].userPrincipalName) 13 | | where isnotempty(UserAdded) 14 | | summarize ['Users Added']=make_set(UserAdded) by ['Group Name'], startofday(TimeGenerated) 15 | | sort by ['Group Name'] asc, TimeGenerated desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-SummarizeConditionalAccessPoliciesfailures.kql: -------------------------------------------------------------------------------- 1 | //Create a summary showing which of your Azure AD conditional access policies are preventing the most signins and for what reasons 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago (7d) 7 | | project TimeGenerated, ConditionalAccessPolicies, ResultType, ResultDescription 8 | | mv-expand ConditionalAccessPolicies 9 | | extend CAResult = tostring(ConditionalAccessPolicies.result) 10 | | extend ['Conditional Access Policy Name'] = tostring(ConditionalAccessPolicies.displayName) 11 | | where CAResult == "failure" 12 | | summarize ['Count of Failures']=count()by ['Conditional Access Policy Name'], ResultType, ResultDescription 13 | | sort by ['Count of Failures'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/SSPR-PasswordResetInitiatedviaMSGraph.kql: -------------------------------------------------------------------------------- 1 | // Detects when a self service password reset has been initiated via MS Graph and is successful 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where OperationName == "POST UserAuthMethod.ResetPasswordOnPasswordMethods" 7 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 8 | | project TimeGenerated, OperationName, Actor, CorrelationId 9 | | join kind=inner 10 | (AuditLogs 11 | | where OperationName == "Reset password (by admin)" 12 | | extend Target = tostring(TargetResources[0].userPrincipalName) 13 | | where Result == "success" 14 | ) 15 | on CorrelationId 16 | | project GraphPostTime=TimeGenerated, PasswordResetTime=TimeGenerated1, Actor, Target -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-DetectRDPRecon.kql: -------------------------------------------------------------------------------- 1 | //Search for devices connecting to multiple IP addresses via RDP witin a time window and alert when over a particular threshold 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | let timerange=1d; 6 | let window=20m; 7 | let threshold=5; 8 | DeviceNetworkEvents 9 | | where TimeGenerated > ago(timerange) 10 | | where ActionType == "ConnectionSuccess" 11 | | where RemotePort == "3389" 12 | // Exclude Defender for Identity which uses RDP to map your network 13 | | where InitiatingProcessFileName <> "Microsoft.Tri.Sensor.exe" 14 | | summarize ['Target Device List']=make_set(RemoteIP), ['Count of Devices']=dcount(RemoteIP) by bin(TimeGenerated, window), DeviceName 15 | | where ['Count of Devices'] > threshold 16 | | sort by ['Count of Devices'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Intune/IntuneDevices-VisualizeLastContact.kql: -------------------------------------------------------------------------------- 1 | //Visualize when your devices last contacted Intune 2 | 3 | //Data connector required for this query - Intune data sent to Sentinel workspace 4 | 5 | IntuneDevices 6 | | where TimeGenerated > ago(90d) 7 | | where isnotempty(LastContact) 8 | //Retrieve latest record for each DeviceId 9 | | summarize arg_max(TimeGenerated, *) by DeviceId 10 | //Convert string to datetime format 11 | | extend LastContactTime = todatetime(LastContact) 12 | | project DeviceId, LastContactTime 13 | //Exclude devices reporting as 0001-01-01 14 | | where LastContactTime <> todatetime('0001-01-01T00:00:00Z') 15 | //Group by month and render chart 16 | | summarize ['Device Count']=count()by startofmonth(LastContactTime) 17 | | render columnchart with (title="Intune devices by last contact time", xtitle="Month") 18 | -------------------------------------------------------------------------------- /05-00-Analytics-Rule-Olusturma.md: -------------------------------------------------------------------------------- 1 | Azure Sentinel Portalı'nda Analytics sekmesine gidin. 2 | + Create butonuna tıklayın ve yeni bir kural oluşturun. 3 | Kural adı ve tetikleyici koşullarını belirleyin. 4 | 5 | Log query ile ilgili verileri toplayın. Örneğin: 6 | 7 | ``` 8 | SecurityEvent 9 | | where EventID == 4625 10 | | summarize FailedAttempts = count() by TargetUserName 11 | | where FailedAttempts > 10 12 | 13 | ``` 14 | 15 | Bu örnekte, 4625 (başarısız oturum açma) olaylarını izleyip, 10’dan fazla başarısız deneme olduğunda kural tetiklenir. 16 | Kural sıklığını ve tetikleme koşullarını belirleyin (örneğin her 5 dakikada bir). 17 | Uyarı seviyesi ve otomasyon yanıtı ayarlarını yapılandırın (örneğin, playbook kullanarak otomatik eylem). 18 | 19 | Bu kurallar, Sentinel’de tehditleri etkili bir şekilde algılamaya ve yanıt vermeye yardımcı olur. 20 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-VisualizeSSPR.kql: -------------------------------------------------------------------------------- 1 | //Visualize successful self service password resets and account unlocks over time 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where TimeGenerated > ago (180d) 7 | | where OperationName in ("Reset password (self-service)", "Unlock user account (self-service)") 8 | | summarize 9 | ['Password Reset']=countif(OperationName == "Reset password (self-service)" and ResultDescription == "Successfully completed reset."), 10 | ['Account Unlock']=countif(OperationName == "Unlock user account (self-service)" and ResultDescription == "Success") 11 | by startofweek(TimeGenerated) 12 | | render timechart 13 | with ( 14 | ytitle="Count", 15 | xtitle="Day", 16 | title="Self Service Password Resets and Account Unlocks over time") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Identity/IdentityDirectoryEvents-AccountDelegationChanged.kql: -------------------------------------------------------------------------------- 1 | //Alert when Defender for Identity detects a change in kerberos constrained delegation configuration on a device 2 | 3 | //Data connector required for this query - M365 Defender - Identity* tables 4 | 5 | IdentityDirectoryEvents 6 | | where ActionType == "Account Constrained Delegation changed" 7 | | extend AF = parse_json(AdditionalFields) 8 | | extend ['Previous Delegation Setting'] = AF.["FROM AccountConstrainedDelegationState"] 9 | | extend ['Current Delegation Setting'] = AF.["TO AccountConstrainedDelegationState"] 10 | | extend ['Device Operating System'] = AF.TargetComputerOperatingSystem 11 | | project 12 | TimeGenerated, 13 | TargetDeviceName, 14 | ['Device Operating System'], 15 | ['Previous Delegation Setting'], 16 | ['Current Delegation Setting'] -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-RoleAddedtoServicePrincipal.kql: -------------------------------------------------------------------------------- 1 | // Detects when a service principal has been added to an Azure AD role 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where OperationName == "Add member to role" 7 | | where TargetResources[0].type == "ServicePrincipal" 8 | | extend ['Service Principal Object Id'] = tostring(TargetResources[0].id) 9 | | extend ['Application Display Name'] = tostring(TargetResources[0].displayName) 10 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 11 | | extend ['Azure AD Role Added'] = tostring(parse_json(tostring(parse_json(tostring(TargetResources[0].modifiedProperties))[1].newValue))) 12 | | project TimeGenerated, Actor, ['Azure AD Role Added'], ['Service Principal Object Id'], ['Application Display Name'] -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-GuestAddedtoPIM.kql: -------------------------------------------------------------------------------- 1 | //Detect when an Azure AD guest account is assigned to an Azure AD PIM role 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where TimeGenerated > ago (1d) 7 | | where OperationName in ("Add eligible member to role in PIM completed (permanent)", "Add eligible member to role in PIM completed (timebound)", "Add member to role in PIM completed (permanent)", "Add member to role in PIM completed (timebound)") 8 | | extend ['Azure AD Role Name'] = tostring(TargetResources[0].displayName) 9 | | extend Target = tostring(TargetResources[2].userPrincipalName) 10 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 11 | | where Target contains "#ext#" 12 | | project TimeGenerated, OperationName, Actor, Target, ['Azure AD Role Name'] -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-InactiveGuestAccounts.kql: -------------------------------------------------------------------------------- 1 | //Find guest accounts that haven't signed in for a period of time, this example uses 45 days 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | let timerange=180d; 6 | let timeframe=45d; 7 | SigninLogs 8 | | where TimeGenerated > ago(timerange) 9 | | where UserType == "Guest" or UserPrincipalName contains "#ext#" 10 | | where ResultType == 0 11 | | summarize arg_max(TimeGenerated, *) by UserPrincipalName 12 | | join kind = leftanti 13 | ( 14 | SigninLogs 15 | | where TimeGenerated > ago(timeframe) 16 | | where UserType == "Guest" or UserPrincipalName contains "#ext#" 17 | | where ResultType == 0 18 | | summarize arg_max(TimeGenerated, *) by UserPrincipalName 19 | ) 20 | on UserPrincipalName 21 | | project UserPrincipalName 22 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/PIM-UserAssignedRolebutHasntActivated.kql: -------------------------------------------------------------------------------- 1 | // Find users who are assigned a privileged role in Azure AD but haven't activated a role in the last 45 days 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | //Data connector required for this query - Microsoft Sentinel UEBA 5 | 6 | IdentityInfo 7 | | where TimeGenerated > ago(21d) 8 | | where isnotempty(AssignedRoles) 9 | | where AssignedRoles != "[]" 10 | | summarize arg_max(TimeGenerated, *) by AccountUPN 11 | | join kind=leftanti ( 12 | AuditLogs 13 | | where TimeGenerated > ago(45d) 14 | | where OperationName == "Add member to role completed (PIM activation)" 15 | | extend AccountUPN = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 16 | | summarize arg_max(TimeGenerated, *) by AccountUPN) 17 | on AccountUPN -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-DetectPIMActivationsOutsideWorkingHours.kql: -------------------------------------------------------------------------------- 1 | //Detect Azure AD PIM activiations outside of working hours 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | let timerange=30d; 6 | AuditLogs 7 | // extend LocalTime to your time zone 8 | | extend LocalTime=TimeGenerated + 5h 9 | | where LocalTime > ago(timerange) 10 | // Change hours of the day to suit your company, i.e this would find activations between 6pm and 6am 11 | | where hourofday(LocalTime) !between (6 .. 18) 12 | | where OperationName == "Add member to role completed (PIM activation)" 13 | | extend User = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 14 | | extend ['Azure AD Role Name'] = tostring(TargetResources[0].displayName) 15 | | project LocalTime, User, ['Azure AD Role Name'], ['Activation Reason']=ResultReason -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-MultipleUsersSameMFANumber.kql: -------------------------------------------------------------------------------- 1 | //Query your Azure Active Directory audit logs for any phone numbers that have been registered to multiple users for MFA 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where TimeGenerated > ago (30d) 7 | | where Result == "success" 8 | | where Identity == "Azure Credential Configuration Endpoint Service" 9 | | where OperationName == "Update user" 10 | | extend UserPrincipalName = tostring(TargetResources[0].userPrincipalName) 11 | | extend PhoneNumber = tostring(parse_json(tostring(parse_json(tostring(TargetResources[0].modifiedProperties))[1].newValue))[0].PhoneNumber) 12 | | where isnotempty(PhoneNumber) 13 | | summarize Users=make_set(UserPrincipalName) by PhoneNumber 14 | | extend CountofUsers=array_length(Users) 15 | | where CountofUsers > 1 -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Firewall Queries/Devices-NoRDP.kql: -------------------------------------------------------------------------------- 1 | //Find devices that have had no inbound RDP connections in the last 30 days to help build firewall policy 2 | 3 | //Microsoft Sentinel query 4 | 5 | let devices= 6 | DeviceNetworkEvents 7 | | where TimeGenerated > ago (30d) 8 | | where ActionType == "InboundConnectionAccepted" 9 | | where LocalPort == 3389 10 | | distinct DeviceId; 11 | DeviceInfo 12 | | where TimeGenerated > ago (30d) 13 | | distinct DeviceId, DeviceName 14 | | where DeviceId !in (devices) 15 | 16 | 17 | //Advanced Hunting query 18 | 19 | let devices= 20 | DeviceNetworkEvents 21 | | where Timestamp > ago (30d) 22 | | where ActionType == "InboundConnectionAccepted" 23 | | where LocalPort == 3389 24 | | distinct DeviceId; 25 | DeviceInfo 26 | | where Timestamp > ago (30d) 27 | | distinct DeviceId, DeviceName 28 | | where DeviceId !in (devices) -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Firewall Queries/Devices-NoSSH.kql: -------------------------------------------------------------------------------- 1 | //Find devices that have had no inbound SSH connections in the last 30 days to help build firewall policy 2 | 3 | //Microsoft Sentinel query 4 | 5 | let devices= 6 | DeviceNetworkEvents 7 | | where TimeGenerated > ago (30d) 8 | | where ActionType == "InboundConnectionAccepted" 9 | | where LocalPort == 22 10 | | distinct DeviceId; 11 | DeviceInfo 12 | | where TimeGenerated > ago (30d) 13 | | distinct DeviceId, DeviceName 14 | | where DeviceId !in (devices) 15 | 16 | 17 | //Advanced Hunting query 18 | 19 | let devices= 20 | DeviceNetworkEvents 21 | | where Timestamp > ago (30d) 22 | | where ActionType == "InboundConnectionAccepted" 23 | | where LocalPort == 22 24 | | distinct DeviceId; 25 | DeviceInfo 26 | | where Timestamp > ago (30d) 27 | | distinct DeviceId, DeviceName 28 | | where DeviceId !in (devices) -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-MultipleFilesSharedtoGuests.kql: -------------------------------------------------------------------------------- 1 | //Detect when a user shares multiple files to Azure AD guests over a short time frame. 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | //Define a time period to check and the threshold of how many files to alert on. 6 | //In this example it would detect when a user shares 10 or more files to a guest within 30 minutes 7 | let timeframe=30m; 8 | let threshold=10; 9 | OfficeActivity 10 | | where TimeGenerated > ago(1d) 11 | | where Operation in ("SecureLinkCreated", "AddedToSecureLink") 12 | | where TargetUserOrGroupType == "Guest" or TargetUserOrGroupName contains "#EXT#" 13 | | summarize 14 | ['File Share Count']=dcount(OfficeObjectId), 15 | ['List of Files']=make_set(OfficeObjectId) 16 | by UserId, bin(TimeGenerated, timeframe) 17 | | where ['File Share Count'] >= threshold -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Windows Security Events/SecEvents-PotentialRDPRecon.kql: -------------------------------------------------------------------------------- 1 | //Detect when a user connects to 3 or more unique devices via RDP over a 30 minute period 2 | 3 | //Data connector required for this query - Windows Security Events via AMA or Security Events via Legacy Agent 4 | 5 | SecurityEvent 6 | | where TimeGenerated > ago (1d) 7 | | where EventID == "4624" 8 | | where LogonType == 10 9 | | where SubjectDomainName == TargetDomainName 10 | //Account is dropped to lower case to make sure each account is only listed once, i.e Reprise99 and reprise99 are combined 11 | | summarize 12 | ['Distinct device logon count']=dcount(Computer), 13 | ['List of devices']=make_set(Computer) 14 | by tolower(Account), bin(TimeGenerated, 30m) 15 | //Find accounts that have logged on to 3 or more unique devices in less than 30 minutes 16 | | where ['Distinct device logon count'] >= 3 -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-DailySummaryofAdminActivity.kql: -------------------------------------------------------------------------------- 1 | //Create a daily summary of activities completed by your Azure AD privileged users 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | //Data connector required for this query - Microsoft Sentinel UEBA 5 | 6 | let timerange=30d; 7 | IdentityInfo 8 | | where TimeGenerated > ago(21d) 9 | | summarize arg_max(TimeGenerated, *) by AccountUPN 10 | | where isnotempty(AssignedRoles) 11 | | where AssignedRoles != "[]" 12 | | project Actor=AccountUPN 13 | | join kind=inner ( 14 | AuditLogs 15 | | where TimeGenerated > ago(timerange) 16 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 17 | | where isnotempty(Actor) 18 | ) 19 | on Actor 20 | | summarize AdminActivity = make_list(OperationName) by Actor, startofday(TimeGenerated) -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Intune/IntuneDevices-VisualizeDeviceJoinTypebyWeek.kql: -------------------------------------------------------------------------------- 1 | //Visualize the join type (Azure AD joined, Azure AD registered or Hybrid joined) of your MEM/Intune devices per week 2 | 3 | //Data connector required for this query - Intune data sent to Sentinel workspace 4 | 5 | IntuneDevices 6 | //Gets all data generated in 180 days 7 | | where TimeGenerated > ago(180d) 8 | //Optionally filter only devices have contact to Intune in 30 days 9 | | where todatetime(LastContact) > ago (30d) 10 | | summarize arg_max(TimeGenerated, *) by DeviceName, startofweek(TimeGenerated) 11 | | where OS == "Windows" 12 | | summarize JoinSummary=count()by JoinType, startofweek(TimeGenerated) 13 | | where isnotempty(JoinType) 14 | | render columnchart 15 | with ( 16 | kind=unstacked, 17 | ytitle="Device Count", 18 | xtitle="Week", 19 | title="Device count by join type per week") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Security Alert/SecurityAlert-DefenderforIDRecon.kql: -------------------------------------------------------------------------------- 1 | //When Defender for Identity alerts on user and group reconnaissance, parse the relevant accounts, hosts and groups affected 2 | 3 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 4 | 5 | SecurityAlert 6 | | where AlertName == "User and group membership reconnaissance (SAMR)" 7 | | extend x = todynamic(Entities) 8 | | mv-expand x 9 | | parse x with * 'HostName":"' HostName '","Id' * 10 | | parse x with * 'FriendlyName":"' GroupName '","Type":"security-group"' * 11 | | parse x with * '"Name":"' AccountName '","Sid"' * 12 | | summarize 13 | Accounts=make_list_if(AccountName, isnotempty(AccountName)), 14 | Hosts=make_list_if(HostName, isnotempty(HostName)), 15 | Groups=make_list_if(GroupName, isnotempty(GroupName)) 16 | by VendorOriginalId -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-FindUsersFailingNewPasswordSSPR.kql: -------------------------------------------------------------------------------- 1 | //Find users who have failed 3 or more times to set a new password during a SSPR flow. Worth reaching out to them to give them a hand or see if you can onboard them to something like Windows Hello for Business 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where LoggedByService == "Self-service Password Management" 7 | | extend User = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 8 | | extend ['User IP Address'] = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress) 9 | | sort by TimeGenerated asc 10 | | summarize ['SSPR Actions']=make_list_if(ResultReason, ResultReason has "User submitted a new password") by CorrelationId, User, ['User IP Address'] 11 | | where array_length(['SSPR Actions']) >= 3 12 | | sort by User desc 13 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-MFAPercentageperapp.kql: -------------------------------------------------------------------------------- 1 | //Calculate the percentage of signins to each of your Azure AD applications that used MFA 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago(30d) 7 | | where ResultType == 0 8 | | summarize 9 | ['Total Signin Count']=count(), 10 | ['Total MFA Count']=countif(AuthenticationRequirement == "multiFactorAuthentication"), 11 | ['Total non MFA Count']=countif(AuthenticationRequirement == "singleFactorAuthentication") 12 | by AppDisplayName 13 | | project 14 | AppDisplayName, 15 | ['Total Signin Count'], 16 | ['Total MFA Count'], 17 | ['Total non MFA Count'], 18 | MFAPercentage=(todouble(['Total MFA Count']) * 100 / todouble(['Total Signin Count'])) 19 | | sort by ['Total Signin Count'] desc, MFAPercentage asc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-DetectEmailsReadbyAdmins.kql: -------------------------------------------------------------------------------- 1 | //Detects users with global or exchange administrator roles who have accessed email items from mailboxes other than their own 2 | 3 | //Data connector required for this query - Office 365 4 | //Data connector required for this query - Microsoft Sentinel UEBA 5 | 6 | let timeframe=30d; 7 | let adminusers= 8 | IdentityInfo 9 | | where TimeGenerated > ago(21d) 10 | | where AssignedRoles has_any ("Exchange Administrator", "Global Administrator") 11 | | summarize arg_max(TimeGenerated, *) by AccountUPN 12 | | project UserId=AccountUPN; 13 | OfficeActivity 14 | | where TimeGenerated > ago(timeframe) 15 | | where OfficeWorkload == "Exchange" 16 | | where Operation == "MailItemsAccessed" 17 | | where UserId in (adminusers) 18 | | where UserId != MailboxOwnerUPN 19 | | project AccessTime=TimeGenerated, UserId, MailboxOwnerUPN, Folders -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/DNS/DnsEvents-FindStaleDomains.kql: -------------------------------------------------------------------------------- 1 | //Find DNS domains that have not been queried in the last 30 days. These are potentially stale and should be removed. 2 | 3 | //Data connector required for this query - DNS 4 | 5 | let domain="yourdomain.com"; 6 | DnsEvents 7 | | where TimeGenerated > ago(180d) 8 | | where SubType == "LookupQuery" 9 | | where QueryType == "A" 10 | | where Name endswith domain 11 | | summarize LookupCount=count()by Name 12 | //Set a threshold for total lookups to be included, to account for typos and low volume queries 13 | | where LookupCount > 50 14 | | join kind=leftanti 15 | ( 16 | DnsEvents 17 | | where TimeGenerated > ago(30d) 18 | | where SubType == "LookupQuery" 19 | | where QueryType == "A" 20 | | where Name endswith domain 21 | | summarize arg_max(TimeGenerated, Name) by Name 22 | | project TimeGenerated, Name) 23 | on Name -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Firewall Queries/Devices-NoSMB.kql: -------------------------------------------------------------------------------- 1 | //Find devices that have had no inbound SMB connections in the last 30 days to help build firewall policy 2 | 3 | //Microsoft Sentinel query 4 | 5 | let devices= 6 | DeviceNetworkEvents 7 | | where TimeGenerated > ago (30d) 8 | | where ActionType == "InboundConnectionAccepted" 9 | | where LocalPort in ("139","445") 10 | | distinct DeviceId; 11 | DeviceInfo 12 | | where TimeGenerated > ago (30d) 13 | | distinct DeviceId, DeviceName 14 | | where DeviceId !in (devices) 15 | 16 | 17 | //Advanced Hunting query 18 | 19 | let devices= 20 | DeviceNetworkEvents 21 | | where Timestamp > ago (30d) 22 | | where ActionType == "InboundConnectionAccepted" 23 | | where LocalPort in ("139","445") 24 | | distinct DeviceId; 25 | DeviceInfo 26 | | where Timestamp > ago (30d) 27 | | distinct DeviceId, DeviceName 28 | | where DeviceId !in (devices) -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Security Alert/SecurityAlert-PossibleDNSDataTransfer.kql: -------------------------------------------------------------------------------- 1 | //When Defender for Cloud detects possible data transfer via DNS tunnel, use DNS logs to find any other devices that have queried the potentially malicious domain 2 | 3 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 4 | //Data connector required for this query - DNS 5 | 6 | let maliciousdomain= 7 | SecurityAlert 8 | | where AlertName contains "via DNS tunnel" 9 | | mv-expand todynamic(Entities) 10 | | project Entities 11 | | extend MaliciousDomain = tostring(Entities.DomainName) 12 | | where isnotempty(MaliciousDomain) 13 | | distinct MaliciousDomain; 14 | DnsEvents 15 | | where QueryType == "A" 16 | | project Name, ClientIP 17 | | where Name in~ (maliciousdomain) 18 | | summarize ['List of Device IPs']=make_set(ClientIP) by Name -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-ListBulkActivities.kql: -------------------------------------------------------------------------------- 1 | //List the bulk activities attempted by your privileged Azure AD users and parse the results 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where OperationName has_all ("(bulk)", "finished") 7 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 8 | | project TimeGenerated, Actor, ResultDescription, OperationName 9 | | parse ResultDescription with * "Total activities count:" ['Total Activity Count'] ";" * 10 | | parse ResultDescription with * "succeeded activities count" ['Total Succeeded'] ";" * 11 | | parse ResultDescription with * "failed activities count" ['Total Failed'] 12 | | project 13 | TimeGenerated, 14 | Actor, 15 | OperationName, 16 | ['Total Activity Count'], 17 | ['Total Succeeded'], 18 | ['Total Failed'] -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-DetectCredentialBackup.kql: -------------------------------------------------------------------------------- 1 | //Detect when a backup is taken from Windows Credential manager 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | //Microsoft Sentinel query 6 | DeviceEvents 7 | | where ActionType == "CredentialsBackup" 8 | | project 9 | TimeGenerated, 10 | DeviceName, 11 | InitiatingProcessAccountName, 12 | InitiatingProcessCommandLine, 13 | InitiatingProcessFileName, 14 | InitiatingProcessFolderPath 15 | 16 | //Advanced Hunting query 17 | 18 | //Data connector required for this query - Advanced Hunting license 19 | 20 | DeviceEvents 21 | | where ActionType == "CredentialsBackup" 22 | | project 23 | Timestamp, 24 | DeviceName, 25 | InitiatingProcessAccountName, 26 | InitiatingProcessCommandLine, 27 | InitiatingProcessFileName, 28 | InitiatingProcessFolderPath -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Firewall Queries/Devices-NoHTTP.kql: -------------------------------------------------------------------------------- 1 | //Find devices that have had no inbound web connections in the last 30 days to help build firewall policy 2 | 3 | //Microsoft Sentinel query 4 | 5 | let devices= 6 | DeviceNetworkEvents 7 | | where TimeGenerated > ago (30d) 8 | | where ActionType == "InboundConnectionAccepted" 9 | | where LocalPort in ("80","443","8080") 10 | | distinct DeviceId; 11 | DeviceInfo 12 | | where TimeGenerated > ago (30d) 13 | | distinct DeviceId, DeviceName 14 | | where DeviceId !in (devices) 15 | 16 | 17 | //Advanced Hunting query 18 | 19 | let devices= 20 | DeviceNetworkEvents 21 | | where Timestamp > ago (30d) 22 | | where ActionType == "InboundConnectionAccepted" 23 | | where LocalPort in ("80","443","8080") 24 | | distinct DeviceId; 25 | DeviceInfo 26 | | where Timestamp > ago (30d) 27 | | distinct DeviceId, DeviceName 28 | | where DeviceId !in (devices) -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-MFACountPerUser.kql: -------------------------------------------------------------------------------- 1 | //Calculate how often your users are actively challenged for MFA vs when it was previously satisfied per day 2 | //Return users who are challenged over the threshold per day 3 | 4 | //Data connector required for this query - Azure Active Directory - Signin Logs 5 | 6 | let threshold = 5; 7 | SigninLogs 8 | | where TimeGenerated > ago(90d) 9 | | where AuthenticationRequirement == "multiFactorAuthentication" 10 | | extend x=todynamic(AuthenticationDetails) 11 | | mv-expand x 12 | | project TimeGenerated, x, UserPrincipalName 13 | | extend MFAResultStep = tostring(x.authenticationStepResultDetail) 14 | | summarize MFARequired=countif(MFAResultStep == "MFA completed in Azure AD"), PreviouslySatisfied=countif(MFAResultStep == "MFA requirement satisfied by claim in the token") by UserPrincipalName, startofday(TimeGenerated) 15 | | where MFARequired >= threshold -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-ParseIPInfofromSecurityAlert.kql: -------------------------------------------------------------------------------- 1 | //Query to parse IP information from Security Alerts 2 | 3 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 4 | 5 | SecurityAlert 6 | | where AlertName in ("Impossible travel activity", "Atypical Travel", "Anonymous IP address", "Anomalous Token") 7 | | parse Entities with * 'AadUserId": "' aadid_ '",' * 8 | | extend ep_ = parse_json(ExtendedProperties) 9 | | extend s = tostring(ep_["IP Addresses"]) 10 | | extend ipv4_ = extract_all(@"(([\d]{1,3}\.){3}[\d]{1,3})", dynamic([1]), s) 11 | | extend ipv4Add_ = translate('["]', '', tostring(ipv4_)) 12 | | extend ipv6_ = extract_all(@"(([\d|\w]{1,4}\:){7}[\d|\w]{1,4})", dynamic([1]), s) 13 | | extend ipv6Add_ = translate('["]', '', tostring(ipv6_)) 14 | | project TimeGenerated, AlertName, ipv4Add_, ipv6Add_, CompromisedEntity -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Diagnostics/CVE-2021-44228.kql: -------------------------------------------------------------------------------- 1 | //Detection rule for App Gateway rule hits for log4j vulnerability. Retrieve attacked host, malicious IP and malicious User Agent 2 | 3 | //Data connector required for this query - Azure Diagnostics (Application Gateways) 4 | 5 | AzureDiagnostics 6 | | where details_data_s contains "jndi" 7 | | parse-where details_data_s with * 'User-Agent:' MaliciousHost 8 | | project TimeGenerated, Target=hostname_s, Actor=clientIp_s, MaliciousHost 9 | 10 | //Detect uri directly where starts with /$ or contains ldap 11 | AzureDiagnostics 12 | | where TimeGenerated > ago(1d) 13 | | where ResourceType == "APPLICATIONGATEWAYS" 14 | | project TimeGenerated, host_s, originalRequestUriWithArgs_s, clientIP_s 15 | | where originalRequestUriWithArgs_s startswith "/$" or originalRequestUriWithArgs_s contains "jndi" 16 | | parse-where originalRequestUriWithArgs_s with * '://' MaliciousHost -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-FirstTimeWhoAmI.kql: -------------------------------------------------------------------------------- 1 | //Detect when a 'whoami' command is sent for the first time from a device & account combination not seen before 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | DeviceProcessEvents 6 | | where TimeGenerated > ago (30d) and TimeGenerated < ago(1d) 7 | | project DeviceName, InitiatingProcessAccountName, InitiatingProcessCommandLine 8 | | where InitiatingProcessCommandLine contains "whoami" 9 | | distinct DeviceName, InitiatingProcessAccountName 10 | | join kind=rightanti ( 11 | DeviceProcessEvents 12 | | where TimeGenerated > ago(1d) 13 | | project 14 | TimeGenerated, 15 | DeviceName, 16 | InitiatingProcessAccountName, 17 | InitiatingProcessCommandLine 18 | | where InitiatingProcessCommandLine contains "whoami" 19 | ) 20 | on DeviceName, InitiatingProcessAccountName -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-DetectFullMailboxAccess.kql: -------------------------------------------------------------------------------- 1 | //Detect when an Exchange admin grants full mailbox access to another user 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | OfficeActivity 6 | | where RecordType == "ExchangeAdmin" 7 | | where Operation == "Add-MailboxPermission" 8 | | parse-where Parameters with * 'Identity","Value":"' TargetMailbox '"' * 9 | | parse-where Parameters with * 'User","Value":"' UserGivenAccess '"' * 10 | | parse-where Parameters with * 'AccessRights","Value":"' AccessRights '"' * 11 | | project 12 | TimeGenerated, 13 | Actor=UserId, 14 | ['Target Mailbox']=TargetMailbox, 15 | ['Target Mailbox DisplayName']=OfficeObjectId, 16 | ['User Granted Access']=UserGivenAccess, 17 | ['Access Type']=AccessRights 18 | | where tolower(Actor) != "nt authority\\system (microsoft.exchange.servicehost)" 19 | | sort by TimeGenerated desc 20 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-DetectFirstTimeServicePrincipalCreation.kql: -------------------------------------------------------------------------------- 1 | //Detects users who add a service principal to Azure AD for the first time. 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | let knownusers= 6 | AuditLogs 7 | | where TimeGenerated > ago(90d) and TimeGenerated < ago(1d) 8 | | where OperationName == "Add service principal" 9 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 10 | | where isnotempty(Actor) 11 | | distinct Actor; 12 | AuditLogs 13 | | where TimeGenerated > ago(1d) 14 | | where OperationName == "Add service principal" 15 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 16 | | where isnotempty(Actor) 17 | | where Actor !in (knownusers) 18 | | extend AppId = tostring(AdditionalDetails[1].value) 19 | | project TimeGenerated, Actor, AppId -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-ASRAudit.kql: -------------------------------------------------------------------------------- 1 | //Summarize attack surface reduction audit hits for each device 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | DeviceEvents 6 | | where TimeGenerated > ago (1d) 7 | | where ActionType startswith "Asr" 8 | | extend isAudit = tostring(AdditionalFields.IsAudit) 9 | | where isAudit = true 10 | | project 11 | TimeGenerated, 12 | ActionType, 13 | DeviceName, 14 | FileName, 15 | InitiatingProcessAccountDomain, 16 | InitiatingProcessAccountName, 17 | InitiatingProcessCommandLine, 18 | InitiatingProcessParentFileName, 19 | ProcessTokenElevation 20 | | summarize 21 | ['Total ASR audit hits']=count(), 22 | ['Distinct ASR audit rule hits']=dcount(ActionType), 23 | ['List of processes']=make_set(InitiatingProcessCommandLine) 24 | by DeviceName 25 | | sort by ['Total ASR audit hits'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-ParseURL.kql: -------------------------------------------------------------------------------- 1 | //Use the inbuilt KQL parse_url function to return the various components of a URL 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | DeviceEvents 6 | | where ActionType == "BrowserLaunchedToOpenUrl" 7 | | extend Url = parse_url(RemoteUrl) 8 | | extend Hostname = tostring(Url.Host) 9 | | extend Fragment = tostring(Url.Fragment) 10 | | extend Password = tostring(Url.Password) 11 | | extend Path = tostring(Url.Path) 12 | | extend Port = tostring(Url.Port) 13 | | extend Query = tostring(Url.Query) 14 | | extend Schema = tostring(Url.Scheme) 15 | | extend Username = tostring(Url.Username) 16 | | where isnotempty(Hostname) 17 | | project 18 | TimeGenerated, 19 | DeviceName, 20 | RemoteUrl, 21 | Schema, 22 | Port, 23 | Hostname, 24 | Path, 25 | Query, 26 | Username, 27 | Password, 28 | Fragment -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-SummarizeSmartScreenUntrustedFiles.kql: -------------------------------------------------------------------------------- 1 | //Summarize the most common files in your environment flagging Smartscreen untrusted warnings 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | //Microsoft Sentinel query 6 | DeviceEvents 7 | | where TimeGenerated > ago (30d) 8 | | where ActionType startswith "SmartScreen" 9 | | extend SmartScreenExperience = tostring(AdditionalFields.Experience) 10 | | where SmartScreenExperience == "Untrusted" 11 | | summarize Count=count()by FileName 12 | | sort by Count 13 | 14 | //Advanced Hunting query 15 | 16 | //Data connector required for this query - Advanced Hunting license 17 | 18 | DeviceEvents 19 | | where Timestamp > ago(30d) 20 | | where ActionType startswith "SmartScreen" 21 | | where AdditionalFields == @"{""Experience"":""Untrusted""}" 22 | | summarize Count=count()by FileName 23 | | sort by Count -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Security Alert/SecurityAlert-DetectNewAlerts.kql: -------------------------------------------------------------------------------- 1 | //List any new alert types found by the Defender product suite in the last week compared to the previous year 2 | 3 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 4 | 5 | //First find all the existing alerts from the last year excluding the last week 6 | let existingalerts= 7 | SecurityAlert 8 | | where TimeGenerated > ago(365d) and TimeGenerated < ago(7d) 9 | // Exclude alerts from Sentinel itself 10 | | where ProviderName != "ASI Scheduled Alerts" 11 | | distinct AlertName; 12 | //Find new alerts triggered in the last week 13 | SecurityAlert 14 | | where TimeGenerated > ago(7d) 15 | // Exclude alerts from Sentinel itself 16 | | where ProviderName != "ASI Scheduled Alerts" 17 | | where AlertName !in (existingalerts) 18 | | distinct AlertName, ProviderName, ProductName -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-DetectSSPRAfterHours.kql: -------------------------------------------------------------------------------- 1 | //Alert on successful self service password resets at suspicious times 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | // extend LocalTime to your time zone 7 | | extend LocalTime=TimeGenerated + 5h 8 | | where LocalTime > ago(7d) 9 | | where OperationName == "Reset password (self-service)" 10 | | where ResultDescription == "Successfully completed reset." 11 | // Change hours of the day to suit your company, i.e this would find self service password reset events between 11pm and 4am 12 | | where hourofday(LocalTime) !between (4 .. 23) 13 | | extend User = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 14 | | extend ['IP Address of User'] = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress) 15 | | project LocalTime, OperationName, ResultDescription, User, ['IP Address of User'] -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-RedirectURIChanged.kql: -------------------------------------------------------------------------------- 1 | //Alert when the redirect URI list is changed for a service principal 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where OperationName == "Update service principal" 7 | | mv-expand TargetResources 8 | | extend modifiedProperties = parse_json(TargetResources).modifiedProperties 9 | | mv-expand modifiedProperties 10 | | where modifiedProperties.displayName == "AppAddress" 11 | | extend newValue = tostring(parse_json(modifiedProperties).newValue) 12 | | mv-expand todynamic(newValue) 13 | | extend RedirectURI = tostring(newValue.Address) 14 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 15 | | extend ['Service Principal Name'] = tostring(TargetResources.displayName) 16 | | summarize ['List of Redirect URIs']=make_list(RedirectURI) by Actor, ['Service Principal Name'] -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-ConditionalAccessMostFailures.kql: -------------------------------------------------------------------------------- 1 | //Find which users are failing the most Conditional Access policies, retrieve the total failure count, distinct policy count and the names of the failed policies 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago (30d) 7 | | project TimeGenerated, ConditionalAccessPolicies, UserPrincipalName 8 | | mv-expand ConditionalAccessPolicies 9 | | extend CAResult = tostring(ConditionalAccessPolicies.result) 10 | | extend CAPolicyName = tostring(ConditionalAccessPolicies.displayName) 11 | | where CAResult == "failure" 12 | | summarize 13 | ['Total Conditional Access Failures']=count(), 14 | ['Distinct Policy Failure Count']=dcount(CAPolicyName), 15 | ['Policy Names']=make_set(CAPolicyName) 16 | by UserPrincipalName 17 | | sort by ['Distinct Policy Failure Count'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-VisualizeControlsvsNoControls.kql: -------------------------------------------------------------------------------- 1 | //Visualize signins to your Azure AD tenant with no controls (known device, known location or MFA) vs those with at least one of those controls 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago(30d) 7 | | where ResultType == 0 8 | | extend DeviceTrustType = tostring(DeviceDetail.trustType) 9 | | summarize 10 | ['Signins with no controls']=countif(NetworkLocationDetails == '[]' and AuthenticationRequirement == "singleFactorAuthentication" and isempty(DeviceTrustType)), 11 | ['Signins with one or more controls']=countif(NetworkLocationDetails != '[]' or AuthenticationRequirement == "multiFactorAuthentication" or isnotempty(DeviceTrustType)) by 12 | bin(TimeGenerated, 1d) 13 | | render timechart with (title="Azure AD signins no controls vs one or more controls") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-ASRSummary.kql: -------------------------------------------------------------------------------- 1 | //Provides a summary of Attack Surface Reduction rules, which ASR rules are being hit and by which processes 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | //Microsoft Sentinel query 6 | DeviceEvents 7 | | where TimeGenerated > ago(30d) 8 | | where ActionType startswith "Asr" 9 | | where isnotempty(InitiatingProcessCommandLine) 10 | | summarize ['ASR Hit Count']=count()by ActionType, InitiatingProcessCommandLine 11 | | sort by ['ASR Hit Count'] desc 12 | 13 | //Advanced Hunting query 14 | 15 | //Data connector required for this query - Advanced Hunting license 16 | 17 | DeviceEvents 18 | | where TimeGenerated > ago(30d) 19 | | where ActionType startswith "Asr" 20 | | where isnotempty(InitiatingProcessCommandLine) 21 | | summarize ['ASR Hit Count']=count()by ActionType, InitiatingProcessCommandLine 22 | | sort by ['ASR Hit Count'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-Windows11DevicesandUsers.kql: -------------------------------------------------------------------------------- 1 | //Finds Windows 11 devices enrolled in Defender for Endpoint and the last user who logged on interactively 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | DeviceInfo 6 | | where TimeGenerated > ago(60d) 7 | | where isnotempty( OSPlatform) 8 | | summarize arg_max(TimeGenerated, *) by DeviceName 9 | | extend OSBuildString = tostring(OSBuild) 10 | | where OSPlatform == "Windows11" or OSBuildString startswith "22" or OSBuildString startswith "21" 11 | | project DeviceName, OSBuild, OSPlatform, OSVersion 12 | |join kind=inner ( 13 | DeviceLogonEvents 14 | | where LogonType == "Interactive" 15 | | where ActionType == "LogonSuccess" 16 | | where InitiatingProcessCommandLine == "lsass.exe" 17 | | summarize arg_max(TimeGenerated, *) by DeviceName 18 | ) on DeviceName 19 | | project DeviceName, AccountName, OSBuild, OSPlatform, OSVersion -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Active Directory/SecurityEvent-LogonToDeviceListChanged.kql: -------------------------------------------------------------------------------- 1 | //Alert when the 'Log on to' device list is changed for a user 2 | 3 | //Data connector required for this query - Windows Security Events via AMA or Security Events via Legacy Agent 4 | 5 | SecurityEvent 6 | | where EventID == 4738 7 | | where AccountType == "User" 8 | //Include domain accounts only (excluding local accounts) 9 | | where TargetDomainName == SubjectDomainName 10 | | extend ['Allowed Devices'] = case(isnotempty(UserWorkstations) and UserWorkstations != "-" and UserWorkstations != "%%1793", split(UserWorkstations, ","), 11 | (isnotempty(UserWorkstations) and UserWorkstations == "%%1793"), strcat("User can log onto all devices"), 12 | "unknown") 13 | //Exclude other 4738 events where the device list isn't changed 14 | | where ['Allowed Devices'] != "unknown" 15 | | project TimeGenerated, Actor=SubjectAccount, User=TargetAccount, ['Allowed Devices'] -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Active Directory/SecurityEvent-VisualizeAccountsCreatedDisabledDeleted.kql: -------------------------------------------------------------------------------- 1 | //Visualize Active Directory accounts created, disabled and deleted per day 2 | 3 | //Data connector required for this query - Windows Security Events via AMA or Security Events via Legacy Agent 4 | 5 | SecurityEvent 6 | | where TimeGenerated > ago(30d) 7 | | where AccountType == "User" 8 | | project TimeGenerated, Account, EventID, TargetAccount 9 | | where EventID in ("4720", "4725", "4726") 10 | | where TargetAccount !endswith "$" 11 | | summarize 12 | ['Accounts Created']=countif(EventID == "4720"), 13 | ['Accounts Deleted']=countif(EventID == "4726"), 14 | ['Accounts Disabled']=countif(EventID == "4725") 15 | by startofday(TimeGenerated) 16 | | render columnchart 17 | with ( 18 | kind=unstacked, 19 | xtitle="Day", 20 | ytitle="Count", 21 | title="Active Directory User Accounts Created, Disabled and Deleted per day") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-VisualizeGuestsAddedRemovedfromTeams.kql: -------------------------------------------------------------------------------- 1 | //Visualize guests added vs removed from Teams per day over the last 30 days 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | OfficeActivity 6 | | where TimeGenerated > ago(30d) 7 | | where UserType == "Regular" 8 | | where CommunicationType == "Team" 9 | | where OfficeWorkload == "MicrosoftTeams" 10 | | where Operation in ("MemberAdded", "MemberRemoved") 11 | | mv-expand Members 12 | | extend User = tostring(Members.UPN) 13 | | where User contains "#EXT#" 14 | | project TimeGenerated, Operation, User 15 | | summarize 16 | ['Guests Added']=countif(Operation == "MemberAdded"), 17 | ['Guests Removed']=countif(Operation == "MemberRemoved") 18 | by startofday(TimeGenerated) 19 | | render columnchart 20 | with ( 21 | kind=unstacked, 22 | xtitle="Count", 23 | ytitle="Day", 24 | title="Guests Added vs Removed from Teams") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Security Alert/SecurityAlert-FindSigninsforAnomalousToken.kql: -------------------------------------------------------------------------------- 1 | //When an anomalous token alert is flagged, find the specific risk events that flagged the alert 2 | 3 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 4 | //Data connector required for this query - Azure Active Directory - AAD User Risk Events 5 | 6 | let alerts= 7 | SecurityAlert 8 | | where TimeGenerated > ago(1d) 9 | | where AlertName == "Anomalous Token" 10 | | mv-expand todynamic(Entities) 11 | | project Entities 12 | | extend RequestId = tostring(Entities.SessionId) 13 | | distinct RequestId; 14 | //Detections can be offline so retrieve a weeks worth of risk data 15 | AADUserRiskEvents 16 | | where TimeGenerated > ago(7d) 17 | | where RequestId in (alerts) 18 | | project TimeGenerated, UserPrincipalName, RiskEventType, RiskLevel, DetectionTimingType, IpAddress, Location -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-DetectUsermadeOwneronmultipleTeams.kql: -------------------------------------------------------------------------------- 1 | //Detect when a user is made an owner on multiple Teams in a short time frame. 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | //Define a time period to check and the threshold of how many Teams to alert on. 6 | //This example would find users added as an owner to 3 or more Teams within 30 minutes. 7 | let timeframe=30m; 8 | let threshold=3; 9 | OfficeActivity 10 | | where TimeGenerated > ago(1d) 11 | | where Operation == "MemberRoleChanged" 12 | | mv-expand Members 13 | | extend RoleAdded = tostring(Members.Role) 14 | | extend UserAdded = tostring(Members.UPN) 15 | | where RoleAdded == 2 16 | | project TimeGenerated, RoleAdded, UserAdded, TeamName 17 | | summarize 18 | ['Number of Teams Made Owner']=dcount(TeamName), ['Team Names']=make_set(TeamName) by UserAdded, bin(TimeGenerated, timeframe) 19 | | where ['Number of Teams Made Owner'] >= threshold -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Security Alert/SecurityAlert-WhichTablesAreInUse.kql: -------------------------------------------------------------------------------- 1 | //Find which data sources are being used the most, by calculating which analytics rules are querying which tables when alerts are generated 2 | 3 | //This will not account for the use of functions and may double handle table names occasionally, so it can be used as just a rough guide 4 | 5 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 6 | 7 | let tablenames = search * 8 | | summarize make_set($table); 9 | SecurityAlert 10 | | where TimeGenerated > ago (30d) 11 | | where ProviderName == "ASI Scheduled Alerts" 12 | | summarize arg_max(TimeGenerated, *) by SystemAlertId 13 | | extend Query = tostring(parse_json(ExtendedProperties).Query) 14 | | mv-apply table=toscalar(tablenames) to typeof(string) on (where Query contains ['table']) 15 | | summarize QueryCount = count()by ['table'], AlertName 16 | | order by QueryCount -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-WindowsVersionPivotTable.kql: -------------------------------------------------------------------------------- 1 | //Create a pivot table of all Windows OS versions in your environment 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | //Microsoft Sentinel query 6 | DeviceInfo 7 | | where TimeGenerated > ago(30d) 8 | | where isnotempty(OSBuild) 9 | | summarize arg_max(TimeGenerated, *) by DeviceId 10 | | where isnotempty(OSPlatform) 11 | | evaluate pivot(OSBuild, count(), OSPlatform) 12 | | where OSPlatform contains "Windows" 13 | | sort by OSPlatform desc 14 | 15 | //Advanced Hunting query 16 | 17 | //Data connector required for this query - Advanced Hunting license 18 | 19 | DeviceInfo 20 | | where Timestamp > ago(30d) 21 | | where isnotempty(OSBuild) 22 | | summarize arg_max(Timestamp, *) by DeviceName 23 | | where isnotempty(OSPlatform) 24 | | evaluate pivot(OSBuild, count(), OSPlatform) 25 | | where OSPlatform contains "Windows" 26 | | sort by OSPlatform desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-CalculateRiskyUsers.kql: -------------------------------------------------------------------------------- 1 | //Calculate the percentage for all your Azure AD users considered risky. Those requiring single factor authentication, coming from an unknown location and from an unknown device 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago (30d) 7 | | where ResultType == 0 8 | //Include only member accounts if you want to ignore guest signins 9 | | where UserType == "Member" 10 | | extend DeviceTrustType = tostring(DeviceDetail.trustType) 11 | | summarize 12 | ['Total Signins']=count(), 13 | ['At Risk Signins']=countif(NetworkLocationDetails == '[]' and isempty(DeviceTrustType) and AuthenticationRequirement == "singleFactorAuthentication") 14 | by UserPrincipalName 15 | | extend ['At Risk Percentage']=(todouble(['At Risk Signins']) * 100 / todouble(['Total Signins'])) 16 | | sort by ['At Risk Percentage'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-UserswithPrivRolesbutnoActivity.kql: -------------------------------------------------------------------------------- 1 | //Find users who hold a privileged Azure AD role but haven't completed any activities in Azure AD for 45 days 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | //Data connector required for this query - Microsoft Sentinel UEBA 5 | 6 | //Lookup the IdentityInfo table for any users holding a privileged role 7 | IdentityInfo 8 | | where TimeGenerated > ago(21d) 9 | | summarize arg_max(TimeGenerated, *) by AccountUPN 10 | | where isnotempty(AssignedRoles) 11 | | where AssignedRoles != "[]" 12 | | project UserPrincipalName=AccountUPN, AssignedRoles 13 | | join kind=leftanti ( 14 | AuditLogs 15 | | where TimeGenerated > ago(45d) 16 | | extend UserPrincipalName = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 17 | | where isnotempty(UserPrincipalName) 18 | | distinct UserPrincipalName 19 | ) 20 | on UserPrincipalName -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-VisualizeMFAChallengevsPreviouslySatisfied.kql: -------------------------------------------------------------------------------- 1 | //Visualize when your users are actively challenged for MFA vs when it was previously satisfied 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago(90d) 7 | | where AuthenticationRequirement == "multiFactorAuthentication" 8 | | mv-expand todynamic(AuthenticationDetails) 9 | | project TimeGenerated, AuthenticationDetails 10 | | extend MFAResultStep = tostring(AuthenticationDetails.authenticationStepResultDetail) 11 | | summarize 12 | MFARequired=countif(MFAResultStep == "MFA completed in Azure AD"), 13 | PreviouslySatisfied=countif(MFAResultStep == "MFA requirement satisfied by claim in the token") 14 | by bin(TimeGenerated, 1d) 15 | | render timechart 16 | with ( 17 | xtitle="Day", 18 | ytitle="Count", 19 | title="MFA challenges vs MFA previously satisfied over time") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Security Alert/SecurityAlert-MalwareDetectedinISO.kql: -------------------------------------------------------------------------------- 1 | //When Defender for Endpoint detects malware in an ISO file retrieve the ISO file name, which directory it was found in and associated file hashes 2 | 3 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 4 | 5 | SecurityAlert 6 | | where ProviderName == "MDATP" 7 | | where AlertName == "Malware was detected in an iso disc image file" 8 | | mv-expand todynamic(Entities) 9 | | extend Hashes = Entities.FileHashes 10 | | mv-expand Hashes 11 | | extend ['ISO File Name'] = tostring(Entities.Name) 12 | | extend Directory = tostring(Entities.Directory) 13 | | extend ['Hash Type'] = tostring(Hashes.Algorithm) 14 | | extend Hash = tostring(Hashes.Value) 15 | | where isnotempty(['ISO File Name']) 16 | | project 17 | TimeGenerated, 18 | CompromisedEntity, 19 | ['ISO File Name'], 20 | Directory, 21 | ['Hash Type'], 22 | Hash -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Windows Security Events/SecEvents-FindLateralMovementUsers.kql: -------------------------------------------------------------------------------- 1 | //Use your Windows security log to find the users most at risk for lateral movement by finding those that have connected remotely to the most devices 2 | 3 | //Data connector required for this query - Windows Security Events via AMA or Security Events via Legacy Agent 4 | 5 | SecurityEvent 6 | | where TimeGenerated > ago (30d) 7 | | where EventID == "4624" 8 | | where LogonType == 10 9 | | where SubjectDomainName == TargetDomainName 10 | //Summarize total logins, distinct devices and then list all the devices each account has logged onto 11 | //Account is dropped to lower case to make sure each account is only listed once, i.e Reprise99 and reprise99 are combined 12 | | summarize 13 | ['Total logon count']=count(), 14 | ['Distinct device logon count']=dcount(Computer), 15 | ['List of devices']=make_set(Computer) 16 | by tolower(Account) 17 | | sort by ['Distinct device logon count'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-UsersWhoHaventElevatedPIM.kql: -------------------------------------------------------------------------------- 1 | //Find users who have not elevated any roles in Azure AD PIM in 30 days 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where TimeGenerated > ago (365d) 7 | | project TimeGenerated, OperationName, Result, TargetResources, InitiatedBy 8 | | where OperationName == "Add member to role completed (PIM activation)" 9 | | where Result == "success" 10 | | extend ['Last Role Activated'] = tostring(TargetResources[0].displayName) 11 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 12 | | summarize arg_max(TimeGenerated, *) by Actor 13 | | project 14 | Actor, 15 | ['Last Role Activated'], 16 | ['Last Activation Time']=TimeGenerated, 17 | ['Days Since Last Activation']=datetime_diff("day", now(), TimeGenerated) 18 | | where ['Days Since Last Activation'] >= 30 19 | | sort by ['Days Since Last Activation'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Cloud Apps/DCA-FindAzureADAdminActions.kql: -------------------------------------------------------------------------------- 1 | //Use the Defender for Cloud Apps logs to detect when an action is taken in Azure Active Directory that is considered an admin operation 2 | 3 | //Data connector required for this query - M365 Defender - CloudAppEvents 4 | 5 | //Microsoft Sentinel query 6 | CloudAppEvents 7 | | where Application == "Office 365" 8 | | extend Workload=RawEventData.Workload 9 | | where Workload == "AzureActiveDirectory" 10 | | where IsAdminOperation == "true" 11 | | project TimeGenerated, ActionType, AccountDisplayName, ActivityType, RawEventData 12 | 13 | //Advanced Hunting query 14 | 15 | //Data connector required for this query - Advanced Hunting license 16 | 17 | CloudAppEvents 18 | | where Application == "Office 365" 19 | | extend Workload=RawEventData.Workload 20 | | where Workload == "AzureActiveDirectory" 21 | | where IsAdminOperation == "1" 22 | | project Timestamp, ActionType, AccountDisplayName, ActivityType, RawEventData -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-AppswithmostSFAPrivUsers.kql: -------------------------------------------------------------------------------- 1 | //Find the applications that have the most privileged users accessing them using only single factor authentication 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | //Data connector required for this query - Microsoft Sentinel UEBA 5 | 6 | let privusers= 7 | IdentityInfo 8 | | where TimeGenerated > ago(21d) 9 | | summarize arg_max(TimeGenerated, *) by AccountUPN 10 | | where isnotempty(AssignedRoles) 11 | | where AssignedRoles != "[]" 12 | | distinct AccountUPN; 13 | SigninLogs 14 | | where TimeGenerated > ago(30d) 15 | | where UserPrincipalName in (privusers) 16 | | where ResultType == 0 17 | | where AuthenticationRequirement == "singleFactorAuthentication" 18 | | summarize 19 | ['List of Users']=make_set(UserPrincipalName), 20 | ['Count of Users']=dcount(UserPrincipalName) 21 | by AppDisplayName 22 | | sort by ['Count of Users'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-FirstPartyApps.kql: -------------------------------------------------------------------------------- 1 | //Create a temporary table of first party apps from the following sources which are updated daily in the following order 2 | // 1. Microsoft Graph (apps where appOwnerOrganizationId is Microsoft) 3 | // 2. Microsoft Learn doc (https://learn.microsoft.com/troubleshoot/azure/active-directory/verify-first-party-apps-sign-in) 4 | // 3. Custom list of apps (./customdata/MysteryApps.csv) - Community contributed list of Microsoft apps and their app ids 5 | //You can then look up / join this to any data that does not have the friendly name or just use it as a reference 6 | //See https://github.com/merill/microsoft-info for the reference source 7 | 8 | let FirstPartyApps = externaldata (AppId:guid,AppDisplayName:string,AppOwnerOrganizationId:guid,Source:string) [ 9 | h@'https://raw.githubusercontent.com/merill/microsoft-info/main/_info/MicrosoftApps.json' 10 | ] with(format='multijson'); 11 | FirstPartyApps -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-CustomSecurityAttributeSet.kql: -------------------------------------------------------------------------------- 1 | //Detect when a custom security attribute is set on a user 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where OperationName == "Update attribute values assigned to a user" 7 | | extend x = tostring(parse_json(tostring(TargetResources[0].modifiedProperties))[0].displayName) 8 | | extend ["Attribute Value"] = tostring(parse_json(tostring(parse_json(tostring(TargetResources[0].modifiedProperties))[0].newValue))[0]) 9 | | extend Target = tostring(TargetResources[0].userPrincipalName) 10 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 11 | | parse x with * '.' ['Attribute Set Name'] "_" * 12 | | extend ["Attribute Name"]=split(x, "_")[1] 13 | | project 14 | TimeGenerated, 15 | OperationName, 16 | Target, 17 | ['Attribute Set Name'], 18 | ['Attribute Name'], 19 | ['Attribute Value'], 20 | Actor -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-DetectNewPrivilegedGroupAdded.kql: -------------------------------------------------------------------------------- 1 | //Detect when a group is added to Azure AD with the 'Azure AD roles can be assigned to this group' flag enabled 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | AuditLogs 6 | | where TimeGenerated > ago(90d) 7 | | where OperationName == "Add group" 8 | | where parse_json(tostring(TargetResources[0].modifiedProperties))[1].displayName == "IsAssignableToRole" 9 | | where parse_json(tostring(parse_json(tostring(TargetResources[0].modifiedProperties))[1].newValue))[0] == true 10 | | extend GroupName = tostring(parse_json(tostring(parse_json(tostring(TargetResources[0].modifiedProperties))[0].newValue))[0]) 11 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 12 | | extend ['Actor IP Address'] = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress) 13 | | project TimeGenerated, OperationName, GroupName, Actor, ['Actor IP Address'] 14 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-UserReportedSuspiciousMFA.kql: -------------------------------------------------------------------------------- 1 | //Detect when a user reports suspicious MFA activity via the updated user risk integration 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where ResultType == 500121 7 | | mv-expand todynamic(AuthenticationDetails) 8 | | extend AuthResultDetail=AuthenticationDetails.authenticationStepResultDetail 9 | | where AuthResultDetail == "SuspiciousActivityReported" 10 | | project TimeGenerated, UserPrincipalName, ResultType, AppDisplayName, AuthResultDetail, Location, IPAddress, UserAgent, CorrelationId 11 | 12 | //These events are also written to Azure AD Identity Protection 13 | 14 | //Data connector required for this query - Azure Active Directory Identity Protection 15 | 16 | AADUserRiskEvents 17 | | where RiskEventType == "userReportedSuspiciousActivity" 18 | | project TimeGenerated, UserPrincipalName, DetectionTimingType, RiskEventType, RiskState 19 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-DetectRegistryTampering.kql: -------------------------------------------------------------------------------- 1 | //Detect when a user or process attempts to tamper with Defender for Endpoint registry settings 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | DeviceEvents 6 | | where ActionType == "TamperingAttempt" 7 | | extend OriginalRegistryValue = tostring(AdditionalFields.OriginalValue) 8 | | extend Status = tostring(AdditionalFields.Status) 9 | | extend TamperingAction = tostring(AdditionalFields.TamperingAction) 10 | | extend AttemptedRegistryValue = tostring(AdditionalFields.TamperingAttemptedValue) 11 | | extend TargetRegistryKey = tostring(AdditionalFields.Target) 12 | | where TamperingAction == "RegistryModification" 13 | | project 14 | TimeGenerated, 15 | DeviceName, 16 | TamperingAction, 17 | Status, 18 | OriginalRegistryValue, 19 | AttemptedRegistryValue, 20 | TargetRegistryKey, 21 | InitiatingProcessAccountName, 22 | InitiatingProcessCommandLine -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Activity/AzureStorage-FirstTimeStorageKeyEnumeration.kql: -------------------------------------------------------------------------------- 1 | //Detect when a user retrieves keys for Azure storage for the first time compared to the previous time range 2 | 3 | //Data connector required for this query - Azure Activity 4 | 5 | let knownusers= 6 | AzureActivity 7 | | where TimeGenerated > ago(90d) and TimeGenerated < ago(1d) 8 | | where OperationName == "List Storage Account Keys" 9 | | where ActivityStatus == "Succeeded" 10 | | project-rename Actor=Caller 11 | | distinct Actor; 12 | AzureActivity 13 | | where TimeGenerated > ago(1d) 14 | | where OperationName == "List Storage Account Keys" 15 | | where ActivityStatus == "Succeeded" 16 | | project-rename Actor=Caller 17 | | where Actor !in (knownusers) 18 | | project 19 | TimeGenerated, 20 | Actor, 21 | ['Actor IP Address']=CallerIpAddress, 22 | ['Storage Account Name']=Resource, 23 | ['Azure Subscription Id']=SubscriptionId, 24 | ['Azure Resource Group']=ResourceGroup -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-ConditionalAccessPoliciesNotinUse.kql: -------------------------------------------------------------------------------- 1 | //Find Azure AD conditional access policies that have no hits for 'success' or 'failure' over the last month 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | //Check that these policies are configured correctly or still required 6 | SigninLogs 7 | | where TimeGenerated > ago (30d) 8 | | project TimeGenerated, ConditionalAccessPolicies 9 | | mv-expand ConditionalAccessPolicies 10 | | extend CAResult = tostring(ConditionalAccessPolicies.result) 11 | | extend ['Conditional Access Policy Name'] = tostring(ConditionalAccessPolicies.displayName) 12 | | summarize ['Conditional Access Result']=make_set(CAResult) by ['Conditional Access Policy Name'] 13 | | where ['Conditional Access Result'] !has "success" 14 | and ['Conditional Access Result'] !has "failure" 15 | and ['Conditional Access Result'] !has "unknownFutureValue" 16 | | sort by ['Conditional Access Policy Name'] asc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-LegacyAuthPivotTable.kql: -------------------------------------------------------------------------------- 1 | //Create a pivot table showing all your users who have signed in with legacy auth, which applications they are using (such as IMAP or ActiveSync) and the count of each 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | //Microsoft Sentinel query 6 | SigninLogs 7 | | where TimeGenerated > ago(30d) 8 | | where ResultType == 0 9 | | where ClientAppUsed !in ("Mobile Apps and Desktop clients", "Browser") 10 | | where isnotempty(ClientAppUsed) 11 | | evaluate pivot(ClientAppUsed, count(), UserPrincipalName) 12 | 13 | //Advanced Hunting query 14 | 15 | //Data connector required for this query - Advanced Hunting with Azure AD P2 License 16 | 17 | AADSignInEventsBeta 18 | | where Timestamp > ago(30d) 19 | | where ErrorCode == 0 20 | | where ClientAppUsed !in ("Mobile Apps and Desktop clients", "Browser") 21 | | where isnotempty(ClientAppUsed) 22 | | evaluate pivot(ClientAppUsed, count(), AccountUpn) -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-SummarizeGuestDomainbyType.kql: -------------------------------------------------------------------------------- 1 | //Summarize guest activity by external Azure AD guests (those that belong to another Azure AD tenant) vs External Guests (such as Gmail) to your tenant 2 | //For each domain list the total number of signins and distinct user 3 | 4 | //Data connector required for this query - Azure Active Directory - Signin Logs 5 | 6 | SigninLogs 7 | | where TimeGenerated > ago (30d) 8 | | where UserType == "Guest" 9 | | where ResultType == 0 10 | | extend ['Guest Domain'] = tostring(split(UserPrincipalName, "@")[1]) 11 | | summarize 12 | ['External Azure AD Guest Logins']=countif(ResourceTenantId != HomeTenantId), 13 | ['External Azure AD Guest Distinct Users']=dcountif(UserPrincipalName, ResourceTenantId != HomeTenantId), 14 | ['External Guest Logins']=countif(ResourceTenantId == HomeTenantId), 15 | ['External Guest Distinct Users']=dcountif(UserPrincipalName, ResourceTenantId == HomeTenantId) 16 | by ['Guest Domain'] 17 | -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/OfficeActivity-SummaryofExternalActivity.kql: -------------------------------------------------------------------------------- 1 | //Create a set of users external to your organization who have accessed Office files after being shared. Events are grouped by the user who shared the document, and what activities were performed against it and by which external account. 2 | 3 | //Data connector required for this query - Office 365 4 | 5 | OfficeActivity 6 | | project LinkCreatedTime=TimeGenerated, Operation, UserWhoShared=UserId, OfficeObjectId 7 | | where Operation in ('AddedToSecureLink', 'SecureLinkCreated', 'SecureLinkUpdated', 'SharingInvitationCreated') 8 | | join kind=inner 9 | (OfficeActivity 10 | | project LinkClickedTime=TimeGenerated, Operation, UserWhoAccessed=UserId, OfficeObjectId) 11 | on OfficeObjectId 12 | | where UserWhoAccessed !endswith "yourdomain.com" and UserWhoAccessed != "app@sharepoint" 13 | | extend ExternalOperation=Operation1 14 | | summarize ExternalUsers=make_set(UserWhoAccessed) by UserWhoShared, OfficeObjectId, ExternalOperation -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-DetectConditionalAccessChangesAfterHours.kql: -------------------------------------------------------------------------------- 1 | //Detect changes to Azure AD Conditional Access policies on weekends or outside of business hours 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | let Saturday = time(6.00:00:00); 6 | let Sunday = time(0.00:00:00); 7 | AuditLogs 8 | | where OperationName has "conditional access" 9 | // extend LocalTime to your time zone 10 | | extend LocalTime=TimeGenerated + 5h 11 | // Change hours of the day to suit your company, i.e this would find activations between 6pm and 6am 12 | | where dayofweek(LocalTime) in (Saturday, Sunday) or hourofday(LocalTime) !between (6 .. 18) 13 | | extend ['Conditional Access Policy Name'] = tostring(TargetResources[0].displayName) 14 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 15 | | project LocalTime, 16 | OperationName, 17 | ['Conditional Access Policy Name'], 18 | Actor 19 | | sort by LocalTime desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-AppsWithMoreGuests.kql: -------------------------------------------------------------------------------- 1 | //Find Azure AD applications that have more guests than members accessing them 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | //Microsoft Sentinel Query 6 | SigninLogs 7 | | where TimeGenerated > ago(30d) 8 | | where ResultType == "0" 9 | | summarize Guests=dcountif(UserPrincipalName,UserType == "Guest"), Members=dcountif(UserPrincipalName,UserType == "Member") by AppDisplayName 10 | | where Guests > Members 11 | | sort by Guests desc 12 | 13 | //Advanced Hunting query 14 | 15 | //Data connector required for this query - Advanced Hunting with Azure AD P2 License 16 | 17 | AADSignInEventsBeta 18 | | where Timestamp > ago(30d) 19 | | where LogonType == @"[""interactiveUser""]" 20 | | where ErrorCode == "0" 21 | | summarize Guests=dcountif(AccountUpn,IsGuestUser == "true"), Members=dcountif(AccountUpn,IsGuestUser == "false") by Application 22 | | where Guests > Members 23 | | sort by Guests desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-InactivePrivilegedUsers.kql: -------------------------------------------------------------------------------- 1 | //Find users who hold privileged Azure AD roles but haven't signed onto Azure for 30 days 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | //Data connector required for this query - Microsoft Sentinel UEBA 5 | 6 | let applications = dynamic(["Azure Active Directory PowerShell", "Microsoft Azure PowerShell", "Graph Explorer", "ACOM Azure Website", "Azure Portal", "Azure Advanced Threat Protection"]); 7 | IdentityInfo 8 | | where TimeGenerated > ago(21d) 9 | | where isnotempty(AssignedRoles) 10 | | project-rename UserPrincipalName=AccountUPN 11 | | where AssignedRoles != "[]" 12 | | summarize arg_max(TimeGenerated, *) by UserPrincipalName 13 | | join kind=leftanti ( 14 | SigninLogs 15 | | where TimeGenerated > ago(30d) 16 | | where AppDisplayName in (applications) 17 | | where ResultType == "0" 18 | ) 19 | on UserPrincipalName 20 | | project UserPrincipalName, AssignedRoles -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-SingleFactorSigninsFromPrivUsers.kql: -------------------------------------------------------------------------------- 1 | //Finds users who hold a privileged Azure Active Directory role who are signing into applications using single factor 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | //Data connector required for this query - Microsoft Sentinel UEBA 5 | 6 | let privusers= 7 | IdentityInfo 8 | | where TimeGenerated > ago(21d) 9 | | summarize arg_max(TimeGenerated, *) by AccountUPN 10 | | where isnotempty(AssignedRoles) 11 | | where AssignedRoles != "[]" 12 | | distinct AccountUPN; 13 | SigninLogs 14 | | where TimeGenerated > ago(30d) 15 | | where UserPrincipalName in~ (privusers) 16 | | where ResultType == 0 17 | | where AuthenticationRequirement == "singleFactorAuthentication" 18 | | summarize 19 | ['List of Applications']=make_set(AppDisplayName), 20 | ['Count of Applications']=dcount(AppDisplayName) 21 | by UserPrincipalName 22 | | sort by ['Count of Applications'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/EmailEvents-VisualizeBlockedEmailDeviation.kql: -------------------------------------------------------------------------------- 1 | //Visualize the deviation of email being blocked to your Office 365 tenant per day 2 | //Query adapted from https://github.com/samikroy/kql-store/blob/main/Deviation%20in%20Security%20Events.md 3 | 4 | //Data connector required for this query - M365 Defender - Email* tables 5 | 6 | //Find the average blocked email per day 7 | let AverageBlockedEmail = toscalar(EmailEvents 8 | | where TimeGenerated > ago(250d) 9 | | where DeliveryAction == "Blocked" 10 | | summarize Count=count() by bin(TimeGenerated, 1d) 11 | | summarize avg(Count)); 12 | //Find the total count of blocked email per day 13 | EmailEvents 14 | | where TimeGenerated > ago(250d) 15 | | where DeliveryAction == "Blocked" 16 | | summarize Count=count() by bin(TimeGenerated, 1d) 17 | | extend Deviation = (Count - AverageBlockedEmail) / AverageBlockedEmail 18 | | project-away Count 19 | //Visualize the deviation per day 20 | | render columnchart with (title="Deviation of email blocked per day") -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Audit-PivotTableofPrivilegedUserActions.kql: -------------------------------------------------------------------------------- 1 | //Create a pivot table showing all the actions taken by your privileged users 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | //Data connector required for this query - Microsoft Sentinel UEBA 5 | 6 | //Lookup the IdentityInfo table for any users holding a privileged role 7 | let privusers= 8 | IdentityInfo 9 | | where TimeGenerated > ago(21d) 10 | | summarize arg_max(TimeGenerated, *) by AccountUPN 11 | | where isnotempty(AssignedRoles) 12 | | where AssignedRoles != "[]" 13 | | distinct AccountUPN; 14 | //Search for all actions taken by those users in the last 7 days 15 | AuditLogs 16 | | where TimeGenerated > ago(7d) 17 | | extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName) 18 | | where Actor in (privusers) 19 | //Create a pivot table counting each action for each user 20 | | evaluate pivot(OperationName, count(), Actor) 21 | | order by Actor asc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-GuestsInvitedbutnotRedeemed.kql: -------------------------------------------------------------------------------- 1 | //Lists guests who have been invited but not yet redeemed their invites. 2 | 3 | //Data connector required for this query - Azure Active Directory - Audit Logs 4 | 5 | //Excludes newly invited guests (last 30 days). 6 | let timerange=365d; 7 | let timeframe=30d; 8 | AuditLogs 9 | | where TimeGenerated between (ago(timerange) .. ago(timeframe)) 10 | | where OperationName == "Invite external user" 11 | | extend GuestUPN = tolower(tostring(TargetResources[0].userPrincipalName)) 12 | | project TimeGenerated, GuestUPN 13 | | join kind=leftanti ( 14 | AuditLogs 15 | | where TimeGenerated > ago (timerange) 16 | | where OperationName == "Redeem external user invite" 17 | | where CorrelationId <> "00000000-0000-0000-0000-000000000000" 18 | | extend d = tolower(tostring(TargetResources[0].displayName)) 19 | | parse d with * "upn: " GuestUPN "," * 20 | | project TimeGenerated, GuestUPN) 21 | on GuestUPN 22 | | distinct GuestUPN -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-ParseUserAgent.kql: -------------------------------------------------------------------------------- 1 | //Parses the user agent into its various components to allow hunting on specific browser versions or patch levels 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | extend UserAgentDetail = todynamic(parse_user_agent(UserAgent, "browser")) 7 | | extend UserAgentFamily = tostring(parse_json(tostring(UserAgentDetail.Browser)).Family) 8 | | extend UserAgentMajorVersion = toint(parse_json(tostring(UserAgentDetail.Browser)).MajorVersion) 9 | | extend UserAgentMinorVersion = toint(parse_json(tostring(UserAgentDetail.Browser)).MinorVersion) 10 | | extend UserAgentPatch = toint(parse_json(tostring(UserAgentDetail.Browser)).Patch) 11 | | project 12 | TimeGenerated, 13 | UserPrincipalName, 14 | AppDisplayName, 15 | ResultType, 16 | IPAddress, 17 | Location, 18 | UserAgentFamily, 19 | UserAgentMajorVersion, 20 | UserAgentMinorVersion, 21 | UserAgentPatch, 22 | UserAgent -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Intune/IntuneDevices-RetrieveDeviceInfoAfterWipe.kql: -------------------------------------------------------------------------------- 1 | //When an Intune admin initiates a remote wipe of a managed device, retrieve all the relevant information about the device 2 | 3 | //Data connector required for this query - Intune data sent to Sentinel workspace 4 | 5 | IntuneAuditLogs 6 | | where TimeGenerated > ago (1d) 7 | | where OperationName == "wipe ManagedDevice" 8 | | extend DeviceId = tostring(parse_json(tostring(parse_json(Properties).TargetObjectIds))[0]) 9 | | project TimeGenerated, Actor=Identity, DeviceId 10 | | join kind=inner( 11 | IntuneDevices 12 | //Go back 7 days to make sure we have information on the device and retrieve the lastest record 13 | | where TimeGenerated > ago(7d) 14 | | summarize arg_max(TimeGenerated, *) by DeviceId 15 | ) 16 | on DeviceId 17 | | project 18 | TimeGenerated, 19 | Actor, 20 | DeviceId, 21 | Model, 22 | SerialNumber, 23 | OS, 24 | PrimaryUser=UserEmail, 25 | Ownership, 26 | ManagedBy, 27 | LastContact -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-SummarizeGuestConditionalAccess.kql: -------------------------------------------------------------------------------- 1 | //Summarize which conditional access policies your inbound guests have logged any successes or failures against. 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago (90d) 7 | | where UserType == "Guest" 8 | | where AADTenantId != HomeTenantId and HomeTenantId != ResourceTenantId 9 | | project ConditionalAccessPolicies, UserPrincipalName, HomeTenantId 10 | | project-rename GuestTenantId=HomeTenantId 11 | | mv-expand ConditionalAccessPolicies 12 | | extend CAResult = tostring(ConditionalAccessPolicies.result) 13 | | extend CAName = tostring(ConditionalAccessPolicies.displayName) 14 | | where CAResult has_any ("success", "failure") 15 | | extend ['Conditional Access Result']=strcat(CAName, " || ", CAResult) 16 | | summarize 17 | ['Conditional Access Outcomes']=make_set(['Conditional Access Result']) 18 | by 19 | GuestTenantId 20 | | order by GuestTenantId asc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Office 365/EmailEvents-MostBlockedDomains.kql: -------------------------------------------------------------------------------- 1 | //Visualize the most blocked domains sending email inbound to your users 2 | 3 | //Data connector required for this query - M365 Defender - Email* tables 4 | 5 | //Microsoft Sentinel query 6 | EmailEvents 7 | | where TimeGenerated > ago (7d) 8 | | where EmailDirection == "Inbound" 9 | | where DeliveryAction == "Blocked" 10 | | extend Domain = tostring(split(SenderMailFromAddress, "@")[-1]) 11 | | summarize BlockedCount=count()by Domain 12 | | where isnotempty(Domain) 13 | | sort by BlockedCount desc 14 | | render barchart 15 | 16 | //Advanced Hunting query 17 | 18 | //Data connector required for this query - Advanced Hunting license 19 | 20 | EmailEvents 21 | | where Timestamp > ago (7d) 22 | | where EmailDirection == "Inbound" 23 | | where DeliveryAction == "Blocked" 24 | | extend Domain = tostring(split(SenderMailFromAddress, "@")[-1]) 25 | | summarize BlockedCount=count()by Domain 26 | | where isnotempty(Domain) 27 | | sort by BlockedCount desc 28 | | render barchart -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Azure Active Directory/Identity-MultipleCAFailures.kql: -------------------------------------------------------------------------------- 1 | //Detect when a user is blocked by Conditional Access after failing 3 unique CA policies or 3 unique applications over a 2 hour period 2 | 3 | //Data connector required for this query - Azure Active Directory - Signin Logs 4 | 5 | SigninLogs 6 | | where TimeGenerated > ago(1d) 7 | | where ResultType == "53003" 8 | | mv-expand ConditionalAccessPolicies 9 | | extend ['CA Policy Name'] = tostring(ConditionalAccessPolicies.displayName) 10 | | where ConditionalAccessPolicies.result == "failure" 11 | | summarize 12 | ['Total count of logon failures']=count(), 13 | ['Count of failed applications']=dcount(AppDisplayName), 14 | ['List of failed applications']=make_set(AppDisplayName), 15 | ['Count of failed policy names']=dcount(['CA Policy Name']), 16 | ['List of failed policy names']=make_set(['CA Policy Name']) 17 | by UserPrincipalName, bin(TimeGenerated, 2h) 18 | | where ['Count of failed applications'] >= 3 or ['Count of failed policy names'] >= 3 -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Defender for Endpoint/Device-LocalUserswithAdmin.kql: -------------------------------------------------------------------------------- 1 | //Summarize all local user accounts (non-domain) per device which have logged on with administrative rights 2 | 3 | //Data connector required for this query - M365 Defender - Device* tables 4 | 5 | DeviceLogonEvents 6 | | where TimeGenerated > ago(7d) 7 | | project 8 | TimeGenerated, 9 | AdditionalFields, 10 | IsLocalAdmin, 11 | LogonType, 12 | DeviceName, 13 | AccountDomain, 14 | AccountName 15 | | extend LocalLogon = toboolean(AdditionalFields.IsLocalLogon) 16 | | where LocalLogon == true 17 | | where IsLocalAdmin == true 18 | | where LogonType == "Interactive" 19 | | distinct AccountDomain, AccountName, DeviceName 20 | //Split domain from device name to match for local logons 21 | | extend Device = split(DeviceName, ".")[0] 22 | | where Device == AccountDomain 23 | | summarize ['Local Accounts with Admin']=make_set(AccountName), ['Count of Admin Accounts']=dcount(AccountName) by DeviceName 24 | | sort by ['Count of Admin Accounts'] desc -------------------------------------------------------------------------------- /Sentinel-KQL-Sorguları/Security Alert/SecurityAlert-RetrieveEmailforSuspiciousEmailPatterns.kql: -------------------------------------------------------------------------------- 1 | //When a user is flagged for suspicious email sending patterns retrieve all the email they have sent around the time of the Alert 2 | 3 | //Data connector required for this query - Security Alert (free table that other Defender products send alert info to) 4 | //Data connector required for this query - M365 Defender - Email* tables 5 | 6 | SecurityAlert 7 | | where TimeGenerated > ago (7d) 8 | | where ProviderName == "OATP" 9 | | where AlertName == "Suspicious email sending patterns detected" 10 | | mv-expand todynamic(Entities) 11 | | extend SenderFromAddress = tolower(tostring(Entities.MailboxPrimaryAddress)) 12 | | project AlertTime=TimeGenerated, SenderFromAddress 13 | | join kind=inner ( 14 | EmailEvents 15 | ) 16 | on SenderFromAddress 17 | | where EmailDirection == "Outbound" 18 | | where TimeGenerated between ((AlertTime - timespan(1h)) .. (AlertTime + timespan(1h))) 19 | | project TimeGenerated, Subject, AttachmentCount, RecipientEmailAddress --------------------------------------------------------------------------------