参考资料:https://github.com/wnagzihxa1n/APP-SECURITY-404/blob/master/2.%E7%BB%84%E4%BB%B6%E5%AF%BC%E5%87%BA%E6%BC%8F%E6%B4%9E-Intent%E6%8B%92%E7%BB%9D%E6%9C%8D%E5%8A%A1/%E7%BB%84%E4%BB%B6%E5%AF%BC%E5%87%BA%E6%BC%8F%E6%B4%9E-Intent%E6%8B%92%E7%BB%9D%E6%9C%8D%E5%8A%A1.md

工具:https://mp.weixin.qq.com/s?__biz=MzU4NTgzMzQ4NQ==&mid=2247484426&idx=1&sn=6a7069b57b4457eb4b9b1fc44a1f49b0&chksm=fd85c968caf2407ee95e381b62c9a6924ddf3b8cc402df96c823987a160d870c9e072258c5ac&mpshare=1&scene=23&srcid=08140JksuKvKZskXsOqZ2JbN&sharer_sharetime=1597401346780&sharer_shareid=90f1ee6dedb9b967505567982ba5e26c%23rd

一.概述

组件就不多介绍了,安卓的四大组件:activity,service,broadcastReciver,ContentProvider

导出: 其他的应用或组件通过发送intent对象的方式调用其他组件。

intent是一种消息传递对象,intent的基本知识放个博客链接:

https://blog.csdn.net/salary/article/details/82865454

这个漏洞的主要原理还是在于对intent对象的处理没有添加异常事件所导致。

二.漏洞复现

二.1 简单类型

先写了两个activity,其中一个activity通过发送intent对象方式来启动另一个activity,并把数据存在了intent对象中,一起发送过去。

第一个activity

package com.example.twoapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent =new Intent(MainActivity.this,Activity1.class); //本质都是构造了compantName对象(封装了被调用组件的信息)
//传输数据给Activity
intent.putExtra("str1","i am str1");
startActivity(intent);
}
}

第二个activity

package com.example.twoapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast; public class Activity1 extends AppCompatActivity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_1);
Intent intent=getIntent();
String str1=intent.getStringExtra("str1");
Toast.makeText(this,"str1="+str1,Toast.LENGTH_SHORT).show();
}
}

这里其实很简单,就是主活动通过发送包含数据的intent方式调用另一个活动组件,另一个活动通过getStringExtr方法来将数据取出来,再生成一个消息提示框,将取出来的字符串拼接好,放入对话框中

那么假设,我把第一个存数据的代码注释掉,会发生什么呢

package com.example.twoapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent =new Intent(MainActivity.this,Activity1.class); //本质都是构造了compantName对象(封装了被调用组件的信息)
//传输数据给Activity
//intent.putExtra("str1","i am str1");
startActivity(intent);
}
}

第二个活动的代码不变,这时候运行一下我们的app。

发现虽然没有这key-value存在,但是似乎自动生成了个key-value,不过value默认是null。这里没啥问题,但如果我的

第二个活动代码是这样写的话

package com.example.twoapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast; public class Activity1 extends AppCompatActivity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_1);
Intent intent=getIntent();
String str1=intent.getStringExtra("str1");
if(str1.equals("i am str1"))
{
Toast.makeText(this,"str1="+str1,Toast.LENGTH_SHORT).show();
} }
}

这里有点意思是equals方法,这个方法是继承父类object类的,也就意味着只有对象才能引用这个对象

然后这里有可能出现str1为null的情况,String str1其实是引用,赋值给null是没毛病的,只是这里出问题

应该是程序员的忽视了,这里的修改意见应该是先判断是否为null,如果是null就没必要比较了,因为不可能

相等的,null和对象相等不存在好吧,如果不是null,那值得一比,不过第一个比较是用==,因为不是比字符串

的值了,没必要用equals,也不可能用。

这里运行一下,肯定会出现空指针异常,这里可以查下崩溃日志

点击android studio左下角的按钮就可以查看崩溃日志

这里报的是空指针异常,达到目的了,一是没有预防,二是没有去处理这个异常,添加try/catch是个不错的方式

二.2 复杂类型的组件导出

之前那种简单的组件导出基本上不会出现了,不过还有一种更复杂一些的,前面算是程序员粗心导致的,后面这种是算是逻辑错误

这里聚集在获取intent中数据的方法getStringExtra这里,这个地方要去看sdk的源码,因为我是mac,command+鼠标单击就会自动

跳转到这个对应方法的源码,不过一开始我sdk版本设置的过高,30的安卓10的版本直接裂开,根本没有源码,我就去改了build.gradle里面的

sdk版本号,然后同步了一下,没想到成功了,改成了29之后,本来下sdk时,就直接把源码也下载下来,这下就可以看源码了。

这里先看getStringExtra的源码

public @Nullable String getStringExtra(String name) {
return mExtras == null ? null : mExtras.getString(name);
}
private Bundle mExtras;
 

这个mExtras的类型是Bundle,这里的再跟着mExtras.getString的方法看看,发现是在BaseBundle类中找到的这个方法,应该是它父类的方法

   @Nullable
public String getString(@Nullable String key) {
unparcel();
final Object o = mMap.get(key);
try {
return (String) o;
} catch (ClassCastException e) {
typeWarning(key, o, "String", e);
return null;
}
}

unparcel是反序列化方法,跟进去一波。

 @UnsupportedAppUsage
/* package */ void unparcel() {
synchronized (this) {
final Parcel source = mParcelledData;
if (source != null) {
initializeFromParcelLocked(source, /*recycleParcel=*/ true, mParcelledByNative);
} else {
if (DEBUG) {
Log.d(TAG, "unparcel "
+ Integer.toHexString(System.identityHashCode(this))
+ ": no parcelled data");
}
}
}
} private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
boolean parcelledByNative) {
if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
Slog.wtf(TAG, "Attempting to unparcel a Bundle while in transit; this may "
+ "clobber all data inside!", new Throwable());
} if (isEmptyParcel(parcelledData)) {
if (DEBUG) {
Log.d(TAG, "unparcel "
+ Integer.toHexString(System.identityHashCode(this)) + ": empty");
}
if (mMap == null) {
mMap = new ArrayMap<>(1);
} else {
mMap.erase();
}
mParcelledData = null;
mParcelledByNative = false;
return;
} final int count = parcelledData.readInt();
if (DEBUG) {
Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ ": reading " + count + " maps");
}
if (count < 0) {
return;
}
ArrayMap<String, Object> map = mMap;
if (map == null) {
map = new ArrayMap<>(count);
} else {
map.erase();
map.ensureCapacity(count);
}
try {
if (parcelledByNative) {
// If it was parcelled by native code, then the array map keys aren't sorted
// by their hash codes, so use the safe (slow) one.
parcelledData.readArrayMapSafelyInternal(map, count, mClassLoader);
} else {
// If parcelled by Java, we know the contents are sorted properly,
// so we can use ArrayMap.append().
parcelledData.readArrayMapInternal(map, count, mClassLoader);
}
} catch (BadParcelableException e) {
if (sShouldDefuse) {
Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
map.erase();
} else {
throw e;
}
} finally {
mMap = map;
if (recycleParcel) {
recycleParcel(parcelledData);
}
mParcelledData = null;
mParcelledByNative = false;
}
if (DEBUG) {
Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ " final map: " + mMap);
}
}

这里不知道为啥我跟不进这个函数,裂开,parcel这个类,是一个共享内存,将序列化数据存进去,同时也可以通过parcel对象

将对象反序列化取出来,这里大概知道intent发送数据,是先将key -value的值序列化存进parcel,然后其他组件再通过parcel

对象通过类加载器和类名,解析反序列化出相对应的对象出来,存进map中。

---以下源码源自王师傅

/* package */ void readArrayMapInternal(ArrayMap outVal, int N,
ClassLoader loader) {
if (DEBUG_ARRAY_MAP) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Log.d(TAG, "Reading " + N + " ArrayMap entries", here);
}
int startPos;
while (N > 0) {
if (DEBUG_ARRAY_MAP) startPos = dataPosition();
String key = readString();
Object value = readValue(loader);
if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read #" + (N-1) + " "
+ (dataPosition()-startPos) + " bytes: key=0x"
+ Integer.toHexString((key != null ? key.hashCode() : 0)) + " " + key);
outVal.append(key, value);
N--;
}
outVal.validate();
}

再跟readValue方法

public final Object readValue(ClassLoader loader) {
int type = readInt(); switch (type) {
case VAL_NULL:
return null; case VAL_STRING:
return readString(); case VAL_INTEGER:
return readInt(); ...... case VAL_SERIALIZABLE:
return readSerializable(loader); ...... default:
int off = dataPosition() - 4;
throw new RuntimeException(
"Parcel " + this + ": Unmarshalling unknown type code " + type + " at offset " + off);
}
}

这个readSerializable方法有点意思,其实就是反序列化把对象取出来

再跟进去看看

private final Serializable readSerializable(final ClassLoader loader) {
String name = readString();
if (name == null) {
// For some reason we were unable to read the name of the Serializable (either there
// is nothing left in the Parcel to read, or the next value wasn't a String), so
// return null, which indicates that the name wasn't found in the parcel.
return null;
} byte[] serializedData = createByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(serializedData);
try {
ObjectInputStream ois = new ObjectInputStream(bais) {
@Override
protected Class<?> resolveClass(ObjectStreamClass osClass)
throws IOException, ClassNotFoundException {
// try the custom classloader if provided
if (loader != null) {
Class<?> c = Class.forName(osClass.getName(), false, loader);
if (c != null) {
return c;
}
}
return super.resolveClass(osClass);
}
};
return (Serializable) ois.readObject();
} catch (IOException ioe) {
throw new RuntimeException("Parcelable encountered " +
"IOException reading a Serializable object (name = " + name +
")", ioe);
} catch (ClassNotFoundException cnfe) {
throw new RuntimeException("Parcelable encountered " +
"ClassNotFoundException reading a Serializable object (name = "
+ name + ")", cnfe);
}
}

发现就是查找反序化类,然后反序列化对象出来,如果没有找到反序列化的类,就会抛出异常,这里就是突破口

假设传入的序列化的类找不到,而且并没有用try/catch来处理异常,那么就会崩溃。

poc 贴下王师傅的

public class MainActivity extends Activity {
private static final String TAG = "MainActivity"; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
Intent intent = new Intent();
intent.setComponent(new ComponentName(target_package_name, target_component_name));
intent.putExtra("serializable_key", new DataSchema());
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
} class DataSchema implements Serializable {
private static final long serialVersionUID = -1L;
}

APP-SECURITY-404 组件导出漏洞复现的更多相关文章

  1. Weblogic WLS-WebServices组件反序列化漏洞复现

    漏洞分析: 当weblogic使用WLS-WebServices组件时,该组件会调用XMLDecoder解析XML数据,由此就产生了该漏洞 影响版本: weblogic<10.3.6版本 复现过 ...

  2. uniapp解决测评有组件导出风险,解决APP反编译,回编译后app无法打开的问题

    1.APP反编译 使用hbx云打包,打包出apk 拿到apk后,先下载反编译工具 https://pan.baidu.com/s/1A5D8x_pdSELlHYl-Wl6Xnw 提取码 6vzd 使用 ...

  3. struts2漏洞复现分析合集

    struts2漏洞复现合集 环境准备 tomcat安装 漏洞代码取自vulhub,使用idea进行远程调试 struts2远程调试 catalina.bat jpda start 开启debug模式, ...

  4. 【S2-052】漏洞复现(CVE-2017-9805)

    一.漏洞描述 Struts2 的REST插件,如果带有XStream组件,那么在进行反序列化XML请求时,存在未对数据内容进行有效验证的安全隐患,可能发生远程命令执行. 二.受影响版本 Struts2 ...

  5. struts2(s2-052)远程命令执行漏洞复现

    漏洞描述: 2017年9月5日,Apache Struts发布最新安全公告,Apache Struts2的REST插件存在远程代码执行的高危漏洞,该漏洞由lgtm.com的安全研究员汇报,漏洞编号为C ...

  6. Weblogic 'wls-wsat' XMLDecoder 反序列化_CVE-2017-10271漏洞复现

    Weblogic 'wls-wsat' XMLDecoder 反序列化_CVE-2017-10271漏洞复现 一.漏洞概述  WebLogic的 WLS Security组件对外提供webservic ...

  7. nginx解析漏洞复现

    nginx解析漏洞复现 一.漏洞描述 该漏洞与nginx.php版本无关,属于用户配置不当造成的解析漏洞 二.漏洞原理 1. 由于nginx.conf的如下配置导致nginx把以’.php’结尾的文件 ...

  8. Weblogic-SSRF 漏洞复现

    0x01 环境搭建 我这里使用的是vulhub,它几乎包含了所有的漏洞环境.(建议安装在ubuntu上) 有需要的小伙伴来企鹅群自取. 安装好vulhub之后需要cd 到weblogic ssrf 目 ...

  9. 【漏洞复现】S2-052 (CVE-2017-9805)

    一.漏洞描述 Struts2 的REST插件,如果带有XStream组件,那么在进行反序列化XML请求时,存在未对数据内容进行有效验证的安全隐患,可能发生远程命令执行. 二.受影响版本 Struts2 ...

随机推荐

  1. 7.2-5 usermod

    7.2 usermod:修改用户信息 usermod 命令用于修改系统已经存在的用户的账号信息.   -c comment         修改用户password文件中用户的说明栏,同useradd ...

  2. 【遥感数字图像处理实验】Erdas版详细图文实验教程(8实验全)

    @ 目录 1. 专栏简介 2. 专栏地址 3. 专栏目录 1. 专栏简介 遥感数字图像的处理,是对遥感数字图像的计算机处理.与工业和医学数字图像不同,遥感数字图像类型更为多样,内容更为复杂.因此,遥感 ...

  3. 『动善时』JMeter基础 — 27、通过JMeter函数助手实现参数化

    目录 1.测试计划中的元件 2.数据文件内容 3.函数助手配置 (1)函数助手的打开方式 (2)函数助手界面介绍 (3)编辑后的函数助手界面 4.HTTP请求组件内容 5.线程组元件内容 6.脚本运行 ...

  4. 3d分层悬停效果

    3d分层悬停效果 写在前面 经过了2个星期的努力,我回来了!会继续将我学习路上遇到的问题,以及一些笔记,demo分享给大家 实现效果 致我最爱的backpink 实现思路 将6张图片,通过定位叠在一起 ...

  5. ubuntu设置允许root用户登录

    一.允许ssh登录root用户(命令行) 1.修改root 密码,sudo passwd root 2.修改ssh配置, sudo vim /etc/ssh/shd_config,修改文件中的Perm ...

  6. Go语言web开发---Beego基础

    一.框架 框架:可复用的设计组件,它规定了应用的体系结构,明确了整个设计,协作各个组件之间的依赖关系,责任分配,和流程控制.通俗解释框架就是一堆代码的集合,为了提高软件的开发效率和质量,一般都会使用框 ...

  7. 立体显示与BCN双稳态手性向列相

    立体显示与BCN双稳态手性向列相 狭缝光栅立体显示 技术介绍: 人的左右眼间距大约是65MM,左右眼透过视差光栅看到不同的视角图像,经大脑融合形成立体视觉. 技术优点: 2D/3D可切换: 低成本: ...

  8. csp-s模拟测试52-53

    留坑.... 改完题再说吧. 留坑....最近考得什么鬼??模拟53T1 u(差分) 一道差分题????然而考场没有想到如何维护斜率上的差分,事后经miemeng和cyf的生(xuan)动(xue)讲 ...

  9. linux文件系统和日志分析

    一.Linux文件系统 1.inode与block 1.概述: (1)文件数据包括元信息与实际信息 (2)文件存储在硬盘上,硬盘最小存储单位是"扇区",每个扇区存储512字节 (3 ...

  10. MySQL explain type 连接类型

    查看使用的数据库版本 select version(); 5.7.30 官方提供的示例数据sakila 下载地址: https://dev.mysql.com/doc/index-other.html ...