首页
社区
课程
招聘
[原创]CVE-2017-13286漏洞分析及利用
发表于: 2021-7-18 20:34 20689

[原创]CVE-2017-13286漏洞分析及利用

2021-7-18 20:34
20689

这个漏洞与其他同期在Android公告上披露的漏洞,都是源与框架中Parcelable对象的写入(序列化)与读出(反序列化)的不一致所造成的。例如将其中一个成员变量以long类型写入,而以int类型读入时,这种不同步就能被利用进行攻击。

当所有数据都被序列化装载进Bundle后,接下来则需要依次在Bundle头部写入携带所有数据的长度、Bundle魔数(0x4C444E42)和键值对的数量。下面是完整的Bundle简单结构图:

反序列化过程则完全是一个对称的逆过程,依次读入Bundle携带所有数据的长度、Bundle魔数(0x4C444E42)、键值对。读键值对的时候,调用对象的readFromParcel方法,从Bundle读取相应长度的数据,重新构建这个对象。

这个流程是AppA在请求添加一个帐号:
AppA请求添加一个帐号
system_server接受到请求,找到可以提供帐号服务的AppB,并发起请求
AppB返回了一个Bundle给系统,系统把Bundle转发给AppA
这个Bundle对象中包含的一个键值对{KEY_INTENT:intent},最终会传递到AppA,由后者调用startActivity(intent)调起一个Activity

上次过程涉及到两次序列及两次反序列化的数据传输。
第一次序列化:普通AppB将Bundle序列化后通过Binder传递给system_server。
第一次反序列化:system_server通过Bundle的一系列readXXX(如readInt、readParcelable)函数对AppB传输的Bundle进行反序列化,获得键KEY_INTENT以及这个键对应的值——一个intent对象,进行安全检查。
第二次序列化:若检查通过,对上面检查的对象都进行第二次序列化,
第二次反序列化:最后Settings中反序列化后重新获得{KEY_INTENT:intent},调用startActivity。
如果对象的写入(序列化)与读出(反序列化)不一致,那么就可以构造合适的数据,数据中包含了恶意的Intent,在第二次序列化和反序列化过程中绕过system_server的检测,却能够在最后被Settings成功读取出来并调用startActivity。下面将会介绍具体的绕过过程。

可以看到,writeToParcel中写入了一个32位的mIsShared,但是在createFromParcel中少读了一个32位的mIsShared。导致在读写内存时错位。

因此,我们可以在序列化的Bundle构造如下数据:

在Autherticator App中构造恶意Bundle。在system_server发生的第一次反序列化时,识别到第一个对象为OutputConfiguration,调用它的readToParcel将数据读入。由于它的readToParcel不会读入mIsShared,因此把我们构造的'0'作为ArrayList的长度读入了,确认到ArrayList的长度为0,也就结束了第一个键值对的读入。读取第二个键值对时,识别到值的类型为ByteArray(13),因此会把我们的恶意Intent作为ByteArray的数据读入,而不会进行检查。

然后,第二次序列化时system_server通过writeint将mIsShared写入Bundle,多出了一个32位的‘0’,后续内容不变。

最后,Settings反序列化读入Bundle,由于仍然不读入mIsShared,因此刚刚写入的mIsShared会被读到ArrayList的长度中,识别到ArrayList的长度为0,就认为读OutputConfiguration完毕。接下来开始读第二个键值对,把0连同紧接着的1,认为是第二个键值对的键为null,然后6作为值的类型被读入,认为是long,于是后面把13和接下来ByteArray length的8字节作为第二个键值对的值,就结束了对long的读取。最终,恶意KEY_INTENT显现出来作为第三个键值对!

在AndroidManifest文件中设置

实现AuthenticatorService

实现MyAuthenticator,addAccount方法中构建恶意Bundle

public class User implements Parcelable {
    private String name;
    private int age;
     
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
     
    private User(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }
 
    @Override
    public int describeContents() {
        return 0;
    }
 
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
 
    }
     
    public void readFromParcel(Parcel reply) {
        name = reply.readString();
        age = reply.readInt();
    }
 
    public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {
        @Override
        public User createFromParcel(Parcel source) {
            return new User(source);
        }
 
        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };
}
public class User implements Parcelable {
    private String name;
    private int age;
     
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
     
    private User(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }
 
    @Override
    public int describeContents() {
        return 0;
    }
 
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
 
    }
     
    public void readFromParcel(Parcel reply) {
        name = reply.readString();
        age = reply.readInt();
    }
 
    public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {
        @Override
        public User createFromParcel(Parcel source) {
            return new User(source);
        }
 
        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };
}
private static final int VAL_NULL = -1;
private static final int VAL_STRING = 0;
private static final int VAL_INTEGER = 1;
private static final int VAL_MAP = 2;
private static final int VAL_BUNDLE = 3;
private static final int VAL_PARCELABLE = 4;
private static final int VAL_SHORT = 5;
private static final int VAL_LONG = 6;
private static final int VAL_NULL = -1;
private static final int VAL_STRING = 0;
private static final int VAL_INTEGER = 1;
private static final int VAL_MAP = 2;
private static final int VAL_BUNDLE = 3;
private static final int VAL_PARCELABLE = 4;
private static final int VAL_SHORT = 5;
private static final int VAL_LONG = 6;
public void writeToParcel(Parcel dest, int flags) {
        if (dest == null) {
            throw new IllegalArgumentException("dest must not be null");
        }
        dest.writeInt(mRotation);
        dest.writeInt(mSurfaceGroupId);
        dest.writeInt(mSurfaceType);
        dest.writeInt(mConfiguredSize.getWidth());
        dest.writeInt(mConfiguredSize.getHeight());
        dest.writeInt(mIsDeferredConfig ? 1 : 0);
        dest.writeInt(mIsShared ? 1 : 0);
        dest.writeTypedList(mSurfaces);
}
 
private OutputConfiguration(@NonNull Parcel source) {
        int rotation = source.readInt();
        int surfaceSetId = source.readInt();
        int surfaceType = source.readInt();
        int width = source.readInt();
        int height = source.readInt();
        boolean isDeferred = source.readInt() == 1;
        // missing write mIsShared
        ArrayList<Surface> surfaces = new ArrayList<Surface>();
        source.readTypedList(surfaces, Surface.CREATOR);
 
        checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
        ...
        ...
        ...
}
public void writeToParcel(Parcel dest, int flags) {
        if (dest == null) {
            throw new IllegalArgumentException("dest must not be null");
        }
        dest.writeInt(mRotation);
        dest.writeInt(mSurfaceGroupId);
        dest.writeInt(mSurfaceType);
        dest.writeInt(mConfiguredSize.getWidth());
        dest.writeInt(mConfiguredSize.getHeight());
        dest.writeInt(mIsDeferredConfig ? 1 : 0);
        dest.writeInt(mIsShared ? 1 : 0);
        dest.writeTypedList(mSurfaces);
}
 
private OutputConfiguration(@NonNull Parcel source) {
        int rotation = source.readInt();
        int surfaceSetId = source.readInt();
        int surfaceType = source.readInt();
        int width = source.readInt();
        int height = source.readInt();
        boolean isDeferred = source.readInt() == 1;
        // missing write mIsShared
        ArrayList<Surface> surfaces = new ArrayList<Surface>();
        source.readTypedList(surfaces, Surface.CREATOR);
 
        checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
        ...
        ...
        ...
}
<service
    android:name=".AuthenticatorService"
    android:exported="true"
    android:enabled="true">
    <intent-filter>
        <action android:name="android.accounts.AccountAuthenticator" />
    </intent-filter>
    <meta-data android:name="android.accounts.AccountAuthenticator"
                android:resource="@xml/authenticator" />
</service>
<service
    android:name=".AuthenticatorService"
    android:exported="true"
    android:enabled="true">
    <intent-filter>
        <action android:name="android.accounts.AccountAuthenticator" />
    </intent-filter>
    <meta-data android:name="android.accounts.AccountAuthenticator"
                android:resource="@xml/authenticator" />
</service>
public class AuthenticatorService extends Service {
    public AuthenticatorService() {
    }
 
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        MyAuthenticator authenticator = new MyAuthenticator(this);
        return authenticator.getIBinder();
    }
}
public class AuthenticatorService extends Service {
    public AuthenticatorService() {
    }
 
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2023-11-29 10:56 被kanxue编辑 ,原因:
上传的附件:
收藏
免费 4
支持
分享
最新回复 (2)
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
你好,能交流一下这个漏洞吗?新手,想学习一下
2021-12-6 19:01
0
雪    币: 530
活跃值: (906)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
mb_voqzrhfh 你好,能交流一下这个漏洞吗?新手,想学习一下
抱歉很少上看雪,如果还需要的话可以联系我邮箱cs2015cjh@163.com
2022-5-17 09:44
0
游客
登录 | 注册 方可回帖
返回
//