Tuesday, 14 November 2017

Undo Five/Nine (Crypto 300, Lisbon CTF)

Last week I had the opportunity to participate in the on-site Bsides Lisbon CTF. I teamed up with some workmates and we tried to solve some of the challenges.

One of the challenges I was working on was "Crypto 300: Undo Five/Nine". I didn't take notes on the description, but basically they gave a piece of PHP code "snip.php" and two other files: "readme.txt" and "readme.txt.fsociety".

A quick look at "snip.php" helps us to understand how the other two files were used or generated:

$crypted = fopen($file . ".fsociety", "w");
$fp = fopen($file, "r+");
$clear = fread($fp, 2048);
// destroy original file
destroy_file($fp,strlen($clear));

// generate unique key
$key = gen_aes_key();
$aes = new Crypt_AES(CRYPT_AES_MODE_ECB);
$aes->setKeyLength(128);
$aes->setKey($key);

// create encrypted file
$clear = $aes->encrypt($clear);
fwrite($crypted,$clear,strlen($clear));

As we can see, it seems this PHP script read a plaintext secret from "readme.txt" and destroys it somehow. Then an encryption key is generated and the plaintext is encrypted using AES-128 in ECB mode. The encrypted secret is then stored in "readme.txt.fsociety".

So it seems we should be able to recover that encrypted message somehow. Since key is not stored, it was obvious that we were facing some kind of weakness in that key generation. Let's have a look:

function gen_aes_key() {
 $key = "";
 for ($i = 0;$i < 16;$i++)
   $key.= chr(mt_rand(0, 255));
 return $key;
}

Well, that makes sense. "mt_rand" function generates a random value via the Mersenne Twister Random Number Generator. This function, as its documentation warns, is not secure for cryptographic purposes. I googled for a while, and I found more information about this issue, where we can find the following information:

"Common misuses of mt_rand() include generation of anti-CSRF tokens, custom session tokens (not relying on PHP's builtin sessions support, which uses a different PRNG yet was also vulnerable until recently), password reset tokens, passwords, database backup filenames, etc. If one of these items is exposed and another is generated later without the web application or server reseeding the PRNG, then an attack is possible where the seed is cracked from the item generated earlier and is then used to infer the unknown item generated later."

It seems we should have at least another call to "mt_rand" and to have access to its results in order to be able to exploit this issue, but we only have an encrypted message, and a destroyed file. Let's have a look to the piece of code that destroys that file:

function destroy_file($fp,$len) {
  $random = "";
  for ($i = 0;$i < $len;$i++)
    $random.= chr(mt_rand(0, 255));
  fseek($fp, 0);
  fwrite($fp, substr($random, 0, $len));
  fclose($fp);
}

Bingo! It was overwritten using "random" values generates with the same function, which means that if we can obtain the seed, we could regenerate all the stream and grab the encryption key.

Let's do it! I was reading the seed cracker documentation for a while. It wasn't as easy as I initially thought, since this tool has several modes of operation, but I finally understood that the proper syntax was as follows:

./php_mt_seed [first_num] [first_num] 0 255 [second_num] [second_num] 0 255 ...

But I had a bunch of bytes, so I decided to generate it using a few lines of PHP code:

$fp = fopen("readme.txt", "r+");
$clear = str_split( fread($fp, 2048) );
foreach ($clear as $v) {
    echo ord($v) . ' ' . ord($v) . " 0 255 ";
}

This code generated of the parameter that I needed. In a minute or two, I got an answer: "844114388". Now we need to regenerate all the stream based on this seed. Let's go back to a piece of PHP code:

mt_srand(844114388);
for ($i = 0;$i < 64;$i++)
    echo chr(mt_rand(0, 255));

When we generate this stream, we will see a number of bytes that should be the same to the information we can found in "readme.txt". After those bytes, we should have the encryption key (16 bytes).

$ php gen.php  | xxd
00000000: 7b36 0ee9 f9b9 1cfe d0bb d0e6 1311 5828  {6............X(
00000010: fcfe 84a6 7453 03f6 85b6 e270 76c3 41f8  ....tS.....pv.A.
00000020: aec4 9ca5 f658 dda4 20f2 1c9f 5d14 b5b1  .....X.. ...]...
00000030: beb5 1669 3135 31f9 30bc 9438 d0ac d0d6  ...i151.0..8....

So let's see if we can decrypt the file:

$ openssl enc -aes-128-ecb -d -K "f658dda420f21c9f5d14b5b1beb51669" -in readme.txt.fsociety
flag{the_darkarmy_is_now_on_to_you}

Bang! We got it! Unfortunately, I wasn't able to submit this flag. Why? First because, for some reason, I had in mind that "snip.php" was generating integers and then they were being truncated, so I spend around an hour reading the tool and trying to modify it. Second, because I got a syntax error on my code 10 seconds before CTF's deadline, so I wasn't able to fix it and to submit the flag. Anyway, I enjoyed the CTF :)

Monday, 19 December 2016

Evil-Maid attacks with Hibernation

I have shared the speech I gave in the last RootedCon Valencia, about an Evil-Maid attack technique exploiting Windows hibernation files.

This technique is not new (and I didn't discover it for the first time), but it isn't very well documented.

I have also written about this kind of attacks in Areopago21 blog (in Spanish).

In this post I am going to focus on the hands-on part.

Summarizing: If we get physical access to a computer powered on (but locked) or in suspension. We can try to recover the critical volatile information (session identifiers, clear-text passwords, cryptographic keys, etc.) from the hibernation file.

In order to obtain the hibernation file, we need to extract it from the hard drive. We could boot the computer from an external device (forensic Linux distro, Hirens bootcd, etc.) or we could take out the hard drive from the computer. If the hard drive is encrypted, it gets more complicated.

This file is in the root of the drive: c:\hiberfyl.sys. Even for Windows, this file is hidden by default and it is locked, so we can’t read it.

The hibernation file is never deleted, only its headers are modified when it is used for rebooting. This way, if the computer has been hibernated at any time in the past, we will have this file. If not, we need to force an hibernation.

This is possible even if the computer is locked, if the user has activated the hibernation option:


Unfortunately, starting from Windows 7, the hibernation feature is disabled by default. Although some laptop manufactures enable it.

However, there is a way to force hibernation. If the battery reaches critical level, the computer is hibernated automatically. This is configured by default in all Windows version up to Windows 10.


Once we have the hibernation file, we can work with it:

The basic tool for the task is Volatility; with it we can do the following things:
• Obtaining information about the hibernation file: vol.exe hibinfo -f hiberfil.sys
• Convert it to raw format: vol.exe imagecopy -f hiberfil.sys -O hiberfil.bin
• Convert it to DMP format (Windbg compatible): vol.exe raw2dmp -f hiberfil.sys -O hiberfil.dmp
• Obtaining the browsing history: vol.exe iehistory -f hiberfil.sys
• Obtaining local password hashes: vol.exe hashdump -f hiberfil.sys
• Obtaining Truecrypt cryptographic keys: vol.exe truecryptpassphrase -f hiberfil.sys

Example of usage:


We also have multiple community plugins for other tasks: mimikatz, bitlocker, bitcoin, etc.

For the conversion we can also use the tools from Matt Suiche (just released), previously known as MoonSols Windows Memory Toolkit. They work better than Volatility and they support Windows up to version 10.

Despite we have a Mimikatz plugin for Volatility, it is very limited so it’s better to work directly with Mimikatz. For that we have to:
• Convert the hiberfil.sys file to a format compatible with Windbg (DMP):
  o vol.exe raw2dmp -f hiberfil.sys -O hiberfil.dmp –profile=Win7SP0x64
• Load the DMP into Windbg:
  o .symfix => Configures the Microsoft symbol repositories.
  o .reload => Reloads the needed symbols.
  o .load wow64exts => Loads the module for debugging WOW64 processes.
  o !wow64exts.sw => Activates WOW64 extensions.
• Load Mimikatz module in Windbg:
  o .load c:\Users\rpinuaga\Desktop\bad-hibernation\demo\mimilib64.dll => Loads the Mimikatz module.
  o !process 0 0 lsass.exe => Looks for the lsass process (Local Security Authority Subsystem Service).
  o .process /r /p fffffa800424e910 => Configures the context to the lsass process.
  o !mimikatz

And it’s done, here we have the results:


Note: Volatility only supports hibernation files from Windows up to version 7 (starting in Windows 8 the format changes a bit). The new tool from Matt Suiche in theory allows it, but last time I checked the file resulting from the conversion was not recognized by Volatility.

Wednesday, 17 February 2016

SQL LIKE clauses wildcard injection

I’m going to talk about a little known vulnerability and traditionally considered of low risk, although as we are going to see in some situations it can have a big impact.

This vulnerability involves the possibility of injecting a wildcard in the search field of a LIKE clause in a SQL statement.

OWASP covers briefly this kind of injections in its guides.

In SQL we have 2 types of wildcards:
  • % equivalent to any string of cero or more characters.
  • _ equivalent to any character.
An application is vulnerable to this attack when it uses the LIKE operator with a user received parameter not filtering any of these 2 wildcards.

For example if we have the following URL:
http://www.example.com/fruit.php?name=apple

That shows a text extracted from the database with a SQL query like the following:
SELECT text FROM table WHERE fruit LIKE ‘$name’

Instead of using the simple form:
SELECT text FROM table WHERE fruit=‘$name’

Even if the $name parameter is sanitized for avoiding SQL injection (for example filtering the single quote) it’s still possible to inject wildcards in the search field, as following:
http://www.example.com/fruit.php?name=ap%

In an application like this, altering the search logic it’s not critical, but what happens if we have another application like the following? (Where we don’t know the name of the users).
http://www.example.com/userphoto.php?name=john

We can easily do the following and obtain a listing of all available users:
http://www.example.com/userphoto.php?name=a%
http://www.example.com/userphoto.php?name=b%
http://www.example.com/userphoto.php?name=c%


We can automate the process with a simple script that will go pulling the names of each user character by character (like in the war games movie).

In what situations this kind of vulnerabilities can be dangerous?

  • In login forms. I have found sometimes this vulnerability in the “username” field of some forms and less commonly even in the “password” field.
  • In password recovery forms. This vulnerability can allow us to reset the password of other users.
  • In fields containing session identifiers or tokens. This vulnerability can allow us to “steal” or “predict” the tokens or the sessions ids of other users.

It’s incredible but this works sometimes:

The injection of the % wildcard sometimes can be hard, because this character is usually filtered to avoid encoding attacks o precisely because it is wrongly decoded (in this case we can replace it by %25 or %2525).

Some time ago I found a curious situation where an application authenticated users with a session identifier stored in a database. The extraction of the values stored was made with a vulnerable query, this way:
http://www.example.com/admin/private.php?sessionid=0123456789

The server filtered the % wildcard, but the _ character was permitted. With the following trick we could exploit the vulnerability:
http://www.example.com/admin/privado.php?sessionid=__________

If we wanted to access a specific session, we only needed to do a sweep:
http://www.example.com/admin/privado.php?sessionid=0_________
http://www.example.com/admin/privado.php?sessionid=1_________
http://www.example.com/admin/privado.php?sessionid=2_________


Why some programmers fall with this evident bug?

I suppose that sometimes it’s only an oversight, but I have detected that some programming frameworks encapsulate the SQL statements transparently for the programmer but internally they use the LIKE operator, without him even knowing.

For example the following Django sentence:
result=Entry.objects.get(headline__contains='Lennon')

Results in:
SELECT ... WHERE headline LIKE '%Lennon%'

This can easily lead to multiple vulnerabilities if the developer is not careful.

Also this other bug very similar in the Propel module of Symfony.