Attack Surface

Your Exposed .git Directory Is a Source-Code Fire Sale: What Attackers Actually Pull From It

2MNY Admin October 13, 2026 5 min read 4 views

Most teams treat an exposed /.git/ directory as a cosmetic misconfiguration. It is worse: with the config, index, HEAD and reflog readable, an attacker can enumerate your tracked files, your remotes, your commit timeline — and reconstruct the repository byte-for-byte offline. Here is what each file actually leaks, and how to prove (not guess) whether yours is exposed.

A deployment shortcut that has survived two decades of tooling improvements: git clone your project onto the web server, point the vhost at the checkout, ship. It works, so nobody questions it — until someone notices that the directory your site lives in also contains a .git folder, and that most web servers happily serve dot-directories unless told otherwise. One GET /.git/config later, your repository metadata is in an attacker's terminal.

The usual response when this shows up in a scan report is a shrug: "it's just config, the real code isn't there." That response is wrong in an expensive way. The .git directory is not a leftover — it is the repository. The working tree is the disposable part. Here is what each readable file hands over, and how our free website security scan proves the exposure with structured evidence rather than a bare existence check.

What Each File in .git Actually Leaks

/.git/config — your remotes and sometimes credentials. This INI file lists every remote your deployment used, including the clone URL. If your team cloned over HTTPS with an embedded token, or uses a remote that embeds a username, that string is sitting in plaintext one request away. Even without credentials, the remote reveals where your private code lives (GitLab self-hosted? GitHub Enterprise? A CI mirror?), which internal hostnames exist, and often the project's real name — useful for phishing your developers with frightening accuracy.

/.git/index — a complete inventory of every tracked file. The index is a binary file with a well-documented format: magic bytes DIRC, a format version, an entry count, and one entry per tracked file with its path and SHA-1 object hash. An attacker who reads the index doesn't need to guess whether config/production.yml or admin_panel_v2/ exists — the index enumerates every filename in your project, including files that no public URL would ever expose. It is a filesystem map of your application that you published by accident.

/.git/HEAD and /.git/packed-refs — the branch layout. HEAD shows the checked-out branch or a detached commit hash. packed-refs lists branch and tag hashes. Together they tell an attacker which branch is deployed and give the exact object identifiers needed for the next step.

/.git/logs/HEAD — your commit timeline. The reflog carries timestamps and commit messages. Suddenly your deployment cadence, the commit that shipped last Thursday's hotfix, and the typo'd message describing an internal incident are all public. Attackers use reflog timestamps to correlate with other recon and to identify the juiciest recent changes to diff.

/.git/objects/ — the payload. With object files (or pack files, listed via objects/info/packs) readable, everything above collapses into the worst case: tools like dvcs-ripper walk the object database and reconstruct the entire repository offline — every version of every file, full history included. Your source is not "partially exposed"; it is downloaded.

Existence Checks vs Proof

Many scanners flag .git exposure by fetching /.git/HEAD and grepping for ref: — and stop there. The problem is that plenty of servers answer 200 with the site's HTML page for any path (SPA routers and catch-all rewrite rules do this constantly), so a naive grep produces both false positives and, worse, shallow findings that read as low severity.

Our git_history_leak scanner takes a proof-level approach with three confirmation gates on every probe. First, the HTTP status must be a real 200 — redirect chains that dissolve into some other page die here. Second, the body must have the exact shape of the file it claims to be: a binary DIRC magic header with a sane entry count for the index, structural INI parsing of [core] and [remote "..."] blocks for the config, strict line formats for HEAD, packed-refs and reflog. A catch-all HTML page cannot fake a DIRC header. Third, soft-404 baselines are compared so that custom error pages which return 200 OK never produce a finding.

When the scanner does fire, the report shows structured evidence: the parsed remote URL with credentials masked, the index entry count with up to twenty sampled tracked paths, the branch from HEAD, reflog timestamps — each probe individually confirmed, so you know exactly which files are readable and how deep the leak goes. Read-only GETs only: the scanner never fetches a git object, never writes, never exploits.

What to Do About It

If it is exposed right now: treat it as a source-code disclosure incident, not a config tweak. Rotate any credential that ever appeared in the repository (assume the full history was downloaded), then remove the exposure. Deleting .git from the web root is the minimum; if you were cloned with a token in the remote URL, revoke that token too.

To prevent it permanently:

  • Deploy builds, not checkouts. Run git archive, a CI artifact, or rsync of the built output to the server — the repository never touches the web root.
  • Block dot-directories at the web server. Nginx: location ~ /\. { deny all; }. Apache: RedirectMatch 404 /\. or equivalent. This protects .git, .svn, .hg and friends in one rule.
  • Verify from the outside. Point the 2MNY scanner at your site and read the Git History Leak section — it fetches each probe through the same Tor-routed, browser-fingerprinted transport a real attacker would use, so a finding means a real external observer can read it, not just a localhost test.

An exposed .git directory is one of the few findings that upgrades itself with zero attacker effort: no exploit, no bypass, no race — just HTTP GETs against a file your server should never have shown. Close it before someone patient finds it, because on the internet, someone patient always does.

Is your website affected?

Find out in minutes — free scan, professional report, no credit card.

Start Free Scan