特权更高的 ADCS ESC_CES 攻击
ADHDMurky 2026-10-08 19:50 中国香港

针对 AD CS 的 NTLM 中继长期聚焦于经典 Web 注册,但证书注册服务(CES)同样能签发证书:Web 注册用表单 POST 传 CSR 与 CertAttrib 模板,CES 用 SOAP/WSTEP 的 RequestSecurityToken,CSR 封在 BinarySecurityToken、模板藏在 AdditionalContext。
长期以来,当人们谈到针对 AD CS 的 NTLM 中继时,话题通常集中在以下端点上的经典 Web 注册(Web Enrollment):
/certsrv/
/certsrv/certfnsh.asp但 AD CS 还有一个组件,可以提供额外的权限提升向量。
AD CS 由以下组件构成(供想深入了解的人参考):
- 证书颁发机构(Certification Authority)
——核心 CA 服务,负责签发、续期、吊销和管理证书。
- 证书颁发机构 Web 注册(CA Web Enrollment)
——用于请求和获取证书的经典
/certsrv/Web 界面。 - 在线响应器(Online Responder)
——提供用于证书吊销检查的 OCSP 响应。
- 网络设备注册服务(NDES)
——网络设备用来注册证书的 NDES/SCEP 服务。
- 证书注册 Web 服务 / 证书注册策略 Web 服务
——基于 Web 服务的注册/策略组件:CES 为证书注册 Web 服务,CEP 为证书注册策略 Web 服务。
再深入看 AD CS 的角色服务,还有另一个同样可以通过 HTTP(S) 暴露的组件:
证书注册服务(Certificate Enrollment Services,简称 CES)CES 通常位于如下端点:
https://ca.example.local/CA_CES_Kerberos/service.svc/CES
https://ca.example.local/CA_CES_NTLM/service.svc/CES这很有意思,因为 CES 同样可以接受证书注册请求。换句话说,如果攻击者能够以一个被允许注册某模板的主体身份认证到 CES,CES 或许也能返回一张证书。
于是引出了这个显而易见的问题:
如果 Web 注册可以通过 NTLM 中继被滥用从而获取证书,那么 CES 是否也能以类似方式被滥用?
为了回答这个问题,我开始对比经典 Web 注册与 CES 实际使用的 HTTP 请求。我考察了协议、认证协商、请求体、响应格式,以及两个端点周围的防护措施。
由于 CES 通常通过 HTTPS 暴露,且往往拥有自己的受信任 TLS 证书,拦截并理解其流量需要搭起 mitmproxy 或 Burp 这类工具,并正确处理 HTTPS 层。
结果是:Web 注册与 CES 都执行证书注册,但它们是两套完全不同的 HTTP 协议。
Web 注册
Web 注册使用普通的 HTTP 表单 POST。请求的内容类型是:
Content-Type: application/x-www-form-urlencoded客户端提交 CSR 的请求形如:
POST /certsrv/certfnsh.asp HTTP/1.1
Host: ca.example.local
Content-Type: application/x-www-form-urlencoded
Authorization: NTLM <token>
Mode=newreq&CertRequest=<CSR>&CertAttrib=CertificateTemplate:User&TargetStoreFlags=0&SaveCert=yes&ThumbPrint=重要字段是:
CertRequest=<PKCS10 CSR>
CertAttrib=CertificateTemplate:<template>例如:
CertAttrib=CertificateTemplate:User如果包含 SAN/UPN,该属性可能形如:
CertAttrib=CertificateTemplate:User%0d%0aSAN:upn=user@example.local在 Web 注册中,CSR 通过表单字段发送:CertRequest=;证书模板通过 CertAttrib=CertificateTemplate:<template>发送。
经典 Web 注册通常返回 HTML。如果请求被接受,响应往往包含一个请求 ID 或如下链接:
certnew.cer?ReqID=42客户端随后单独获取证书:
GET /certsrv/certnew.cer?ReqID=42所以流程通常是:
1. 生成私钥与 CSR
2. 把 CSR POST 到 /certsrv/certfnsh.asp
3. 从 HTML 响应中解析出 ReqID
4. GET /certsrv/certnew.cer?ReqID=<id>
5. 合并私钥与证书
6. 保存为 PFXCES(证书注册服务)
证书注册服务是另一个 AD CS 角色服务。它暴露一个 WCF 服务端点,通常位于如下路径下:
/CA_CES_Kerberos/service.svc/CES
/CA_CES_NTLM/service.svc/CES典型 URL 是:
https://ca.example.local/CA_CES_Kerberos/service.svc/CES与 Web 注册不同,CES 不接受简单的 application/x-www-form-urlencoded请求。
CES 使用 SOAP/WSTEP。WSTEP 是 Web Services Trust Enrollment Protocol(Web 服务信任注册协议)的缩写。实践中,客户端发送一条 WS-Trust 的 SOAP RequestSecurityToken消息。
CES 使用:
Content-Type: application/soap+xml; charset=utf-8一条 CES 注册请求发送到:
POST /CA_CES_Kerberos/service.svc/CES HTTP/1.1
Host: ca.example.local
Content-Type: application/soap+xml; charset=utf-8
Authorization: Negotiate <token>请求体是 XML,包含一个 SOAP 信封,简化版本如下:
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope
xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<a:Action s:mustUnderstand="1">
http://schemas.microsoft.com/windows/pki/2009/01/enrollment/RST/wstep
</a:Action>
<a:MessageID>urn:uuid:...</a:MessageID>
<a:To s:mustUnderstand="1">
https://ca.example.local/CA_CES_Kerberos/service.svc/CES
</a:To>
</s:Header>
<s:Body>
<RequestSecurityToken
PreferredLanguage="en-US"
xmlns="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
<TokenType>
http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3
</TokenType>
<RequestType>
http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue
</RequestType>
<BinarySecurityToken
ValueType="http://schemas.microsoft.com/windows/pki/2009/01/enrollment#PKCS10"
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd#base64binary"
xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
BASE64_PKCS10_CSR
</BinarySecurityToken>
<AdditionalContext
xmlns="http://schemas.xmlsoap.org/ws/2006/12/authorization">
<ContextItem Name="CertificateTemplate">
<Value>User</Value>
</ContextItem>
</AdditionalContext>
</RequestSecurityToken>
</s:Body>
</s:Envelope>CSR 不是以表单字段发送的,而是嵌入在一个 WS-Security 元素中:
<BinarySecurityToken
ValueType="http://schemas.microsoft.com/windows/pki/2009/01/enrollment#PKCS10"
EncodingType="...#base64binary">
BASE64_PKCS10_CSR
</BinarySecurityToken>证书模板不是以 CertAttrib发送的,而是在 AdditionalContext内发送:
<AdditionalContext xmlns="http://schemas.xmlsoap.org/ws/2006/12/authorization">
<ContextItem Name="CertificateTemplate">
<Value>User</Value>
</ContextItem>
</AdditionalContext>这是最重要的差异之一。
Web 注册:
CertAttrib=CertificateTemplate:UserCES:
<ContextItem Name="CertificateTemplate">
<Value>User</Value>
</ContextItem>CES 通常直接在 SOAP 响应中返回签发的证书材料。响应包含一个或多个 <BinarySecurityToken>。常见的返回令牌类型包括:
X509v3 certificate
PKCS7 response因此与 Web 注册不同,CES 通常不需要单独发一次请求去取证书(即那条 GET /certsrv/certnew.cer?ReqID=<id>)。
流程通常是:
1. 生成私钥与 CSR
2. 构建 SOAP RequestSecurityToken
3. 把 SOAP POST 到 /service.svc/CES
4. 解析 SOAP 响应
5. 提取 X509v3 和/或 PKCS7 的 BinarySecurityToken
6. 合并私钥与证书
7. 保存为 PFX认证与注册
Web 注册与 CES 都可以由 IIS 的 Windows 认证保护。这意味着认证层可能涉及:
NTLM
Negotiate
Kerberos然而,两个端点的默认行为不同。
经典 Web 注册通常接受:
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM而 CES 默认只使用:
WWW-Authenticate: Negotiate我写了个随手脚本验证这一点,以确保无误:

在我看来,这意味着我可以协商出 NTLM 认证——而事实证明确实如此。我构建了一个工具,用 Negotiate 向 CES 端点发送同样的 SOAP 请求。
概念验证如下:

它对 Negotiate 的 NTLM 与 Kerberos 两种模式都有效。
拦截与中继
那么,为什么 Web 注册的工具不能自动对 CES 生效?
ntlmrelay 工具期望发送类似这样的内容:
Mode=newreq&CertRequest=...&CertAttrib=CertificateTemplate:User到:
/certsrv/certfnsh.asp而 CES 端点不认识那个请求——它期望的是包含 WS-Trust 请求的 SOAP XML:
<RequestSecurityToken>
<BinarySecurityToken>CSR</BinarySecurityToken>
<AdditionalContext>
<ContextItem Name="CertificateTemplate">
<Value>User</Value>
</ContextItem>
</AdditionalContext>
</RequestSecurityToken>发送到:
/service.svc/CES概念是相似的:提交 CSR、请求模板、接收证书——但协议完全不同。
基于以上所有认识和我目前学到的东西,我尝试构建一个工具,能够拦截一次认证请求并将其中继到 CES 端点以获取证书。
免责声明:该工具大量基于现有的 impacket ntlmrelay 工具,我只是修改了请求处理部分。
这是我们的拦截与中继工具:

拿到那份诱人的用户认证:

以及轻松的 Un-PAC-The-Hash与 Pass-The-Certificate:

工具在此获取:https://github.com/ADHDMurky/ADCS_Tools/
抱歉这篇文章有点长,附上一张鱼人(Murloc)的照片:

本文转载自微信公众号「securitainment」,查看原文。版权归原作者所有,如有侵权请联系删除。