When a deployment copies a working tree to the web root without excluding .git/, the server hands out the entire object database. Because Git is content-addressed, that is enough to rebuild every tracked file at the latest commit (and usually the whole history), recovering source that was never meant to be public along with the config, remote URLs, and any secret that was committed.
Fingerprint first#
.git/HEAD is the cheapest tell: it is tiny, always present, and its contents are unmistakable.
curl -s https://target/.git/HEAD # => "ref: refs/heads/main" (a ref line, not an HTML 404 page)
curl -s https://target/.git/config # remote URLs, often credentials in the URL
A ref: line (or a 40-char SHA) confirms it. If the response is an HTML error page with a 200, the server has a catch-all and you must verify by fetching a known object path rather than trusting the status code.
With directory listing#
If the server lists directories (Options +Indexes), mirror the whole folder:
wget -r -np -R 'index.html*' https://target/.git/
cd target && git checkout -- . # materialize the working tree from the recovered objects
Without directory listing#
Usually listing is off, so you fetch the known Git paths and walk the object graph. git-dumper automates exactly this: read .git/HEAD and the refs, pull objects/info/packs and the pack index/pack files, then fetch loose objects by hash as they are discovered.
git-dumper https://target/.git/ loot/
cd loot && git log --oneline && git checkout -- .
Doing it by hand, the paths that matter are HEAD, config, packed-refs, logs/HEAD (the reflog, which names commits no ref points to), objects/info/packs, each objects/pack/pack-<sha>.idx and .pack, and loose objects at objects/<first2>/<rest38>. A loose object is zlib-compressed; git cat-file -p <sha> decodes it once it is in a local .git.
# the reflog reveals commit SHAs even for branches that were deleted or force-pushed
curl -s https://target/.git/logs/HEAD
git cat-file -p <sha> # read a recovered commit/tree/blob
What to read first#
configandlogs/HEADgive remote URLs and the full ref history, including deleted branches.git log -pacross the recovered history is where the real loot is, feed it to secrets in history.- The source itself is the map for the next stage: hardcoded endpoints, credentials, and the application's own vulnerabilities.
Tools#
- git-dumper: recover a
.gitwithout directory listing. - GoGitDumper / goop: alternative dumpers that brute-force common object paths.