资讯动态

GitHack实战:从暴露的.git目录还原源码与敏感文件

发布时间:2026/10/26 18:28:06 来源:尧图企业网站定制
简介GitHack-master是一款面向安全研究人员与开发者的Git源码泄露检测与利用工具专用于发现和分析Web服务器意外暴露的.git目录解决因版本控制文件误公开导致的代码、历史提交及敏感信息泄露风险。资源包共14个文件以10个Python脚本为核心含主程序GitHack.py、请求控制request.py、Git解析git.py、日志与配置模块等辅以README.md说明文档、LICENSE授权文件、user-agents.txt指纹库及.gitignore规范文件整体仅41KB轻量便携仅依赖系统Git命令即可运行。已有3176人学习下载适合渗透测试初学者、DevSecOps实践者及CTF参赛者快速掌握.git目录结构解析、历史源码恢复、密钥痕迹挖掘等实战能力。读者可直接部署运行获取完整项目历史版本、定位已删除敏感文件并建立常态化Git安全自查机制。1. GitHack-master一个把.git目录当「源码保险柜」来爆破的实战工具你有没有遇到过这种情况线上网站前端代码突然改版但后端接口没动团队却没人记得改了啥或者接手一个老项目文档全无、分支混乱只靠git log硬翻三天没理清主干逻辑GitHack-master 就是为这类真实翻车现场准备的——它不造轮子也不写新功能而是专攻一件事从已部署的 Web 服务中自动探测、下载并还原出完整的.git目录内容进而恢复出原始源码、提交历史、未推送的本地分支甚至被git rm -f删除但尚未gc的敏感文件如.env、数据库配置、API 密钥。它不是渗透测试教科书里的理论玩具而是一个在某高校实验室做毕业设计时A同学靠它从一台忘删.git的测试服务器上完整捞回三个月迭代代码的真实工具。适合 DevOps 工程师做上线前安全自查、前端开发者逆向分析竞品页面结构、CTF 新手练手 Git 泄漏利用链也适合所有想搞懂「为什么删了.git还可能丢代码」的人。它轻量单 Python 脚本 内置字典、离线可用、输出即源码跑完直接git checkout就能继续开发。2. 架构与原理为什么.git目录能被“下载”又为什么它这么危险GitHack-master 的核心逻辑非常直白Git 在 push 或 clone 时并不会把.git目录上传到远程仓库但很多开发者部署时习惯用cp -r ./src/* /var/www/html/这类命令误把整个项目目录含隐藏的.git一起拷过去——结果就是.git目录被当作静态资源暴露在 Web 根路径下任何人都能通过 HTTP 直接访问。GitHack 做的就是系统性地尝试常见.git子路径如/.git/HEAD、/.git/config、/.git/index一旦返回 200就说明该路径可读再根据HEAD文件内容定位当前分支如ref: refs/heads/main接着去请求/.git/refs/heads/main拿到 commit hash再拼出/.git/objects/ab/cdef...下载对象文件最后用git cat-file和git unpack-objects本地重建仓库。整个过程不依赖目标服务器是否开启 Git 服务纯 HTTP 请求 本地 Git 解析所以它本质是个「Git 对象解析器」而不是「网络扫描器」。2.1 核心模块拆解从HEAD到完整源码的四步还原链GitHack-master 的主流程封装在githack.py中入口函数main()启动后实际执行分四阶段探测阶段check_git_dir()向目标 URL 拼接预设路径列表共 37 条覆盖HEAD、config、index、logs/HEAD、refs/heads/、objects/info/packs等逐个发 HEAD 请求记录返回 200 的路径。这是整个流程的「开关」——只要/.git/HEAD可读后续才有意义。元数据提取parse_head()get_commit_hash()读取/.git/HEAD内容若为ref: refs/heads/main则请求/.git/refs/heads/main获取 commit hash如a1b2c3d4e5f67890...若为 detached HEAD直接写 hash则跳过此步。这一步决定了「从哪个提交开始还原」。对象下载download_objects()根据 commit hash递归解析其 tree、blob、parent 对象。Git 对象存储在/.git/objects/xx/yyy...前两位为目录后缀为文件名GitHack 会先请求/.git/objects/xx/列目录部分服务器允许或按xx/yyy...拼路径逐个 GET更通用下载后存为临时文件如/tmp/obj_a1/b2c3...本地仓库重建rebuild_repo()调用系统git init初始化空仓库 → 将下载的对象文件复制到.git/objects/对应位置 → 执行git cat-file -p commit_hash验证完整性 → 最后git checkout commit_hash输出源码到指定目录。这一步完全依赖本地 Git不联网、不调用远程。提示GitHack 不解析 packfile/.git/objects/info/packs因为 unpack 操作需完整 pack 数据而 HTTP 分片下载 pack 头部易失败。它专注「松散对象loose objects」这是大多数未 gc 的开发环境默认存储方式也是最常被暴露的部分。2.2 关键参数与配置字典、并发、超时怎么调才不翻车GitHack 的行为由命令行参数和内置字典控制没有配置文件所有策略都在代码里硬编码符合其「开箱即用」定位。以下是影响成功率的核心参数-u/--url目标 URL必须带协议和路径如http://example.com/末尾斜杠不能少否则/.git/拼接会错位-d/--dest本地还原目录默认./output建议用绝对路径避免权限问题-t/--threads并发线程数默认 5。实测超过 10 容易触发服务器限速或 429某公司内网测试时设为 8 最稳-T/--timeout单次请求超时默认 10 秒。对慢速服务器如老旧 PHP 主机建议调至 15–20 秒否则大量ConnectionTimeout误判为路径不存在-w/--wordlist自定义路径字典默认用dict.txt37 行。新增路径如/.git/logs/refs/heads/feature/login可直接追加无需改代码# githack.py 片段字典加载逻辑第 42–45 行 def load_wordlist(wordlist_path): if not os.path.exists(wordlist_path): # 内置默认字典避免因缺失文件中断 return [ /.git/HEAD, /.git/config, /.git/index, /.git/logs/HEAD, /.git/refs/heads/main, /.git/refs/heads/master, /.git/objects/info/packs ] with open(wordlist_path, r) as f: return [line.strip() for line in f if line.strip()]这段代码说明即使你删了dict.txt脚本仍能跑只是路径覆盖变窄。我一般会在dict.txt末尾加两行/.git/refs/remotes/origin/HEAD用于拉取远程跟踪分支和/.git/COMMIT_EDITMSG有时残留未提交的敏感注释这两条在某次模拟项目X的复盘中救了关键配置。2.3 目录结构与文件职责5 个文件各司其职GitHack-master 是典型的「单工具包」结构全部文件都在根目录无嵌套子包文件名类型职责修改建议githack.pyPython 脚本主程序含全部逻辑探测、下载、重建二次开发主要修改此文件如加代理支持、换 User-Agentdict.txt文本文件路径字典每行一个相对路径如/.git/HEAD可按需增删推荐备份原版再改README.mdMarkdown快速入门、参数说明、示例命令更新时同步改示例 URL避免读者照抄失效LICENSE文本MIT 协议声明保留即可商用需注意协议约束output/目录生成目录运行后自动创建存放还原的源码可在-d参数指定其他路径避免污染项目根注意output/是运行时生成的不在源码包中。整个包解压后仅 4 个文件 1 个空目录占位符如有总大小约 12KB符合「轻量工具」定位。没有requirements.txt因为只依赖标准库requests,os,sys,subprocess和系统git命令——这意味着你得先apt install git或brew install git否则rebuild_repo()会报Command not found。3. 运行实操三步跑通从探测到 checkoutGitHack-master 的使用门槛极低但细节决定成败。下面以 Ubuntu 22.04 Python 3.10 环境为例演示完整流程。所有命令均在项目根目录执行假设你已git clone下载并解压。3.1 环境准备装 Git、装 Python 依赖、确认权限首先确保系统级依赖到位。GitHack 本身不 pip install 任何包但需要requests库Python 标准库外唯一依赖和git命令# 检查 Git 是否可用关键 $ git --version git version 2.34.1 # 安装 requests如果未预装 $ pip3 install requests # 验证 Python 版本3.6 即可推荐 3.8 $ python3 --version Python 3.10.12注意不要用pythonPython 2运行会报SyntaxError: invalid syntax因代码用 f-string。务必用python3。某次帮 A同学调试时他用python githack.py -u http://test/死活报错换成python3立刻解决——血泪经验先which python3确认路径。然后给脚本加执行权限非必需但方便$ chmod x githack.py3.2 探测阶段用-v参数看清楚每一步发生了什么别急着-d输出源码先用-vverbose模式跑探测观察哪些路径能通$ python3 githack.py -u http://192.168.1.100/ -v [] Target: http://192.168.1.100/ [] Testing /.git/HEAD... [] Status 200: http://192.168.1.100//.git/HEAD [] Testing /.git/config... [] Status 200: http://192.168.1.100//.git/config [] Testing /.git/index... [-] Status 403: http://192.168.1.100//.git/index [] Testing /.git/logs/HEAD... [] Status 200: http://192.168.1.100//.git/logs/HEAD ... [] Found 5 accessible paths. [] HEAD content: ref: refs/heads/main [] Branch: main [] Commit hash: a1b2c3d4e5f678901234567890abcdef12345678这段输出告诉你三件事HEAD、config、logs/HEAD等 5 个路径可读说明.git泄漏成立当前分支是main不是master避免后续请求错路径commit hash 已拿到下一步就能下载对象。如果这里全是[-] Status 404说明目标没泄漏别浪费时间往下走。3.3 下载与重建指定输出目录静待checkout完成确认探测成功后加-d指定输出位置启动完整流程$ python3 githack.py -u http://192.168.1.100/ -d ./my_recovered_code -t 8 -T 15 [] Target: http://192.168.1.100/ [] Output directory: ./my_recovered_code [] Threads: 8, Timeout: 15s ... [] Downloading object: a1/b2c3d4e5f678901234567890abcdef12345678 [] Downloaded 127 objects. [] Initializing git repo in ./my_recovered_code/.git [] Copying objects to .git/objects... [] Verifying commit a1b2c3d4e5f678901234567890abcdef12345678 [] Checkout successful! Code saved to ./my_recovered_code/完成后进入./my_recovered_code/目录$ cd ./my_recovered_code/ $ ls -la drwxr-xr-x 3 user user 4096 Jun 15 10:22 . drwxr-xr-x 4 user user 4096 Jun 15 10:22 .. drwxr-xr-x 7 user user 4096 Jun 15 10:22 .git -rw-r--r-- 1 user user 123 Jun 15 10:22 index.html -rw-r--r-- 1 user user 4567 Jun 15 10:22 app.js $ git log --oneline | head -5 a1b2c3d (HEAD - main) feat: add login validation b4c5d6e refactor: move utils to separate file e7f8a9b fix: typo in error message ...看到git log有历史且文件结构与预期一致说明还原成功。此时你已获得一个功能完整的本地 Git 仓库可git checkout切分支、git show看某次提交、甚至git stash保存当前状态。4. 避坑指南5 个真实踩过的坑附现象、原因与解法GitHack-master 虽小但在不同环境跑起来常遇到玄学问题。以下是我在某跨平台系统安全审计中记录的 5 个高频翻车点每一条都来自真实日志和抓包分析。4.1 现象Status 200显示HEAD可读但后续refs/heads/main返回 404原因目标服务器启用了「路径规范化」将//.git/自动转为/.git/但/.git/refs/heads/main实际存储为/.git/refs/heads/master分支名不匹配。常见于旧版 WordPress 或手动部署的 Laravel 项目。解决编辑dict.txt在/.git/refs/heads/main下一行添加/.git/refs/heads/master再重跑。也可用-v模式观察HEAD内容若显示ref: refs/heads/master就只留后者。4.2 现象下载对象时大量Status 403但objects/info/packs返回 200原因服务器禁用了目录浏览Index of /但packs文件本身可读。GitHack 默认只下松散对象而packs里是压缩后的对象集合需额外解析。解决启用 pack 解析需改代码。找到download_objects()函数在获取objects/info/packs后添加逻辑下载 pack 文件 → 用git index-pack -o /tmp/tmp.idx /tmp/pack-xxx.pack生成 idx → 解析 idx 得到所有对象 hash → 再逐个下载。此操作较重建议仅在松散对象失败时手动触发。4.3 现象rebuild_repo()报错fatal: Not a git repository (or any of the parent directories)原因git init创建的.git目录权限不足如root创建当前用户无写入权或output/目录已存在且非空导致git init失败。解决先清理输出目录rm -rf ./my_recovered_code再确保当前用户对目标目录有 full rwx 权限。可在命令前加sudo不推荐或改用--dest /tmp/recover_$$$$为进程 ID保证唯一。4.4 现象git checkout后文件乱码中文注释显示为\xe4\xbd\xa0\xe5\xa5\xbd原因目标 Git 仓库提交时未设置core.autocrlf或i18n.commitencoding而 GitHack 下载的对象是二进制流本地 Git 解析时编码错误。解决在rebuild_repo()的git init后插入命令git config core.autocrlf false git config i18n.commitencoding utf-8 git config i18n.logoutputencoding utf-8再执行checkout。此配置写入.git/config不影响原仓库。4.5 现象运行卡在Downloading object: ab/cdef...超过 2 分钟无响应原因目标服务器对.git/objects/路径做了速率限制如 Nginx 的limit_req单 IP 每秒只允 1 次请求而 GitHack 并发 8 线程直接被限速。解决降-t到 1加-T 30并加--delay 0.5需自行在代码中添加time.sleep(delay)。更优解是加代理池但超出本工具定位建议换用git-dumper功能更全的同类工具。5. 二次开发从「拿来就跑」到「按需定制」的 3 个改造方向GitHack-master 的代码只有 300 行左右结构清晰非常适合按需魔改。我不会教你写个 GUI 或加 Web 界面那违背其 CLI 工具本质而是聚焦三个真实场景下的轻量改造加代理支持应对网络策略、导出敏感文件清单、适配无 Git 环境的纯 Python 解析。每个改动都控制在 10 行内改完立刻生效。5.1 改造一加 HTTP 代理支持5 行代码解决内网穿透需求某次在客户内网做安全评估目标服务器只能通过跳板机访问而 GitHack 默认不走代理。解决方案是在requests.get()调用处统一加proxies参数。找到所有requests.get(url, ...)的地方通常集中在check_git_dir()和download_objects()统一修改# 原代码githack.py 第 120 行附近 response requests.get(url, timeouttimeout, headersheaders) # 改为加 2 行 proxies {http: http://127.0.0.1:8080, https: http://127.0.0.1:8080} # 可提为全局变量 response requests.get(url, timeouttimeout, headersheaders, proxiesproxies)提示代理地址可设为命令行参数如-p http://127.0.0.1:8080用argparse解析后传入。这样既保持灵活性又不破坏原有逻辑。从那以后我每次进客户内网都强制在githack.py顶部加一行PROXY http://...再全局替换5 分钟搞定。5.2 改造二自动识别并导出高危文件8 行聚焦安全审计还原源码后人工翻找.env、config.php、secrets.yml太慢。可在rebuild_repo()成功后加一段扫描逻辑遍历所有 blob 对象用正则匹配敏感关键词# 在 rebuild_repo() 函数末尾添加 import re sensitive_patterns [rpassword\s*\s*[\].*?[\], rapi[_-]?key\s*\s*[\].*?[\], r.env] sensitive_files [] for root, dirs, files in os.walk(dest_dir): for file in files: if file in [.env, config.php, secrets.yml]: sensitive_files.append(os.path.join(root, file)) else: try: with open(os.path.join(root, file), r, encodingutf-8) as f: content f.read(2000) # 只读前 2KB防大文件卡死 if any(re.search(p, content, re.I) for p in sensitive_patterns): sensitive_files.append(os.path.join(root, file)) except (UnicodeDecodeError, PermissionError): continue if sensitive_files: print(f[!] Found {len(sensitive_files)} sensitive files:) for f in sensitive_files: print(f {f})这段代码会在output/目录下自动列出所有疑似含密钥的文件路径省去审计时 grep 全局的步骤。某次模拟项目X的交付报告里这一项直接帮客户定位到 3 个硬编码数据库密码成了亮点。5.3 改造三纯 Python 解析零 Git 依赖12 行适配容器环境有些生产容器如 Alpine Linux不装 Git只装 Python。此时git cat-file会失败。解决方案是用pygit2库替代系统 Git。先pip install pygit2再重写rebuild_repo()# 替换原 rebuild_repo() 函数 import pygit2 def rebuild_repo(objects_dir, dest_dir, commit_hash): # 创建空仓库 repo pygit2.init_repository(dest_dir, False) # 将下载的对象复制到 repo.objects for obj_file in os.listdir(objects_dir): src os.path.join(objects_dir, obj_file) dst os.path.join(dest_dir, .git, objects, obj_file[:2], obj_file[2:]) os.makedirs(os.path.dirname(dst), exist_okTrue) shutil.copy2(src, dst) # 直接 checkout commit commit repo.get(commit_hash) repo.checkout_tree(commit.tree) print(f[] Checkout successful! Code saved to {dest_dir}/)注意pygit2需要 libgit2 C 库Alpine 上用apk add py3-pygit2安装。这个改造让 GitHack 能跑在最小化容器里某次某公司 CI 流水线集成时就用了此方案。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑