安卓KeyStore栈溢出漏洞分析(CVE-2014-3100)

相关内容链接:
1. http://securityintelligence.com/android-keystore-stack-buffer-overflow-to-keep-things-simple-buffers-are-always-larger-than-needed/#.U7OUAflmgmv
2. 有关keystore的内部原理讲解的系列文章:
(1)http://nelenkov.blogspot.co.il/2011/11/ics-credential-storage-implementation.html
(2)http://nelenkov.blogspot.co.il/2011/12/ics-credential-storage-implementation.html
(3)http://nelenkov.blogspot.co.il/2012/05/storing-application-secrets-in-androids.html
(4)http://nelenkov.blogspot.tw/2012/07/jelly-bean-hardware-backed-credential.html
(5)http://nelenkov.blogspot.co.il/2013/08/credential-storage-enhancements-android-43.html
—–
原文连接: http://blogs.360.cn/360mobile/2014/07/01/cve-2014-3100/
作者:申迪

CVE-2014-3100是安卓平台KeyStore的一个栈溢出漏洞。该漏洞是去年9月IBM的Roee Hay & Avi Dayan发现并秘密报告给Google,并于6月23日被公开[1]。与此同时,Google官方也放出了漏洞测试代码。[2]

近日发现有一些国内媒体以《Android最新漏洞:影响86%用户财产安全》为标题对此漏洞进行了报道。但我们的观点是,该漏洞仅在4.3的系统中存在,而其危害也被媒体有所夸大。

Keystroe是安卓平台的密钥存储服务,4.3以前以Locol Socket的形式接收用户对于服务的访问。4.3以后则作为一个binder服务的形式存在。

图 1 keystore原理图,援引自IBM研究员的分析文章[1]

问题所在:

android.security.KeyStore类中的get方法允许传入一个名为keyName的String类型,客户端可以传入一个超长的字符串。

Class<?> keystoreClass = Class.forName(“android.security.KeyStore”);

Method getInstance = keystoreClass.getMethod(“getInstance”);

Method get = keystoreClass.getMethod(“get”, String.class);

Object keystore = getInstance.invoke(null);

String keyName = “AAAA AAAA AAAA AAAA ”

+ “AAAA AAAA AAAA AAAA ”

+ “AAAA AAAA AAAA AAAA ”

+ “AAAA AAAA AAAA AAAA ”

+ “AAAA AAAA AAAA AAAA ”

+ “AAAA AAAA AAAA AAAA ”

+ “AAAA AAAA AAAA AAAA ”

+ “AAAA AAAA AAAA AAAA ”

+ “AAAA AAAA AAAA AAAA ”

+ “AAAA AAAA AAAA AAAA ”

+ “AAAA AAAA AAAA AAAA ”

+ “AAAA AAAA AAAA AAAA ”

+ “AAAA AAAA AAAA AAAA ”

+ “AAAA AAAA AAAA AAAA ”

+ “AAAA AAAA AAAA AAAA ”

+ “AAAA AAAA AAAA AAAA ”

+ “AAAA AAAA AAAA AAAA ”

+ “AAAA AAAA AAAA AAAA ”

+ “AAAA AAAA AAAA AAAA ”

+ “AAAA AAAA AAAA AAAA”;

get.invoke(keystore, keyName);

再看服务端的处理:

首先在栈上分配了一段缓冲区,大小是NAME_MAX

1068 ResponseCode getKeyForName(Blob* keyBlob, const android::String8& keyName, const uid_t uid,
1069 const BlobType type) {
1070 char filename[NAME_MAX];
1071 encode_key_for_uid(filename, uid, keyName);
然后调用encode_key对keyName进行转码

264static int encode_key_for_uid(char* out, uid_t uid, const android::String8& keyName) {
265 int n = snprintf(out, NAME_MAX, “%u_”, uid);
266 out += n;
267
268 return n + encode_key(out, keyName);
269}
这里面认为keyName.lenth()是缓冲区的长度,并且将内容转码后复制到栈上的char name,如果keyName.lenth()超长,则栈溢出

248static int encode_key(char* out, const android::String8& keyName) {
249 const uint8_t* in = reinterpret_cast(keyName.string());
250 size_t length = keyName.length();
251 for (int i = length; i > 0; –i, ++in, ++out) {
252 if (*in < ‘0’ || *in > ‘~’) {
253 *out = ‘+’ + (*in >> 6);
254 *++out = ‘0’ + (*in & 0x3F);
255 ++length;
256 } else {
利用及其危害:

理论上,攻击利用程序有机会在Keystore进程内部执行任意代码,并且获取一些应用的关键密钥,比如VPN。

但是想要达到这种目的,需要解决以下问题:

– 使用ROP绕过数据执行保护(DEP)

– 解决ASLR 动态地址随机化问题

– 绕过Stack Canaries栈返回前的安全检查

– 编码问题,输入的buffer数据会被encode_key编码,这意味着shellcode在最终执行前会有一部分字节被修改

当然,KeyStore程序在崩溃之后会重新启动,所以理论上给了攻击程序反复尝试以利用成功的可能性

结论:

本文简单介绍了CVE-2014-3100的原理,并强调该漏洞仅在4.3中存在且目前仅有理论上的利用可能性。同时呼吁国内某些媒体能够抱有一种严谨的态度来报道安全事件,不要用夸张的不实描述引起不必要的恐慌。

参考:

[1] Android KeyStore Stack Buffer Overflow: To Keep Things Simple, Buffers Are Always Larger Than Needed, Roee Hay & Avi Dayan

http://securityintelligence.com/android-keystore-stack-buffer-overflow-to-keep-things-simple-buffers-are-always-larger-than-needed/#.U7IqDz-Sxn9

[2]AOSP https://android.googlesource.com/platform/cts/+/android-4.4.4_r1/tests/tests/security/src/android/security/cts/KeystoreExploitTest.java