Monday, March 6, 2017 At 9:57AM
Aon’s Cyber Labs recently discovered and disclosed a vulnerability in Skype for Business caused by mishandling of cryptographic information. Improper use of string objects resulted in reduced entropy for database passwords storing sensitive user data. This vulnerability was detected by decompiling the unobfuscated Java source code stored within the application container. This is very similar to an issue discussed previously by Stephen Komal at Aon’s Cyber Labs two years ago, described here.
The database stored in the application container under databases/EncryptedDataStore.sqlite on the Android platform used a weaker than optimal key for storage of secure data. While it is likely that for practical purposes the key’s length itself will be sufficient to prevent compromise in the majority of cases, the key is malformed, reducing its entropy significantly. The danger of this vulnerability is that attackers may find a system with a key that is much more easily cracked by brute force than what was originally intended by the software’s authors. In the worst-case scenario, attackers could trivially crack the database key, decrypt the database, and retrieve private user information.
During account set up, each user’s personal data is encrypted in a sqllite database on the Android device using a password for a PBKDF2 key derivation function which is randomly generated, using Java’s SecureRandom functionality. However, that key is immediately converted to a Java string.
com/microsoft/office/lync/platform/database/util/SfbDataBaseProvider.java 53: private String generateRandomPassword() { 54: byte[] arrby = new byte[42]; 55: new SecureRandom().nextBytes(arrby); 56: return new String(arrby); 57: }
The database key is initialized as follows:
com/microsoft/office/lync/platform/database/util/SfbDataBaseProvider.java 59: private String getDbKeys() { 60: String string2; 61: String string3 = string2 = CredentialsStoreManager.getInstance().getDatabasePassword(); 62: if (string2 != null) return string3; 63: Trace.d(TAG, “getDbKeys, Database Encryption not stored, creating new password”); 64: string3 = this.generateRandomPassword(); 65: try { 66: CredentialsStoreManager.getInstance().setDatabasePassword(string3); 67: return string3; 68: } 69: catch (CredentialStoreException var2_2) { 70: Trace.e(TAG, “getDbKeys, storage failed!!!!!”); 71: DatabaseAnalytics.reportKeyStorageError(var2_2); 72: return string3; 73: }
Each user is given a new database password for decryption, which cannot be easily retrieved without rooting the device. Aon retrieved the PBKDF key derivation password by constructing a “hooking” program that dumped out the database password from memory, which was then used to decrypt the database. This could easily be accomplished on any rooted device. The function in question was located by decompiling the unobfuscated Java class files.
com/microsoft/office/lync/platform/CredentialsStoreManager.java 312: public String getDatabasePassword() { 313: Account account = this.getLyncAccount(); 314: if (account != null) { 315: return this.accountManager.getUserData(account, “persistanceKey”); 316: } 317: Trace.i(“CredentialsStoreManager”, “getDatabasePassword, returning null because no database password is currently stored”); 318: return null; 319: }
By “hooking” into this function the database password was found. A sample database password is shown below. Note that this password only relates to a Aon specific test account.
Key data:
EF BF BD 6C EF BF BD EF BF BD 1D EF BF BD EF BF BD 68 09 0D EF BF BD 0A EF BF BD EF BF BD EF BF BD D7 BB EF BF BD EF BF BD 57 30 6A 18 EF BF BD 2E 15 EF BF BD 3F 09 65 38 EF BF BD 31 EF BF BD EF BF BD EF BF BD 04 EF BF BD EF BF BD EF BF BD EF BF BD EF BF BD
In this case a large proportion of the password has been replaced with the unicode “replacement character” byte sequence (EF BF BD.)
The underlying cause of this issue is the use of Java’s String class to hold binary cryptographic data. Java’s String class is not meant to hold raw bytes, and actually will encode strings differently based on each platform’s encoding preferences. The bytes originally being generated by the function above are incorrectly transformed into UTF-8 when used in a string, converting over half the bytes to invalid character sequences. The entire issue can be resolved by removing the use of the String class in favor of the use of byte arrays.
Additionally, it should be noted that the application chronically uses Java strings when handling cryptographic material, compounding the difficulty of resolving the issue. For instance, the application uses a class to store the password during some operations and stores the password itself as a string:
com/microsoft/office/lync/platform/CredentialsStoreManager.java 668: public static class Password { 669: private final String m_passwordEncrypted; 670: private String m_passwordPlaintext = null; 671: 672: private Password(String string2) { 673: this.m_passwordEncrypted = string2; 674: } 675: 676: public static Password fromEncrypted(Context context, String string2) { 677: return new Password(string2); 678: } 679: 680: public static Password fromPlainText(Context object, String string2) { 681: if (TextUtils.isEmpty((CharSequence)string2)) { 682: object = “”; 683: return new Password((String)object); 684: } 685: object = CryptoUtils.encrypt(string2); 686: return new Password((String)object); 687: }
When Skype private keys are loaded, a similar issue occurs:
com/microsoft/office/lync/platform/CredentialsStoreManager.java 396: String loadPrivateKey(Account object, ICredentialStore.Service service) throws SfbCryptoException { 397: String string2; 398: String string3 = string2 = null; 399: if (object == null) return string3; 400: object = this.accountManager.getUserData((Account)object, this.privateKeyAccountDataKey(service)); 401: string3 = string2; 402: if (object == null) return string3; 403: return CryptoUtils.decrypt((String)object);
Remediation
The issue was remediated by converting the password bytes to base64 before they are returned by generateRandomPassword(). This ensures that the original bytes are preserved and are not altered due to character encoding. The fix was applied in the other areas of the application that were similarly vulnerable.
com/microsoft/office/lync/platform/database/util/SfbDataBaseProvider.java private String generateRandomPassword() { byte[] arrby = new byte[42]; new SecureRandom().nextBytes(arrby); - return new String(arrby); + return Base64.encodeToString(arrby,0); }
Disclosure
Aon disclosed this issue to Microsoft in November 2016 and it was remediated in production builds of Skype for Business in December 2016 in version 16.11.0.0.
Microsoft did not assign a CVE or disclose technical details of the vulnerability. Aon researcher John Dunlap was listed on the January “Security Researcher Acknowledgements” which can be found at https://technet.microsoft.com/en-us/security/cc308589.aspx.
Author: John Dunlap
©Aon plc 2023