// HackTricks · Network Services

Git

Git

To dump a .git folder from a URL use https://github.com/arthaud/git-dumper

Use https://www.gitkraken.com/ to inspect the content

If a .git directory is found in a web application you can download all the content using wget -r http://web.com/.git. Then, you can see the changes made by using git diff.

The tools: Git-Money, DVCS-Pillage and GitTools can be used to retrieve the content of a git directory.

The tool https://github.com/cve-search/git-vuln-finder can be used to search for CVEs and security vulnerability messages inside commits messages.

The tool Gitrob searches for sensitive data in an organization’s repositories and in repositories associated with its employees.

Repo security scanner is a command line-based tool that was written with a single goal: to help you discover GitHub secrets that developers accidentally made by pushing sensitive data. And like the others, it will help you find passwords, private keys, usernames, tokens and more.

Here you can find a study of GitHub dorks: https://securitytrails.com/blog/github-dorks

Faster /.git dumping & dirlisting bypass (2024–2026)

  • holly-hacker/git-dumper is a 2024 rewrite of the classic GitTools dumper with parallel fetching (>10x speedup). Example: python3 git-dumper.py https://victim/.git/ out && cd out && git checkout -- .[1]
  • Ebryx/GitDump brute-forces object names from .git/index, packed-refs, etc. to recover repos even when directory traversal is disabled: python3 git-dump.py https://victim/.git/ dump && cd dump && git checkout -- .[2]

Quick post-dump triage

cd dumpdir
# reconstruct working tree
git checkout -- .
# show branch/commit map
git log --graph --oneline --decorate --all
# list suspicious config/remotes/hooks
git config -l
ls .git/hooks

Secret/credential hunting (current tooling)

  • TruffleHog v3+: detector-based scanning with automatic Git history traversal. Detectors can combine pattern matching, decoding or entropy checks, and live credential verification; --only-verified keeps only results that a detector could verify. trufflehog git file://$PWD --only-verified --json > secrets.json[7]
  • Gitleaks: fast ruleset-based scanning of a working tree or Git history. Current releases use gitleaks git . --report-format json --report-path gitleaks.json; older v8 releases used gitleaks detect -v --source . --report-format json --report-path gitleaks.json, which remains useful when reproducing an older environment.[8]

Server-side Git integrations as an attack surface

If the application clones attacker-controlled repositories and later runs git operations from a web/API action (status, diff, checkout, pull, merge, commit, dependency sync, package install, branch switch…), treat Git itself as part of the server-side attack surface.[3]

Git config / hook abuse when the backend uses the native Git CLI

If you can write or append to .git/config, several directives can turn a low-impact file write into server-side command execution during later Git actions:[3]

DirectiveCommon triggerAbuse
core.fsmonitorgit status, git diffExecutes an external helper on common working-tree operations
core.sshCommand / core.gitProxygit fetch, git push, git cloneReplaces the transport command
credential.helperAuthenticated Git operationsRuns a shell helper for credential lookup/storage
filter.<name>.clean / filter.<name>.smudgegit add, git checkout, git cloneExecutes external clean/smudge filters
diff.externalgit diffExecutes an external diff tool
core.hooksPathcommit / checkout / merge / pushRedirects hook execution to an attacker-controlled directory
  • Append-only writes can still work: Git accepts duplicate INI sections and the last value may win, so appending a second [core] section can be enough.
  • Direct hook write also works if you can plant files inside .git/hooks/ such as post-checkout, post-merge, or pre-commit.
  • This is mainly a native Git CLI primitive. JGit/libgit2/go-git usually do not execute the same hooks/config helpers, although they can still be exploitable through path and symlink handling.

hooksPath overwrite via path traversal

Modern web apps sometimes regenerate .git/config from user-controlled repo names, dependency names, refs, or workspace identifiers. If one value lands inside core.hooksPath and another one controls where the generated config is written, you can chain both into RCE:[3][6]

  • Path traversal in hooksPath: if a repo/dependency name is copied into hooksPath, inject ../../.. to escape the intended hooks directory and point to a writable location. This is effectively a path traversal in Git config.
  • Overwrite another repo’s .git/config: if ref / branch / destination path controls where generated Git metadata is written, traverse into another workspace and replace its config.
  • Force intermediate directories to exist: abuse clone destination controls so the backend creates paths such as ../../git_hooks for you.
  • Ship executable hooks: set the executable bit inside Git metadata so every clone writes the hook with mode 100755:
    git update-index --chmod=+x pre-commit
  • Find a native Git code path: libraries like JGit ignore hooks. Hunt for features that fall back to system Git so hooks will actually run.
  • Race the config rewrite: if the app restores .git/config right before invoking Git, keep overwriting it while triggering the Git action to win a race condition.

Buried bare repositories & repo-detection abuse

Git discovers repositories by walking upward and looking for HEAD, config, objects/, and refs/. Therefore, an attacker can bury a bare repository inside a normal repository subdirectory and wait until the service runs Git from there.[3][5]

  1. Create a nested bare repo.
  2. Change bare = true to bare = false.
  3. Add core.worktree and an execution directive such as core.fsmonitor.
  4. If the service later cds into that folder and runs git status / git diff, Git loads the buried config and executes your helper.

Also check whether deleting/corrupting .git/HEAD could make Git stop recognizing the intended repository and fall through to a planted bare repo in the same or parent directory.

Git argument injection and unsafe shell construction

When untrusted filenames, branch names, refs, or paths are passed to Git without --, values starting with - / -- may be parsed as options instead of positional arguments.[4]

# Vulnerable
 git checkout $branch
 git rm $path

# Safer
 git checkout -- "$branch"
 git rm -- "$path"

Interesting primitives to test:[3]

  • --pathspec-from-file=<file> with git rm to make Git read pathspecs from an arbitrary file and leak content via errors.
  • Any workflow that joins argv into a shell string instead of using a real argument array.
  • Cases where the backend safely escapes Git arguments but still embeds an unescaped working directory in a shell command like cd #{working_directory} && git ...; if you can control the directory name, shell syntax such as $(id) may execute before Git starts.

Git stores symlinks as blob targets. If checkout materializes real symlinks and the web UI/API follows symlinks, a repository-scoped file read/write primitive can become filesystem escape:[3]

repo/
  link -> ../../../etc/

Then repo/link/passwd may resolve to /etc/passwd, and writes through that path may escape the repository boundary too.[3]

Extra pivot points:[3]

  • Blacklist bypass via repo-internal symlinks: if the app blocks direct .git/ reads with a prefix check, look for alternative paths such as node_modules/pkg/.git/config that resolve back to the real repo root through symlinks.
  • JGit core.symlinks abuse: even if JGit ignores classic config-to-RCE directives, writing symlinks = true in .git/config can make the next pull/checkout materialize attacker-controlled symlinks, exposing broader filesystem paths or even other tenants’ repositories if storage is shared.

Quick checklist when you find a Git-backed file primitive

  • File read: inspect .git/config for credentials, remote URLs, internal paths, repo layout, and deployment metadata.
  • File write / append: target .git/config, .git/hooks/*, or alternate repo markers (HEAD, config, objects/, refs/) to escalate into command execution or repo confusion.
  • File delete: test whether removing .git/HEAD changes which repository Git discovers.
  • Path traversal / arbitrary folder creation: look for generated clone destinations, dependency sync paths, worktrees, hook paths, and package install directories.
  • Package installation features: local npm dependencies ("pkg": "./", file:) may create symlinks under node_modules/ that help bypass path blacklists and reach .git/config indirectly.

References