跳到主要内容

Git 仓库敏感信息取证与凭据恢复

在 Web 安全评估、主机内部侦察与应急响应中,遗留的 Git 仓库(如开放目录遍历的 .git 目录、系统备份中的 *.git 裸仓库,或开发人员日常工作目录)是极高价值的凭据与源码来源。

Git 作为内容寻址的分布式版本控制系统,其底层对象数据库(Object Database)记录了历史上所有提交的文件内容与变更记录。即便敏感信息(如数据库密码、API 密钥、SSH 私钥)在最新一次提交中已被显式删除,其历史对象与引用仍完整保存在仓库中


典型场景与审计切入点

在打靶与实战中,常见的 Git 取证场景分为三类:

场景分类出现环境核心关注对象
Web 端 .git 目录泄露Web 根目录下遗留 .git/,或目录遍历可下载HEADindexlogs/HEADobjects/
主机内部 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 仓库中检索到历史变更时,重点关注以下文件与凭据模式:

  1. 私钥与证书id_rsaid_ed25519server.key*.pfx*.pem
  2. 环境配置.env.env.productionconfig.phpsettings.pyapplication.yml
  3. 认证凭据:硬编码的数据库连接串、AWS/云厂商 AccessKey、JWT 签名密钥、Webhook 认证 Token。
  4. 内部架构信息:内网域名、内部 API 路由、数据库服务器内网 IP。

防御建议与安全加固

  1. 禁止公网暴露 .git 目录:在 Nginx、Apache 等 Web 服务器中配置全局阻断规则:
    location ~ /\.git {
    deny all;
    return 404;
    }
  2. 提交前自动化敏感扫描:在本地引入 pre-commit 钩子,或在 CI 流程中使用 TruffleHogGitleaks 拦截误提交凭据。
  3. 历史泄露凭据彻底清除:一旦凭据进入提交历史,单纯 git rm 无法阻断风险:
    • 立即在业务侧注销并轮换已泄露的密钥与密码;
    • 使用 git-filter-repo 或 BFG Repo-Cleaner 从所有分支与 Tag 历史中永久剥离敏感对象。
  4. 限制服务器上的仓库残留:线上生产环境应仅通过打包归档(如 Docker 镜像或制品压缩包)分发代码,禁止直接在生产路径 git pull 保留版本历史。

来源案例

  • HackMyVM Fuxa:在目标主机上发现遗留的 Git bare 仓库,通过 git log -p 审计提交历史成功恢复 Root SSH 私钥。
  • HackMyVM Gitdwn:利用 git stash listgit stash show -p 从暂存区中提取开发者遗留的内部服务鉴权 Token。