安卓pwn - De1taCTF(BroadcastTest)

本文详细剖析了安卓BroadcastTest的漏洞利用过程。通过分析Parcelable接口的序列化和反序列化,发现类型不一致可能导致的安全问题。在题目中,通过构造特定的payload,影响Bundle的序列化顺序,从而在Receiver中插入新的键值对"command=getflag",达到触发目标条件的目的。然而,由于ArrayMap的序列化特性,payload的构造需精确计算,避免因字符串补0导致的序列化顺序变化,最终通过爆破找到合适的关键键值对。
摘要由CSDN通过智能技术生成

BroadcastTest

背景

逆向APK可知程序中仅有MainActivity$Message和三个Receiver类。
前者实现了一个Parcelable类,后三个则是广播。
其中Receiver1是exported的,接收并向Receiver2发送广播,Receiver2和3则非exported,只能接收内部发送的广播。
功能为Receiver1接收base64传入的data,然后将其反序列化得到一个Bundle,再广播给Receiver2。
Receiver2检查Bundle中“command”存在且值非"getflag",然后再次发送广播给Receiver3。
Receiver3检查Bundle中"command"存在且值为"getflag",通过则回显正确。

简单搜索可以找到这篇文章,描述了Parcel中对于读出和写入时类型不一致会产生的漏洞。

原理

Android提供了独有的Parcelable接口来实现序列化的方法,只要实现这个接口,一个类的对象就可以实现序列化并可以通过Intent或Binder传输。
其中,关键的writeToParcel和readFromParcel方法,分别调用Parcel类中的一系列write方法和read方法实现序列化和反序列化。
可序列化的Parcelable对象一般不单独进行序列化传输,需要通过Bundle对象携带。 Bundle的内部实现实际是Hashmap,以Key-Value键值对的形式存储数据。例如, Android中进程间通信频繁使用的Intent对象中可携带一个Bundle对象,利用putExtra(key, value)方法,可以往Intent的Bundle对象中添加键值对(Key Value)。Key为String类型,而Value则可以为各种数据类型,包括int、Boolean、String和Parcelable对象等等,Parcel类中维护着这些类型信息。

// Keep in sync with frameworks/native/include/private/binder/ParcelValTypes.h.
    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_FLOAT = 7;

对Bundle进行序列化时,依次写入携带所有数据的长度、Bundle魔数(0x4C444E42)和键值对。见BaseBundle.writeToParcelInner方法

int lengthPos = parcel.dataPosition();
parcel.writeInt(-1); // dummy, will hold length
parcel.writeInt(BUNDLE_MAGIC);
int startPos = parcel.dataPosition();
parcel.writeArrayMapInternal(map);
int endPos = parcel.dataPosition();
// Backpatch length
parcel.setDataPosition(lengthPos);
int length = endPos - startPos;
parcel.writeInt(length);
parcel.setDataPosition(endPos);

pacel.writeArrayMapInternal方法写入键值对,先写入Hashmap的个数,然后依次写入键和值

/**
   * Flatten an ArrayMap into the parcel at the current dataPosition(),
   * growing dataCapacity() if needed.  The Map keys must be String objects.
   */
  /* package */ void writeArrayMapInternal(ArrayMap<String, Object> val) {
   
...
      final int N = val.size();
      writeInt(N);
     ... 
      int startPos;
      for
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值