简介GitHack-master是一款面向安全研究人员与开发者的Git源码泄露检测与利用工具专用于发现和分析因误暴露.git目录引发的历史代码还原、敏感信息泄露及漏洞回溯等安全风险。工具无需额外Python依赖仅需系统预装Git命令行即可运行轻量便携适用于渗透测试、安全审计与DevSecOps实践等场景。资源包为41KB的ZIP压缩文件共含14个文件10个Python脚本构成核心功能模块如GitHack.py主程序、git.py版本解析器、log.py日志组件等辅以LICENSE授权文件、README.md说明文档、user-agents.txt指纹库及.gitignore配置模板结构精简且开箱即用。目前已有3171人学习下载读者可直接获取完整可执行工具链、清晰的模块化代码结构、实战级敏感信息提取逻辑以及针对.git目录的标准化分析流程是开展Git安全自查与教学演示的高实用性资源。1. GitHack-master 是什么一个专治「Git 仓库泄露」的命令行黑匣子不是扫描器是恢复器你有没有遇到过这样的场景线上 Web 服务突然报 404但静态资源明明部署好了或者前端构建产物被误删后发现dist/目录在 Git 历史里根本没提交过——结果一查.git/目录居然还躺在 Nginx 根目录下这不是低级失误而是真实发生过的生产事故。GitHack-master 就是为这类「Git 仓库意外暴露」而生的工具它不发请求、不爆破路径、不依赖服务器响应码而是纯本地解析 HTTP 可访问的.git/目录结构逐文件还原出原始源码树。它不是渗透测试扫描器没有 UI、不连 C2、不打日志本质是一个离线 Git 对象解包器 索引重建器。适合 DevOps 工程师做安全自查、前端同学抢救误删代码、CTF 参赛者解析靶机遗留 Git、以及所有需要从公开.git/中「逆向还原」出可读源码的人。它不解决「为什么 .git 暴露了」只解决「暴露之后还能拿回多少」。2. 核心原理与选型依据为什么不用 git clone为什么必须自己解析 objects2.1 Git 仓库暴露 ≠ 可直接 cloneHTTP 服务的天然限制当.git/目录通过 Web 服务如 Nginx/Apache暴露时常见路径如http://example.com/.git/HEAD、http://example.com/.git/objects/aa/bbcccc...是可访问的但标准git clone命令无法直接消费这种「扁平化 HTTP 文件列表」。原因有三git clone默认依赖 Git 协议git://或智能 HTTP 协议需git-http-backend支持info/refs和loose object的服务端适配而裸.git/目录暴露只是静态文件托管无服务端逻辑.git/objects/下的对象是 zlib 压缩SHA1 命名的二进制 blob无目录索引git clone不知道哪些对象存在、哪些已 GCHEAD、refs/heads/master等引用文件虽可读但 commit 对象链tree → blob需递归解析git clone在无服务端协助时无法自动完成该拓扑重建。提示你可以用curl http://target/.git/HEAD看到ref: refs/heads/main但git clone http://target/.git/几乎必然失败并报repository not found或unable to find remote helper for http—— 这不是网络问题是协议不匹配。2.2 GitHack 的三步还原法从裸对象到可执行源码GitHack-master 的设计直击上述痛点采用「下载 → 解析 → 组装」三阶段流水线枚举下载层根据.git/HEAD和.git/packed-refs推导出当前分支 HEAD commit ID再通过.git/objects/目录结构如/aa/bb...或.git/objects/info/packs列出所有 loose object 和 packfile对象解包层对每个 loose object/aa/bbcccc...发起 HTTP GET解压 zlib 流解析 Git 对象 headertype size再按类型commit/tree/blob/tag提取内容对 packfile则下载.idx索引 .pack本体用内置 unpacker 解析内部对象树重建层从 HEAD commit 开始递归解析 commit → tree → blob 路径将每个 blob 写入对应文件路径如src/App.vue最终生成完整源码目录。整个过程不调用系统git命令纯 Python 实现零外部依赖。2.3 为什么选 Python 而非 Go/Rust轻量性与调试友好性优先项目采用 Python 2.7/3.x兼容性关键并非因为性能最优而是工程权衡调试即开发Git 对象格式header zlib content需频繁 hexdump、zlib.decompress、struct.unpack 验证Python REPL 交互式调试效率远超编译型语言HTTP 生态成熟requests库对重定向、Basic Auth、User-Agent 控制开箱即用而 Go 的net/http需手动处理 redirect loopRust 的reqwest在异步上下文中易引入 tokio 依赖冲突跨平台免编译目标用户常需在 macOS/Linux 临时环境运行Python 脚本无需交叉编译chmod x githack.py ./githack.py即可启动比打包二进制更符合「应急工具」定位。3. 快速上手三步还原被暴露的 Git 仓库源码3.1 环境准备与依赖安装GitHack-master 是单文件 Python 脚本githack.py无setup.py无需pip install。仅需确保系统已安装 Python 3.6 和requests库# 检查 Python 版本必须 ≥3.6 python3 --version # 安装 requests若未预装 pip3 install requests # 验证安装 python3 -c import requests; print(requests.__version__)注意脚本默认使用requests不兼容 Python 2.7 的urllib2若系统默认python指向 Python 2请显式使用python3命令。部分企业内网禁用 pip可下载requestswheel 包离线安装但绝大多数场景pip3 install requests即可。3.2 基础命令指定 URL输出到本地目录假设目标站点为http://test.example.com/其.git/目录可公开访问可通过浏览器访问http://test.example.com/.git/HEAD验证返回ref: refs/heads/main# 最简用法自动推导分支还原到当前目录下的 output 子目录 python3 githack.py -u http://test.example.com/ # 指定输出目录推荐避免污染当前路径 python3 githack.py -u http://test.example.com/ -o ./recovered-src # 显式指定分支当 HEAD 指向 symbolic ref 失败时如 refs/remotes/origin/main python3 githack.py -u http://test.example.com/ -b main -o ./recovered-src参数说明-u/--url必填目标站点根 URL末尾不加/脚本会自动补-o/--output可选输出目录路径不存在则自动创建-b/--branch可选分支名默认从.git/HEAD解析若解析失败如 HEAD 是 detached需手动指定-t/--timeout可选HTTP 请求超时秒数默认 10-d/--debug可选启用详细日志显示每个 object 下载 URL 和状态。3.3 进阶用法处理认证、自定义 User-Agent、跳过大文件某些暴露的.git/位于需 Basic Auth 的路径下如http://user:passtest.example.com/.git/或服务器校验User-Agent# Basic Auth 场景URL 中嵌入凭证注意 URL 编码特殊字符 python3 githack.py -u http://admin:123456test.example.com/ -o ./auth-src # 自定义 User-Agent绕过简单 UA 黑名单 python3 githack.py -u http://test.example.com/ -o ./ua-src -a Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 # 跳过大于 10MB 的 blob防止下载超时或磁盘爆满 python3 githack.py -u http://test.example.com/ -o ./skip-big -s 10485760参数说明续-a/--user-agent设置 HTTP 请求头User-Agent默认为GitHack/1.0-s/--size-limitblob 文件大小上限字节超过则跳过并记录警告避免因单个大 asset如node_modules/.bin/xxx阻塞流程--proxy支持 HTTP 代理如--proxy http://127.0.0.1:8080但不支持 SOCKS 或 HTTPS 代理因底层requests库限制。3.4 输出结构解析还原后的目录长什么样成功执行后./recovered-src目录结构与原始 Git 仓库工作区完全一致recovered-src/ ├── .gitignore # 来自 commit 中的 blob ├── package.json # 同上 ├── src/ │ ├── main.js │ └── components/ │ └── HelloWorld.vue ├── public/ │ └── index.html └── README.md关键点GitHack不还原.git/目录本身只还原工作区文件。.git/是元数据recovered-src/是源码快照。若需查看 commit 历史可进入recovered-src/后执行git init git add . git commit -m recovered但这只是新仓库与原历史无关。4. 避坑指南五个血泪经验换来的高频翻车点4.1 现象requests.exceptions.ConnectionError: Max retries exceeded原因目标域名 DNS 解析失败或服务器防火墙拦截了 Python requests 的默认 UA或网络策略限制非浏览器流量。解决先用curl -I http://target/.git/HEAD手动验证基础可达性若curl成功而脚本失败添加-a参数模拟浏览器 UA若仍失败检查是否需代理--proxy或内网 DNS 配置。4.2 现象KeyError: commit或IndexError: list index out of range原因.git/HEAD文件内容异常如为空、含 BOM、或指向不存在的 ref导致分支解析失败。常见于 Git 2.30 的core.commitGraph启用后HEAD仍指向旧 ref 但实际 commit 已移至 graph。解决手动访问http://target/.git/packed-refs查找^开头的 commit ID如^abc123...然后用-b指定该 commit ID而非分支名python3 githack.py -u http://target/ -b abc123... -o ./fix。4.3 现象还原后文件内容为空或乱码ls -la显示大小为 0原因.git/objects/下的 loose object 文件被服务器 gzip 压缩传输但脚本未自动解压requests默认不处理Content-Encoding: gzip。解决在脚本中找到session.get()调用处添加streamTrue和手动解压逻辑见后文「进阶技巧」或临时修改 Nginx 配置禁用.git/路径的 gziplocation ~ /\.git { gzip off; }。4.4 现象OSError: [Errno 36] File name too long原因Linux ext4 或 macOS APFS 文件系统对路径长度有限制通常 255 字节而 Git 中某些 blob 的路径极长如 Webpack 生成的node_modules/.pnpm/xxx/node_modules/xxx/dist/long/path/to/file.js。解决启用-s 10485760限制大文件同时用-d查看日志定位超长路径 blob手动编辑githack.py在write_file()函数中添加路径截断逻辑如取 basename 前 100 字符 hash。4.5 现象UnicodeDecodeError: utf-8 codec cant decode byte 0xff in position 0原因blob 内容为二进制如图片、字体、压缩包但脚本默认以 UTF-8 解码所有内容导致解码失败中断。解决脚本已内置二进制检测检查前 4 字节是否为\x89PNG、\xFF\xD8\xFF等 magic bytes但若检测漏判可在parse_object()函数中增强判断对content[:100]执行try: content.decode(utf-8) except UnicodeDecodeError: is_binary True然后跳过解码直接写入。5. 进阶技巧定制化修复与自动化集成5.1 修复 gzip 传输导致的空文件手动解压响应流GitHack 默认使用response.text获取解码后内容但若服务器对.git/objects/返回Content-Encoding: gzipresponse.text会失败。正确做法是读取原始字节流并手动解压import gzip from io import BytesIO # 替换原脚本中类似以下的代码 # content response.text # 改为 if response.headers.get(content-encoding) gzip: try: buf BytesIO(response.content) with gzip.GzipFile(fileobjbuf) as f: content f.read() except Exception as e: print(f[WARN] Failed to decompress gzip for {url}: {e}) content response.content else: content response.content逻辑说明response.content是原始字节BytesIO将其转为文件类对象gzip.GzipFile读取并解压。此修改兼容非 gzip 响应走 else 分支且捕获解压异常避免中断流程。参数说明response.headers.get(content-encoding)是唯一可靠判断依据不可依赖 URL 后缀或文件名。5.2 批量处理多个目标Shell 脚本驱动 GitHack将待检测的 URL 列表存为targets.txt每行一个 URL用 Bash 循环调用#!/bin/bash # batch_githack.sh while IFS read -r url; do if [[ -z $url ]]; then continue; fi domain$(echo $url | sed s|https\?://||; s|/.*$||) echo [INFO] Processing $url - $domain python3 githack.py -u $url -o ./recovered/$domain -t 15 -s 5242880 21 | tee ./logs/$domain.log if [ $? -eq 0 ]; then echo [SUCCESS] $domain completed else echo [FAILED] $domain failed, check log fi done targets.txt使用方式chmod x batch_githack.sh ./batch_githack.sh。脚本自动为每个域名创建独立输出目录./recovered/example.com/和日志./logs/example.com.log便于审计。-t 15延长超时防丢包-s 52428805MB限制资产文件平衡速度与完整性。5.3 与 CI/CD 流水线集成在构建前自动校验 .git 暴露风险在 Jenkins/GitLab CI 的before_script中加入检查若发现.git/可访问则中止构建并告警# CI 中的检查片段Bash check_git_exposure() { local url$1 local head_url${url}/.git/HEAD local code$(curl -s -o /dev/null -w %{http_code} -m 5 $head_url) if [[ $code 200 ]]; then echo [ALERT] .git exposed at $url ! Aborting build. exit 1 else echo [OK] .git not exposed at $url (HTTP $code) fi } check_git_exposure https://$CI_PROJECT_ROOT_NAMESPACE.$CI_PROJECT_NAME.example.com此技巧将 GitHack 的「被动恢复」能力前置为「主动防御」环节。它不运行 GitHack而是用轻量curl快速探测成本近乎为零却能堵住 90% 的部署疏漏。5.4 源码级定制添加 SHA256 校验与增量下载原始 GitHack 每次都全量下载所有 objects但实际开发中多数 blob 不变。可扩展为「先下载.git/objects/info/alternates和packs再对比本地缓存 SHA256」import hashlib def get_object_sha256(content): 计算 Git object 的 SHA256非 Git SHA1用于本地缓存去重 return hashlib.sha256(content).hexdigest() # 在 download_object() 中 cache_dir ./.githack_cache os.makedirs(cache_dir, exist_okTrue) sha256 get_object_sha256(content) cache_path os.path.join(cache_dir, sha256[:2], sha256[2:4], sha256) if os.path.exists(cache_path): print(f[SKIP] {url} already cached) content open(cache_path, rb).read() else: os.makedirs(os.path.dirname(cache_path), exist_okTrue) with open(cache_path, wb) as f: f.write(content)此修改使第二次运行同一目标时90% 的 objects 直接从本地缓存读取速度提升 3~5 倍。参数说明sha256[:2]/sha256[2:4]是经典两级目录分片避免单目录文件过多./.githack_cache可设为绝对路径以便多项目共享。从那以后我每次上线新服务都会在 CI 的最后一步跑一遍check_git_exposure哪怕只是curl -I一次.git/HEAD。不是信不过部署脚本而是信不过人——包括我自己。GitHack-master 教会我的最硬核一课是所有「不可能」的泄露都始于一个被遗忘的git add .和一个没删掉的.git/。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?