作者:申迪  

转载请注明出处: http://blogs.360.cn/360mobile

BlueBox于7月30日宣布安卓从2010年以来一直存在一个apk签名问题[1],并且会在今年Blackhat上公布细节。

利用该漏洞可以提升权限,突破沙箱限制。我在细节公开之前对这个漏洞进行成功利用,在此分享一些漏洞利用的细节。

一、关于APK签名

安卓APP在发布之前需要进行签名,签名信息被放在apk压缩包的/META-INF目录中。通常出于以下目的,系统会校验这个签名:

-          安装前验证apk中文件数据的完整性。

-          识别apk的身份。如果一个apk有系统签名,会有更高的权限;如果两个apk的签名一致,这两个应用可以共享数据。一些特定场景下一些应用会验证其他应用是否有某个特定签名,比如webkit会验证一个插件程序是否是由Adobe签出。

发布应用之前,使用一个自签名的证书对apk进行签名是可以满足需求的。使用”keytool  –printcert –file  CERT.RSA”命令可以查看证书的详细信息。下图是一个普通的自签名证书。可以看到所有人和签发人是同一个。

然而签名文件格式符合RFC2315[2]和RFC2459[3] 的规范,所以这个证书同样可以使由CA颁布的。如果使用一个CA颁布的证书发行APK程序,那么在META-INF目录中的CERT.RSA中可能会存在一个证书链,包含根证书和子证书。

在安卓上我们可以使用如下代码获取到应用内证书链中的所有证书:

sig=packageManager.getPackageInfo(pkgName,PackageManager.GET_PERMISSIONS| PackageManager.GET_SIGNATURES);
 
for(Signature sig : pkginfo.signatures)
 
      Log.d("TEST", sig.toCharsString()+"\n");

二、问题所在

这个漏洞的Google Bug ID 是13678484,从AOSP上的修复代码可以发现问题出现在签名校验的JarVerifier类中[4]。最关键的代码是在JarUtils中,添加了chainCheck的选项,可以对所有证书进行校验。

-    private static X509Certificate findCert(Principal issuer, X509Certificate[] candidates) {
+    private static X509Certificate findCert(Principal issuer, X509Certificate[] candidates,
+            X509Certificate subjectCert, boolean chainCheck) {
         for (int i = 0; i < candidates.length; i++) {
             if (issuer.equals(candidates[i].getSubjectDN())) {
+                if (chainCheck) {
+                    try {
+                        subjectCert.verify(candidates[i].getPublicKey());
+                    } catch (Exception e) {
+                        continue;
+                    }
+                }
                 return candidates[i];
             }
         }

如果没有这句代码会怎样?我们可以这么构造一个恶意证书:

  1. 开发机上生成一个根证书(记为CA),并用这个证书去颁发一个子证书(记为SIGN)
  2. 然后使用这个子证书为我们即将发布的apk签名,这时APK中的.Rsa文件将包含两个证书,一个是SIGN,一个是CA。并且APK所有的文件都是可以用这个RSA文件验证其合法性的
  3. 对这个RSA文件进行篡改,只修改CA证书的内容(替换后的CA记为FakeCA),不修改证书中的SignerInfo部分,不影响PackageManger在安装前使用SignerInfo.encryptedDigest对APK包数据完整性进行校验
  4. 这个APK可以被成功安装,并且包含两个证书,一个是SIGN,一个是FakeCA

如果你之前像我一样不了解PKCS7签名文件格式,那么在尝试恶意修改rsa文件时将会遇到一些麻烦,我推荐使用开源项目pyASN1去修改RSA文件。因为这种格式本质上是使用DER编码,使用ASN1做了序列化,使用pyASN1能让我们快速熟悉这种文件格式,并且快速着手进行签名篡改。

我将文件修改代码放到了我的github上[5],注意这不是一份自动篡改证书的工具,内部包含了一些路径和格式的硬编码,你如果想使用这份代码,需要理解代码的意思并做一些修改。

我使用自己写的工具将一个证书篡改成由Adobe颁发的:

三、如何利用

上文已经提到过,PackageManger在安装APK时并不校验证书链上所有证书的合法性,只要存在被指定的SIGN能够校验APK中所有文件的合法性即可。

但是签名证书的另外一个功能,验证身份,则受到了这个漏洞的影响。系统中多处使用getPackageInfo获取安装包证书,如果获取到多个证书,通常认为只要有一个证书可信即可。比如WebKit插件认证adobe flash player插件的逻辑如下[6]

225    private static boolean containsPluginPermissionAndSignatures(PackageInfo pkgInfo) {
226
227        // check if the plugin has the required permissions
228        String permissions[] = pkgInfo.requestedPermissions;
229        if (permissions == null) {
230            return false;
231        }
232        boolean permissionOk = false;
233        for (String permit : permissions) {
234            if (PLUGIN_PERMISSION.equals(permit)) {
235                permissionOk = true;
236                break;
237            }
238        }
239        if (!permissionOk) {
240            return false;
241        }
242
243        // check to ensure the plugin is properly signed
244        Signature signatures[] = pkgInfo.signatures;
245        if (signatures == null) {
246            return false;
247        }
248        if (SystemProperties.getBoolean("ro.secure"false)) {
249            boolean signatureMatch = false;
250            for (Signature signature : signatures) {
251                for (int i = 0; i < SIGNATURES.length; i++) {
252                    if (SIGNATURES[i].equals(signature)) {
253                        signatureMatch = true;
254                        break;
255                    }
256                }
257            }
258            if (!signatureMatch) {
259                return false;
260            }
261        }
262
263        return true;
264    }
265

从这段代码以及PluginManager.java的其他代码,可以看到WebKit是这样认证一个APK是否是Adobe FlashPlayer插件的:

-   APK证书中包含Adobe的签名证书,证书数据写死在代码中(PluginManager. SIGNATURE_1),这是adobe使用的签名

-   APK申请了android.webkit.permission.PLUGIN权限

-   APK声明了一个服务,Intent是android.webkit.PLUGIN,有个meta信息是type,type的值必须是native

在以上要求中唯一强制性限制就是证书验证,使用Fakeid,我们已经绕过了这个限制,其他校验自然不成问题。

在4.4以前,任何使用webview并且在访问了请求flash的页面(比如新浪首页)都会被我们的APK程序注入,下图是2345手机浏览器被注入的情况:

这里可以看到com.example.noperm和com.adobe.flashplayer都被认为是可信插件,成功进行了注入。

但是只是注入成功,我们的代码并没有得到执行机会。我们注入成功是因为webkit调用了PluginManager.getPluginClass将我们之前注册过接收android.webkit.PLUGIN 的服务load起来。但是我们的代码并不会被回调。希望注入代码被回调,还需要我们进一步理解webkit插件开发的规范。

291    /* package */
292    Class<?> getPluginClass(String packageName, String className)
293            throws NameNotFoundException, ClassNotFoundException {
294        Context pluginContext = mContext.createPackageContext(packageName,
295                Context.CONTEXT_INCLUDE_CODE |
296                Context.CONTEXT_IGNORE_SECURITY);
297        ClassLoader pluginCL = pluginContext.getClassLoader();
298        return pluginCL.loadClass(className);
299    }

四、理解Webkit Plugin,突破沙箱限制执行代码

上文中我们已经看到了webkit调用getPluginClass将我们的apk load到了虚拟机中,但是没有任何代码被触发。

实际上Webkit Plugin的核心是native程序,apk中的java代码仅仅是被JNI调用的。想要触发代码执行,还需要在我们的APK中放一个符合规范的SO文件供Webkit调用

AOSP中恰好有一份浏览器插件的代码[7],所以我们很容易了解插件编写规范。

首先我们需要导出4个接口。

extern "C" {
EXPORT NPError NP_Initialize(NPNetscapeFuncs* browserFuncs, NPPluginFuncs* pluginFuncs, void *java_env);
EXPORT NPError NP_GetValue(NPP instance, NPPVariable variable, void *value);
EXPORT const char* NP_GetMIMEDescription(void);
EXPORT void NP_Shutdown(void);
};

并且在回调函数中告诉浏览器我们是一个Flash插件,这样在Webkit遇到页面的Flash请求时,会加载所有插件libs目录下的so文件并询问他是何种插件.

我们要做的就是告诉浏览器:我是一个flash处理插件。

const char *NP_GetMIMEDescription(void)
{
    return"application/x-shockwave-flash:swf:ShockwaveFlash;application/futuresplash:spl:Futu\
reSplash Player";
}

这样一来我们的SO也被加载了,代码已经突破沙箱执行了:

我将部分关键代码和编译好的poc放到了github上:

https://github.com/retme7/FakeID_poc_by_retme_bug_13678484/

相关链接:

[1] http://bluebox.com/blog/technical/android-fake-id-vulnerability/

[2] https://www.ietf.org/rfc/rfc2315.txt

[3] https://www.ietf.org/rfc/rfc2459

[4] https://android.googlesource.com/platform/libcore/+/android-cts-4.1_r4%5E%21/

[5]https://github.com/retme7/FakeID_poc_by_retme_bug_13678484/

[6]AOSP/frameworks/base/core/java/android/webkit/PluginManager.java

[7]AOSP/frameworks/base/tests/BrowserTestPlugin/

FakeID签名漏洞分析及利用(一)的更多相关文章

  1. FakeID签名漏洞分析及利用(二)

    本文转自:http://blog.csdn.net/l173864930/article/details/38409521 继上一次Masterkey漏洞之后,Bluebox在2014年7月30日又公 ...

  2. Vivotek 摄像头远程栈溢出漏洞分析及利用

    Vivotek 摄像头远程栈溢出漏洞分析及利用 近日,Vivotek 旗下多款摄像头被曝出远程未授权栈溢出漏洞,攻击者发送特定数据可导致摄像头进程崩溃. 漏洞作者@bashis 放出了可造成摄像头 C ...

  3. CVE-2016-10190 FFmpeg Http协议 heap buffer overflow漏洞分析及利用

    作者:栈长@蚂蚁金服巴斯光年安全实验室 -------- 1. 背景 FFmpeg是一个著名的处理音视频的开源项目,非常多的播放器.转码器以及视频网站都用到了FFmpeg作为内核或者是处理流媒体的工具 ...

  4. CVE-2016-10191 FFmpeg RTMP Heap Buffer Overflow 漏洞分析及利用

    作者:栈长@蚂蚁金服巴斯光年安全实验室 一.前言 FFmpeg是一个著名的处理音视频的开源项目,使用者众多.2016年末paulcher发现FFmpeg三个堆溢出漏洞分别为CVE-2016-10190 ...

  5. SEIG Modbus 3.4 CVE-2013-0662 漏洞分析与利用

    前言 Schneider Electric Modbus Serial Driver 会监听 27700 端口,程序在处理客户端发送的数据时会导致栈溢出. 测试环境: windows xp sp3 相 ...

  6. word漏洞分析与利用

    众所周知,溢出漏洞从应用形式上可分为远程服务溢出漏洞和客户端(本地)溢出漏洞两类.远程服务溢出漏洞大家很熟悉了,红色代码.冲击波.振荡波等蠕虫都利用了此类漏洞,漏洞的调试和利用有相应的一套方法,前面的 ...

  7. 【转+自己研究】新姿势之Docker Remote API未授权访问漏洞分析和利用

    0x00 概述 最近提交了一些关于 docker remote api 未授权访问导致代码泄露.获取服务器root权限的漏洞,造成的影响都比较严重,比如 新姿势之获取果壳全站代码和多台机器root权限 ...

  8. 一步一步pwn路由器之wr940栈溢出漏洞分析与利用

    前言 本文由 本人 首发于 先知安全技术社区: https://xianzhi.aliyun.com/forum/user/5274 这个是最近爆出来的漏洞,漏洞编号:CVE-2017-13772 固 ...

  9. SSRF漏洞分析与利用

    转自:http://www.4o4notfound.org/index.php/archives/33/ 前言:总结了一些常见的姿势,以PHP为例,先上一张脑图,划√的是本文接下来实际操作的 0x01 ...

随机推荐

  1. Spring 笔记 -06- 从 MySQL 建库到 登录验证数据库信息(maven)

    Spring 笔记 -06- 从 MySQL 建库到 登录验证数据库信息(maven) 本篇和 Spring 没有什么关系,只是学习 Spring,必备一些知识,所以放在这里了. 本篇内容: (1)M ...

  2. 犀牛Rhino教程合集37部

    犀牛Rhino教程合集37部 教程说明:英文视频教程,部分有中文字幕,大部分有工程文件 教程格式:Flv.MP4格式,大部分高清,确保能看清软件上的文字 发货方式:百度网盘下载链接(教程较多,可转存到 ...

  3. go语言练习:指针

    指针是一个变量,存储的是另一个变量的地址 package main import "fmt" func main() { var a string = "hello&qu ...

  4. C#中使用反射遍历一个对象属性和值以及百分数

    对某个类的实例化对象, 遍历获取所有属性(子成员)的方法(采用反射): using (var context = new YZS_TRAEntities()) { ).FirstOrDefault() ...

  5. jmeter之数据库相关

    一.JDBC Connection Configuration 1.Variable Name Bound to Pool-Variable Name:连接池名称, JDBC Request通过此名称 ...

  6. axios的配置项

    最近在学习vue,涉及到axios的ajax操作,记录一下相关Config,方便日后查阅 { // `url`是将用于请求的服务器URL url: '/user', // `method`是发出请求时 ...

  7. JBoss 7 更改response header中的Server参数

    jboss服务器缺省情况下会在HTTP response header中显示自身的标识,如下 Server: Apache-Coyote/1.1 出于安全考虑,如果不想让人知道服务器类型,可以用以下方 ...

  8. 【gp数据库】你一定会用到的运维语句宝典

    -- 查询未空闲的进程信息 select * from pg_stat_activity where current_query<>'<IDLE>'; 结果可查看数据库名,进程 ...

  9. mysql 表格操作指令大全(Show、desc、create、insert into、delete、select、drop、update、alter)

    使用数据库 use 数据库名 eg: use weiying 2. 查看库里所有的表 Show tables 3.查看所表的结构  desc 表名 desc score 4.建表create tabl ...

  10. 实现一个协程版mysql连接池

    实现一个协程版的mysql连接池,该连接池支持自动创建最小连接数,自动检测mysql健康:基于swoole的chanel. 最近事情忙,心态也有点不积极.技术倒是没有落下,只是越来越不想写博客了.想到 ...