Level 11
Using the credentials obtained from the previous post, we can log in to Level 11 where we are presented with the following screen:
We can immediately see that this challenge will have to do something with cookies, since the description mentions that ‘Cookies are protected with XOR encryption.’ Let’s take a look at the cookies we do have:
We can see that we have a cookie called ‘data’, which appears to be base64 encoded. Before going any further, let’s take a look at the source to see what exactly we’re dealing with:
<html>
<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'];
}
}
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>
Let’s walkthrough the PHP code and see what it does:
- We first define an array with the following values
- showpassword – no
- bgcolor – #ffffff
- Then, we create an ‘xorencrypt’ function, which takes a string as input and performs an XOR operation with a censored key.
- We then create a ‘loadData’ function which loads the default array into $mydata, checks to see if there is data in our cookie, and if so attempts to base64_decode it, perform the XOR operation on it, and decode the JSON into an array.
- If this is indeed an array, and the proper values are found, the values of mydata are updated with the values from the cookie.
- If not, the default values are kept
- The value of $mydata is returned.
- We then create a ‘saveData’ function which is used to set a cookie to data which is JSON encoded, XOR’d, and then base64 encoded.
- The execution of the PHP first calls loadData to process any cookie data.
- Then the background color request value is handled
- Finally, whatever data has been loaded is set as our cookie value.
- The default array used to generate the default cookie value
- The default cookie value
- The functions used to encrypt and encode our data
<?
$orig_cookie = base64_decode('ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw');
function xor_encrypt($in) {
$text = $in;
$key = json_encode(array( "showpassword"=>"no", "bgcolor"=>"#ffffff"));
$outText = '';
// Iterate through each character
for($i=0;$i<strlen($text);$i++) {
$outText .= $text[$i] ^ $key[$i % strlen($key)];
}
return $outText;
}
print xor_encrypt($orig_cookie);
?>
In this example, we are using the base64_decoded value of our cookie as our Encrypted_Data and we are using the json_encoded default array as our Original_Data. We know these are the two values to use because these are the values sent to the XOR function in the loadData and saveData functions.
Running this code gives us the following:
root@bt:~/natas# php5 level11.php
qw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jq
We can see that this key is the value ‘qw8j’ repeated many times. Therefore, we can deduce that ‘qw8j’ is our key, which we can use on any input to generate the encrypted output.
Let’s adjust our PHP code to XOR the json_encode(d) new array, which sets the ‘showpassword’ value to ‘yes’:
function xor_encrypt() {
$text = json_encode(array( "showpassword"=>"yes", "bgcolor"=>"#ffffff"));
$key = "qw8J";
$outText = '';
// Iterate through each character
for($i=0;$i<strlen($text);$i++) {
$outText .= $text[$i] ^ $key[$i % strlen($key)];
}
return $outText;
}
print base64_encode(xor_encrypt());
We now use the json_encoded representation of our adjusted array to be XOR’d with our derived key. Executing the code gives us the following:
root@bt:~/natas# php5 level11.php
ClVLIh4ASCsCBE8lAxMacFMOXTlTWxooFhRXJh4FGnBTVF4sFxFeLFMK
Let’s use this value as our cookie and see what happens:
As we hoped, the value was decoded, unencrypted, then json_decoded successfully, and the password is returned. We can use these credentials to log in to the next level.
I want to give a ‘Thank you!’ to Reddit user NearOrFar. I had derived the correct key, but did not recognize that it was 4 repeating characters. When using the long key to obtain the new cookie, since the key did not end on a ‘complete cycle’ of the 4 character key, the resulting array was skewed, and was not being encrypted correctly. NearOrFar was able to help diagnose this problem, and after making the truncation to 4 characters, the issue was solved.
More writeups to come.
-Jordan
