APP-SECURITY-404 组件导出漏洞复现
一.概述
组件就不多介绍了,安卓的四大组件: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 组件导出漏洞复现的更多相关文章
- Weblogic WLS-WebServices组件反序列化漏洞复现
漏洞分析: 当weblogic使用WLS-WebServices组件时,该组件会调用XMLDecoder解析XML数据,由此就产生了该漏洞 影响版本: weblogic<10.3.6版本 复现过 ...
- uniapp解决测评有组件导出风险,解决APP反编译,回编译后app无法打开的问题
1.APP反编译 使用hbx云打包,打包出apk 拿到apk后,先下载反编译工具 https://pan.baidu.com/s/1A5D8x_pdSELlHYl-Wl6Xnw 提取码 6vzd 使用 ...
- struts2漏洞复现分析合集
struts2漏洞复现合集 环境准备 tomcat安装 漏洞代码取自vulhub,使用idea进行远程调试 struts2远程调试 catalina.bat jpda start 开启debug模式, ...
- 【S2-052】漏洞复现(CVE-2017-9805)
一.漏洞描述 Struts2 的REST插件,如果带有XStream组件,那么在进行反序列化XML请求时,存在未对数据内容进行有效验证的安全隐患,可能发生远程命令执行. 二.受影响版本 Struts2 ...
- struts2(s2-052)远程命令执行漏洞复现
漏洞描述: 2017年9月5日,Apache Struts发布最新安全公告,Apache Struts2的REST插件存在远程代码执行的高危漏洞,该漏洞由lgtm.com的安全研究员汇报,漏洞编号为C ...
- Weblogic 'wls-wsat' XMLDecoder 反序列化_CVE-2017-10271漏洞复现
Weblogic 'wls-wsat' XMLDecoder 反序列化_CVE-2017-10271漏洞复现 一.漏洞概述 WebLogic的 WLS Security组件对外提供webservic ...
- nginx解析漏洞复现
nginx解析漏洞复现 一.漏洞描述 该漏洞与nginx.php版本无关,属于用户配置不当造成的解析漏洞 二.漏洞原理 1. 由于nginx.conf的如下配置导致nginx把以’.php’结尾的文件 ...
- Weblogic-SSRF 漏洞复现
0x01 环境搭建 我这里使用的是vulhub,它几乎包含了所有的漏洞环境.(建议安装在ubuntu上) 有需要的小伙伴来企鹅群自取. 安装好vulhub之后需要cd 到weblogic ssrf 目 ...
- 【漏洞复现】S2-052 (CVE-2017-9805)
一.漏洞描述 Struts2 的REST插件,如果带有XStream组件,那么在进行反序列化XML请求时,存在未对数据内容进行有效验证的安全隐患,可能发生远程命令执行. 二.受影响版本 Struts2 ...
随机推荐
- USB中TOKEN的CRC5与CRC16校验(神奇的工具生成Verilog实现)
USB2.0IP设计 最近,在学习USB2.0IP的设计,其中包含了CRC校验码的内容,之前学习千兆以太网曾经用到过CRC32校验(https://www.cnblogs.com/Xwangzi66/ ...
- ffmpeg安装之mac安装
转发自白狼栈:查看原文 关于ffmpeg的安装,有的人可能要折腾很久,甚至折腾一个礼拜,究其原因,基本都是编译安装惹的祸. 我们提供4种安装方式,最复杂的莫过于centos7上的编译安装. ffmpe ...
- 『动善时』JMeter基础 — 31、JMeter中BeanShell断言详解
目录 1.BeanShell简介 2.Beanshell的内置变量和方法 3.BeanShell断言界面详解 4.BeanShell断言的使用 (1)测试计划内包含的元件 (2)登陆接口请求界面内容 ...
- python实现布隆过滤器及原理解析
python实现布隆过滤器及原理解析 布隆过滤器( BloomFilter )是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地 ...
- Resnet-50网络结构详解
解决的问题: 由于梯度消失,深层网络很难训练.因为梯度反向传播到前面的层,重复相乘可能使梯度无穷小.结果就是,随着网络的层数更深,其性能趋于饱和,甚至迅速下降. 核心思想: 引入一个恒等快捷键(也称之 ...
- AI人工智能天机芯芯片
AI人工智能天机芯芯片 描述 2019年刊出的<自然>封面文章,展示了清华大学类脑计算研究中心团队研发的新型人工智能芯片"天机芯(Tianjic)".这是世界首款异构融 ...
- Jittor 的Op, Var算子
Jittor 的Op, Var算子 要使用jittor训练模型,需要了解两个主要概念: Var:Jittor的基本数据类型 Operations:Jittor的算子与numpy类似 首先,开始使用Va ...
- 从“信息化”到“智慧化”,GVS视声将如何赋能智慧医院?
4月23日-25日,2021年中华医院信息网络大会(CHINC)盛大举办,今年首次携手中国医院建筑与装备创新发展大会,同期同地亮相杭州国际博览中心,塑造了全新的"双引擎"品牌盛会. ...
- Java并发:乐观锁
作者:汤圆 个人博客:javalover.cc 简介 悲观锁和乐观锁都属于比较抽象的概念: 我们可以用拟人的手法来想象一下: 悲观锁:像有些人,凡事都往坏的想,做最坏的打算:在java中就表现为,总是 ...
- Docker与k8s的恩怨情仇(二)—用最简单的技术实现“容器”
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 上次我们说到PaaS的发展历史,从Cloud Foundry黯然退场,到Docker加冕,正是Docker& ...