Skip to content

Latest commit

 

History

History

Shiva

An Android Application exploiting CVE-2019-9376

The bug

One of the constructors for the class com.android.accounts.Account does not check that the account has a valid name. This allows the creation of an account with empty name, which can then be used in the call to AccountManager.addAccountExplicitly().

Internally, addAccountExplicitly causes the account to be inserted inside the database of all accounts, without checking whether the name is empty or not:

private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
        Bundle extras, int callingUid, Map<String, Integer> packageToVisibility,
        String opPackageName) {
    Bundle.setDefusable(extras, true);
    if (account == null) {
        return false;
    }
    if (!isLocalUnlockedUser(accounts.userId)) {
        Log.w(TAG, "Account " + account.toSafeString() + " cannot be added - user "
                + accounts.userId + " is locked. callingUid=" + callingUid);
        return false;
    }
    synchronized (accounts.dbLock) {
        synchronized (accounts.cacheLock) {
            accounts.accountsDb.beginTransaction();
            try {
                if (accounts.accountsDb.findCeAccountId(account) >= 0) {
                    Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
                            + ", skipping since the account already exists");
                    return false;
                }
                long accountId = accounts.accountsDb.insertCeAccount(account, password);
                if (accountId < 0) {
                    Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
                            + ", skipping the DB insert failed");
                    return false;
                }
                // Insert into DE table
                if (accounts.accountsDb.insertDeAccount(account, accountId) < 0) {
                    Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
                            + ", skipping the DB insert failed");
                    return false;
                }
                if (extras != null) {
                    for (String key : extras.keySet()) {
                        final String value = extras.getString(key);
                        if (accounts.accountsDb.insertExtra(accountId, key, value) < 0) {
                            Log.w(TAG, "insertAccountIntoDatabase: "
                                    + account.toSafeString()
                                    + ", skipping since insertExtra failed for key " + key);
                            return false;
                        } else {
                            AccountManager.invalidateLocalAccountUserDataCaches();
                        }
                    }
                }

                if (packageToVisibility != null) {
                    for (Entry<String, Integer> entry : packageToVisibility.entrySet()) {
                        setAccountVisibility(account, entry.getKey() /* package */,
                                entry.getValue() /* visibility */, false /* notify */,
                                accounts);
                    }
                }
                accounts.accountsDb.setTransactionSuccessful();

                logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
                        accountId,
                        accounts, callingUid);

                insertAccountIntoCacheLocked(accounts, account);
            } finally {
                accounts.accountsDb.endTransaction();
            }
        }
    }
    if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
        addAccountToLinkedRestrictedUsers(account, accounts.userId);
    }

the call results in an exception being thrown when it tries to insert the account into the cache, but at that point the transaction has already been marked as successful, and so the malicious account is added to the database. At this point, restarting the phone will result in a bootloop.

Malicious Application

The malicious application exploits this by creating a custom Parcel, and using it to create an account. The malicious application implements a custom Authenticator, so that it is able to add the account without any permission. After the malicious account has been added to the AccountManager, even uninstalling the application will result in a bootloop.