git daemon exposes repositories over the git:// protocol on TCP 9418 with no authentication and no transport encryption by design: it is meant for anonymous read of public mirrors. When it is pointed at an internal or private repository tree, anyone who can reach the port can clone the code and its full history.
Fingerprint and enumerate#
nmap -p9418 -sV <target>
# the daemon only serves a repo that is explicitly exported, so test specific paths
git ls-remote git://<target>/project.git # refs listed => the repo is served
A repository is served only when it is marked exportable: a git-daemon-export-ok file in the bare repo, or the daemon started with --export-all (which exports every repository under its base path regardless of the marker). An --export-all daemon is the common misconfiguration, it turns the whole base directory into anonymous-readable repositories.
Clone and loot#
git clone git://<target>/project.git
cd project && git log -p --all # then mine it as in secrets-in-history
There is no authentication step, so a successful clone is immediate source and history disclosure. If you do not know the repository names, try common ones (.git, project.git, the app name) and scrape any CI or deployment config you already have for git:// URLs.
Writable and extended services#
The daemon's service set matters. By default only upload-pack (clone/fetch) is enabled, but a daemon started with --enable=receive-pack accepts anonymous pushes, letting you write to the repository, which combined with server-side hooks is code execution (see hooks and config execution). An enabled upload-archive service has historically allowed reaching unintended objects. Check what the daemon answers:
# anonymous push is accepted only if receive-pack is enabled for the repo
git push git://<target>/project.git HEAD:refs/heads/test
A push that is accepted rather than refused is a writable anonymous repository.