For the complete documentation index, see llms.txt. This page is also available as Markdown.

Git

A version control system often saving lots of information about how files were changes

Description

Git is a version control system that allows you to save the state of files. It is often used with source code and published on Github. There are a few keywords that Git uses you need to know to understand the terminology:

  • Repositories: The entirety of your git system, with the files, history, and information. Can be created using git init

  • Commits: "Save states" of the files in your repository. Whenever you change something and you are happy with the change, you can commit it so it's saved as a snapshot that you can later go back to. Can be created using git commit -m "message"

  • Branches: Parallel to your main repository, branches are sidesteps to slowly work on a new feature for example, and then later in time merge it into the main branch. Can be created using git checkout -b newfeature

All the information about your Git repository gets saved in a .git directory that is at the root of your repository. The git command interacts with this directory and lots of tools can get information from it. So if you ever find a .git directory you'll know the current directory is a Git repository.

To find everything in a repository without having to think of every command, you can use a tool like GitKraken to explore the repository visually. Just open the directory in that tool and you can see a timeline of what commits and branches were made.

Finding Git on websites

In some cases, you'll find that the website you're testing uses Git by finding a .git directory. Normally this should be hidden by a 403 Forbidden for example, but this is not always the case. Sometimes you can see a list of files, or you can directly access .git/HEAD instead.

Web - Directory Listing

When you visit the .git directory on the website, and you can see a list of files relating to git, you know that directory listing is on. This makes it really easy to download everything at once recursively and then examine the repository on your own machine.

wget -r http://example.com/.git

Git-dumper

When a website disables directory listing, but the .git directory can still be found with something like .git/HEAD, you might be able to use git-dumper on it to extract all the files without having the need for directory listing. This tool understands the Git file structure and can find all the related files:

A tool to dump a git repository to your local machine, without needing directory listing

Then use the source code to perform more targeted attacks, or look for secrets, even in history:

The trufflehog tool can also be useful for large repositories where manually searching would take too long. It has a few built-in formats for credentials like private keys or AWS secrets. Run it locally on a cloned repository like this:

Tip: The .git/config file may not be cloned, but if found locally, can contain git credentials used to push and pull to a remote origin. These may be re-used elsewhere or allow you to explore more of the remote git origin.

Attacking Git Commands (RCE)

Git is a very flexible system, allowing many settings to be changed to decide how CLI tools interact with the repository. These configuration variables can allow executing arbitrary commands however when certain git commands are executed. The core.fsmonitor variable in .git/config is a common one that can be set to a bash command to execute:

Many shell extensions like Starship use git to get the current repository and are vulnerable to this, as well as Visual Studio Code (now only with Trusted Mode). To find such issues, you can create a malicious repository with as many landmines as possible that trigger on different commands. This creates an empty repository with most known ways to execute commands:

Create a repository with .git/config and hooks GIT landmines (lib/payload = payload)

This same config variable can be set via environment variables:

A bunch more techniques like it are described in the following article:

Various tricks including bare repos, symlinks and argument injection

A little-known feature of git is that symlinks can also be tracked, with any destination, even outside the worktree. If some application reads or writes files after cloning a malicious repository, that can follow symlinks and read/write into the broader filesystem.

Tip: This combines well with the fsmonitor trick. If you can write to .git/config this way through link2/config in the above example, you'll achieve RCE on the next git command.

Most remotes (e.g. GitHub) fully support storing this. They won't be sanitized when you clone from such a source. Applications that automatically clone malicious repositories can miss this fact. This is a great way to get symlinks on the target filesystem.

Git by design also supports updating files. Updating a repository, for example, can swap one file for a symlink. This could let you perform TOCTOU exploits (Filesystem) where you'd normally require low-privilege shell access to write symlinks. With git you can swap them out at will!

Bare repositories

While most repositories in Git have a .git folder containing the metadata, there exists another format where you don't need such a folder: a bare repository. Its contents look like when you're inside a .git folder but with just some different options in config.

Git recognizes both by first looking for .git/HEAD, and if that doesn't exist, checks if the current directory resembles a bare repository (HEAD, etc.). We can turn a bare repo into a trap just like regular repo's by making two changes to the config:

The "worktree" is the directory where the actual files are stored that git tracks. We've just told Git it can find that directory at ./worktree, so we need to create it. To ensure the currently empty directories are kept, we should fill them. What better way than to just create a single commit with our payload in it.

The final process looks something like this with the sh payload stored in worktree/pwn:

You can now push this repo to any remote, or host it yourself on 127.0.0.1 with the following command:

Any victim who now clones this repository and deletes a critical git file like .git/HEAD opens themselves up for RCE on the next git command:

An idea where this applies to Git wrappers perfectly is with directory deletion/cleanup. In the following example, RyotaK performed a Race Condition in deleting a repository where files were deleted one by one. This made it possible to trigger a git command while the .git directory was deleted, but none of the fake bare repo files were yet. They could extend this gap by adding large directories that take a while to delete in between .git and our bare repo files (similar to Increasing the Window, but including timing samples to find the deterministic deletion order and inject one perfect directory).

Race condition in repository cleanup exploitable through fake bare repo

Git Hooks

There is another feature called "hooks" that allow you to run bash scripts when a certain action happens with the repository. When a git commit is executed, for example, the pre-commit hook gets triggered. If you can write these hooks you can let whoever runs the git commit execute arbitrary commands.

You can find these hooks in the .git/hooks directory. If you are able to write a pre-commit file here, you can put any executable file in its place and it will be run on commit:

Then just make sure the file is actually executable with chmod:

Git Snippets

If you're running a git repository, you might need some complicated actions from time to time. This is a collection of some common actions as commands to quickly copy and paste.

Last updated