Exposed .git Directories: Dumping Repos the Careful Way
A web root that serves .git/ is a classic misconfiguration with modern impact: full source, commit history, and whatever secrets once lived in the tree. You do not need an RCE. You need HEAD, objects, and patience.
Test for it early on every new host. It still appears on staging, static mirrors, and rushed container deploys.
Detect without downloading the world
Request /.git/HEAD. A response like ref: refs/heads/main is the tell. Follow with /.git/config for remotes and /.git/logs/HEAD for recent commits. Directory listing on /.git/ is optional; many servers disable listing but still serve individual objects.
If HEAD is 403/404, try adjacent VCS leftovers (.svn/entries, .hg/) and move on. Do not fuzz object hashes blindly for hours against a production CDN—you will only generate noise.
Confirm the host is in scope. Finding .git on a third-party microsite parked on the same IP is a different conversation.
Reconstruct with restraint
Use established dumpers that fetch index, packs, and loose objects over HTTP. Prefer tools that stop on missing paths rather than infinite retry loops. Work against staging when the policy prefers it; production dumps should be as small as needed to prove completeness.
Clone into an isolated working directory. Then:
- Search history for secrets with a local scanner
- Note infrastructure endpoints and unfinished authz checks in the code
- Identify whether the exposed branch matches what production runs
I've opened dumps where the live site was a minified build but .git still held API keys for a forgotten cloud project. Validate keys with least-privilege checks the program allows; revoke reporting through their secure channel when required.
Partial exposures happen. Sometimes packed-refs downloads and objects 404 behind a WAF rule that only blocks /objects/. Report what you actually retrieved. A partial dump that still yields config remotes and a few blobs can be enough if those blobs hold secrets.
Hash the commit you recovered and name the branch. Triage reproduces faster when they can git log -1 against your sample instead of guessing which snapshot you meant.
Impact that survives triage
".git is downloadable" plus a reconstructed file tree is usually enough for a solid medium/high, depending on secret presence. Escalate when history contains production credentials, private keys, or customer data. Mention packfile size only as context; impact lives in the contents.
Do not publish the full dump. Provide HEAD, a file listing sample, and redacted secret evidence. Offer the archive privately.
Fixes worth recommending
Remove .git from deploy artifacts, serve the built site from a clean directory, block path prefixes at the reverse proxy, and rotate every credential that ever touched the repository. Adding an empty /.git/config deny rule without deleting objects is incomplete if objects remain reachable.
If HEAD is exposed but objects are not, say so honestly—it may still leak branch names and remotes, which is a smaller issue.
One careful reconstruction beats a noisy scraper that tripped every alarm for a finding the program already auto-detects.