Git 仓库敏感信息取证与凭据恢复
在 Web 安全评估、主机内部侦察与应急响应中,遗留的 Git 仓库(如开放目录遍历的 .git 目录、系统备份中的 *.git 裸仓库,或开发人员日常工作目录)是极高价值的凭据与源码来源。
Git 作为内容寻址的分布式版本控制系统,其底层对象数据库(Object Database)记录了历史上所有提交的文件内容与变更记录。即便敏感信息(如数据库密码、API 密钥、SSH 私钥)在最新一次提交中已被显式删除,其历史对象与引用仍完整保存在仓库中。
典型场景与审计切入点
在打靶与实战中,常见的 Git 取证场景分为三类:
| 场景分类 | 出现环境 | 核心关注对象 |
|---|---|---|
Web 端 .git 目录泄露 | Web 根目录下遗留 .git/,或目录遍历可下载 | HEAD、index、logs/HEAD、objects/ |
| 主机内部 Git Bare 裸仓库 | /var/repo/*.git、/opt/*.git、GitLab/Gitea 数据目录 | git --git-dir 指令历史、分支提交差异 |
| 开发工作区残留 | 用户家目录或 /var/www 下带有 .git 的工作副本 | git stash 贮藏列表、git reflog 游离提交 |
核心取证与数据恢复方法
方法 A:Bare 裸仓库历史穿透取证
裸仓库(Bare Repository,目录名通常以 .git 结尾)不包含工作目录(Working Tree),无法直接 ls 看到源码文件。可通过 --git-dir 参数直接对其执行审查:
# 1. 查看分支与最近提交记录
git --git-dir=/path/to/project.git log --oneline -n 10
# 2. 查看所有分支的提交差异与增删详情
git --git-dir=/path/to/project.git log -p --all
# 3. 按关键字搜索敏感变更(如包含 password, key, token 的增删)
git --git-dir=/path/to/project.git log -S "password" -p
git --git-dir=/path/to/project.git log -S "BEGIN OPENSSH PRIVATE KEY" -p
# 4. 导出特定历史提交中的某个文件
git --git-dir=/path/to/project.git show <commit-hash>:config/database.yml
方法 B:Git Stash(工作进度暂存)取证
开发人员在切换分支或拉取代码时,常使用 git stash 临时封存尚未提交的工作内容。这些修改往往包含正在调试的测试口令、硬编码私钥或敏感接口配置:
# 1. 检查是否存在暂存记录
git stash list
# 2. 查看最近暂存内容的变更差异(diff 格式)
git stash show -p stash@{0}
# 3. 提取暂存中的特定文件
git show stash@{0}:id_rsa
git show stash@{0}:.env
方法 C:Git Reflog 与游离提交(Orphan/Dangling Commit)恢复
开发人员使用 git reset --hard 回滚提交、或使用 git commit --amend 覆盖敏感信息时,被移除的 Commit 并没有立即被垃圾回收,而是仍存留在对象库中:
# 1. 查看操作历史引用日志
git reflog show --all
# 2. 查找悬空对象(未被任何分支/标签引用的 commit 和 blob)
git fsck --lost-found
# 3. 逐个查看悬空提交的内容
git show <dangling-commit-hash>
# 4. 对悬空 blob(文件)直接还原内容
git cat-file -p <dangling-blob-hash>
方法 D:直接针对 Object 底层文件扫描
当 Git 命令受限或仓库结构损坏时,可直接针对 .git/objects 目录执行解压缩取证。Git 对象使用 zlib 压缩存储:
# 使用 Python 一键遍历并解压所有 git object 并检索敏感词
python3 - << 'EOF'
import os, zlib
git_objects = ".git/objects"
for root, _, files in os.walk(git_objects):
for f in files:
if len(f) == 38: # commit/blob/tree 对象的后 38 位 hex
full_path = os.path.join(root, f)
try:
with open(full_path, "rb") as fp:
data = zlib.decompress(fp.read())
if b"PRIVATE KEY" in data or b"password" in data:
print(f"[+] Hit in {full_path}:")
print(data[:300].decode(errors='ignore'))
except Exception:
pass
EOF
敏感信息快速识别清单
从 Git 仓库中检索到历史变更时,重点关注以下文件与凭据模式:
- 私钥与证书:
id_rsa、id_ed25519、server.key、*.pfx、*.pem。 - 环境配置:
.env、.env.production、config.php、settings.py、application.yml。 - 认证凭据:硬编码的数据库连接串、AWS/云厂商 AccessKey、JWT 签名密钥、Webhook 认证 Token。
- 内部架构信息:内网域名、内部 API 路由、数据库服务器内网 IP。
防御建议与安全加固
- 禁止公网暴露
.git目录:在 Nginx、Apache 等 Web 服务器中配置全局阻断规则:location ~ /\.git {deny all;return 404;} - 提交前自动化敏感扫描:在本地引入
pre-commit钩子,或在 CI 流程中使用 TruffleHog、Gitleaks 拦截误提交凭据。 - 历史泄露凭据彻底清除:一旦凭据进入提交历史,单纯
git rm无法阻断风险:- 立即在业务侧注销并轮换已泄露的密钥与密码;
- 使用
git-filter-repo或 BFG Repo-Cleaner 从所有分支与 Tag 历史中永久剥离敏感对象。
- 限制服务器上的仓库残留:线上生产环境应仅通过打包归档(如 Docker 镜像或制品压缩包)分发代码,禁止直接在生产路径
git pull保留版本历史。
来源案例
- HackMyVM Fuxa:在目标主机上发现遗留的 Git bare 仓库,通过
git log -p审计提交历史成功恢复 Root SSH 私钥。 - HackMyVM Gitdwn:利用
git stash list与git stash show -p从暂存区中提取开发者遗留的内部服务鉴权 Token。