SIK-2016-020
Title:
Master Password Decryption of My Passwords App
Report ID
SIK-2016-020
Summary:
- Vendor: Erkan Molla
- Product: My Passwords
- Affected Version: 4.5.0
- Severity: High
- Short summary:
Because of security issue SIK-2016-019 an attacker can steal the stored encrypted masterpassword. The following attack describes the decryption scheme.
Details:
The vulnerability SIK-2016-019 and this one (the decryption vulnerability) allow an attacker with physical control over the device to extract all password stored in the app. He first extracts the shared preferences to obtain the “master_key” and the “random_key”. Afterwards, he deobfuscated the “master_key” to obtain the user’s master password and logs into the app as if he were the original user. This completely breaks the security of the password manager. Once the master password is known, it also becomes possible to reconstruct the complete database decryption process and, given the database file, perform the decryption offline on a computer or secondary device without relying on the original password manager app.
The app stores an encrypted version of the user’s master password (“master_key”) in the shared preferences file. With the vulnerability described above, an attacker who steals or finds a device can extract this encrypted master password without requiring any special privileges or system-level exploits. Additionally, the same file is used to store an additional value called “random_key”:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="random_key">YVl0RzJhS1NM</string>
<string name="master_key">//S2ErOUo4rDmcU4qIZvig==</string>
<boolean name="show_welcome_screen" value="false" />
</map>
With these two values, it is possible to reconstruct the user’s master password and log into the app. The attacker can thus extract all of the user’s stored passwords. The way the master password is encrypted suffers from several weaknesses. In the following, we denote the key used to encrypt the master key with
“real_key”.
Firstly, the encryption key (the “real_key”) is weak. It is only 9 bytes long and not even uniformly distributed across the 8^9 theoretically possible values. Instead, the key bytes are chosen from a universe of 62 unique NN characters. In the code, the key bytes are chosen from the string:
$#@!6AaBb2Cc2Dd3Ee4Ff5Gg6Hh7k8Jp9KkL8Mm2Nn3o4Pp5Qq6Rr7Ss8Tt9UuVv7Ww2Xx3Yy4Zz5$#@!
This string contains duplicates and further shifts the probability distribution away from uniformity. The character “H”, for instance, is only contained once in the 81 characters, while the character “9” appears twice, making a “9” more likely than an “H”. Additionally, the selection of the key bytes is not made using a secure source of randomness such as Java’s SecureRandom class. Instead, the java.math.Random() class is used, which is predictable. In total, the encryption key is derived using a non-standard algorithm and have unfavorable statistical properties which make it easier to guess the key. An attacker who attempts a brute-force attack on the “real_key” benefits from these issues.
Secondly, the app stores an obfuscated version of the “real_key” in the “random_key” tag in the shared preferences file. This obfuscation is also weak and can be reversed, regardless of the vulnerabilities in the construction of the “real_key” described above. The obfuscation scheme is as follows: The first two characters of the “real_key” become the first three characters of the “random_key”. The third character of the “real_key” becomes the fourth character of the “random_key”. This pattern is repeated:
random_key: YmG2YmG2YmG2 YmG realkey chars 1-2 2 realkey char 3 YmG realkey char 4-5 2 realkey char 6 YmG realkey char 7-8 2 realkey char 9
This means that the app obfuscated two keys together, one alone, two together, etc. All obfuscations are independent of one another. If the first two characters of the “real_key” are equal to the characters four and five, both pairs will be obfuscated to the same “random_key” character triples. This allows us to build a simple lookup table for all possible characters plus all possible combinations of two characters. Once this table has been built, the attacker can perform an inverse lookup to check which original “real_key” value corresponds to a given “random_key”. With this technique, the original key that is used to encrypt the master password can be retrieved. Once the user has recovered the “real_key” which is used for encrypting the master password, he can decrypt the master password and use this password to log into the app. The app performs various hashes on the key to derive the key that is finally used for the AES encryption of the master password, but these transformations can simply be reproduced by the attacker once the input key is known.
Workaround
Users should make sure to choose a secure lock screen such as a sufficiently long and complicated password to prevent an attacker from starting the password manager app even if he has managed to extract the master password. Furthermore, users should encrypt their phone to prevent data extraction directly from the storage.
The impact is reduced, because the HTML viewer component (see SIK-2016-019) is no longer included in version 6.0.0. However, the older version 4.5.0 of the app is still offered on Google Play to users who are running older versions of Android and thus cannot use the new app version.
Suggested Mitigation
Developers should never attempt to design or implement their own cryptographic primitives or protocols. Proper crypto implementations are available in many well-tested and popular libraries such as BouncyCastle, OpenSSL, or can simply be accessed through the Java Crypto API. These libraries also contain methods for securely generating random keys with uniform probability distributions. Furthermore, proper encryption of the master password using a standard algorithm such as AES would also have prevented our lookup table-based attack presented above. Lastly, developers should check if passwords must really be stored using reversible encryption. Usually, storing only a hash is the better option.
Timeline
- 2016-11-11 Vulnerability Reported
- 2017-02-17 Fixed in Version 7.2.1