OverTheWire Natas Level 11

Level 11 of the OverTheWire Natas wargames is a good one. It wasn’t one that could instantly be solved either. It involved programming, encryption, and HTTP. All fun!

It started with a dialog to set the background color. When you click the “Set Color” button, it sets a cookie in your browser. But as the dialog says, the cookie is protected.

I looked at the source code, as I always do. It was a little more lengthy than previous levels.

<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></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'];



<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>";


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

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

I viewed my cookie, using the amazing, Edit This Cookie, Chrome extension. Note that the last character is a “=”, but it’s encoded in the cookie as “%3D” due to encoding.

I then took this cookie value and figured that maybe I could decode it. XOR encryption is reversible if you know 2/3 parts. You have A XOR KEY = C, for example. If you know A and C, you can derive KEY by XORing A and C together the same way. I ended up writing some code in PHP (since the original was in PHP)


function find_xor_key($decrypted, $encrypted) {
 // This value should be the return of xor_encrypt with a base64 decode
 $base64_decoded = base64_decode($encrypted);

 // Before xoring, get the decrypted value into the same state it was
 $json_encoded = json_encode($decrypted);

 $outText = '';

 for($i=0; $i < strlen($json_encoded); $i++) {
  $outText .= $json_encoded[$i] ^ $base64_decoded[$i % strlen($base64_decoded)];

 return $outText;

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

$key = find_xor_key($cookie, "ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw=") . "\n";

print "XOR Key: " . $key . "\n";


When ran, this produces the following.

mandreko$ php natas11_decode.php 
XOR Key: qw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jq

So now I know that the XOR key is “qw8J”. It’s repeated over and over, because the string it’s encoding is longer than the key, so it gets repeated, otherwise it’d be XOR to nothing.

I then took this XOR key and tried to use it to encode a new cookie value, so that I could switch the “showpassword” value to “yes”. I wrote the following script to do so.


function custom_xor($key, $in) {
        $outText = '';
        for($i = 0; $i < strlen($in); $i++) {
                $outText .= $in[$i] ^ $key[$i % strlen($key)];
        return $outText;

$cookie = array("showpassword"=>"yes", "bgcolor"=>"#ffffff");
$key = "qw8J";

print "New Cookie Value: " . base64_encode(custom_xor($key, json_encode($cookie))) . "\n";


When ran, it produced the following.

mandreko$ php natas11_encode.php 

I then pasted that cookie value into my Edit This Cookie extension, and refreshed the page. It then presented me with the password to the next level.

comments powered by Disqus