敏感文件泄露的核心不是"能下载到一个文件",而是拿到源码之后读懂了它。 这四关的终点都不是"下载到压缩包",而是从源码里翻出一个不设防的后门文件,访问它,Flag 就出来了。
⚠️ 本文所有测试均在授权靶场中完成,仅用于安全学习与防御研究,请勿用于未授权目标。
〇、先看通关记录
| 关卡 | 难度 | 泄露点 | 关键动作 |
|---|---|---|---|
| 备份文件泄露 | 1 级 | 网站根目录的backup.zip | 猜文件名 → 下载 → 解压审计 → 访问后门 |
| SVN 泄露(旧版) | 2 级 | .svn/entries+.svn/text-base/ | 读文件列表 → 拉.svn-base副本 → 访问后门 |
| SVN 泄露(新版) | 2 级 | .svn/wc.db(SQLite) | 下载wc.db→sqlite3查文件名 → 访问后门 |
| Git 泄露 | 2 级 | .git/(目录浏览未关) | 解析.git/index→ 拿文件名 → 访问后门 |
四关殊途同归:先用任意方式拿到"文件清单",再从清单里挑出可疑的后门文件,直接访问它。
一、什么是敏感文件泄露
1.1 定义与危害
定义:服务器上本该只在内部使用的文件(源码、备份、配置、版本控制元数据)被放在了 Web 目录下且可被直接访问,攻击者顺着 URL 就能拿到。
本质:不是代码写错了,而是部署时"忘了收拾"。这是它和注入类漏洞最大的区别——所以它更像"信息收集"的延伸,而不是"注入"的同类。
危害:泄露源码与数据库密码 → 顺藤摸瓜找到后门、密钥、Flag → 为进一步的 RCE 铺路。
1.2 必背:常见敏感文件清单
| 类型 | 文件名示例 | 泄露什么 |
|---|---|---|
| 源码备份 | www.zip、web.zip、backup.zip、website.tar.gz | 整站源码 |
| 数据库备份 | db.sql、database.sql、backup.sql | 数据库数据 |
| 编辑器备份 | index.php.bak、index.php~、index.php.swp | 单文件源码 |
| 配置文件 | .env、config.php、web.config | 数据库密码、密钥 |
| 版本控制 | .git/config、.svn/entries | 源码 + 版本信息 |
| 系统文件 | /etc/passwd、/proc/self/environ | 系统信息 |
| 日志文件 | /var/log/apache2/access.log | 用于 LFI to RCE |
💡 这一关的靶场描述是"我有一个备份我网站的好习惯"——基本等于明示:去猜备份文件名。
二、核心机制:为什么"直接访问后门"就能拿 Flag
这是这几关最容易困惑的地方:为什么下载了源码、看到那个文件名,访问一下 Flag 就出来了?
因为那个文件本身就是个后门。它的源码长这样:
<?php $flag_file = '/tmp/flag.txt'; if (file_exists($flag_file)) { $content = file_get_contents($flag_file); echo htmlspecialchars($content, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); } else { echo "flag 文件不存在"; } ?>逐行翻译成人话:
| 代码 | 在做什么 |
|---|---|
$flag_file = '/tmp/flag.txt' | 只认一个地址:机密文件在这 |
file_exists(...) | 先看看它在不在 |
file_get_contents(...) | 在就整个读出来 |
echo htmlspecialchars(...) | 打印到页面上 |
它和正常网页的区别只有一个:
正常页面(index.php) | 后门文件(bac123321123.php) | |
|---|---|---|
| 访问前要验证吗 | 要(登录、鉴权、参数校验) | 不要 |
| 访问后做什么 | 按业务逻辑处理 | 二话不说读/tmp/flag.txt并输出 |
🔑一句话:直接访问能拿到 Flag,不是因为这个 URL 有魔法,而是因为服务器上躺着一个不设防、专门用来读 Flag 的脚本。而"知道这个文件叫什么名字",就是从泄露的源码里来的——这才是源码审计的价值:你看到的不是页面表象,而是服务器的底牌。
三、实战复盘
3.1 1 级:备份文件泄露
目标:从网站备份文件中拿到源码,找到读 Flag 的入口。
思路:靶场描述"我有一个备份我网站的好习惯",等于把方向说了——去猜备份文件名。
Payload:
http://<靶场地址>/backup.zip成功下载到整站源码压缩包。
审计与利用:
解压
backup.zip,翻源码目录。发现一个命名很可疑的文件 ——
bac123321123.php(正常业务不会这么取名)。打开一看,正是第二章那个"读
/tmp/flag.txt"的后门。直接访问
http://<靶场地址>/bac123321123.php,拿到 Flag。
小结:这关考的不是技术难度,而是信息收集的常识——备份文件名有固定字典,猜中即可。
3.2 2 级:SVN 源码泄露(旧版text-base)
信息收集:用dirb扫目录
dirb http://<靶场地址>/ /usr/share/wordlists/dirb/common.txt扫出/.svn/entries返回 200 —— 确认存在 SVN 源码泄露。
原理:开发者用 SVN 管理代码,发布网站时忘了删掉.svn隐藏目录。而这个目录里:
.svn/entries:记录了所有受版本控制的文件列表。.svn/text-base/<文件名>.svn-base:每个文件在版本库中的原始副本(纯源码)。
利用:
访问
http://<靶场地址>/.svn/entries,拿到文件列表。列表里有个随机命名的文件,极其可疑:
3b96e9fc-ae54-46bf-9e30-c167f825d7cf.php拉它的源码副本确认:
http://<靶场地址>/.svn/text-base/3b96e9fc-ae54-46bf-9e30-c167f825d7cf.php.svn-base看到的正是第二章那个后门。
既然确认是后门,直接访问原文件:
http://<靶场地址>/3b96e9fc-ae54-46bf-9e30-c167f825d7cf.php拿到 Flag
⚠️ 易错点:三种 URL,只有两种能通
| 访问路径 | 结果 | 原因 |
|---|---|---|
/3b96e9fc-...php | ✅ | 网站根目录下真实存在这个文件,服务器执行它 |
/.svn/text-base/3b96e9fc-...php | ❌ 404 | .svn/text-base/下只有.php.svn-base,没有.php |
/.svn/text-base/3b96e9fc-...php.svn-base | ✅ | 源码副本,服务器当静态文件返回(浏览器显示或下载) |
📌要分清"执行"和"显示": 以
.php结尾 → PHP 处理器执行它,输出运行结果; 以.php.svn-base结尾 → 不匹配.php的处理规则,服务器只把它当普通文件原样吐出来。 所以副本用来"看代码",原文件用来"跑代码"。
3.3 2 级:SVN 泄露进阶(新版wc.db)
这一关出题人把上一关的捷径堵了——.svn/text-base/目录被删掉。但 SVN 从 1.7 开始换了存储方式,源码依然在。
| SVN 版本 | 源码存哪 | 怎么利用 |
|---|---|---|
| 旧版(< 1.7) | .svn/text-base/*.svn-base | 直接访问副本;或用dvcs-ripper批量还原 |
| 新版(≥ 1.7) | .svn/wc.db(SQLite 数据库) | 下载wc.db,用sqlite3查询 |
利用:
扫到
/.svn/,发现text-base/没了,但多了一个wc.db(约 12K)。下载下来:
curl -o /tmp/wc.db http://<靶场地址>/.svn/wc.db用
sqlite3查文件清单(NODES表是核心):
sqlite3 /tmp/wc.db "SELECT local_relpath FROM NODES WHERE kind='file';"列表里又出现一个随机命名的可疑文件:
ef217624-b2d3-4bad-8b33-9b975db7d46a.php直接访问它,成功返回 Flag。
NODES表关键字段:
| 字段 | 说明 |
|---|---|
local_relpath | 文件在工作副本中的相对路径(如index.php) |
kind | 类型:file/dir |
checksum | 文件校验和,指向真正的源码副本 |
content | 内容字段,对普通文件通常为空(见下方提醒) |
⚠️一个容易踩的坑:不少教程会写
SELECT content FROM NODES WHERE ...来直接读源码。但在 SVN 1.7+ 里,普通文件的content字段基本都是NULL——真正的文件内容被单独放在.svn/pristine/<校验和前两位>/<校验和>.svn-base,通过checksum关联。 所以稳妥的做法是:先用local_relpath拿到文件名清单,再顺着checksum去读pristine/;嫌麻烦就直接上dvcs-ripper一键还原。
3.4 2 级:Git 源码泄露(.git/index)
漏洞确认:直接访问http://<靶场地址>/.git/,目录浏览是开着的,返回了标准结构:
HEAD config index objects/确认存在 Git 源码泄露。
原理:和 SVN 一样,开发者发布时忘了删.git目录。其中:
| 文件 / 目录 | 作用 |
|---|---|
.git/HEAD | 指向当前分支 |
.git/config | 仓库配置 |
.git/index | 核心索引,记录当前版本所有文件的路径与哈希 |
.git/objects/ | 所有文件内容的压缩对象 |
利用:
用 VSCode 打开
.git/index(二进制文件),右侧的文本解码区能看到明文文件路径。直接读出了:
index.php 3367f1809c1eb10b2e87c5544da3a097.png 3b96e9fc-ae54-46bf-9e30-c167f825d7cf.php ← 可疑那个随机命名的
.php就是后门。直接访问它:
http://<靶场地址>/3b96e9fc-ae54-46bf-9e30-c167f825d7cf.php拿到 Flag
另一种思路(还原整站源码):index只给文件名,要拿内容还得配objects/,用工具一把梭:
python3 GitHack.py http://<靶场地址>/.git/💡
.git/index为什么能直接看出文件名?它是二进制格式,但其中的文件路径是以明文、用\0分隔存储的——所以随便一个文本 / 十六进制编辑器,往右边一看,文件名就全露出来了,不用装任何工具。
四、SVN 与 Git:一张表和一个比喻
这两个漏洞常被统称"源码泄露双雄",核心区别在于文件内容怎么存:
| 维度 | SVN | Git |
|---|---|---|
| 核心目录 | .svn/ | .git/ |
| 关键文件 | .svn/entries(旧)、.svn/wc.db(新) | .git/index、.git/HEAD |
| 内容存储 | 直接存原文副本(text-base/、pristine/) | 存压缩对象(objects/) |
| 拿源码难度 | 低:访问.svn-base就能看 | 中:要用工具(GitHack)还原对象 |
| 常见利用 | 读entries/ 查wc.db找文件名 | 读index找文件名,或用工具还原 |
一个好记的比喻:
SVN 像复印机:把原稿复印一份塞进你的抽屉(
.svn/text-base),找到复印件内容直接就能看。Git 像保险箱:把原稿锁进保险箱(
.git/objects),只给你一张清单(.git/index);清单写着"文件在哪",但要把原稿掏出来得用工具。
两者本质完全一样:开发者忘了把"管理工具的抽屉"从网站目录里搬走,被攻击者翻了个底朝天。
五、工具速查
| 用途 | 工具 / 命令 |
|---|---|
| 目录扫描(找泄露点) | dirb、dirsearch、御剑 |
| 还原 SVN 源码 | rip-svn.pl -v -u http://<目标>/.svn/(dvcs-ripper,Kali 自带) |
| 还原 Git 源码 | python3 GitHack.py http://<目标>/.git/ |
| 解析新版 SVN | sqlite3 wc.db "SELECT local_relpath FROM NODES WHERE kind='file';" |
| 手动看 Git 索引 | VSCode,或xxd .git/index |
六、安全防御建议(运维 / 开发者视角)
这一节的答案很朴素:敏感文件泄露几乎全是部署环节的疏忽,防御重点是"上线前把不该留在 Web 目录里的东西清掉"。
发布前清理版本控制目录:
.svn、.git、.hg一律删除;更稳妥的做法是用git archive/svn export导出干净副本再部署。备份文件不放 Web 目录:备份产物放在 Web 根目录之外,或至少加鉴权;备份完及时删除。
关闭目录浏览:Nginx
autoindex off;、ApacheOptions -Indexes。在 Web 服务器层直接拒绝:拦截
.svn、.git、.bak、.swp、.zip、.sql、.env等路径 / 后缀的访问。源码与运行目录分离:Web 根目录只放对外需要的文件。
统一错误信息:不要把详细报错回显给用户,避免二次泄露路径信息。
七、总结
这一专题四关下来,真正的收获不是"会下载.zip",而是这条固定套路:
找泄露点:目录扫描 + 常识字典(
backup.zip、.svn、.git……)。拿文件名清单:
text-base目录、wc.db的NODES表、.git/index,本质都是"文件清单"的三种不同载体。审计清单:业务不会用随机 UUID 给
.php命名——越不像人取的名字,越像后门。直接访问后门:它不设防,不需要参数,访问即执行。