Git Script Injection

This privilege escalation technique exploits a cron-scheduled script maintained in a Git repository. Instead of modifying the script file directly, which may not be writable or may be tracked centrally, the attacker leverages Git operations to inject malicious code. If a cronjob executes a script from a local Git clone, and that clone regularly pulls from a bare Git repository, it is possible to modify the script via Git (clone β†’ commit β†’ push). Once the root-owned clone fetches and executes the updated content, the payload runs with elevated privileges. This method is particularly effective in DevOps-style environments where Git is used as part of automation pipelines.

[bob@server home]$ cat git/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3B...<SNIP>...aXQ=
-----END OPENSSH PRIVATE KEY-----

[bob@server tmp]$ ./linpeas.sh
╔══════════╣ Backup files (limited 100)
-rw-r--r-- 1 root root 66 Jan 15  2021 /etc/crontab.bak

[bob@server tmp]$ cat /etc/crontab.bak
*/3 * * * * /root/git-server/backups.sh
*/2 * * * * /root/pull.sh

The objective is to gain root access by injecting a reverse shell into a script (backups.sh) that is executed by root on a schedule. Since this script is maintained in a Git repository, the attack leverages Git operations to propagate the malicious change. The script located at /root/git-server/backups.sh is executed by root every three minutes, while /root/pull.sh runs every two minutes.

[bob@server tmp]$ ./pspy64
2025/08/04 16:02:01 CMD: UID=0     PID=17170  | /bin/sh -c /root/pull.sh
2025/08/04 16:02:01 CMD: UID=0     PID=17171  | git pull
2025/08/04 16:03:01 CMD: UID=0     PID=17191  | /bin/sh -c /root/git-server/backups.sh

The output from pspy shows that /root/pull.sh is simply running git pull every two minutes inside /root/git-server/, and then every three minutes the script backups.sh in that same directory is executed by root. This establishes a Git-based automation flow:

  • /git-server/ is a bare Git repository. A bare repository contains only Git metadata (commits, trees, objects) and does not have a working copy of files like scripts or code. It serves as the equivalent of a remote Git server (e.g., GitHub) and is not directly editable.

  • /root/git-server/ is a Git clone of the bare repository β€” a working copy where files like backups.sh exist on disk. The root user regularly pulls updates into this working copy via cron.

Therefore, any updates pushed to the bare repository at /git-server/ will be fetched into /root/git-server/ by the root cron job. Since /git-server/ cannot be edited directly (being bare), a writable clone must be created. This is done from the user-writable /tmp directory:

After cloning, the repository contains the current version of backups.sh, which is just a placeholder:

Once it's confirmed that /git-server/ is a bare Git repository used by the root cron job, the attacker must interact with it from their own machine. Since direct write access to /git-server/ is not available externally, a remote Git clone is performed over SSH. The Git server is listening on a non-default port (43022), and a private SSH key (id_rsa) has been obtained during enumeration. Using this key, the repository is cloned remotely from the attacker's machine:

This creates a working copy on the attacker’s machine, where real files like backups.sh can be edited. Git user identity is configured:

To escalate privileges, the backups.sh file is modified to include a reverse shell payload. The payload connects back to the attacker's IP over TCP:

Changes are staged and committed:

The repository is pushed back using:

At this point, the updated backups.sh containing the reverse shell has been committed and successfully pushed to /git-server/. Within two minutes, the root cron job running pull.sh will pull this change into /root/git-server/. Within three minutes, the root cron job running backups.sh will execute it.

Last updated

Was this helpful?