Tuesday, October 30, 2012

OvertheWire - Natas Wargame Level 11 Writeup

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:

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.

We can see at the end of the source that if $data['showpassword'] is set to 'yes', then the password will be displayed. Therefore, this is our goal.

Let's stop and think about exactly what we need to do, and what we have to do it. We want to obtain the password for natas12, which is only displayed if the 'showpassword' key of $data is set to 'yes'. This would only occur if during the loadData function our cookie contained the encoded, encrypted version of the array. However, to get the encrypted version of any input, we first need the key so that when it is unencrypted by the 'loadData' function, it will be usable.

We are given the following pieces of data:

The default array used to generate the default cookie value

The default cookie value

The functions used to encrypt and encode our data

The key to this challenge is to realize that we don't need to brute force the key. The property of XOR encryption that will drastically help us in this challenge is that while:

Original_Data XOR KEY = Encrypted_Data

The following is also true:

Original_Data XOR Encrypted_Data = KEY

This helps us because we can see above that we are given the default original data, as well as the default encrypted data. Let's use the following PHP code (recycling some of their functions) to generate our key:

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.

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.

3 comments:

I made a similar mistake to you regarding the length of the key. However, where you reduced the keylength to the actual 4 characters, I just (rather more messily) extended the key by one character in order to fit the length of the new message to be encrypted.

Interesting, I used the same process and the encrypted cookie data I got returned by the php script was "ClVLAh4ASAsCBE8FAxMaUFMOXRlTWxoIFhRXBh4FGlBTVF4MFxFeDFMK" (slightly different, and thus false). Any ideas as to what I did wrong?