针对 HackTheBox "Nexus" 权限提升的 PoC 与 write-up:以 root 身份运行的 Gitea 模板同步服务存在通过伪造 Git 对象进行路径遍历的漏洞。仅供教育用途。
通过伪造 Git 对象实现 Gitea 模板同步路径遍历 → root
针对 HackTheBox 机器 Nexus 的概念验证权限提升漏洞利用。
一个由 root 拥有的 systemd 服务将 Gitea 模板 仓库同步到磁盘时
未对条目名称进行清理,允许已认证的低权限用户
通过目录遍历以 root 身份写入任意文件——此处用于将 SSH 密钥
植入 /root/.ssh/authorized_keys。
此代码仅供教育目的和授权安全测试 使用——即 HackTheBox 等实验环境,或您拥有或已获得 明确书面许可进行测试的系统。未经授权对您不控制的系统使用此代码是非法的。 作者对滥用不承担任何责任。
如果 Nexus 机器在 HackTheBox 上仍处于活跃状态,请勿发布或分享 此漏洞利用——HTB 规则禁止分发针对活跃机器的解决方案。
| 类别 | 路径遍历 (CWE-22) → 本地权限提升 |
| 组件 | gitea-template-sync.service(自定义 /etc/gitea/template-sync.py) |
| 运行身份 | root,由 gitea-template-sync.timer 每约 60 秒触发 |
| 根本原因 | 对来自 Git tree 对象的未清理名称使用 os.path.join() |
| 影响 | 已认证低权限用户 → root |
同步脚本从 Gitea 中标记为模板的仓库读取文件,并将其写入本地文件系统。 目标路径大致构建如下:
dest = os.path.join(base_dir, name) # name comes straight from the Git tree
open(dest, "w").write(content)
os.path.join() 不会消除 .. 序列。因此,名称为遍历路径的 tree 条目
(例如 ../../../../../root/.ssh/authorized_keys)会使特权进程
写入预期目录之外的位置。
Git 将内容和名称分开存储:
name → object 映射(文件名就存在于此);tree 条目中的文件名只是文本,因此它可以是 ..——但标准
Git 客户端拒绝创建这样的条目,正是为了防止这种攻击。
此 PoC 通过将原始 Git 对象直接写入 .git/objects/
(手动计算 SHA-1 id)并手动构造 refs/heads/main,
绕过了客户端检查。推送的仓库随后包含一个名为 .. 的 tree 条目,
root 同步进程会解析并跟随它。
www-data 身份 RCE。.env 中的明文凭据,被复用为
系统密码 → 以低权限用户 SSH 登录。root。贯穿所有三个阶段的主题:特权进程信任攻击者控制的数据而不进行验证 (伪造的 MIME 类型、复用的密码、未清理的文件名)。
http://localhost:3000 访问。git、python3、curl 和 ssh 可用(机器上默认存在)。chmod +x nexus_privesc.sh
./nexus_privesc.sh -t <TARGET_IP> -u <GITEA_USER> -p <GITEA_PASS> [-d DEPTH]
示例:
./nexus_privesc.sh -t 10.129.21.192 -u jones -p 'REDACTED' -d 5
/tmp/.exploit_key)。.. 遍历 tree,映射到
/root/.ssh/authorized_keys,内容为攻击者公钥。root 身份登录。在目标上监控同步:
tail -f /var/log/template-sync.log
成功运行会记录类似如下的一行:
synced: ../../../../../root/.ssh/authorized_keys
DEPTHDEPTH 是从同步/暂存目录向上爬升到文件系统根目录(/)所需的 .. 层级数。
如果日志中同步成功但 root SSH 失败,说明遍历落在了错误的层级——请用 -d 4 或
-d 6 重试。确切值可以通过读取
/etc/gitea/template-sync.py 中的暂存路径来确认。
切勿仅使用 os.path.join() 从不受信任的名称构建文件系统路径。
规范化并限制路径,例如:
base = os.path.realpath(base_dir)
dest = os.path.realpath(os.path.join(base, name))
if not dest.startswith(base + os.sep):
raise ValueError("path traversal blocked")
拒绝包含 ..、绝对路径或路径分隔符的 tree 条目名称。
在专用的低权限账户下运行同步服务,而非 root。
在 Gitea 中启用 receive.fsckObjects = true 以拒绝格式错误的推送对象。
<your name / handle> — 用于 HackTheBox 上的教育用途。
| 标志 | 描述 | 默认值 |
|---|
-t | 目标 IP(用于最终的 root SSH) | —(必需) |
-u | Gitea 用户名 | —(必需) |
-p | Gitea 密码 | —(必需) |
-d | 从同步目录到达 / 所需的 .. 层级数 | 5 |