Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

订阅源联系隐私© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
LeakValue — CVE-2022-20452 的漏洞利用,通过 LazyValue 在 recycle() 之后使用 Parcel,在 Android 上实现从已安装应用到系统应用(或其他应用)的权限提升。 | Kitploit
工具/GitHubGitHub/michalbednarski/leakvalue
Android安全权限提升漏洞分析漏洞利用移动安全二进制利用
GitHubmichalbednarski/leakvalue

LeakValue

CVE-2022-20452 的漏洞利用,通过 LazyValue 在 recycle() 之后使用 Parcel,在 Android 上实现从已安装应用到系统应用(或其他应用)的权限提升。

查看仓库
34765234年前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

Android 13 引入了许多增强功能,以强化 Parcel 序列化机制

这是 Android 安全与隐私团队关于所做改进的演示

这很棒,绝对能消除许多漏洞或使其无法利用。同时,他们描述了如何攻破我之前的利用程序,该程序允许应用将代码加载到其他应用(包括系统应用)中

但现在我带着一个新利用程序回来了,它能以不同的方式实现同样的效果。它依赖于上述 Parcel 强化期间引入的以下漏洞:

  • CVE-2022-20452 (公告, 补丁)
  • CVE-2022-20474 (公告, 补丁)

应用显示文本的截图。标题:LeakValue。正文:创建了 6 个 ValueLeaker。正在锁定 ActivityTaskManagerService。已锁定 ActivityTaskManagerService。正在解锁 ActivityTaskManagerService。已解锁 ActivityTaskManagerService。leakedBinders=[android.os.BinderProxy@f06702e]。泄露接口:android.app.IApplicationThread。请求执行代码。Shellcode 已在 uid=1000 pid=6904 packageName=com.android.settings uid=1000(system) gid=1000(system) groups=1000(system),1007(log),1065(reserved_disk),1077(external_storage),3001(net_bt_admin),3002(net_bt),3003(inet),3007(net_bw_acct),9997(everybody) context=u:r:system_app:s0 中执行。屏幕底部有两个按钮:START 和 MANUAL TESTING

(另外,应用执行时的 logcat;利用行为在日志中会产生明显噪声)

Parcel 与 Parcelable 不匹配漏洞简介

Android 的 Parcel 类是进程间通信的基础

对象可以实现 Parcelable 接口,以便将其写入 Parcel,例如(摘自 AOSP):```java public class UsbAccessory implements Parcelable { public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public UsbAccessory createFromParcel(Parcel in) { String manufacturer = in.readString(); String model = in.readString(); String description = in.readString(); String version = in.readString(); String uri = in.readString(); IUsbSerialReader serialNumberReader = IUsbSerialReader.Stub.asInterface( in.readStrongBinder());

        return new UsbAccessory(manufacturer, model, description, version, uri,
                serialNumberReader);
    }
};

public void writeToParcel(Parcel parcel, int flags) {
    parcel.writeString(mManufacturer);
    parcel.writeString(mModel);
    parcel.writeString(mDescription);
    parcel.writeString(mVersion);
    parcel.writeString(mUri);
    parcel.writeStrongBinder(mSerialNumberReader.asBinder());

} }

请注意,`Parcel` 内部会存储执行写入或读取时的位置,`readString()` 会将数据解析为 String 并同时推进位置。该位置可以通过 [`dataPosition()`](https://developer.android.com/reference/android/os/Parcel#dataPosition())/[`setDataPosition()`](https://developer.android.com/reference/android/os/Parcel#setDataPosition(int)) 手动获取/设置。`Parcelable` 接口的实现必须确保其 `writeToParcel` 和 `createFromParcel` 写入/读取的数据量相同,否则所有后续读取都会从错误的偏移量处获取数据。

[`Bundle`](https://developer.android.com/reference/android/os/Bundle)(可在进程间发送的键值映射)可以包含[可通过 `writeValue()` 写入 Parcel 的各种对象](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/Parcel.java;l=1792-1937)。当从 `Parcel` 读取 `Bundle` 的内容时,系统中可用的任何 `Parcelable` 类都可以被读取。

`Bundle` 通过将整个经 Parcel 序列化的数据的长度写入 `Parcel`,然后仅[将原始 Parcel 的相关部分复制到存储在 `mParcelledData` 中的辅助 Parcel](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/BaseBundle.java;l=1675-1683) 来延迟对内容的实际解析(例如,这允许 [`Activity.onSaveInstanceState()`](https://developer.android.com/reference/android/app/Activity#onSaveInstanceState(android.os.Bundle)) 提供在 `system_server` 中不可用的 `Parcelable`,整个 `Bundle` 随后会被原样传递到 `system_server` 并返回,而无需解析内容)。

然而,一旦访问了 `Bundle` 中的任何值,`Bundle` 内的所有值都会被[解包(unparcelled)](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/BaseBundle.java;l=227-313),并且[每个存在的键值对都会被解析](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/Parcel.java;l=3613-3632)。如果这样的映射包含一个 `Parcelable`,其 `writeToParcel` 和 `createFromParcel` 方法不平衡,并且随后该 `Bundle` 被转发到另一个进程,那么那个进程可能会看到不同的 `Bundle` 内容。这使得所有此类[类中的不匹配成为系统中可利用的漏洞](https://github.com/michalbednarski/ReparcelBug),因为系统中存在一些[将 `Bundle` 检查为安全的位置](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/services/core/java/com/android/server/accounts/AccountManagerService.java;l=5037-5046),随后这些 `Bundle` 会被转发到另一个进程。

在这篇 writeup 中,我将这种先呈现一种内容、转发后又呈现另一种内容的 `Bundle` 称为自变化的 `Bundle`。

这里的另一个重要事项是,除了仅字节(字符串、数字以及由上述类型构成的对象)之外,`Parcel` 还可以包含文件描述符(File Descriptors)和 `Binder`。`Binder` 是可以对其发起 RPC 调用的对象,也就是说,一个进程创建 `Binder` 对象并重写 [`onTransact()` 方法](https://developer.android.com/reference/android/os/Binder#onTransact(int,%20android.os.Parcel,%20android.os.Parcel,%20int))。然后 `Binder` 被传递给另一个进程,在上面的示例代码中,你可以看到用于将其读写到 `Parcel` 的 `read`/`writeStrongBinder()` 调用。在另一个进程中,当使用 `readStrongBinder()` 时,会创建一个 `BinderProxy` 对象(隐藏在 [`IBinder` 接口](https://developer.android.com/reference/android/os/IBinder) 后面)。然后那个进程可以对该对象调用 [`transact()`](https://developer.android.com/reference/android/os/IBinder#transact(int,%20android.os.Parcel,%20android.os.Parcel,%20int)),并且在原始对象中会执行 `onTransact()`。不过,通常人们不会手动编写 `transact()`/`onTransact()`,而是[使用 AIDL](https://developer.android.com/guide/components/aidl) 代替。

# `LazyValue` 登场:自变化 `Bundle` 的终结

由于过去存在大量 `writeToParcel`/`createFromParcel` 不匹配的类,Android 13 [引入 `LazyValue`](https://android.googlesource.com/platform/frameworks/base/+/9ca6a5e21a1987fd3800a899c1384b22d23b6dee%5E%21/),从而解决了系统中任何此类类存在即可允许构建自变化 `Bundle` 的问题。

现在,当使用 `writeValue` 时,如果写入的值不是原始类型,[该值的长度也会被写入 `Parcel`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=2331-2344;drc=03c34f57c05feecfb090de3917787f049cb5f804)。

当普通应用直接使用 `Parcel.readValue()` 时,[除了一种情况外,其他一切都与之前相同:如果从 `Parcel` 读取的 `length` 与实际读取的数据大小不匹配,则会打印一条警告](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=4330-4348;drc=03c34f57c05feecfb090de3917787f049cb5f804)(不过请注意,[`Slog.wtfStack` 永远不会抛出异常](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/util/Slog.java;l=108-116;drc=23c7543b8e608ebcbb38b952761b54bb56065577))。

而 `Bundle` 现在则改用 [`Parcel.readLazyValue()`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=4350-4420;drc=03c34f57c05feecfb090de3917787f049cb5f804)。

让我们更仔细地看看它是如何工作的:在 `LazyValue` 类中,有一段[很好的注释,解释了 `Parcel` 内 `LazyValue` 数据的结构](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=4392-4399;drc=03c34f57c05feecfb090de3917787f049cb5f804):```
                     |   4B   |   4B   |
mSource = Parcel{... |  type  | length | object | ...}
                     a        b        c        d
length = d - c
mPosition = a
mLength = d - a

mSource 是对调用 readLazyValue() 的原始 Parcel 的引用

mPosition 和 mLength 描述整个 LazyValue 数据在原始 Parcel 中的位置和长度,包括 type 和 length

"length"(开头没有 "m")指的是写入 Parcel 的长度值,不包括头部(type 和 length)

那么,当有人(系统或应用)从从 Parcel 读取的 Bundle 中取值时,会发生以下情况:

下载工具