OverTheWire Natas Level 11 Walkthrough

In previous posts, we covered levels 0-5 and 6-10 of OverTheWire’s Natas wargame, which is a great introduction to web security.

This write-up is meant to be for beginners, with lots of links to other resources in case you aren’t familiar with a given tool or concept.

What is Natas?

Natas is an online hacking game meant to help you learn and practice security concepts.

OverTheWire is a website with a number of “war games”, which are online hacking games that allow you to practice security concepts. If you are looking for a beginner introduction to web security (albeit an older tech stack), then Natas is a great place to start.

Natas is hosted on different subdomains following the pattern of http://natas<level#>.natas.labs.overthewire.org. As you progress through the levels, you’ll need to increment the level number in the URL in order to view the correct level.

Each level requires the levels below it to be solved, so you will need the level 11 flag found in level 10 to begin this walkthrough. As before, make sure you keep notes and write down the passwords as you find them!

Level 11 ➔ 12

Level 11 (username natas11 and the password found in the previous blog post) opens up with the ability to customize the background color. Finally, dark mode!

The front page also says that the site uses XOR encryption. 😁 As in earlier levels, we’re given the source code:

This source code is a bit more involved than previous files, so let’s go through it function-by-function.

We start with a default value $defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");

Interestingly enough, the background color and the inclusion of the password are both stored in the same array. Interesting security choice but let’s continue.

The next function XOR encrypts an input. The key is redacted, so finding the key will probably be a crucial part of this level.

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

Next up, the loadData() function takes the cookie value, decodes it, and then uses it to set web page values (the background color and whether we see the password):

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

If there is a “data” array from the browser cookie, the value is base64 decoded, then XOR encrypted (since XOR is reversible with the same mathematical function, this is equivalent to XOR decoding), then JSON decoding.

Then the bgcolor is read out of the array and checked against a hex color regex. If the “showpassword” key exists, it is also copied over to the $mydata value.

There’s also a saveData() function, which encodes and encrypts the data array and saves it as a cookie in the user’s browser:

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

There’s a function to get the bgcolor request from the user:

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

And lastly, saveData() is called with the default values loaded in:

$data = loadData($defaultdata);
saveData($data);

Again, those default values are array( "showpassword"=>"no", "bgcolor"=>"#ffffff");

If we open up Dev Tools and look at the Application tab (Storage if you’re using Firefox), we can see that the cookie value (for background color = #ffffff, or white) is ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw%3D

The %3D at the end is a URL-encoded version of =. So our cookie value is actually ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw=.

We know that this cookie includes showpassword=no and we want to change it to showpassword=yes.

We’ll need to XOR decrypt it, but there’s a problem: we don’t have the key.

Remember that our cookie value was generated by: setcookie("data", base64_encode(xor_encrypt(json_encode($d)))); We don’t have ability to XOR encrypt yet, but we can take steps to get there.

First, let’s use a PHP compiler to make a cookie without XOR encryption, using just array( "showpassword"=>"no", "bgcolor"=>"#ffffff");

This gives us a cookie value of eyJzaG93cGFzc3dvcmQiOiJubyIsImJnY29sb3IiOiIjZmZmZmZmIn0=.

Get the XOR key

We have the encoded cookie, which is: ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw=

And we have a cookie generated with the same original values (bgColor: #ffffff, showpassword: no). That value is eyJzaG93cGFzc3dvcmQiOiJubyIsImJnY29sb3IiOiIjZmZmZmZmIn0=

Because of the associative property of XOR, it doesn’t matter what order you do XOR in, you should get the same answer.

  • You can do plaintext XOR'd with the key to get the ciphertext.
  • You can XOR the ciphertext with the key to get the plaintext.
  • And, you can XOR the ciphertext (which we have) with the plaintext (which we also now have) to get the key.

What this means for our challenge is that we can XOR the two cookies together to get the XOR key, then use this to create a new cookie and encrypt it.

Use CyberChef or a similar tool to XOR ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw= with eyJzaG93cGFzc3dvcmQiOiJubyIsImJnY29sb3IiOiIjZmZmZmZmIn0=.

This gives us an XOR key of qw8J (repeating).

We can double check our work with CyberChef again, and decrypt the encrypted cookie to make sure it has the bgColor and showpassword data in it:

Now that we have the key, let’s make a JSON object of {"showpassword":"yes","bgcolor":"#ffffff"}, then XOR encrypt it, and then base64-encode it.

This gives us a cookie value of ClVLIh4ASCsCBE8lAxMacFMOXTlTWxooFhRXJh4FGnBTVF4sFxFeLFMK.

Natas Level 11 Solution

Open up Dev Tools on the Natas level 11 page, and change the cookie value to ClVLIh4ASCsCBE8lAxMacFMOXTlTWxooFhRXJh4FGnBTVF4sFxFeLFMK.

Refresh the page, and you should see the level 12 password:

Takeaway: XOR with a short and/or reused key is not good encryption.