ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Git目录泄露漏洞:从原理到实战利用与防御

Git目录泄露漏洞:从原理到实战利用与防御 1. 项目概述从一次实战演练看Git目录泄露的攻防本质最近在复盘一个CTF靶场里的Web题目题目名字就叫“Git目录泄露”。这其实是一个在真实渗透测试和CTF比赛中都非常经典的漏洞场景但很多新手朋友可能只停留在“用工具扫一下然后下载源码”的层面。这次我打算结合这道题以及我这些年遇到过的各种情况把Git目录泄露这个漏洞从原理到利用再到防御彻底掰开揉碎了讲清楚。这不仅仅是写一个Writeup更是想分享一套遇到这类问题时你应该怎么想、怎么做的完整思路。简单来说Git目录泄露漏洞就是开发或运维人员不小心把项目根目录下的.git文件夹部署到了线上Web服务器的可访问目录里。这个.git文件夹是Git版本控制系统的核心里面存放了项目的所有版本历史、分支信息、提交记录甚至在某些情况下能直接还原出完整的项目源代码。攻击者一旦能访问到这个目录就相当于拿到了项目的“开发后台”审计源码、寻找数据库配置、硬编码的密钥、未完成的敏感功能接口都成为了可能。这道题就是一个绝佳的实战案例它模拟了真实环境中由于配置疏忽导致.git目录暴露的场景我们的目标就是利用这个泄露找到隐藏的Flag。2. 漏洞原理与初步探测.git目录里到底有什么在开始实战之前我们必须先搞清楚我们试图访问的.git目录到底是个什么东西为什么它如此重要。2.1.git目录结构解析当你执行git init初始化一个仓库时Git会在项目根目录创建这个.git文件夹。它的结构大致如下每一个文件都有其特定用途.git/ ├── HEAD # 指向当前所在的分支 ├── config # 项目特有的配置信息 ├── description # 仓库描述信息 ├── hooks/ # 客户端或服务端的钩子脚本 ├── info/ # 全局性排除文件.gitignore的补充 ├── objects/ # **核心**Git的数据对象库所有文件内容、提交、树对象都压缩存储在这里 │ ├── pack/ # 打包后的对象文件用于节省空间 │ └── [0-9a-f][0-9a-f]/ # 松散对象存储按SHA-1哈希的前两位分目录 └── refs/ # 指向各个分支、标签的指针 ├── heads/ # 分支指针 └── tags/ # 标签指针对于攻击者而言最有价值的是HEAD、index暂存区文件不一定总是存在、logs/操作日志以及objects/目录。特别是objects/目录它使用SHA-1哈希值作为文件名来存储所有数据。只要我们能获取到足够的对象文件理论上就能重建项目的某个版本甚至整个历史。2.2 如何判断存在Git泄露在实战中我们不会一上来就盲目扫描。通常会有一些蛛丝马迹引导我们怀疑存在Git泄露。直接访问探测这是最简单粗暴的方法。直接在目标URL后拼接/.git/例如http://target.com/.git/。如果服务器配置不当没有禁止目录列表你可能会看到一个403 Forbidden这通常意味着目录存在但无列表权限甚至是一个目录列表返回200状态码列出文件。更精确的探测是访问/.git/HEAD文件这个文件几乎总是存在的内容通常是ref: refs/heads/master。如果能成功下载这个文件那么Git泄露几乎可以确认。使用目录扫描工具当直接访问被屏蔽或需要批量检测时工具就派上用场了。像dirsearch、gobuster、ffuf这类工具加载一个包含/.git/、/.git/HEAD、/.git/config等常见路径的字典进行爆破效率很高。# 使用dirsearch的示例命令 python3 dirsearch.py -u http://target.com -e php,html,js -w /path/to/wordlists/git.txt这里的git.txt字典文件就需要包含针对.git目录的常见路径。观察网站特征有时网站底部、robots.txt文件或注释里会提到使用了Git。例如在robots.txt里看到Disallow: /.git/这反而是一个强烈的暗示。或者About页面写着“Powered by Git version x.x.x”这些都值得深入探查。注意直接访问/.git/目录时返回403状态码比返回404更有希望。403表示服务器理解请求但拒绝授权说明这个路径是存在的404则表示路径不存在。这是一个重要的排查技巧。3. 利用工具进行源码还原从碎片到完整项目确认漏洞存在后下一步就是尝试把泄露的源码“偷”回来。这里我们主要讨论两种主流方法使用自动化工具和手动分析还原。3.1 自动化利器GitHack与GitTools对于大多数情况自动化工具是首选能快速还原源代码。GitHack这是一个非常经典的Python脚本。它的原理是递归地下载.git目录下所有能访问到的文件然后根据Git的对象存储机制解析index文件、HEAD和refs来重建工作目录。python2 GitHack.py http://target.com/.git/需要注意的是GitHack有Python2和Python3的不同版本使用时需注意环境。它的优点是简单快捷但有时在复杂的仓库结构或存在pack文件时可能还原不完整。GitTools这是一套更强大、更手工化的工具集包含多个脚本如gitdumper.sh、extractor.sh等。我更喜欢用它因为它更可控。# 1. 使用gitdumper下载整个.git目录 ./gitdumper.sh http://target.com/.git/ ./output_folder # 2. 使用extractor从下载的.git文件夹中提取源码 ./extractor.sh ./output_folder ./extracted_codegitdumper.sh会尝试下载所有它能找到的Git对象文件extractor.sh则负责解析这些对象并还原出文件。这个过程能让你更清楚地看到Git是如何存储数据的。3.2 手动还原理解Git对象存储了解手动还原有助于你在工具失效时进行排查也能加深对漏洞原理的理解。Git对象主要有四种blob文件内容、tree目录结构、commit提交信息、tag标签。定位当前分支首先查看HEAD文件它指向当前分支如ref: refs/heads/master。找到最新提交根据HEAD的指引查看refs/heads/master文件里面是一个40位的SHA-1哈希值如a1b2c3...这就是最新提交的ID。下载提交对象提交对象存储在.git/objects/a1/b2c3...哈希前两位作目录后38位作文件名。你需要下载这个文件。它是经过zlib压缩的可以用python或git cat-file命令查看。# 在本地创建一个临时git仓库并查看对象 mkdir temp cd temp git init # 将下载的object文件放入 .git/objects/a1/b2c3... 位置 git cat-file -p a1b2c3... # 可以看到提交的详细信息包括其对应的tree对象哈希递归解析tree和blob从提交对象中找到顶层tree对象的哈希然后下载并解析它。tree对象列出了该提交下的所有文件和子目录对应子tree及其哈希。继续递归下载和解析这些blob文件内容和tree子目录最终就能还原出整个项目快照。这个过程非常繁琐但能让你彻底明白即使.git/index暂存区文件丢失只要objects/目录下的关键对象还在源码就有可能被恢复。4. 实战Writeup深度剖析不止于下载源码现在让我们回到题目本身。假设我们通过工具已经成功下载了泄露的源码。真正的挑战才刚刚开始——代码审计。题目往往不会把Flag明晃晃放在下载的源码根目录里。4.1 审计思路与常见危险函数拿到源码后我通常会按照以下步骤进行快速审计文件清单梳理先用tree命令或直接浏览了解项目结构。重点关注config/,inc/,includes/配置文件、包含文件目录。api.php,admin.php,upload.php功能接口文件。flag.php,key.txt,.env可能直接存放敏感信息的文件。database.php,config.inc.php数据库配置文件。搜索敏感关键词使用grep进行全局搜索是最高效的方式。# 搜索可能包含flag的字符串 grep -r flag\|key\|secret\|password\|token --include*.php --include*.txt --include*.env . # 搜索危险函数PHP为例 grep -r eval\|assert\|system\|exec\|shell_exec\|passthru\|popen\|proc_open\|反引号 --include*.php . # 搜索数据库操作语句寻找SQL注入点 grep -r SELECT\|INSERT\|UPDATE\|DELETE.*\$_GET\|\$_POST\|\$_REQUEST --include*.php . # 搜索文件操作函数寻找文件包含/读取漏洞 grep -r file_get_contents\|include\|require\|fopen.*\$_GET\|\$_POST --include*.php .跟踪核心业务流程找到网站的主入口文件通常是index.php顺着它的逻辑看它如何接收参数$_GET,$_POST,$_REQUEST如何调用其他文件最终输出什么。题目中的漏洞往往就藏在某个参数的处理流程中。4.2 案例精讲从源码到漏洞利用以一道常见的CTF题为例类似输入内容中提到的mfw。假设我们通过GitHack下载源码后发现结构如下. ├── index.php ├── templates/ │ ├── home.php │ ├── about.php │ └── flag.php └── .git/ (已下载)审计index.php发现关键代码?php $page $_GET[page] ?? home; $file templates/ . $page . .php; assert(strpos($file, ..) false); include($file); ?漏洞分析代码获取page参数拼接成文件路径。使用assert()函数对拼接后的字符串进行安全检查试图防止目录穿越..。但这里存在一个字符串拼接代码注入的经典漏洞。assert()的参数是字符串且会被当作PHP代码执行。我们可控的$page被直接拼接进了这个字符串。构造Payload 我们的目标是读取templates/flag.php的内容。assert()的语句是assert(strpos(templates/.$page..php, ..) false)如果我们传入pageabc) or system(cat templates/flag.php);//那么拼接后成为assert(strpos(templates/abc) or system(cat templates/flag.php);//.php, ..) false)abc)提前闭合了strpos()的第一个参数。or是逻辑或由于strpos(...)可能返回false未找到..所以会执行后面的system()函数。system(cat templates/flag.php);执行系统命令读取flag文件。//将后面的.php, ..) false)全部注释掉避免语法错误。这样我们就通过一个assert()函数将文件包含漏洞升级为了代码执行漏洞成功读取flag。实操心得遇到assert()和eval()要格外警惕。它们的功能都是执行字符串形式的代码是代码注入的高发区。审计时一定要看用户输入是否未经严格过滤就直接拼接到这些函数的参数字符串中。5. 进阶利用与深度挖掘历史记录中的宝藏有时Flag并不在当前版本的源码里而是藏在历史的某个提交中。这就是.git泄露比单纯源码泄露更危险的地方——它暴露了历史。5.1 查看提交历史在成功下载并还原.git文件夹后你可以将其视为一个不完整的本地仓库。进入该目录尝试使用git log命令查看提交历史。cd downloaded_git_folder git log --oneline如果logs目录和必要的引用信息完整你就能看到所有的提交记录、作者、时间和提交说明。提交说明里可能会有“修复安全漏洞”、“移除硬编码密码”、“添加flag”等提示性信息。5.2 比较文件差异如果你怀疑Flag被写入后又删除或者分散在多次提交里就需要比较不同版本间的差异。查看某次提交的改动git show commit-hash可以显示该次提交的具体改动内容。比较两个提交之间的差异git diff commit-hash-1 commit-hash-2可以比较两次提交的整体差异。如果你想看某个特定文件的变化可以加上路径git diff hash1 hash2 -- path/to/file。检查所有分支和标签别忘了git branch -a和git tag看看有没有其他分支或标签里藏着东西。在输入内容提到的leak_snake例题中就需要对31个版本进行两两git diff从差异中拼凑出完整的Flag。这个过程可以写简单的脚本自动化# 假设获取了所有提交的哈希列表 commits$(git log --prettyformat:%H | tac) # 反转顺序从旧到新 prev for commit in $commits; do if [ -n $prev ]; then echo Diff between $prev and $commit: git diff $prev $commit --no-prefix | grep -E ^\[^\] | sed s/^\// | tr -d \n echo fi prev$commit done这个脚本会依次比较相邻提交的差异并提取出所有新增的行拼接起来可能就能得到分散的Flag字符串。5.3 恢复被删除的文件或分支如果在历史中发现了删除文件的操作你可以尝试恢复它。# 找到删除该文件的提交 git log --all --full-history -- path/to/deleted_file # 在删除前的那个提交中将文件检出 git checkout commit-hash-before-delete -- path/to/deleted_file对于被删除的分支可以通过git reflog命令查看仓库的引用日志找到分支最后指向的提交然后根据那个提交创建新分支来恢复。6. 防御方案与最佳实践让漏洞无处可藏分析了这么多攻击手法作为开发或运维人员该如何避免成为受害者呢防御必须从源头和部署两个环节抓起。6.1 开发与构建环节使用.gitignore这是第一道防线确保敏感文件如配置文件、密钥、编译产物不会被意外提交。但这对.git目录本身无效。在构建脚本中排除.git无论是使用Webpack、Maven、Gradle还是简单的Shell脚本在将代码打包用于部署时必须确保.git目录被排除在外。# 示例使用rsync排除.git目录进行部署 rsync -avz --exclude.git ./src/ userproduction-server:/var/www/html/使用Docker的.dockerignore如果使用Docker容器化部署在Dockerfile的构建上下文中利用.dockerignore文件排除.git避免它被打进镜像层。6.2 Web服务器配置这是最关键的一环确保即使.git目录被错误地放到了Web根目录也无法被外部访问。Apache配置 在虚拟主机配置或.htaccess文件中拒绝访问所有以点开头的目录。DirectoryMatch /\.(git|svn|ht) Require all denied /DirectoryMatch # 或者更直接地拒绝所有点开头 DirectoryMatch ^\.|/\. Require all denied /DirectoryMatchNginx配置 在server块中添加location规则。location ~ /\.(git|svn|ht) { deny all; access_log off; log_not_found off; } # 或者更通用 location ~ /\. { deny all; }通用方法在Web根目录下放置一个空的index.html或index.php文件到.git目录内这样当访问/.git/时服务器会默认展示这个索引文件而不是目录列表但更好的做法还是直接返回403。6.3 自动化安全检查将Git目录扫描纳入到CI/CD流水线或定期的安全扫描中。使用静态代码分析工具如gitleaks可以在代码提交时扫描仓库历史中是否意外包含了密钥、密码等敏感信息。使用漏洞扫描器在部署前或对线上环境进行定期扫描时使用如Nuclei、Acunetix等工具它们都有检测.git、.svn等目录泄露的模板。人工复查在项目上线前养成手动检查Web根目录下是否有.git文件夹的习惯。7. 排查与应急响应如果泄露已经发生如果你怀疑或已经确认自己的网站存在Git泄露应该立即采取以下步骤立即隔离如果可能将受影响的服务器或服务暂时下线或通过防火墙规则立即阻断对/.git/路径的访问。清除泄露源登录服务器彻底删除Web可访问目录下的.git文件夹。注意不要只删除内容要删除整个目录rm -rf .git。评估影响检查泄露的源码中是否包含数据库连接信息、API密钥、加密盐值、后台管理路径等敏感信息。立即更改所有涉及的密码、密钥、令牌。即使它们看起来是哈希或加密过的也应视为已泄露。审查代码历史看是否有已修复但历史上存在的高危漏洞被暴露。代码审计与加固对泄露的代码进行一次彻底的安全审计修复发现的所有漏洞。特别是因为泄露而暴露的潜在漏洞。监控与日志分析检查Web服务器日志看是否有大量访问/.git/及相关文件的异常请求评估是否已被攻击者利用。
返回列表