资讯动态

certsync攻击链深度剖析(3/4):用CA私钥离线批量伪造UPN证书的X.509细节全解

发布时间:2026/8/23 12:43:07 来源:尧图企业网站定制
certsync攻击链深度剖析(3/4)用CA私钥离线批量伪造UPN证书的X.509细节全解【免费下载链接】certsyncDump NTDS with golden certificates and UnPAC the hash项目地址: https://gitcode.com/gh_mirrors/ce/certsynccertsync 是一款绕过 DRSUAPI、通过黄金证书golden certificate远程转储 NTDS 的渗透工具。本文是系列第 3 篇完整拆解它的第 3 步拿到 CA 私钥后如何离线批量伪造 UPN 证书——逐字段讲清每一张 X.509 证书里写了什么、为什么这样写、以及哪些细节会被蓝队一眼识破。 回顾certsync 的四步攻击链先把前一篇的脉络拉回来。certsync 的全部逻辑由 certsync/entry.py 中CertSync.run()方法驱动共四个阶段阶段动作说明1️⃣ LDAP 侦察拉取用户列表、CA 信息、CRL纯 LDAP 读操作痕迹最小2️⃣ 窃取 PKI备份 CA 证书 私钥借助 ADCS 服务账户权限执行 certutil3️⃣离线伪造为每个用户签发 UPN 证书本篇主角全程不与目标通信4️⃣ PKINIT UnPAC逐用户认证并还原哈希用伪造证书走 PKINITPAC 里直接带 NT/LM 哈希第 3 步的价值在于拿到 CA 私钥的那一刻你就拥有了整张域用户证书的印刷厂。之后所有证书都在攻击者本机离线生成无需再向 CA 服务器提交任何请求——这也是它比 certipy 请求式打法的网络痕迹更轻的核心原因。 伪造前的四样原料run()在进入伪造阶段前entry.py第 234 行附近必须集齐四样东西CA 私钥 CA 证书第 2 步通过backup_ca_pki()在 ADCS 服务器上以certutil -backupkey备份得到也可以用-ca-pfx直接加载已有的 PFX 文件跳过备份CRL 发行点CRL Distribution Point从 LDAP 的CNCDP,CNPublic Key Services容器查得get_crl()第 435 行伪造证书要把这个 URL 原样抄进去用户清单来自第 1 步的 LDAP 枚举每个用户只取两个字段——sAMAccountName用户名和objectSid转成 RID 整数可选模板证书-template cert.pfx一张真实签发过的用户证书用于照抄其扩展字段。一个常见疑问为什么需要用户的 SID答案在第 4 篇的 UnPAC 环节揭晓——伪造证书里必须让 KDC 认账这个人是谁而 UPN 是 Kerberos 世界的身份证。 X.509 逐字段拆解一张伪造证书里到底写了什么伪造逻辑集中在两个函数forge_cert_base()第 262-312 行负责搭证书骨架User.forge_cert()第 47-67 行负责填入每个用户的身份信息。下面按 X.509 结构逐字段过。1. 密钥对每次伪造先生成一把 2048 位 RSA 密钥key generate_rsa_key(2048)forge_cert_base()第一步就是为用户证书生成一把全新的 RSA 2048 私钥。注意这把密钥属于伪造的用户证书与 CA 私钥无关——CA 私钥只用来签名。 蓝队视角默认模式下所有用户的伪造证书共用同一把 2048 位私钥。只要抽查任意两张证书的公钥哈希一比对就原形毕露这就是-randomize参数存在的意义下文详述。2. Issuer直接继承 CA 证书的主题名cert cert.issuer_name(self.ca_cert.subject)X.509 里 Issuer 必须等于上级 CA 的 Subject这样证书链才闭合。certsync 不是手工拼字符串而是直接取 CA 证书的 subject 对象天然保证字节级一致。3. SubjectCN用户名域名forge_cert()中通过cert_id_to_parts([(UPN, alt_upn)])把用户名转成CNxxxdomain.local形式作为主题名。这里有个细节CN 写的是 UPN 而不是 sAMAccountName因为后续 KDC 侧是按 UPN 解析身份的CN 与 SAN 必须指向同一个 principal。4. 序列号与有效期serial_number x509.random_serial_number() not_valid_before utcnow() - 1天 not_valid_after utcnow() 365天序列号x509.random_serial_number()随机生成符合 RFC 5280 要求但默认模式下所有伪造证书共享同一个序列号——又是一处批量指纹有效期起始时间往回拨 1 天容忍时间漂移有效期 365 天。回拨 1 天是个容易被忽略的小技巧如果攻击者时钟比 DC 快从未来生效的证书会直接认证失败。5. SubjectAlternativeName整个攻击的灵魂字段 ⭐这是最关键的 X.509 细节代码在第 52-63 行alt_upn encoder.encode(UTF8String(alt_upn)) sans.append(x509.OtherName(PRINCIPAL_NAME, alt_upn)) cert cert.add_extension(x509.SubjectAlternativeName(sans), False)三个要点不是普通的 DNS/IP SAN而是OtherName类型。微软定义了一个私有 OID1.3.6.1.4.1.311.20.2.3代码里的PRINCIPAL_NAME常量专门承载 Kerberos principal把 UPN 以UTF8String编码塞进 SAN标记为非关键扩展criticalFalse与真实 ADCS 签发的证书行为一致为什么必须有它AD 证书策略要求用户身份证书必须含 UPN SANKDC 验证 PKINIT 时会从 SAN 的 OtherName 中取 principal 并与认证请求比对。没有这个字段证书就只是一张漂亮但无用的 X.509PKINIT 会被直接拒绝。6. AuthorityKeyIdentifier指回 CA 公钥x509.AuthorityKeyIdentifier.from_issuer_public_key(self.ca_key.public_key())写入 CA 公钥的哈希值让任何验签方都能把这张证书挂到正确的 CA 上。7. SubjectKeyIdentifier无模板模式默认从用户公钥本身派生from_public_key非关键扩展。8. CRLDistributionPoints抄自 LDAP 的真实 CRL 地址x509.DistributionPoint(full_name[URI(self.crl)])把第 1 步从 LDAP 拿到的 CRL URL 原样写入。意义在于即使有人拿这张证书去做常规链验证CRL 检查走的还是正常路径——伪造证书看起来完全合规。9. 模板模式照抄真实证书的扩展指纹生产环境中真实签发的证书往往带有 ADCS 模板注入的额外扩展KeyUsage、应用策略等。如果伪造证书扩展列表与 CA 真实发过的证书对不上反而显眼。所以支持-template cert.pfxskip_extensions [AKI, SAN, EKU, CRLDistributionPoints] for extension in template_cert.extensions: if extension.oid in skip_extensions: continue cert cert.add_extension(extension.value, extension.critical)逻辑是把模板证书的所有扩展照抄过来唯独跳过 4 个必须动态生成的字段——AKI要指回 CA、SAN要换成目标用户 UPN、EKU 与 CDP与具体身份/CA 相关。这样伪造证书的扩展指纹与域内真实证书几乎无法区分。⚠️ 注意照抄扩展时也继承了critical标志位连哪些扩展是关键的这一元特征都一并复刻了。10. 签名CA 私钥在这里才登场cert cert.sign(ca_key, signature_hash_algorithm())最后一步用 CA 私钥对整张证书做数字签名哈希算法跟随 CA 证书自身的签名算法CA 是 SHA256 就用 SHA256是 SHA384 就跟随。至此一张密码学上完全合法的域用户证书就打印完成了——验签方沿着证书链验证结果会是有效。这就是黄金证书名字的由来不是复制某张已知证书而是用 CA 私钥随时镀金出任意身份的证书。⚡ 批量伪造默认复印还是-randomizerun()第 236-243 行的分支揭示了批量策略默认复印模式只调用一次forge_cert_base()生成一个基础证书 一把基础密钥然后循环里每个用户仅替换 Subject/SAN 并重新签名。优点是快——几百个用户也就几十秒缺点是全域伪造证书共享同一私钥、同一序列号、同一有效期批量指纹极其显著-randomize随机模式每个用户单独走一遍forge_cert_base()密钥、序列号、有效期全部独立。伪造耗时明显增加每个用户都要生成 RSA 密钥并随机化但任意两张证书之间都找不到公共指纹蓝队做横向比对时只能逐个验签工作量完全不同。选型建议目标域用户少、时间紧时用默认模式面对有 ADCS 证书审计能力会比对序列号/密钥复用的环境-randomize基本是必选项。️ OPSEC让批量不那么明显伪造本身是离线的真正会暴露节奏的是第 4 步的 PKINIT 洪峰。工具为此留了三个旋钮均见entry.py的参数定义第 578-602 行-timeout两次 PKINIT 之间的固定间隔秒-jitter在 timeout 基础上追加 0~jitter 的随机抖动打平周期性-ldap-filter缩小枚举范围只伪造需要的用户降低总量。配合-randomize伪造证书在网络行为随机间隔和静态特征独立密钥/序列号两个维度都尽量伪装成域内正常签发的样子。 本篇小结X.509 要素certsync 的填法目的SubjectCNuserdomain与 UPN 对齐SAN OtherName (OID .311.20.2.3)目标用户 UPNUTF8StringPKINIT 身份锚点Serial / 有效期随机 / -1天~365天合法合规AKI / SKI指向 CA / 用户公钥证书链闭合CRL DP抄 LDAP 真实地址验证路径正常其余扩展-template照抄抹掉模板指纹签名CA 私钥 CA 的哈希算法黄金证书成立一句话总结CA 私钥 域用户证书的无限印钞机UPN SAN 是这张钞票上唯一的防伪水印-randomize决定你的印钞机是复印机还是真印钞厂。下一篇4/4我们进入攻击链最后一环拿着这批伪造证书发起 PKINIT从 PAC 中 UnPAC 出每个用户的 NT/LM 哈希并复盘完整的 NTDS 转储输出。【免费下载链接】certsyncDump NTDS with golden certificates and UnPAC the hash项目地址: https://gitcode.com/gh_mirrors/ce/certsync创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价