Problem :lock:

We arrive at a webpage that can change it’s background color dynamically.

Solution :key:

Let’s take a look at the source (no screenshot this time because it doesn’t fit).

<? 

$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);

?>

...

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

?>
Full Page Source
<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": "natas11", "pass": "<censored>" };</script></head>
<?

$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>

From the source, we can summarize that we need to change the value of $data["showpassword"] to have the value yes in order to get the flag. So let’s try to write out the flow of this PHP script.

  1. We init this array var.

     $defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");
    
  2. Call loadData() with that arr var as the param.

     $data = loadData($defaultdata);
    
  3. Now we’re inside the loadData() function, let’s take a look at it’s main components.

    1. The first 2 lines are just init vars, $_COOKIE is a superglobal var that holds the value of our browser cookies, doesn’t actually need to be globaled as indicated in it’s documentation so let’s take that as a hint.

    2. array_key_exists(key, array) checks if key:key is present in array:array.

    3. Our cookie will then get decrypted in this line.

       $tempdata = json_decode(xor_encrypt(base64_decode($_COOKIE["data"])), true);
      
    4. preg_match(regex, string) checks if the string:string can be matched with the regular expression:regex.

    5. If we manage to pass all ifs, our cookie will be parsed, this is the only way we can change the $data['showpassword'] value.

    6. When we return, values in $mydata will then be copied into $data.

  4. We’re back outside the loadData() function, the next couple of lines just sets the background color to the value that was sent through the html form, then we call another function.

     saveData($data);
    

    Which is just a one line function of encoding the $data array into JSON, then calling xor_encrypt() on it, then encode it into base64.

     setcookie("data", base64_encode(xor_encrypt(json_encode($d))));
    

    Finally, it’ll set the value submitted into our cookie as the data cookie.

So now, we have a general idea of the workflow of this script. The only way we can change the value of $data['showpassword'] is through $_COOKIE["data"]. So let’s try to open up our cookie.

Now we have a base64 string (after running urldecode() because setcookie() automatically runs urlencode() before actually setting the cookie) of out data cookie which is ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw=.

Now we need to convert this string back into it’s original state, an array, first “layer” we need to peel off is the base64 encoding.

Now we need to “reverse” what xor_encrypt() did to this string, upon a bit of googling about XORs, I stumbled upon a simple and similar implementation of this cipher, a wiki, and finally a more “human” explanation:

… XOR encryption is completely vulnerable to known-plaintext attacks …

cryptotext = plaintext XOR key
key = cryptotext XOR plaintext = plaintext XOR key XOR plaintext

… XORring the plaintexts cancel each other out, leaving just the key …

To recap:

  1. in order to know what the XOR key is, we need 2 things:

    • The original string (which we can get)
    • The XOR-ed string (which we already have)
  2. we also know the formula:

     A XOR B = C
     B XOR C = A
     C XOR A = B
    

    Also A XOR B is the same as B XOR A meaning that we pretty much just need to XOR 2 things to get the other 1 thing.

So let’s find out what the original string was.

Now we know both things needed to know the key, and we know how to get it, let’s modify the xor_encrypt() function a bit to get the key.

We got the repeating string qw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jq, let’s take a look again at the xor_encrypt() function, on this line inside the for loop:

$outText .= $text[$i] ^ $key[$i % strlen($key)];

The modulo % part basically says to repeat the $key index if the $text is longer than the key, meaning that our real key could be just qw8J, not the long repeating pattern, let’s test out both of them.

Looks like we’re right, the key is qw8J, we have successfully peeled 2 “layers” of our cookie, with the last one being JSON encoding, but since we can easily read this JSON code, let’s just keep it as it is.

We want to change showpassword to yes, let’s also change the background to a better color, our new JSON code that we want to “layer” up is:

{"showpassword":"yes","bgcolor":"#00ffff"}

So let’s xor_encrypt() it and then encode it into base64.

Now we just need to change our cookie with this string.

You can download my php script here.

Btw I’m using the Cookie Editor plugin for mozilla firefox, it makes editing cookies much easier.

Flag :checkered_flag:

EDXp0pS26wLKHZy1rDBPUZk0RKfLGIR3

Takeaway :books: