cmc-addext
I use the CMC id-cmc-addExtensions control to inject arbitrary X.509 extensions
into an AD CS certificate request. On an ESC1-shaped template, that lets a
low-privileged domain user forge a certificate whose Subject Alternative Name and
whose szOID_NTDS_CA_SECURITY_EXT SID both point at Administrator - and PKINIT to
Domain Admin on a fully patched CA and DC with
StrongCertificateBindingEnforcement = 2.
Full writeup, with the disassembly and the reasoning:
https://0xmaz.me/posts/certsrv-id-cmc-addExtensions-KB5014754-bypass/
Reverse-engineering notes (functions, offsets, both binaries): REVERSING.md.
How it works
Two defects, chained.
certsrv.exe routes request extensions through a dispatcher that masks the caller
flag with & 0xf0000 and recognises only two allowlists (0x50000 and 0x90000).
The id-cmc-addExtensions handler calls it with 0x80002, and
0x80002 & 0xf0000 = 0x80000 - neither value - so no allowlist runs. Every
extension I put in the CMC control reaches SetCertificateExtension, including the
SAN and szOID_NTDS_CA_SECURITY_EXT. The signer only needs a certificate the CA
itself issued (CryptMsgGetAndVerifySigner with CMSG_SIGNER_ONLY_FLAG checks the
signature, not the chain), so --auto-signer enrolls a throwaway to sign with.
certpdef.dll is supposed to stamp the requester's real SID into that extension -
the KB5014754 defence. On the enrollee-supplies-subject path it doesn't generate
anything; it copies the SID out of the request. Since the first defect lets me put
any SID in the request, I supply the Administrator's SID next to the Administrator
UPN, and the KDC's strong binding check passes.
Requirements on the target
- An ESC1-shaped template:
ENROLLEE_SUPPLIES_SUBJECT, a clientAuth EKU, enroll
rights for the low-privileged user, and no SUBJECT_ALT_REQUIRE_UPN. No
default-published template has this shape - the built-ins carry
REQUIRE_UPN/REQUIRE_DNS, which rebind the SAN and SID to the caller. The
point is that KB5014754 was documented as making a leftover ESC1 template
survivable; on this path it doesn't.
- MS-ICPR (RPC, port 135) to the CA. CES is optional.
- Any low-privileged domain account (to auto-enroll the throwaway signer).
Install
python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
# certipy-ad and impack