Azure AKS Certificate Signing Request Created or Approved

Detects an identity creating a client-authentication CertificateSigningRequest (signer kubernetes.io/kube-apiserver-client) or approving a CSR on AKS (Azure Kubernetes Service), excluding node bootstrap and platform controllers. Adversaries submit and self-approve a CSR against the kube-apiserver-client signer to mint a long-lived client certificate for an arbitrary subject (for example a Common Name in system:masters), giving durable authenticated access that survives token revocation. Coverage includes workload service accounts (system:serviceaccount:*), so a compromised in-cluster token forging a certificate is not excluded.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2026/07/09"
  3integration = ["azure"]
  4maturity = "production"
  5updated_date = "2026/07/09"
  6
  7[rule]
  8author = ["Elastic"]
  9description = """
 10Detects an identity creating a client-authentication CertificateSigningRequest (signer
 11kubernetes.io/kube-apiserver-client) or approving a CSR on AKS (Azure Kubernetes Service), excluding node bootstrap and
 12platform controllers. Adversaries submit and self-approve a CSR against the kube-apiserver-client signer to mint a
 13long-lived client certificate for an arbitrary subject (for example a Common Name in system:masters), giving durable
 14authenticated access that survives token revocation. Coverage includes workload service accounts
 15(system:serviceaccount:*), so a compromised in-cluster token forging a certificate is not excluded.
 16"""
 17false_positives = [
 18    """
 19    Node bootstrap, cert-manager, and platform controllers legitimately create and approve CSRs. Validate the requesting
 20    identity and the certificate subject, and add exclusions for verified automation.
 21    """,
 22]
 23from = "now-9m"
 24index = ["logs-azure.platformlogs-*"]
 25language = "kuery"
 26license = "Elastic License v2"
 27name = "Azure AKS Certificate Signing Request Created or Approved"
 28note = """## Triage and analysis
 29
 30### Investigating Azure AKS Certificate Signing Request Created or Approved
 31
 32AKS kube-audit events are carried under the flattened `azure.platformlogs.properties.log.*` subtree and share the ARM
 33operation `event.action: Microsoft.ContainerService/managedClusters/diagnosticLogs/Read`. A CSR created against the
 34`kubernetes.io/kube-apiserver-client` signer lets the requester choose the certificate's subject (Common Name and
 35organization/groups); once approved it mints a client certificate for an arbitrary identity that yields access not tied
 36to a token. The default `CertificateSubjectRestriction` admission controller blocks requests for the `system:masters`
 37group, so attackers commonly request a Common Name matching an existing privileged user (or another privileged group)
 38instead, making the requested subject the key thing to decode. Node and kubelet certificates use the
 39`kube-apiserver-client-kubelet` and `kubelet-serving` signers (whose subject is constrained to the node) and are out of
 40scope; cert-manager and application CSRs use their own signers.
 41
 42### Possible investigation steps
 43
 44- Identify the requesting identity in `azure.platformlogs.properties.log.user.username` (and its groups in
 45  `azure.platformlogs.properties.log.user.groups`) and whether it should submit or approve CSRs. A workload service
 46  account (`system:serviceaccount:<ns>:<sa>`) or `masterclient` (the local cluster-admin cert) is the higher-concern
 47  case.
 48- Confirm the signer in `azure.platformlogs.properties.log.requestObject.spec.signerName` and decode the base64 CSR in
 49  `azure.platformlogs.properties.log.requestObject.spec.request` to read the requested Common Name and organization
 50  (groups); a subject in `system:masters` or another privileged group is the escalation.
 51- Determine whether the same or a related identity approved the CSR (`verb:update`/`patch` on the `approval`
 52  subresource), which indicates self-approval, and inspect `azure.platformlogs.properties.log.userAgent`.
 53- Evaluate the source in `azure.platformlogs.properties.log.sourceIPs` and pivot on it for follow-on privileged API
 54  activity using the newly issued certificate.
 55
 56### False positive analysis
 57
 58- Node bootstrap (`kube-apiserver-client-kubelet` signer), kubelet-serving CSRs, and cert-manager/application CSRs
 59  (custom signers) are out of scope by design; the kube-controller-manager `certificate-controller` and the AKS
 60  `aksService` approver are excluded by identity.
 61- Manual CSR approval by an administrator, or an operator that legitimately mints client certificates, may surface;
 62  baseline those identities and exclude the specific validated account rather than re-broadening to all `system:*`,
 63  which would blind the rule to compromised workload service accounts.
 64
 65### Response and remediation
 66
 67- If unauthorized, deny or delete the CSR, revoke the issued certificate, and rotate the cluster CA if a privileged
 68  certificate was minted.
 69- Review the RBAC that allowed CSR creation and approval, and audit actions taken with the certificate.
 70- Collect kube-audit and identity artifacts per incident response procedures.
 71"""
 72references = [
 73    "https://kubernetes.io/docs/reference/access-authn-authz/certificate-signing-requests/",
 74    "https://microsoft.github.io/Threat-Matrix-for-Kubernetes/",
 75    "https://github.com/inguardians/peirates",
 76    "https://raesene.github.io/blog/2022/12/21/Kubernetes-persistence-with-Tocan-and-Teisteanas/",
 77    "https://cloud.google.com/blog/topics/threat-intelligence/escalating-privileges-azure-kubernetes-services",
 78    "https://www.aquasec.com/blog/kubernetes-rbac-privilige-escalation/",
 79]
 80risk_score = 47
 81rule_id = "3f6c636a-1ca5-410c-bdad-f8b59a1f2b49"
 82setup = """The Azure Fleet integration collecting AKS diagnostic logs forwarded through Event Hub into the `azure.platformlogs`
 83data stream is required for this rule. Enable either the `kube-audit` or the `kube-audit-admin` log category (Microsoft
 84recommends `kube-audit-admin` alone to reduce volume, as it only drops read-only get/list events). CSR create and
 85approval are mutating operations recorded in both categories with the same `auditID`, so clusters that enable both
 86categories may generate two alerts per event.
 87"""
 88severity = "medium"
 89tags = [
 90    "Domain: Cloud",
 91    "Domain: Kubernetes",
 92    "Data Source: Azure",
 93    "Data Source: Azure Platform Logs",
 94    "Data Source: Kubernetes",
 95    "Use Case: Threat Detection",
 96    "Tactic: Credential Access",
 97    "Resources: Investigation Guide",
 98]
 99timestamp_override = "event.ingested"
100type = "query"
101
102query = '''
103data_stream.dataset:azure.platformlogs and
104  event.action:"Microsoft.ContainerService/managedClusters/diagnosticLogs/Read" and
105  azure.platformlogs.category:("kube-audit" or "kube-audit-admin") and
106  azure.platformlogs.properties.log.stage:"ResponseComplete" and
107  azure.platformlogs.properties.log.objectRef.resource:"certificatesigningrequests" and
108  azure.platformlogs.properties.log.responseStatus.code: "200" and
109  azure.platformlogs.properties.log.requestObject.status.conditions.type: "Approved" and
110  (
111    (
112      azure.platformlogs.properties.log.verb:"create" and
113      azure.platformlogs.properties.log.requestObject.spec.signerName:"kubernetes.io/kube-apiserver-client"
114    ) or (
115      azure.platformlogs.properties.log.verb:("update" or "patch") and
116      azure.platformlogs.properties.log.objectRef.subresource:"approval"
117    )
118  ) and
119  not azure.platformlogs.properties.log.user.username:(
120    system\:node\:* or system\:bootstrap\:* or "aksService" or "hcpService" or
121    "readinessChecker" or system\:serviceaccount\:kube-system\:*
122  )
123'''
124
125
126[[rule.threat]]
127framework = "MITRE ATT&CK"
128[[rule.threat.technique]]
129id = "T1649"
130name = "Steal or Forge Authentication Certificates"
131reference = "https://attack.mitre.org/techniques/T1649/"
132
133
134[rule.threat.tactic]
135id = "TA0006"
136name = "Credential Access"
137reference = "https://attack.mitre.org/tactics/TA0006/"
138
139[rule.investigation_fields]
140field_names = [
141    "@timestamp",
142    "event.action",
143    "azure.platformlogs.category",
144    "azure.platformlogs.properties.log.verb",
145    "azure.platformlogs.properties.log.user.username",
146    "azure.platformlogs.properties.log.user.groups",
147    "azure.platformlogs.properties.log.userAgent",
148    "azure.platformlogs.properties.log.sourceIPs",
149    "azure.platformlogs.properties.log.objectRef.resource",
150    "azure.platformlogs.properties.log.objectRef.name",
151    "azure.platformlogs.properties.log.objectRef.subresource",
152    "azure.platformlogs.properties.log.requestObject.spec.signerName",
153    "azure.platformlogs.properties.log.requestObject.spec.request",
154    "azure.platformlogs.properties.log.responseStatus.code",
155]

Triage and analysis

Investigating Azure AKS Certificate Signing Request Created or Approved

AKS kube-audit events are carried under the flattened azure.platformlogs.properties.log.* subtree and share the ARM operation event.action: Microsoft.ContainerService/managedClusters/diagnosticLogs/Read. A CSR created against the kubernetes.io/kube-apiserver-client signer lets the requester choose the certificate's subject (Common Name and organization/groups); once approved it mints a client certificate for an arbitrary identity that yields access not tied to a token. The default CertificateSubjectRestriction admission controller blocks requests for the system:masters group, so attackers commonly request a Common Name matching an existing privileged user (or another privileged group) instead, making the requested subject the key thing to decode. Node and kubelet certificates use the kube-apiserver-client-kubelet and kubelet-serving signers (whose subject is constrained to the node) and are out of scope; cert-manager and application CSRs use their own signers.

Possible investigation steps

  • Identify the requesting identity in azure.platformlogs.properties.log.user.username (and its groups in azure.platformlogs.properties.log.user.groups) and whether it should submit or approve CSRs. A workload service account (system:serviceaccount:<ns>:<sa>) or masterclient (the local cluster-admin cert) is the higher-concern case.
  • Confirm the signer in azure.platformlogs.properties.log.requestObject.spec.signerName and decode the base64 CSR in azure.platformlogs.properties.log.requestObject.spec.request to read the requested Common Name and organization (groups); a subject in system:masters or another privileged group is the escalation.
  • Determine whether the same or a related identity approved the CSR (verb:update/patch on the approval subresource), which indicates self-approval, and inspect azure.platformlogs.properties.log.userAgent.
  • Evaluate the source in azure.platformlogs.properties.log.sourceIPs and pivot on it for follow-on privileged API activity using the newly issued certificate.

False positive analysis

  • Node bootstrap (kube-apiserver-client-kubelet signer), kubelet-serving CSRs, and cert-manager/application CSRs (custom signers) are out of scope by design; the kube-controller-manager certificate-controller and the AKS aksService approver are excluded by identity.
  • Manual CSR approval by an administrator, or an operator that legitimately mints client certificates, may surface; baseline those identities and exclude the specific validated account rather than re-broadening to all system:*, which would blind the rule to compromised workload service accounts.

Response and remediation

  • If unauthorized, deny or delete the CSR, revoke the issued certificate, and rotate the cluster CA if a privileged certificate was minted.
  • Review the RBAC that allowed CSR creation and approval, and audit actions taken with the certificate.
  • Collect kube-audit and identity artifacts per incident response procedures.

References

Related rules

to-top