Pages

Friday, October 11, 2013

Piercing Through WhatsApp’s Encryption


WhatsApp has been plagued by numerous issues in their security: easily stolen passwords, unencrypted messages and even a website that can change anyone’s status. But that streak is not yet over.
To be clear: this post is not about using IMEI numbers as your password. That issue has been fixed. Logging in on a new device currently works as follows:
  • The phone posts its phone number to a HTTPS URL to request an authentication code,
  • the phone receives an authentication code in the text message,
  • the authentication code is used, again over HTTPS, to obtain a password.
These passwords are quite long and never visible to the user, making them hard to steal from a phone.

Authentication Overview

With the password, the client can log in to the not-really-XMPP server that WhatsApp uses. For this it uses the custom SASL mechanism WAUTH-1. To log in with the phone number XXXXXXXXXXXX, the following happens (I’m showing the XML representation of the protocol, this is not what is actually sent):
  • The client sends:
    
    
    <auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" user="XXXXXXXXXXXX" mechanism="WAUTH-1" />

  • The server responds with a some challenge (here YYYYYYYYYYYYYYYYYYYY):
    
    
    <challenge xmlns="urn:ietf:params:xml:ns:xmpp-sasl">YYYYYYYYYYYYYYYYYYYY</challenge>

  • To respond to the challenge, the client generates a key using PBKDF2 with the user’s password, the challenge data as the salt and SHA1 as the hash function. It only uses 16 iterations of PBKDF2, which is a little low these days, but we know the password is quite long and random so this does not concern me greatly. 20 bytes from the PBKDF2 result are used as a key for RC4, which is used to encrypt and MAC XXXXXXXXXXXX || YYYYYYYYYYYYYYYYYYYY || UNIX timestamp:
    
    
    <response xmlns="urn:ietf:params:xml:ns:xmpp-sasl">ZZZZZZZZZZZZZ</response>

  • From now on, every message is encrypted and MACed (using HMAC-SHA1) using this key.
  • Mistake #1: The same encryption key in both directions

    Lets recall how RC4 is supposed to work: RC4 is a PRNG that generates a stream of bytes, which are xored with the plaintext that is to be encrypted. By xoring the ciphertext with the same stream, the plaintext is recovered.
    However, recall that:
    
    
    (A ^ X) ^ (B ^ X) = A ^ B
    In other words: if we have two messages encrypted with the same RC4 key, we can cancel the key stream out!
    As WhatsApp uses the same key for the incoming and the outgoing RC4 stream, we know that ciphertext byte i on the incoming stream xored with ciphertext byte i on the outgoing stream will be equal to xoring plaintext byte i on the incoming stream with plaintext byte i of the outgoing stream. By xoring this with either of the plaintext bytes, we can uncover the other byte.
    This does not directly reveal all bytes, but in many cases it will work: the first couple of messages exchanged are easy to predict from the information that is sent in plain. All messages still have a common structure, despite the binary encoding: for example, every stanza starts with 0xf8. Even if a byte is not known fully, sometimes it can be known that it must be alphanumeric or an integer in a specific range, which can give some information about the other byte.

    Mistake #2: The same HMAC key in both directions

    The purpose of a MAC is to authenticate messages. But a MAC by itself is not enough to detect all forms of tampering: an attacker could drop specific messages, swap them or even transmit them back to the sender. TLS counters this by including a sequence number in the plaintext of every message and by using a different key for the HMAC for messages from the server to the client and for messages from the client to the server. WhatsApp does not use such a sequence counter and it reuses the key used for RC4 for the HMAC.
    When an attacker retransmits, swaps or drops messages the receiver can not notice that, except for the fact that the decryption of the message is unlikely to be a valid binary-XMPP message. However, by transmitting a message back to the sender at exactly the same place in the RC4 stream as it was originally transmitted will make it decrypt properly.  :)

No comments:

Post a Comment