Insight – LABS, 2014/04/13 and

author: [email protected]

0 x00 background


After poking around on Github, the official version of Diff only changed one location in PHP:

#! diff - if ( $hmac ! = $hash ) { + if ( hash_hmac( 'md5', $hmac, $key ) ! == hash_hmac( 'md5', $hash, $key ) ) {Copy the code

WP developers say only vaguely that this version fixes a vulnerability that allows cookies to be forged. Hard to think for a long time to look at the code, and even incidentally dug out a 0day, only to find that he thought too much, the principle of the hole is actually very simple, that is the side channel attack.

0 x01 details


I will not explain the specific side channel attack, this place is a side channel attack on HMAC, using the time difference to determine the HMAC.

HMAC is an encrypted hash. For example, HMAC-MD5 is used in WP. It is a hexadecimal string with 32 bits in length, but encrypted with a key to prevent replay attacks.

With a Timing attack, a full HMAC can be obtained to forge cookies.

First of all, let’s take a look at what the cookie looks like after logging in to wordpress. Take cloud Drops as an example:

wordpress_logged_in_7065d11a793a3ec8482214fcc4f0a55b=insight-labs%7C1397480887%7Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  
Copy the code

The hash behind the cookie name wordpress_logged_in_ looks mysterious.

#! php if ( ! defined( 'COOKIEHASH' ) ) { $siteurl = get_site_option( 'siteurl' ); if ( $siteurl ) define( 'COOKIEHASH', md5( $siteurl ) );Copy the code

That is to say, it is actually the URL of WP website, such as ‘http://drops.wooyun.org’,md5 ‘for Drops, you will know it.

But the contents of the cookie is not so simple, % 7 c is vertical bar | on the keyboard.

Wp’s cookies are delimited by a vertical bar with the user name in front, cookie expiration time in the middle, and 32-bit HMAC in the back.

The source of the HMAC is complicated, but if we can get the HMAC, we can log in to any known user (WP does not record the login session locally, relying on cookies).

HMAC Timing Attack Timing comparison

Even if you don’t see the PHP source code, from a rational perspective, string comparison in any programming language should be implemented like this:

If the first character of two strings is different, then the following characters are meaningless even if they are identical, so return False.

If the first character is the same, go to the second character, if the second character is the same, judge the third character… Up to the last character, if one of the characters is different, the subsequent judgment is terminated and False is returned, if all are the same, True is returned.

I think you get the idea here, that’s judgment

'abcdef'=='zxcvbn'
Copy the code

than

'abcdef'=='abcdeg'
Copy the code

It takes a short time.

0 x02 attack


Construct the POC:

Here I have a 32-bit MD5 hash:

f2835bb2a6ab584fc5cf268bb384c598  
Copy the code

There’s a simple PHP program

#! php <? php $hash='f2835bb2a6ab584fc5cf268bb384c598' if($_GET['hmac']! =$hash){ exit('Go away... ') } echo 'You are admin! '? >Copy the code

Then I submit:

00000000000000000000000000000000
Copy the code

And record the time it takes from the submission to the server to return the result (down to microseconds, or at least milliseconds) after the submission

10000000000000000000000000000000.. 20000000000000000000000000000000.. 30000000000000000000000000000000 . . . f0000000000000000000000000000000Copy the code

From 0 to F, then compare the time they took:

0 0.005450913  
1 0.005829198  
2 0.004905407  
3 0.005286876  
4 0.005597611  
5 0.004814430  
6 0.004969118  
7 0.005335884  
8 0.004433182  
9 0.004440246  
a 0.004860263  
b 0.004561121  
c 0.004463188  
d 0.004406799  
e 0.004978907  
f 0.004887240 
Copy the code

And so on… It’s about the same, and it’s f in the first place, but 7 is the longest.

But statistics tell us that any of the minute differences after repeated amplification, so this time we take each request to run 500 times, and then write down, note that in turn to run, if the first 0 run 500 times running 1 to 500, again a second time when compared to the same content server returns are fast, seems to be some sort of caching mechanism.

After running 0-F 500 times in turn, draw a graph of the recorded time difference:

As you can see, multiple retries increase the statistical significance of the error.

As can be seen from the figure, in most cases the reaction speed of 0-E is less than 0.005 ms, but f is longer than this time.

Now that we know the first digit we can use the same method to calculate the second digit, this time putting in the first digit we know:

f0000000000000000000000000000000  
f1000000000000000000000000000000  
f2000000000000000000000000000000  
.  
.  
.  
ff000000000000000000000000000000  
Copy the code

I won’t go into the same process, but you can try to figure out whether the average or the standard deviation is a good way to calculate all the response times.

With this attack, we only need 16*500*32=256000 requests to get the full HMAC cookie.

The time difference is not removed (it always exists, except with constant time comparison algorithm), but because of the addition of hMAC, the attacker’s payload is randomized, so it cannot guess which character is the time difference.

But in the Internet environment, this hole is still a little chicken ribs, but if it is in the same room or virtual host with the station, the effect should be good.

The hole in WP has been patched up. I believe you will draw inferiorinferiorinferiorinferiorinferiorafter all. There are too many places where =.

PS: The number of short meals +=1