
Writeup di rilevamento Splunk per CVE-2026-54121 (CertiGhost): abuso della certificate chase di AD CS che porta alla compromissione completa del dominio. Rilevamento validato in laboratorio, passaggi di triage e indagine sull'incidente contro un DC live.
Questo documento descrive il rilevamento Splunk realizzato per CVE-2026-54121. Segue il formato standard dei casi d'uso di rilevamento, così da poter essere inserito in un catalogo di rilevamenti nello stesso modo in cui lo manterrebbe un vero team di detection engineering.
Panoramica
Costruzione del rilevamento
Risposta e validazione
Roadmap
Metadati
Avviso : Certighost ( CVE-2026-54121 )
Rileva l'abuso del meccanismo di inseguimento (chase) dell'enrollment dei certificati AD CS descritto in CVE-2026-54121. La regola segnala gli eventi del ciclo di vita dei certificati sulla CA in cui il target di inseguimento fornito dal richiedente (l'attributo cdc) non si risolve nel Domain Controller legittimo, che è esattamente il meccanismo usato da Certighost per far emettere alla CA un certificato contenente i dati di identità del Domain Controller a un richiedente con privilegi bassi, con conseguente compromissione completa del dominio e takeover.
Il rilevamento è incentrato sul target di inseguimento cdc anziché sulla creazione di account macchina, perché l'avviso Microsoft per CVE-2026-54121 elenca come precondizione sull'account o un valore predefinito di ms-DS-MachineAccountQuota o un account già controllato dall'attaccante. Una regola basata sulla creazione di account mancherebbe del tutto il secondo percorso e dipenderebbe inoltre dal fatto che la creazione dell'account e lo sfruttamento avvengano nella stessa finestra temporale. Il controllo su cdc copre entrambi i percorsi e non ha tale dipendenza.
Questa regola copre la fase di richiesta ed emissione del certificato della catena di attacco.
| Tattica | Tecnica (ID) | Evidenza |
|---|---|---|
| Privilege Escalation | Abuse Elevation Control Mechanism (T1548) | Un utente di dominio con privilegi bassi scala ai privilegi del Domain Controller tramite un certificato AD CS falsificato |
| Credential Access | Steal or Forge Authentication Certificates (T1649) | Nucleo di Certighost - attributi cdc/rmd usati per ottenere un certificato con il materiale di identità del DC |
| Credential Access | OS Credential Dumping: DCSync (T1003.006) | TGT per l'account del DC usato per eseguire la replica MS-DRSR - estrae krbtgt e tutti gli hash degli account |
| Credential Access | Golden Ticket (T1558.001) | Con l'hash di krbtgt, l'attaccante falsifica TGT Kerberos all'infinito, sopravvivendo ai reset delle password |
| Defense Evasion | Use Alternate Authentication Material: Pass-the-Ticket (T1550.003) | TGT basato su certificato usato per autenticarsi come DC, poi DCSync eseguito con quel TGT |
| Persistence | Create Account: Local Account (T1136.001) | Account macchina creato tramite ms-DS-MachineAccountQuota per autenticarsi a un endpoint LDAP malevolo |
| Lateral Movement | Use Alternate Authentication Material (T1550) | Il certificato del DC garantisce accesso completo alla rete equivalente all'account del Domain Controller |
| Discovery | Domain Account (T1087.002) | L'enumerazione LDAP iniziale scopre CA, DC, SID del dominio, GUID e valore MAQ |
4886 (Richiesta di certificato ricevuta)4887 (Certificato rilasciato)4888 (Richiesta di certificato negata)![Sorgenti di log confermate attive in Splunk, host DC01, EventCodes 4886/4887 presenti, target chase 172[.]66[.]66[.]33 estratto per gli ID richiesta 12, 13, 14](images/log-sources.png)
index=wineventlog-lab sourcetype=WinEventLog source="WinEventLog:Security"
(EventCode=4886 OR EventCode=4887 OR EventCode=4888) cdc
| fields _time EventCode Request_ID Requester Attributes Subject host Message
| rex field=Message "(?m)^cdc:(?<chase_cdc>\S+)"
| where isnotnull(chase_cdc) AND lower(chase_cdc)!="dc01.atlasvec.lab"
| stats min(_time) as _time
values(Requester) as requester
values(Attributes) as template
values(chase_cdc) as chase_target
values(Subject) as cert_subject
values(EventCode) as codes
values(host) as ca_host
by Request_ID
| eval status = case(mvfind(codes,"4887")>=0,"ISSUED",
mvfind(codes,"4888")>=0,"DENIED",
true(),"PENDING")
| eval severity = "critical"
| sort - _time
| rename ca_host as "CA Host", Request_ID as "Request ID",
requester as "User Requester", template as "Certificate Template",
chase_target as "Chase Target (cdc)", cert_subject as "Issued Subject",
status as "Outcome", severity as "Severity",
_time as "Timestamp"
| table "Timestamp" "CA Host" "Request ID" "User Requester" "Certificate Template" "Chase Target (cdc)" "Issued Subject" "Outcome" "Severity"

received, poi issued o denied) viene mostrato come un'unica riga.