Exposed .git directory: git-dumper and secrets in the history

How to spot an exposed .git folder on a website, rebuild the repository with git-dumper and find passwords and keys in the commit history.

· 3 min read

If the developer deployed the site by copying the project folder as it was, the .git directory travelled with it. From outside you can download it and rebuild all the source code and, even better, its whole history: every password someone committed by mistake and then "deleted" is still in an old commit.

It is one of the most rewarding findings on a machine: you go from black box to having the code, and often straight credentials.

Detecting it

curl -s http://target.htb/.git/HEAD
# ref: refs/heads/master   ← exposed

Other signs:

  • nmap -sC reports it on its own: the http-git script prints Git repository found!.
  • Directory fuzzing with any common wordlist finds .git/ (more in the web fuzzing guide).
  • Also try every vhost and subdomain: on LinkVortex it only exists on dev..

If /.git/ returns 403 but /.git/HEAD or /.git/config return 200, it is exposed anyway: only directory listing is forbidden.

Dumping it with git-dumper

pipx install git-dumper
git-dumper http://target.htb/.git/ ./repo
cd repo && git status && git log --oneline --all

git-dumper follows the references from HEAD, downloads the objects one by one and rebuilds the working tree, even with directory listing disabled.

Note

If it fails, GitTools (gitdumper.sh + extractor.sh) is the classic alternative. The extractor dumps each commit into its own folder, handy when the repository is broken.

Hunting for secrets in the history

First the current code: config files, .env, database connection strings and hidden routes. Then the history:

git log -p --all | grep -iE "pass|secret|token|key|pwd" | head -50   # changes with keywords
git log --all --stat                                                  # which files each commit touched
git show <commit>                                                     # a whole commit
git log --all --diff-filter=D --name-only                             # files that were deleted
git stash list; git branch -a; git tag                                # forgotten branches and stash

To avoid searching by hand, let a secret scanner do it:

trufflehog git file://$(pwd) --no-update
gitleaks detect -s . -v
What to look for Why
Commits with messages like "remove credentials" or "cleanup" The one right before has the password (OpenSource, Seal)
The token signing key (SECRET_KEY, JWT_SECRET) Lets you forge admin sessions (Secret; see JWT attacks)
.git/config Sometimes the remote URL carries a username and password: https://user:pass@gitea...
Hooks and CI (.git/hooks, .gitlab-ci.yml) They reveal how it is deployed and what runs after a push (Bitlab)

Reading the code to find the bug

With the code, you stop searching blind. Look for the language's dangerous functions and trace where their input comes from:

grep -rnE "system\(|exec\(|eval\(|popen|shell_exec|unserialize|pickle.loads|render_template_string" .
grep -rnE "SELECT .*\+|query\(.*\\\$" .     # queries built by concatenation

That leads to the guide for each bug: command injection, SSTI, deserialization or SQL injection.

The same trick elsewhere

  • SVN: /.svn/wc.db is a SQLite database with the files; svn or svn-extractor rebuild it. There are credentials in an old revision on Worker.
  • Mercurial and Bazaar: /.hg/ and /.bzr/, with equivalent tools.
  • Docker images: each layer is like a commit. docker save + unpacking the layers shows files that were deleted later (Bolt).
  • Gitea or GitLab servers inside the network: browse public repositories and the history of each one.

Practise it on real machines

Hack The Box machines where this technique is used. Each ▶ jumps to the minute of the IppSec video where it shows up.

Exposed .git repository 10 machines

See all 10 machines →

Git history 9 machines

See all 9 machines →

Spotted a mistake or something missing? Tell us