diff --git a/hunting/azure/docs/entra_suspicious_odata_client_requests.md b/hunting/azure/docs/entra_suspicious_odata_client_requests.md new file mode 100644 index 000000000..c0da0cbe7 --- /dev/null +++ b/hunting/azure/docs/entra_suspicious_odata_client_requests.md @@ -0,0 +1,79 @@ +# Microsoft Entra Infrequent Suspicious OData Client Requests + +--- + +## Metadata + +- **Author:** Elastic +- **Description:** Identifies infrequent OData client requests in Microsoft Entra ID. This behavior may indicate an adversary using a custom or Azure-managed app ID to authenticate on behalf of a user. This is a rare event and may indicate an attempt to bypass conditional access policies (CAP) and multi-factor authentication (MFA) requirements. The app ID specified may not be commonly used by the user based on their historical sign-in activity. The OData client is used in ROADTools, a toolset leveraged by threat actors to automate OAuth and OIDC workflows in Microsoft Entra ID following phishing or token theft. + +- **UUID:** `0d3d2254-2b4a-11f0-a019-f661ea17fbcc` +- **Integration:** [azure](https://docs.elastic.co/integrations/azure) +- **Language:** `[ES|QL]` +- **Source File:** [Microsoft Entra Infrequent Suspicious OData Client Requests](../queries/entra_suspicious_odata_client_requests.toml) + +## Query + +```sql +FROM logs-azure.auditlogs* METADATA _id, _index + +// Only Microsoft Entra ID audit logs +| WHERE event.dataset == "azure.auditlogs" + +// Identify logs with the known suspicious OData user agent + AND azure.auditlogs.properties.additional_details.value LIKE "Microsoft.OData.Client/*" + AND azure.auditlogs.identity != "Device Registration Service" + +// Extract time window for pattern analysis +| EVAL time_window = DATE_TRUNC(1d, @timestamp) + +// Normalize actor: prefer user UPN if available, else fallback to app name +| EVAL actor = COALESCE( + azure.auditlogs.properties.initiated_by.user.userPrincipalName, + azure.auditlogs.properties.initiated_by.app.displayName, + "unknown" +) + +// Keep core fields +| KEEP @timestamp, actor, source.ip, azure.auditlogs.operation_name, azure.auditlogs.properties.activity_display_name, azure.auditlogs.identity, azure.auditlogs.properties.tenantId, azure.auditlogs.properties.initiated_by.app.servicePrincipalId, azure.auditlogs.properties.category, azure.auditlogs.properties.additional_details.value, time_window + +// Group by actor per day +| STATS + count = COUNT(), + unique_ips = COUNT_DISTINCT(source.ip), + operations = VALUES(azure.auditlogs.operation_name), + identities = VALUES(azure.auditlogs.identity), + ips = VALUES(source.ip), + categories = VALUES(azure.auditlogs.properties.category), + clients = VALUES(azure.auditlogs.properties.additional_details.value) + BY actor, time_window + +// Optional: prioritize less frequent actors +| SORT count ASC +``` + +## Notes + +- Review `azure.auditlogs.properties.additional_details.value` for `Microsoft.OData.Client/*` User-Agent strings. This is uncommon for legitimate first-party Microsoft applications and may indicate use of ROADTools or custom automation to register a device and obtain a PRT. +- Check `azure.auditlogs.properties.initiated_by` for both `user` and `app` fields. The presence of both may suggest an OAuth on-behalf-of (OBO) flow, where the app is acting with delegated permissions from a phished user token. +- Review `azure.auditlogs.properties.activity_display_name` and `operation_name` for device registration operations like `Add device` or `Add registered owner to device`. When combined with suspicious user-agents, this may indicate unauthorized device registration. +- Review `azure.auditlogs.identity` for operations performed by `Device Registration Service`. This service name is commonly associated with device join flows that, if abused, may enable persistence via PRT acquisition. +- Correlate with `azure.signinlogs` for sign-ins using the same `correlation_id` or `userPrincipalName`. Look for signs of previous OAuth token issuance or multi-geo IP behavior surrounding the device registration. +- Investigate whether the device registered (under `azure.auditlogs.properties.target_resources`) corresponds to known or authorized endpoints. Devices with names like `DESKTOP-ATTACKER1` or unexpected OS versions may indicate rogue joins. +- The source IP is likely to be Microsoft-managed infrastructure as requests are proxied through Azure services. Pivoting into which user principal the request is targeting and events leading up to the request may provide additional context. + +## MITRE ATT&CK Techniques + +- [T1078.004](https://attack.mitre.org/techniques/T1078/004) +- [T1550.001](https://attack.mitre.org/techniques/T1550/001) +- [T1098.005](https://attack.mitre.org/techniques/T1098/005) +- [T1071.001](https://attack.mitre.org/techniques/T1071/001) +- [T1556.006](https://attack.mitre.org/techniques/T1556/006) + +## References + +- https://www.volexity.com/blog/2025/04/22/phishing-for-codes-russian-threat-actors-target-microsoft-365-oauth-workflows/ + +## License + +- `Elastic License v2` diff --git a/hunting/azure/queries/entra_suspicious_odata_client_requests.toml b/hunting/azure/queries/entra_suspicious_odata_client_requests.toml new file mode 100644 index 000000000..4b9aa7932 --- /dev/null +++ b/hunting/azure/queries/entra_suspicious_odata_client_requests.toml @@ -0,0 +1,66 @@ +[hunt] +author = "Elastic" +description = """ +Identifies infrequent OData client requests in Microsoft Entra ID. This behavior may indicate an adversary using a custom or Azure-managed app ID to authenticate on behalf of a user. This is a rare event and may indicate an attempt to bypass conditional access policies (CAP) and multi-factor authentication (MFA) requirements. The app ID specified may not be commonly used by the user based on their historical sign-in activity. The OData client is used in ROADTools, a toolset leveraged by threat actors to automate OAuth and OIDC workflows in Microsoft Entra ID following phishing or token theft. +""" +integration = ["azure"] +uuid = "0d3d2254-2b4a-11f0-a019-f661ea17fbcc" +name = "Microsoft Entra Infrequent Suspicious OData Client Requests" +language = ["ES|QL"] +license = "Elastic License v2" +notes = [ + "Review `azure.auditlogs.properties.additional_details.value` for `Microsoft.OData.Client/*` User-Agent strings. This is uncommon for legitimate first-party Microsoft applications and may indicate use of ROADTools or custom automation to register a device and obtain a PRT.", + "Check `azure.auditlogs.properties.initiated_by` for both `user` and `app` fields. The presence of both may suggest an OAuth on-behalf-of (OBO) flow, where the app is acting with delegated permissions from a phished user token.", + "Review `azure.auditlogs.properties.activity_display_name` and `operation_name` for device registration operations like `Add device` or `Add registered owner to device`. When combined with suspicious user-agents, this may indicate unauthorized device registration.", + "Review `azure.auditlogs.identity` for operations performed by `Device Registration Service`. This service name is commonly associated with device join flows that, if abused, may enable persistence via PRT acquisition.", + "Correlate with `azure.signinlogs` for sign-ins using the same `correlation_id` or `userPrincipalName`. Look for signs of previous OAuth token issuance or multi-geo IP behavior surrounding the device registration.", + "Investigate whether the device registered (under `azure.auditlogs.properties.target_resources`) corresponds to known or authorized endpoints. Devices with names like `DESKTOP-ATTACKER1` or unexpected OS versions may indicate rogue joins.", + "The source IP is likely to be Microsoft-managed infrastructure as requests are proxied through Azure services. Pivoting into which user principal the request is targeting and events leading up to the request may provide additional context." +] +mitre = [ + "T1078.004", + "T1550.001", + "T1098.005", + "T1071.001", + "T1556.006", +] +references = ["https://www.volexity.com/blog/2025/04/22/phishing-for-codes-russian-threat-actors-target-microsoft-365-oauth-workflows/"] +query = [ +''' +FROM logs-azure.auditlogs* METADATA _id, _index + +// Only Microsoft Entra ID audit logs +| WHERE event.dataset == "azure.auditlogs" + +// Identify logs with the known suspicious OData user agent + AND azure.auditlogs.properties.additional_details.value LIKE "Microsoft.OData.Client/*" + AND azure.auditlogs.identity != "Device Registration Service" + +// Extract time window for pattern analysis +| EVAL time_window = DATE_TRUNC(1d, @timestamp) + +// Normalize actor: prefer user UPN if available, else fallback to app name +| EVAL actor = COALESCE( + azure.auditlogs.properties.initiated_by.user.userPrincipalName, + azure.auditlogs.properties.initiated_by.app.displayName, + "unknown" +) + +// Keep core fields +| KEEP @timestamp, actor, source.ip, azure.auditlogs.operation_name, azure.auditlogs.properties.activity_display_name, azure.auditlogs.identity, azure.auditlogs.properties.tenantId, azure.auditlogs.properties.initiated_by.app.servicePrincipalId, azure.auditlogs.properties.category, azure.auditlogs.properties.additional_details.value, time_window + +// Group by actor per day +| STATS + count = COUNT(), + unique_ips = COUNT_DISTINCT(source.ip), + operations = VALUES(azure.auditlogs.operation_name), + identities = VALUES(azure.auditlogs.identity), + ips = VALUES(source.ip), + categories = VALUES(azure.auditlogs.properties.category), + clients = VALUES(azure.auditlogs.properties.additional_details.value) + BY actor, time_window + +// Optional: prioritize less frequent actors +| SORT count ASC +''' +] \ No newline at end of file diff --git a/hunting/index.md b/hunting/index.md index dbe59aa6b..a15a7e315 100644 --- a/hunting/index.md +++ b/hunting/index.md @@ -37,6 +37,7 @@ Here are the queries currently available: - [Azure Entra Unusual Client App Authentication Requests on Behalf of Principal Users](./azure/docs/entra_unusual_client_app_auth_request_on_behalf_of_user.md) (ES|QL) - [Azure Entra Unusual Failed Authentication Attempts Behind Rare User Agents](./azure/docs/entra_authentication_attempts_behind_rare_user_agents.md) (ES|QL) - [Microsoft Entra ID Credentials Added to Rare Service Principal](./azure/docs/entra_service_principal_credentials_added_to_rare_app.md) (ES|QL) +- [Microsoft Entra Infrequent Suspicious OData Client Requests](./azure/docs/entra_suspicious_odata_client_requests.md) (ES|QL) ## linux diff --git a/hunting/index.yml b/hunting/index.yml index 66344ba75..b29bff454 100644 --- a/hunting/index.yml +++ b/hunting/index.yml @@ -757,3 +757,12 @@ azure: path: ./azure/queries/entra_service_principal_credentials_added_to_rare_app.toml mitre: - T1098.001 + 0d3d2254-2b4a-11f0-a019-f661ea17fbcc: + name: Microsoft Entra Infrequent Suspicious OData Client Requests + path: ./azure/queries/entra_suspicious_odata_client_requests.toml + mitre: + - T1078.004 + - T1550.001 + - T1098.005 + - T1071.001 + - T1556.006