OverTheWire: Natas

Level 21 > Level 22

natas21-01

Source, cleaned up.

<h1>natas21</h1>
<div id="content">
<p>
<b>Note: this website is colocated with <a href="http://natas21-experimenter.natas.labs.overthewire.org">http://natas21-experimenter.natas.labs.overthewire.org</a></b>
</p>

<?

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

session_start();
print_credentials();

?>

<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html> 

natas21-02

<h1>natas21 - CSS style experimenter</h1>
<div id="content">
<p>
<b>Note: this website is colocated with <a href="http://natas21.natas.labs.overthewire.org">http://natas21.natas.labs.overthewire.org</a></b>
</p>
<?

session_start();

// if update was submitted, store it
if(array_key_exists("submit", $_REQUEST)) {
    foreach($_REQUEST as $key => $val) {
        $_SESSION[$key] = $val;
    }
}

if(array_key_exists("debug", $_GET)) {
    print "[DEBUG] Session contents:<br>";
    print_r($_SESSION);
}

// only allow these keys
$validkeys = array("align" => "center", "fontsize" => "100%", "bgcolor" => "yellow");
$form = "";

$form .= '<form action="index.php" method="POST">';
foreach($validkeys as $key => $defval) {
    $val = $defval;
    if(array_key_exists($key, $_SESSION)) {
        $val = $_SESSION[$key];
    } else {
        $_SESSION[$key] = $val;
    }
    $form .= "$key: <input name='$key' value='$val' /><br>";
}
$form .= '<input type="submit" name="submit" value="Update" />';
$form .= '</form>';

$style = "background-color: ".$_SESSION["bgcolor"]."; text-align: ".$_SESSION["align"]."; font-size: ".$_SESSION["fontsize"].";";
$example = "<div style='$style'>Hello world!</div>";

?>

<p>Example:</p>
<?=$example?>

<p>Change example values here:</p>
<?=$form?>

<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html> 

Interesting - this time the main page is nothing but a check that the admin session variable is equal to 1. Given that there is a second page, it’s pretty safe to assume the vulnerability is there.

Indeed, here it is on the ‘experimenter’ page.

// if update was submitted, store it
if(array_key_exists("submit", $_REQUEST)) {
    foreach($_REQUEST as $key => $val) {
        $_SESSION[$key] = $val;
    }
}

Later in the code there is some mumbo-jumbo about “allow only these keys” and a white-listed array, but the fact is the loop above sucks in everything from $_REQUEST and dumps it in $_SESSION.

I assume that since these pages are explicitly related, they share session variables. One primary purpose of session variables is to maintain state across different pages of a web application, so it’s reasonable to assume that if I can get the admin variable into the ‘experimenter’ page, it will be there on a load of the main page.

Also, note the debug option pulled out of $_GET - might as well use that to monitor things.

First I’ll load the experimenter page with no cookies to get a clean session cookie…

natas21-03

Then, using the new session cookie, add an admin=1 to a POST of the experimenter page. The debug option shows the variable is successfully loaded into the session.

natas21-04

Finally, GET the main page.

natas21-05

Donezo. The same flow could easily have been accomplished using the local proxy to add the POST parameter, or by editing the form HTML using the browser’s developer tools.

chG9fbe1Tq2eWVMgjYYD1MsfIvN461kJ