Sunday, January 20, 2019

Abusing Git Hooks with Python

Git hooks is one of the lesser talked about features of git. Most Developers get a basic introduction to hooks, maybe once early on when there is so much to take in that the beauty of these little gems goes almost unseen. Today I would like to discuss some ways to use, then abuse, this often forgotten facet. Then conclude with a discussion of detection.

What are Git hooks

Simply put, these are scripts that get run at different points in the versioning work-flow. To be clear, this is not the same as the recently reported abuses of git sub-modules. No, this is a feature. And like some many great features it doesn't get the attention it deserves. I blame the ease of CI platforms and the pressure to always try to develop faster more efficient work flows.

In general Git hooks come in two flavors, client-side and server-side. You can further subdivide these groups into pre-action and post-action variants.
let me give an example:
  1. Bob makes a change scripty.py
  2. Bob likes his change so he runs git commit -m "blah" scripty.py (client-side)
  3. Git looks to see if a file named .git/hooks/pre-commit exists (called the pre-commit hook).
  4. If so, and it is executable, Git will execute it as a shell script with no arguments.
  5. If it returns with a 0 or is not found Git goes ahead with the commit.
  6. After the entire commit process is completed, the post-commit hook runs.
There are more steps involved that I am glossing over for the moment. You can read more about all the potentially hook-able events at https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks

Finding targets

As an attacker, you can take advantage of the pre-commit hook to inject your own commands to be run anytime a given repository is committed locally. First, you need to locate some repositories on the client machine. This is easily done by recursively checking directories from the root and noting any that contain a .git directory. Whenever a Git repository is initialized, the .git directory, and all of it's sub-directories are created. One of these sub-directories is the hooks directory, which is filled with sample hook scripts. This is the list of potential targets. renaming any of these files without the .sample extension and making it executable, will cause them to fire off at the appropriate event. You may also want to take note of any existing hooks in play. It would be bad (or at least quickly noticed) if you copied the pre-commit.sample over an already active pre-commit hook.

Having some fun

Okay so you have located a Git repository on the local machine that looks good, now what? Well, with a little creative thinking you can run anything you could run from the command line. The hook script is just a shell script, so it runs top down. I suggest injecting your command(s) near the top, so they run before any other code. For my example, I crafted a script which locates or creates a pre-commit hook for every repository it has access to.

Python code to create the injected string

The command it injects is to run itself again and then exit the commit with a 1, meaning the commit is halted and not allowed through.

Running the script to hook a single repo's pre-commit hook

Since this runs before any other code, it effectively locks this repo from commits. This is a fun prank to pull against some developer friends, but is pretty loud and noticeable. In practice you would probably only want to have the persistence command, and leave off the repo locking.

Artifacts

The reason I bring this up is, I see the checks for Git Hooks missing from a lot (most) security artifact checklists. Furthermore these wouldn't show up as odd commands in the bash history since git commit inside a work repo is expected on developer machines. For these reasons, I have cobbled together a script which will find and list all non-sample files in any Git-hook repos on the local machine.
Finding all active hook files



No comments:

Post a Comment