BB

technology and craziness.

BB

technology and craziness.

Contents

OverTheWire - Natas - Level 10 → Level 11

Information

Warning: This post contains a solution!
Only continue if:
1.) you want to see a possible alternative solution or
2.) you are stuck and need a hint!

Login using given credentials.

URL: http://natas11.natas.labs.overthewire.org
Username: natas11
Password: U82qxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Relevant code from the site source code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?

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

?>

As mentioned on the level page, the cookies are protected with XOR encryption. Before starting with the challenge, let us take a look how XOR is working by using a simple example.

Preparation - XOR encryption

From Wikipedia:

With this logic, a string of text can be encrypted by applying the
bitwise XOR operator to every character using a given key.
To decrypt the output, merely reapplying the XOR function with the key will remove the cipher.

We’ll use the following variables for the example:

  • A (Plaintext): thisisjustatexttotestXOR
  • B (Key): 1234
  • C (Encrypted Text): Result from A⊕B (A XOR B)

So, how is it calculated? This can be perfectly shown with the binary representation. To do so, the strings A and B need to be converted to binary:

1
2
3
A: 01110100 01101000 01101001 01110011 01101001 01110011 01101010 01110101 01110011 01110100 01100001 01110100 01100101 01111000 01110100 01110100 01101111 01110100 01100101 01110011 01110100 01011000 01001111 01010010

B: 00110001 00110010 00110011 00110100

We need to repeat B until it has the same length as A, which will make the XOR calculation easier to show. This will result in the following:

1
2
3
A: 01110100 01101000 01101001 01110011 01101001 01110011 01101010 01110101 01110011 01110100 01100001 01110100 01100101 01111000 01110100 01110100 01101111 01110100 01100101 01110011 01110100 01011000 01001111 01010010

B: 00110001 00110010 00110011 00110100 00110001 00110010 00110011 00110100 00110001 00110010 00110011 00110100 00110001 00110010 00110011 00110100 00110001 00110010 00110011 00110100 00110001 00110010 00110011 00110100

The next step is to perform the XOR operation bit by bit. A simple example, with the first (or last) two bytes (=16 bits).

1
2
3
4
5
6
A: 01110100 01101000 ...
B: 00110001 00110010 ...
><
------------------------
C: 01000101 01011010 ...
 

From this example, it quickly becomes clear how the XOR operation works. A table-based representation of the XOR operation:

ABC
000
011
101
110

Once completely executed, C looks as below:

1
2
3
4
5
A: 01110100 01101000 01101001 01110011 01101001 01110011 01101010 01110101 01110011 01110100 01100001 01110100 01100101 01111000 01110100 01110100 01101111 01110100 01100101 01110011 01110100 01011000 01001111 01010010
B: 00110001 00110010 00110011 00110100 00110001 00110010 00110011 00110100 00110001 00110010 00110011 00110100 00110001 00110010 00110011 00110100 00110001 00110010 00110011 00110100 00110001 00110010 00110011 00110100
><
------------------------
C: 01000101 01011010 01011010 01000111 01011000 01000001 01011001 01000001 01000010 01000110 01010010 01000000 01010100 01001010 01000111 01000000 01011110 01000110 01010110 01000111 01000101 01101010 01111100 01100110

The ASCII representation of C, after converting it from binary to ASCII, is the XOR-encoded string.

  • A (Plaintext): thisisjustatexttotestXOR
  • B (Key): 1234
  • C (Encrypted Text): EZZGXAYABFR@TJG@^FVGEj|f

Now, what is the thing here? How can we use this to solve the challenge?
The answer to these questions is (from Wikipedia):

If the content of any message can be guessed or otherwise known then the key can be revealed.

Using the example above, performing the XOR operation A⊕C should result in B:

1
2
3
4
5
A: 01110100 01101000 01101001 01110011 ...
C: 01000101 01011010 01011010 01000111 ...
><
------------------------------------------
B: 00110001 00110010 00110011 00110100 ...

The secret key B can be recovered.

The challenge

The following things can be read from the source code:

  • A cookie with the name data is set when the page is loaded

    • The default values for data are defined in the array $defaultdata
      • showpasswordno
      • bgcolor#ffffff
  • However, data is not stored in plain text

    1
    2
    3
    
    function saveData($d) {
        setcookie("data", base64_encode(xor_encrypt(json_encode($d))));
    }
    
    • First data (passed as $d) is JSON encoded, then XOR encrypted and base64 encoded

The goal of this level is to change the value of showpassword to yes. It is known from the previous chapter that the key ($key in the source code) can be returned if you know the encrypted string and the unencrypted string.
In this case, we know both values, but we have to extract the exact strings to recover the secret key.

Extract the unencrypted string

  1. The array $defaultdata is filled with the following values

    1
    
    $defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");
    
  2. Those values are used in two places, but the interesting part here is the function saveData

    1
    2
    3
    
    function saveData($d) {
        setcookie("data", base64_encode(xor_encrypt(json_encode($d))));
    }
    
  3. The data is first JSON encoded and then used in the function xor_encrypt as text (variable $text) which is then encrypted

  4. This means, that the JSON encoded $data value is our unencrypted string

Extracting the important parts from the PHP source code results in the string: {"showpassword":"no","bgcolor":"#ffffff"}

1
2
3
4
5
6
<?

$defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");
print(json_encode($defaultdata)); // {"showpassword":"no","bgcolor":"#ffffff"}

?>

Extract the encrypted string

The encrypted string is stored in the cookie named dataClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw=

  1. The function loadData reveals, how the cookie has to be decoded

    1
    
    $tempdata = json_decode(xor_encrypt(base64_decode($_COOKIE["data"])), true);
    
  2. First the encrypted string must be base64 decoded and then XOR encrypted/decrypted

The following PHP code will return the secret key:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?
function xor_encrypt($in) {
    $key = '{"showpassword":"no","bgcolor":"#ffffff"}'; // Use unencrypted string as key
    $text = $in;
    $outText = '';

    // Iterate through each character
    for($i=0;$i<strlen($text);$i++) {
    $outText .= $text[$i] ^ $key[$i % strlen($key)];
    }

    return $outText;
}

// 1. base64 decode encrypted string
$base64decoded = base64_decode("ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw=");

// 2. Execute XOR function
$secretKey = xor_encrypt($base64decoded);

// 3. Print secret key
print($secretKey);
?>

Put everything together

Using the secret key, it is now possible to change the value of showpassword to yes, XOR encrypt the modified array and base64 encode it.
Once this is done, the cookie value in the browser has to be updated. Refreshing the site will reveal the password for the next level.

References