OverTheWire: Natas

Level 11 > Level 12

natas11-01

Source…

 <html>
[...]
<?

$defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");

function xor_encrypt($in) {
    $key = '<censored>';
    $text = $in;
    $outText = '';

    // Iterate through each character
    for($i=0;$i<strlen($text);$i++) {
        $outText .= $text[$i] ^ $key[$i % strlen($key)];
    }

    return $outText;
}

function loadData($def) {
    global $_COOKIE;
    $mydata = $def;
    if(array_key_exists("data", $_COOKIE)) {
        $tempdata = json_decode(xor_encrypt(base64_decode($_COOKIE["data"])), true);
        if(is_array($tempdata) && array_key_exists("showpassword", $tempdata) && array_key_exists("bgcolor", $tempdata)) {
            if (preg_match('/^#(?:[a-f\d]{6})$/i', $tempdata['bgcolor'])) {
                $mydata['showpassword'] = $tempdata['showpassword'];
                $mydata['bgcolor'] = $tempdata['bgcolor'];
            }
        }
    }
    return $mydata;
}

function saveData($d) {
    setcookie("data", base64_encode(xor_encrypt(json_encode($d))));
}

$data = loadData($defaultdata);

if(array_key_exists("bgcolor",$_REQUEST)) {
    if (preg_match('/^#(?:[a-f\d]{6})$/i', $_REQUEST['bgcolor'])) {
        $data['bgcolor'] = $_REQUEST['bgcolor'];
    }
}

saveData($data);



?>

<h1>natas11</h1>
<div id="content">
<body style="background: <?=$data['bgcolor']?>;">
Cookies are protected with XOR encryption<br/><br/>

<?
if($data["showpassword"] == "yes") {
    print "The password for natas12 is <censored><br>";
}

?>

<form>
Background color: <input name=bgcolor value="<?=$data['bgcolor']?>">
<input type=submit value="Set color">
</form>

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

Ah, XOR encryption! Not a great idea, let’s see why.

I will use + to represent XOR since I’m not cool enough to use magic circle-plus symbols in markdown. From the wiki page, the following identities:

1) A + 0 = A
2) A + A = 0
3) A + (B + C) = (A + B) + C

Where P is the plain text, K is the key, and C is the cipher text,

P + K = C

XOR both sides by the plain text (both sides are equal and remain equal after this operation).

P + (P + K) = P + C

Using (3),

(P + P) + K = P + C

Using (2),

0 + K = P + C

Finally, using (1),

K = P + C

So, if you have a sample plain text and cipher text pair - you can determine what key was used to encrpyt. Fortunately for us and unfortunately for them, that is the situation in this challenge. The plain text is the value stored in $defaultdata (after being json-encoded) and the cipher text is the cookie (after being base64-decoded).

$defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");

natas11-02

We don’t even have to do much work to recover the key, we can use their code to our advantage since XOR encrypting and XOR decrypting are the same operation. We’ll modify the xor_encrypt function to take the input value and key as arguments rather than a hard-coded key. Add the plain text and cipher text pairs, and prepare each for the XOR operation (the plain text is json-encoded first, the cipher text is base64 decoded first). If we run this and the output is a repeated pattern (assuming they used a relatively short key, this doesn’t have to be the case), then the plan worked.

<?php

$ptext = json_encode(array("showpassword"=>"no", "bgcolor"=>"#ffffff"));
$ctext = base64_decode("ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw=");

function xor_encrypt($in, $k) {
    $key = $k;
    $text = $in;
    $outText = '';

    for($i=0;$i<strlen($text);$i++) {
        $outText .= $text[$i] ^ $key[$i % strlen($key)];
    }

    return $outText;
}

print xor_encrypt($ptext, $ctext);

?>

Success! The key is qw8J.

otw@iDi:~/natas$ php natas11.php 
qw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jq

The key isn’t our goal, though. What we’d like to do is figure out what value for the cookie we need so that "showpassword"=>"yes" makes it into the page. Modify our previous code to take the plain text value we’d like and output the corresponding cipher text (cookie) value. Might as well change the background to a nice blue color while we’re at it.

<?php

$ptext = array("showpassword"=>"yes", "bgcolor"=>"#0020c2");
$key = "qw8J";

function xor_encrypt($in, $k) {
    $key = $k;
    $text = $in;
    $outText = '';

    for($i=0;$i<strlen($text);$i++) {
        $outText .= $text[$i] ^ $key[$i % strlen($key)];
    }

    return $outText;
}

print base64_encode(xor_encrypt(json_encode($ptext), $key));

?>
otw@iDi:~/natas$ php natas11.php 
ClVLIh4ASCsCBE8lAxMacFMOXTlTWxooFhRXJh4FGnBTVAh6Q0dbeFMK

Inject this into the browser, and bam!

natas11-03

EDXp0pS26wLKHZy1rDBPUZk0RKfLGIR3