/memories

A key-recovery attack for LINE for Android's chat backup encryption.

Primary LanguageGoMIT LicenseMIT

memories

A key-recovery attack for LINE for Android's chat backup encryption.

LINE for Android has a feature to back up individual chats to a ZIP file. Each backup contains images in plaintext and a weakly encrypted chat database, which can be recovered in fifteen minutes at most on a CPU supporting AES-NI.

Usage

Before anything else, compile the package with go build and extract the backup archive (LINE_Android-backup-chat<id>.zip). The encrypted chat database is the linebackup/chat/chat<id> file (and not the one with the .extra extension). Note: chat<id> and chat<id>.extra need to be in the same directory for the decryption program to work.

Decrypt an encrypted chat database (requires both your MID and your chat partner's):

./memories -source <path to the encrypted chat db> -oid <your MID> -tid <your chat partner's MID>

Recover the key of an encrypted chat database and decrypt it:

./memories -bruteforce -source <path to the encrypted chat db>

Write-up

Introduction

Every LINE user has an internal ID that starts with u followed by 32 hex digits (e.g. u61726520762e206375746520f09f929c), which is refered to as PROFILE_MID or tmId in the Android app. (This ID is different from the one you can set in the app, and from the userId exposed by the LINE Messaging API.) While it isn't visible to end users, it isn't secret either: I recovered mine and my chat partners' using LINE for Google Chrome and Chrome's developer tools.

When a chat backup is created, both IDs are concatenated and hashCode()ed. The resulting 32-bit integer is then used as an initialization vector (IV) to derive a 128-bit encryption key, and the chat database is encrypted using AES-ECB with PKCS#5 padding.

After finding both IDs and reimplementing the encryption scheme, I was able to successfully decrypt a backup made from my account.

Key-recovery

On top of the encryption key being derived from a 32-bit integer, the key derivation function (KDF) is fast and only called once. This effectively reduces the size of the key space to 2^32, which can be searched quickly.

A simple way to search the key space is to derive a key from every possible IV, and use it to try to decrypt the first 16-byte block of the ciphertext (which is expected to be SQLite format 3\x00).

Using this attack, I was able to recover a key in less than 4 minutes on a 7 years old laptop (i5-2540M @ 2.60GHz).

Successful key-recovery

Possible improvements

The KDF has collisions (e.g. the IVs -1147136985, -1147146969, -1147157209... all yield the same key), which leads me to believe this attack could be improved.

Lessons learned

  • Do not use 32-bit IVs.
  • Do not use hashCode() as a cryptographic hash function.
  • Do not implement your own KDF.
  • Use a slow KDF to prevent brute-force attacks.
  • Do not use AES in ECB mode.

License

This project is licensed under the terms of the MIT license, except for the contents of the crypto directory which are licensed under the Go BSD-style license.