Skip to content

new: Federated Identity Credential Added To Entra Application Or Managed Identity - #6278

Open
earbona23 wants to merge 1 commit into
SigmaHQ:masterfrom
earbona23:rule/entra-federated-identity-credential-added
Open

new: Federated Identity Credential Added To Entra Application Or Managed Identity#6278
earbona23 wants to merge 1 commit into
SigmaHQ:masterfrom
earbona23:rule/entra-federated-identity-credential-added

Conversation

@earbona23

Copy link
Copy Markdown

Summary

New detection rule: Federated Identity Credential Added To Entra Application Or Managed Identity.

Adding a federated identity credential (FIC) establishes workload identity federation — a trust with an external OIDC issuer that can then obtain tokens and authenticate as the target application or service principal without a secret or certificate. An adversary who can modify an application or service principal can add a FIC pointing at an issuer/subject they control (e.g. an attacker-owned GitHub Actions repo) to gain persistence that survives secret and certificate rotation. This is a distinct persistence primitive from adding a password/certificate credential (already covered by azure_app_credential_added.yml) and from domain federation changes (azure_federation_modified.yml).

Detection logic

Fires on the Entra ID audit event for a FIC being added to either object type:

  • OperationName is Update application (app registration) or Update service principal (service principal / managed identity), and
  • TargetResources.modifiedProperties contains FederatedIdentityCredentials.

The modifiedProperties discriminator is what keeps the rule specific — a benign Update service principal event (e.g. a DisplayName change) does not match. Field taxonomy and the modifiedProperties|contains pattern mirror the existing azure_ad_certificate_based_authencation_enabled.yml.

Verified the exact operation name (Update service principal) and the modified-property display name (FederatedIdentityCredentials) against a real Entra audit-log sample, plus Microsoft's workload identity federation documentation and dirkjanm's research.

MITRE ATT&CK

  • T1098.001 — Account Manipulation: Additional Cloud Credentials (Persistence / Privilege Escalation).

Notes

Complements a hardening check for the same technique — flagging FICs on privileged applications/service principals — so the preventive and detective sides line up.

Validation

  • sigma check --validation-config tests/sigma_cli_conf.yml <rule> → 0 errors, 0 issues (pySigma validators-sigmahq 0.21.0).
  • python3 tests/test_rules.py → 11 tests OK.
  • python3 tests/test_logsource.py → 3 tests OK.

@github-actions github-actions Bot added Rules Review Needed The PR requires review labels Sep 6, 2026

@swachchhanda000 swachchhanda000 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do share the relevant logs. thank you

@swachchhanda000 swachchhanda000 added Author Input Required changes the require information from original author of the rules Additional Data Needed labels Sep 9, 2026
@earbona23
earbona23 force-pushed the rule/entra-federated-identity-credential-added branch from 6ae277a to 74f9ec8 Compare September 9, 2026 19:11
@earbona23
earbona23 force-pushed the rule/entra-federated-identity-credential-added branch from 74f9ec8 to 40292fc Compare September 9, 2026 19:14
@earbona23

earbona23 commented Sep 9, 2026

Copy link
Copy Markdown
Author

Thanks for the review. Below is the audit event this rule is written against, plus the field by field mapping.

About the sample: it is a redacted, representative Microsoft Entra ID audit record in the Log Analytics AuditLogs shape. Every identifier, UPN, IP and repository name is a placeholder (RFC 5737 / RFC 2606 reserved space, example.onmicrosoft.com). It is not a capture from a live or third party tenant.

1. FIC added to an app registration (Update application)

{
  "TimeGenerated": "2026-09-08T14:22:07.4821673Z",
  "Category": "ApplicationManagement",
  "OperationName": "Update application",
  "AADOperationType": "Update",
  "Result": "success",
  "LoggedByService": "Core Directory",
  "CorrelationId": "0f4b9a1c-0000-4c2e-9d5a-1111aaaa2222",
  "InitiatedBy": {
    "user": {
      "id": "33333333-3333-4333-8333-333333333333",
      "displayName": null,
      "userPrincipalName": "app.owner@example.onmicrosoft.com",
      "ipAddress": "203.0.113.24",
      "roles": []
    }
  },
  "TargetResources": [
    {
      "id": "44444444-4444-4444-8444-444444444444",
      "displayName": "example-deploy-app",
      "type": "Application",
      "userPrincipalName": null,
      "modifiedProperties": [
        {
          "displayName": "FederatedIdentityCredentials",
          "oldValue": "[]",
          "newValue": "[{\"Name\":\"gh-oidc-main\",\"Issuer\":\"https://token.actions.githubusercontent.com\",\"Subject\":\"repo:example-org/example-repo:ref:refs/heads/main\",\"Audiences\":[\"api://AzureADTokenExchange\"]}]"
        },
        {
          "displayName": "Included Updated Properties",
          "oldValue": null,
          "newValue": "\"FederatedIdentityCredentials\""
        }
      ]
    }
  ]
}

2. FIC added to a managed identity service principal (Update service principal)

Same property, different target object type. Here the issuer is attacker controlled, which is the case the rule is meant to surface.

{
  "TimeGenerated": "2026-09-08T15:04:51.1093820Z",
  "Category": "ApplicationManagement",
  "OperationName": "Update service principal",
  "AADOperationType": "Update",
  "Result": "success",
  "LoggedByService": "Core Directory",
  "CorrelationId": "7c1e2b40-0000-4a11-b3ce-3333bbbb4444",
  "InitiatedBy": {
    "user": {
      "id": "55555555-5555-4555-8555-555555555555",
      "displayName": null,
      "userPrincipalName": "platform.admin@example.onmicrosoft.com",
      "ipAddress": "198.51.100.77",
      "roles": []
    }
  },
  "TargetResources": [
    {
      "id": "66666666-6666-4666-8666-666666666666",
      "displayName": "example-uami-build",
      "type": "ServicePrincipal",
      "userPrincipalName": null,
      "modifiedProperties": [
        {
          "displayName": "FederatedIdentityCredentials",
          "oldValue": "[]",
          "newValue": "[{\"Name\":\"backup-idp\",\"Issuer\":\"https://oidc.example.test\",\"Subject\":\"workload-1\",\"Audiences\":[\"api://AzureADTokenExchange\"]}]"
        }
      ]
    }
  ]
}

Field mapping

Rule selection Location in the event Sample 1 Sample 2
OperationName OperationName Update application Update service principal
TargetResources.modifiedProperties|contains: 'FederatedIdentityCredentials' TargetResources[].modifiedProperties[].displayName FederatedIdentityCredentials FederatedIdentityCredentials

Both conditions are ANDed, so the operation name alone is never enough. The rule does not parse the serialized newValue, so the internal key casing of that blob is not load bearing for matching. It is included above because it is what an analyst reads during triage: Issuer and Subject are the two values that decide whether the FIC is legitimate.

Why the second condition is what keeps this specific

A benign Update service principal, for example a display name change, carries no FederatedIdentityCredentials entry, so selection does not fire:

"modifiedProperties": [
  {
    "displayName": "DisplayName",
    "oldValue": "\"example-uami-build\"",
    "newValue": "\"example-uami-build-prod\""
  }
]

Cross checks on the two operation names

  • Microsoft's audit activity reference lists both Update application and Update service principal under the ApplicationManagement category: https://learn.microsoft.com/en-us/entra/identity/monitoring-health/reference-audit-activities
  • Azure/Azure-Sentinel, Hunting Queries/AuditLogs/ServicePrincipalFederatedIdentityCredentialAdded.yaml keys off where OperationName in~ ("Update service principal") followed by mv-expand ModProp = TargetResources[0].modifiedProperties | where tostring(ModProp.displayName) =~ "FederatedIdentityCredentials".
  • Elastic's persistence_entra_id_service_principal_federated_issuer_modified keys off where event.action == "Update application" followed by where azure.auditlogs.properties.target_resources.0.modified_properties.0.display_name == "FederatedIdentityCredentials", and dissects "Issuer":"..." out of new_value. That is where the serialized shape shown above comes from.

Note for completeness: a FIC created on a user assigned managed identity through ARM (Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/write) is recorded in the Azure activity log, which is a different logsource. The Update service principal branch here covers the directory side of the same technique.

What changed in this push

  • Rebased onto current master, the branch was behind.
  • Added logsource.definition declaring the TargetResources mapping requirement, mirroring azure_user_account_mfa_disable.yml, since this rule depends on that array being mapped.

Gates on the updated rule: sigma check --fail-on-error --fail-on-issues --validation-config tests/sigma_cli_conf.yml reports 0 errors and 0 issues (pySigma-validators-sigmahq 0.21.0), tests/test_rules.py 11 OK, tests/test_logsource.py 3 OK, yamllint clean.

Happy to tighten TargetResources.modifiedProperties|contains into the stricter TargetResources.modifiedProperties.displayName form if you prefer it. I kept the |contains shape to stay consistent with azure_ad_certificate_based_authencation_enabled.yml in the same folder.

Match verification

Compiled the rule and ran it against these samples with json_matcher v0.0.2, the checker regression-tests.yml uses:

$ sigma convert -t golang_expr --without-pipeline \
    rules/cloud/azure/audit_logs/azure_app_federated_identity_credential_added.yml
(lower(OperationName) == lower("Update application") or lower(OperationName) == lower("Update service principal"))
  and lower(TargetResources?.modifiedProperties) contains lower("FederatedIdentityCredentials")

$ ./json_checker -event fic_added.json -expr "$EXPR" -test-type json
MATCH

$ ./json_checker -event display_name_changed.json -expr "$EXPR" -test-type json
NO-MATCH

The events fed to the checker are the ones shown above with TargetResources mapped, which is exactly what the new logsource.definition line asks the backend for. Without that mapping the array does not resolve, which is why declaring it matters here as it does in azure_user_account_mfa_disable.yml.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Additional Data Needed Author Input Required changes the require information from original author of the rules Review Needed The PR requires review Rules

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants