October 8, 2013

Piercing Through WhatsApp’s Encryption (2)

My previous post received a lot of attention today and some people rightly complained that the results don’t mean much when it can not be reproduced with an official WhatsApp client.

So today I spent some time trying to disassemble the offical clients and running the Android client in the Android Emulator.

Nokia S40

I had some success decompiling the latest version of the Nokia S40 package (version 2.11.4, SHA1: ca47ae53a3c3e1f6830e4b9d50cf58655cf0889d ):

I found this (with some classes and functions given meaningful names by me):

public ho ( byte [] paramArrayOfByte1, byte [] paramArrayOfByte2) { paramArrayOfByte1 = a (paramArrayOfByte1, paramArrayOfByte2, 16 , 20 ); paramArrayOfByte2 = new byte [ 256 ]; this . x = new RC4 (); this . x . setKey ( new ByteBuffer(paramArrayOfByte1)); this . x . encrypt (paramArrayOfByte2, 0 , 256 , paramArrayOfByte2, 0 ); this . c = new HMAC ( new SHA1 ()); this . c . init ( new ByteBuffer(paramArrayOfByte1)); paramArrayOfByte2 = new byte [ 256 ]; this . a = new RC4 (); this . a . setKey ( new ByteBuffer(paramArrayOfByte1)); this . a . encrypt (paramArrayOfByte2, 0 , 256 , paramArrayOfByte2, 0 ); this . y = new HMAC ( new SHA1 ()); this . y . init ( new ByteBuffer(paramArrayOfByte1)); }

I think it’s clear that this.x and this.a are two different instances of RC4 with the same key and this.y and this.c are HMAC instances also with that key.

Android

Decompiling this turned into a pile of garbarge. All strings are obfuscated so it’s very hard to determine which class is doing what. It appears to use some crypto API as I can find references to certain elliptic curves and AES. This is likely Bouncy Castle, which doesn’t mean they actually use ECC or AES.

So on to the Android Emulator, where it turns out it is not following the authentication procedure as described in my previous post. The mechanism is still called WSAUTH-1 , but the initial <auth> contains 45 bytes of data now:

<auth user= "XXXXXXXXXXX" xmlns= "urn:ietf:params:xml:ns:xmpp-sasl" mechanism= "WAUTH-1" > ZZZZZZZZZZZZZZZZZZZ </auth>

After this message the server replies with an encrypted response, lets call this UUUUUUUUUUUU .

The first hint that this is still RC4 is that encrypted messages are of different lengths: a block cipher would require padding and would result in messages of exactly the block size.

45 bytes is also exactly the length of the data in the SASL <response> I described. So lets see if we can repeat the same trick:

I assume the plaintext of the ZZZZZZZZZZZZZZZZZZZ block is still XXXXXXXXXXX || nonce || UNIX time ( XXXXXXXXXXX was the phone number, wich is still included in plain). I do not know what the nonce is in this case, but I do know XXXXXXXXXXX . I could try to make a guess for the timestamp, but lets keep it simple.

block is still ( was the phone number, wich is still included in plain). I do not know what the is in this case, but I do know . I could try to make a guess for the timestamp, but lets keep it simple. I calculate (Z[i] ^ X[i]) ^ U[i] (ignoring the HMAC bytes) and the result is (in hex):

f8 0e be c3 fc 0a 31 33 38 31 32

Recall that all stanzas in WhatsApp’s binary encoding start with 0xf8 . The last 5 bytes are 13812 in ASCII, which looks suspiciously much like the 't' attribute of the <success> message (a UNIX timestamp). Indeed: the 0xbe refers to the string success in WhatsApp’s encoding.

This is very unlikely to be a coincidence, which proves that the latest version of the Android client is vulnerable.

…but wait, where did that RC4 key come from?

Apparently the server is able to generate the RC4 key using no nonce or key exchange with the client. The key is also different on each login, as otherwise the first couple of ciphertext bytes in the <auth> would always be the same.

I suspect the key only depends on the password, but new password is obtained during every login. The client makes an HTTPS request to WhatsApp before the fake-XMPP connection is made and yowsup-cli has a flag that will send your password to the server, which will give you a new one if the password was correct.

Conclusion

I have shown pretty convincingly that the latest versions of the Android and Nokia S40 WhatsApp clients are vulnerable to this problem. I don’t have an iPhone to reproduce the results on iOS nor have I looked at the BlackBerry version, but I don’t think the results will be much different.