timschneeb/postexploitation-toolbox-android

GitHub: timschneeb/postexploitation-toolbox-android

基于 CVE-2024-34740 漏洞的 Android 14 后渗透工具箱,允许攻击者通过注入 system_server 获得最高系统权限并控制设备。

Stars: 0 | Forks: 0

# postexploitation-toolbox-android ## 介绍 本项目包含多个用于 Android 漏洞利用的 post-exploitation 工具,允许以 system 用户 (uid 1000) 执行任意代码。 它是专为 **Android 14** 设计的,特别是针对 Samsung S21 Ultra。它使用了大量的反射 (reflection) 来操作/调用私有字段和方法,这保证在其他 Android 版本上会失效。 大多数功能需要将代码注入到 `system_server` 中(或者对 package cache 具有写权限),这比仅仅在常规系统应用中执行代码更难实现。 本项目基于 [michalbednarski/AbxOverflow](https://github.com/michalbednarski/AbxOverflow),该漏洞利用了 package installer service 中的一个问题,允许我们将自定义数据注入到位于 `/data/system/packages.xml` 的 package cache 中 (CVE-2024-34740)。 这使我们能够为任何共享 UID 注册新的“伪”系统应用。如果我们选择一个共享 UID(例如 `android.uid.system`),我们就可以通过在 manifest 中为组件设置 `android:process` 属性,从而将代码注入到该共享用户下运行的现有进程中。 通过额外将我们组件的进程名设置为 `system`,我们可以有效地在 `system_server` 内部注入并执行 Java 代码,并使用反射操作现有结构。 ## 功能 * 临时使任何应用可调试 * 禁用证书锁定 (Certificate Pinning) * 访问私有应用数据 [(需要一个插件应用)](https://github.com/timschneeb/debuggable-app-data-backup) * 禁用 Android 全系统权限检查(针对硬编码的包名和 UID) * 启动时作为系统应用启动 [我的 Shizuku fork(支持 system 用户)](https://github.com/timschneeb/ShizukuExt-SystemUID) * 集成 Java 反射浏览器 UI,用于探索和修改系统服务内部结构 * 使用 [我的 FabricatedOverlayManager](https://github.com/timschneeb/FabricatedOverlayManager) 即时编辑任何应用的资源 * 在内部屏幕上启动/停止 Samsung DEX(可以使用 `scrcpy --display-id=2` 连接) * 在具有不同 SELinux 上下文的系统进程(system_server, com.android.settings 等)之间无缝切换 * 为任何其他共享 UID 注入签名(允许将此应用安装到 SystemUI、phone 进程、bluetooth 进程等) * 更改安装来源(绕过 Play Store 的侧载检查) * 切换多用户支持(仅限 Samsung) * 使用 DocumentsProvider 将系统文件暴露给常规应用 * 简单的集成 shell ## 用法 如果你想使用 CVE-2024-34740 漏洞安装此 toolbox 应用,请按照以下步骤操作: * 编译 `droppedapk` 子项目(选择构建目标 'System' 或直接运行 './gradlew :assembleSystemDebug') * 这应该在构建期间自动发生:使用本仓库中包含的 `abxdroppedapk.keystore` 对 APK 进行签名(使用密码:`abxdroppedapk`) * 将签名后的 APK 复制到 `app/src/main/assets/droppedapk-release.apk` * 编译 `app` 子项目 * 安装并执行该漏洞利用应用中的说明 Toolbox 将作为 system 用户安装。无需重做漏洞利用即可正常安装注入应用的更新。 ### 将应用注入其他共享 UID 你可以将此应用单独注入到其他 UID 中。 例如,要注入到 `android.uid.phone` (UID 1001): * 打开作为 system 用户注入的 toolbox 应用 * 进入 'Inject into shared UID' 并选择 UID 'Phone',输入包名 `com.example.abxoverflow.droppedapk.phone` 并确认 * 编译 `droppedapk` 子项目并安装 APK(选择构建目标 'Phone' 或直接运行 `./gradlew :installPhoneDebug`) * (APK 必须使用 keystore `abxdroppedapk.keystore` 签名) 对于其他 UID,请查看 `build.gradle`。 此处描述的问题修复对应于 CVE-2024-34740 / A-307288067: * [公告](https://source.android.com/docs/security/bulletin/2024-08-01#Framework) * [公告中链接的补丁](https://android.googlesource.com/platform/frameworks/libs/modules-utils/+/700c28908051ceb55e1456d2d21229bc17c6895a) * 另外两个补丁:[1](https://android.googlesource.com/platform/frameworks/base/+/8b1e072210796e9772d3d07bdbae424b38447db6%5E!/) [2](https://android.googlesource.com/platform/frameworks/base/+/7250d76a8a2d501af62081d88545c301d43106a4%5E!/) # Android Binary XML 在 Android `system_server` 内部,许多服务在 XML 文件中存储其跨重启的状态 ``` $ adb shell su 0 find /data/system -name '*.xml' | sort /data/system/appops_accesses.xml /data/system/cachequota.xml /data/system/device_policies.xml /data/system/device_policy_state.xml /data/system/display-manager-state.xml /data/system/input-manager-state.xml /data/system/inputmethod/subtypes.xml /data/system/install_sessions.xml /data/system/job/jobs_1000.xml /data/system/job/jobs_10131.xml /data/system/log-files.xml /data/system/netpolicy.xml /data/system/notification_policy.xml /data/system/overlays.xml /data/system/packages.xml /data/system/package-watchdog.xml /data/system/sensor_privacy_impl.xml /data/system/sensor_privacy.xml /data/system/shortcut_service.xml /data/system/users/0/app_idle_stats.xml /data/system/users/0/appwidgets.xml /data/system/users/0/package-restrictions.xml /data/system/users/0/settings_global.xml /data/system/users/0/settings_secure.xml /data/system/users/0/settings_system.xml /data/system/users/0/wallpaper_info.xml /data/system/users/0.xml /data/system/users/userlist.xml /data/system/watchlist_settings.xml ``` 历史上这些是带缩进的纯文本 XML 文件,这允许开发人员轻松阅读它们,但是 [在 Android 12 中引入了该格式的二进制版本,理由是 `system_server` 花费的所有时间中有 1.5% 用于这些 XML 操作](https://android.googlesource.com/platform/frameworks/base/+/4ccea8796991d678ead4399130ec31edf63ff4fa%5E%21/) 值得注意的是,此格式仅供系统内部使用,其文件具有 magic value `"ABX\x00"`。它与 APK 内部用于 `AndroidManifest.xml`、`res/xml/*.xml`、`res/layout/*.xml` 等的 [格式](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/libs/androidfw/ResourceTypes.cpp;l=1770;drc=d4e49e63519397789d284a03aea5fafc119cb1b0) 不同,后者没有明确的“magic value”,但通常以 `0300 0800` 开头(这是 [header](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h;l=608;drc=d4e49e63519397789d284a03aea5fafc119cb1b0),包含 `type=RES_XML_TYPE` 和 `headerSize=8`) 每当系统读取这些内部状态 XML 文件之一时,它 [使用文件中的 `"ABX\0"` magic value 来选择 Binary XML 文件的解析器或常规 XML 解析器](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/util/Xml.java;l=188-192;drc=97a370a95275e79c69e79d7ead11aa38934a5575)。这些文件何时保存为 Binary XML [由系统属性控制](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/util/Xml.java;drc=97a370a95275e79c69e79d7ead11aa38934a5575;l=74?q=Xml.java),并且默认启用 当使用 Binary XML 文件时,你可以通过例如 `adb shell su 0 abx2xml /data/system/packages.xml -` 来读取其内容 这种二进制格式的功能之一是提供类型化访问器,因此序列化器提供 `attributeInt(String namespace, String name, int value)` 方法,该方法将值写入为二进制整数,避免了通过 String 的往返,那将是新的分配以及随后的垃圾回收对象 另一种可以直接序列化的类型是字节数组 ``` @Override public XmlSerializer attributeBytesBase64(String namespace, String name, byte[] value) throws IOException { if (namespace != null && !namespace.isEmpty()) throw illegalNamespace(); mOut.writeByte(ATTRIBUTE | TYPE_BYTES_BASE64); mOut.writeInternedUTF(name); mOut.writeShort(value.length); mOut.write(value); return this; } ``` 还有一个类似的方法 `attributeBytesHex`,唯一的区别在于写入的 `TYPE_*` 标签。该标签被 `abx2xml` 工具用于将字节数组转换为适当的 String 表示 `mOut` 是 `FastDataOutput` 的实例,它提供了 Java [`DataOutputStream`](https://docs.oracle.com/javase/8/docs/api/java/io/DataOutputStream.html) 的功能。`writeByte`/`writeShort`/`writeInt`/`writeUTF`/`write` 使用与标准 `DataOutputStream` 相同的格式 与 [`Parcel`](https://github.com/michalbednarski/ReparcelBug2) 类似,如果在写入/读取期间出现不匹配,随后的读取数据将从错误的偏移量获取,但是与 `Parcel` 不同,`BinaryXmlSerializer`/`BinaryXmlPullParser` 的使用错误不会赋予攻击者任意篡改读取数据的能力(在这种情况下,攻击者无法引入新的标签/属性名称/值) 然而,`BinaryXmlSerializer` 类本身或 `FastDataOutput` 中的错误则会导致此问题 在上述方法中,如果我们尝试写入长度为 65536 的字节数组,我们将使用 `writeShort()` 写入长度,这实际上会写入 0,之后将写入实际的数组内容 # 选择 ABX 注入目标 为了利用这种不匹配,我们需要选择一个文件,在该文件中我们能够将任意字节数组注入到 `attributeBytesBase64` 或 `attributeBytesHex`,并且修改该文件对攻击者有价值 [`PackageInstaller`](https://developer.android.com/reference/android/content/pm/PackageInstaller) 类提供了准备安装包的能力。无需任何权限,任何应用都可以将新的待安装 APK 写入临时目录。一旦写入安装所需的所有内容,安装应用可以 [`commit()` `PackageInstaller.Session`](https://developer.android.com/reference/android/content/pm/PackageInstaller.Session#commit(android.content.IntentSender)),这意味着它将无法对安装文件进行任何进一步的更改,并且 `Session` 已准备好进行用户批准或实际安装 这些操作的状态存储在 `/data/system/install_sessions.xml` 中。安装程序应用可以例如将大型 APK 的一半下载到 Package Manager Service 为其 `PackageInstaller.Session` 创建的临时目录中,然后在重启后恢复下载,写入剩余的一半并提交安装 一种可能性是将数据写入 `install_sessions.xml` 以将会话标记为 [staged,这意味着它将在下次启动后安装](https://developer.android.com/reference/android/content/pm/PackageInstaller.Session#isStaged()) 另一种可能性(此处展示的是)更改准备安装文件的临时目录的路径,因为 [`openWrite()`](https://developer.android.com/reference/android/content/pm/PackageInstaller.Session#openWrite(java.lang.String,%20long,%20long))/[`openRead()`](https://developer.android.com/reference/android/content/pm/PackageInstaller.Session#openRead(java.lang.String)) 接受 [任何有效的文件名,只要没有路径遍历](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/os/FileUtils.java;l=964-994;drc=97a370a95275e79c69e79d7ead11aa38934a5575),并且 [将该文件放置在 `stageDir` 字段指向的目录中](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java;l=1703;drc=97a370a95275e79c69e79d7ead11aa38934a5575),该字段 [是从 XML 读取的](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java;l=5223-5224;drc=97a370a95275e79c69e79d7ead11aa38934a5575) # 利用 ABX 注入 现在我们需要将受控字节数组实际传入 `attributeBytesBase64()` `PackageInstaller.Session` 提供了 [`setChecksums()`](https://developer.android.com/reference/android/content/pm/PackageInstaller.Session#setChecksums(java.lang.String,%20java.util.List%3Candroid.content.pm.Checksum%3E,%20byte[])) 方法 在 `system_server` 端,提供的 `Checksum`-s 会根据调用者提供的签名进行可选的验证,然后 [放入 `mChecksums`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java;l=1452;drc=97a370a95275e79c69e79d7ead11aa38934a5575) 当写入 `install_sessions.xml` 时,[`checksum.getValue()` 被传递给 `writeByteArrayAttribute`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java;l=5152;drc=97a370a95275e79c69e79d7ead11aa38934a5575),它随后 [将其传递给 `attributeBytesBase64()`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/com/android/internal/util/XmlUtils.java;l=1917;drc=97a370a95275e79c69e79d7ead11aa38934a5575) 很少有事件会触发 `install_sessions.xml` 的写入,其中之一是 [创建新 `Session`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java;l=961;drc=7d3ffbae618e9e728644a96647ed709bf39ae759),因此此漏洞利用在一个 Session 上设置 `Checksum` 后会创建一个新 `Session` 以确保第一个 `Session` 已保存到文件 现在我们写入长度为 65536 的字节数组,然后在读取时其大小被解释为零,该数组的内容变成 `BinaryXmlPullParser` 解析的原始数据 没有指定属性计数,每个条目都有一个包含 `token` 的标签字节。在低位半字节中有 [`XmlPullParser`](https://developer.android.com/reference/org/xmlpull/v1/XmlPullParser) 中定义的事件类型之一,例如 `START_TAG`、`END_TAG` 或 `END_DOCUMENT`。除了这些类型之外,还有特殊的 [`ATTRIBUTE`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/libs/modules-utils/java/com/android/modules/utils/BinaryXmlSerializer.java;l=77;drc=97a370a95275e79c69e79d7ead11aa38934a5575) 类型,它不通过 `next()` 报告,而是 [在看到 `START_TAG` token 后,解析器会预读接下来的 token,直到看到非 `ATTRIBUTE` token](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/libs/modules-utils/java/com/android/modules/utils/BinaryXmlPullParser.java;l=161-207;drc=5aacbd6c06e4496d45c9e77f4b8f6c9719dd3d4c) 由于没有指定属性计数,我们可以立即通过 `END_TAG` token 继续关闭当前元素。然后我们也关闭 ``,因为所有有趣的元素属性都在 `` 开始标签中,但我们要么已经过了那个点,但是现在我们可以打开一个新的 `` 元素并在那里设置它们 如上所述 `FastDataInput` 与 Java 的 `DataInputStream` 兼容,除了有一个额外的 [`readInternedUTF()`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/libs/modules-utils/java/com/android/modules/utils/FastDataInput.java;l=193;drc=5aacbd6c06e4496d45c9e77f4b8f6c9719dd3d4c) 方法,它可以引用过去的字符串。由于我们不知道之前驻留了哪些字符串,我们总是指定写入的是以前未见过的字符串。这也会将新读取的字符串添加到池中,这可能会导致读取注入点之后写入的数据时出现问题,但是作为注入的一部分,我插入了所有结束标签和 `END_DOCUMENT` token,所以在我的注入之后不会从该文件读取任何其他内容 # 使用被篡改 `stageDir` 的 `PackageInstaller.Session` 一旦系统读取了修改后的 `install_sessions.xml`,我们就会得到 `PackageInstallerSession` 对象,其 `stageDir` 设置为我们控制的值 我的第一个想法是将 `stageDir` 设置为 `/proc/self`,然后读取 `maps` 并写入 `mem`,但是这些没有奏效 当我尝试使用 [`openRead()`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java;l=1775-1804;drc=97a370a95275e79c69e79d7ead11aa38934a5575) 打开 `/proc/self/maps` 时,`system_server` 成功打开了该文件,但是通过 Binder 将该文件传递给 `untrusted_app` 被 SELinux 阻止 然而,写入不是通过将原始文件描述符传递给另一个进程来完成的,而是通过 `system_server` 代理,因为 `system_server` 必须能够在会话提交后撤销写访问权限。这是否意味着我们可以写入 `/proc/self/mem`?事实证明,虽然 `system_server` 可以打开该文件,[但在写入任何内容之前它会在该文件上调用 `Os.chmod()`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java;l=1713;drc=97a370a95275e79c69e79d7ead11aa38934a5575),它无法在 `/proc/self/mem` 上执行此操作。所以我们不能在这里利用它,尽管除此之外 `system_server` 能够打开该文件并按我们指定的偏移量执行写入,并且该文件允许覆盖代码页,这将直接赋予我们代码执行能力 由于该选项不可行,我尝试了下一个想法,替换 `/data/system/packages.xml` 的内容。这是包含 `PackageManagerService` 状态的文件,最值得注意的是安装了哪些应用以及分配给它们哪些 uid 看起来 `system_server` 不被允许直接写入该文件:每当系统写入该文件时,它首先写入临时文件,然后 [用该临时文件替换 `packages.xml` 并对其启用保护](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/pm/ResilientAtomicFile.java;l=118-154;drc=884056f84f3c05c203889206fa8c132ef83f3053) 然而,在读取 `/data/system/packages.xml` 时,系统会 [首先检查 `/data/system/packages-backup.xml` 文件是否存在,如果存在,它将认为主 `packages.xml` 已损坏并改为读取备份](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/pm/ResilientAtomicFile.java;l=173-193;drc=884056f84f3c05c203889206fa8c132ef83f3053)。在正常操作期间,`/data/system/packages-backup.xml` 文件不存在,我们可以使用经过精心构造的、`stageDir` 设置为 `/data/system` 的 `PackageInstallerSession` 创建一个 此外,当我使用 `openRead()` 时,允许 `system_server` 发送 `/data/system/packages.xml` 的只读文件描述符,因此我可以轻松仅包含我的修改的补丁文件,而不会损坏以前的内容 # 授予 `sharedUserId="android.uid.system"` 访问权限 在 `packages.xml` 中,我注册了已安装应用程序的定义,例如: ``` ``` 我们可以将新的 APK 写入 `/data/app` 中的某处(使用另一个 `PackageInstallerSession`)并将新的 `` 元素添加到 `packages.xml` 并以这种方式安装它吗? 是的,但是我们必须在 `` 中提供我们新安装的 APK 的有效签名,并且系统将在启动期间根据 APK 文件检查它 我们可以将 `userId`(而不是 `sharedUserId` 来指示 `AndroidManifest.xml` 中没有 `` 属性的 APK)属性设置为我们想要的值吗? 是的,但是我们不能使用已被另一个包或 `sharedUserId` 使用的值 我们可以为我们的应用设置 `sharedUserId="1000"` 吗? 如果我们这样做,在启动期间系统将通过 [`canJoinSharedUserId()`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java;l=658-750;drc=7ec13b04c3bbaeac99cbbc4db9f9f80492c508fe) 验证该设置 特别是该方法将使用 [`checkCapability()`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/content/pm/SigningDetails.java;l=613-637;drc=97a370a95275e79c69e79d7ead11aa38934a5575) 来检查签名是完全匹配,还是一方的签名与另一方过去的签名之一匹配 这些“过去的签名”来自 `packages.xml`,特别是当我们有带有 `` 的 `` 元素时,我们可以在 `` 下添加 `` 元素以向 [`SigningDetails.mPastSigningCertificates`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/content/pm/SigningDetails.java;l=74-87;drc=97a370a95275e79c69e79d7ead11aa38934a5575) 添加新条目 最后,我们被篡改的 `` 元素如下所示: ``` ``` `` 下的 `` 元素被插入了两次,因为 [最后的过去签名被认为是当前的,因此不被考虑](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/content/pm/SigningDetails.java;l=698-700;drc=97a370a95275e79c69e79d7ead11aa38934a5575) [`flags="2"` 意味着证书被允许用于 `sharedUserId`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/content/pm/SigningDetails.java;l=102-103;drc=97a370a95275e79c69e79d7ead11aa38934a5575) 此外,`` 注册必须应用于在 manifest 中声明 `android:sharedUserId="android.uid.system"` 的应用,因此它必须是执行漏洞利用的 APK 之外的单独 APK # 新安装的 system-uid 应用无法启动 虽然我能够注册受信任用于 `android:sharedUserId="android.uid.system"` 的新证书,但通常使用该证书签名且仅在 manifest 中声明 `sharedUserId` 的应用将无法启动。启动它时,我们会在 `logcat` 中看到以下消息: ``` signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr -------- Abort message: 'JNI FatalError called: (com.example.abxoverflow.droppedapk) frameworks/base/core/jni/com_android_internal_os_Zygote.cpp:1976: selinux_android_setcontext(1000, 0, "default:privapp:targetSdkVersion=33:complete", "com.example.abxoverflow.droppedapk") failed' ``` 这是因为 [`seapp_contexts` 文件](https://cs.android.com/android/platform/superproject/main/+/main:system/sepolicy/private/seapp_contexts) 中的定义没有一个匹配 该文件中的 `user=` 规则 [是从 `uid` 映射的](https://cs.android.com/android/platform/superproject/main/+/main:external/selinux/libselinux/src/android/android_seapp.c;l=819-833;drc=530165a996d8ca5ab5959c33bc040c78951bcb59)(`selinux_android_setcontext()` 的第一个参数),我们这里将是 `user=system`,对于普通应用这是 `user=_app` 另一个要匹配的是 `seinfo=` 规则,它取自 `selinux_android_setcontext()` 的第三个参数直到第一个冒号。最初该值来自将启动的应用签名与 `/system/etc/selinux/plat_mac_permissions.xml` 中定义的签名进行比较 最后,我们的应用尝试匹配 `user=system seinfo=default`,而 `seapp_contexts` 中没有这样的规则 然而,虽然我们新的 `android:sharedUserId="android.uid.system"` 应用的进程无法启动,但如果通过 [`android:process` 属性](https://developer.android.com/guide/topics/manifest/application-element#proc) 指定,应用仍然可以加载到现有进程中。特别是,在 `android.uid.system` 下运行的应用可以指定 `android:process="system"` 以加载到 `system_server` 中 # 导致系统崩溃 通常 [触发 `system_server` 崩溃的应用被认为是具有可忽略安全影响的 Bug](https://bughunters.google.com/learn/invalid-reports/android-platform/5148417640366080/bugs-with-negligible-security-impact#triggering-a-local-temporary-denial-of-service),这里值得一提只是因为它是需要两次 `system_server` 重启的漏洞利用链的一部分 无论如何,我们得到了 `Parcelable` 链: * [`IAlarmManager.set()` AIDL 方法接受 `AlarmManager.AlarmClockInfo`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl;l=32-35;drc=ca41ed611ac9c6584c6d5c38ae8428b8e4f3b135) * [`AlarmClockInfo` 调用不带类型参数的已弃用 `readParcelable()`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/apex/jobscheduler/framework/java/android/app/AlarmManager.java;l=1598;drc=04bf84e220ade9d7ad8ef0b2f7e6ce6ec72841c8)(因为它在 apex 模块中,并且这些没有切换到新方法) * 我指定 [`android.content.pm.PackageParser$Activity`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/content/pm/PackageParser.java;l=8240;drc=7d3ffbae618e9e728644a96647ed709bf39ae759) 作为 `Parcelable` 类 * 读取该类会导致 [调用任何接受单个 `Parcel` 参数的公共构造函数](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/content/pm/PackageParser.java;l=7789-7795;drc=7d3ffbae618e9e728644a96647ed709bf39ae759) * 我指定 [`android.os.PooledStringWriter`,它在提供的 `Parcel` 上调用 `writeInt(0)`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/os/PooledStringWriter.java;l=55;drc=782d49826862cbdc9d020fc9d85f8a6f64675dcb) * 该 `writeInt()` 调用是在作为 [`onTransact()`](https://developer.android.com/reference/android/os/Binder#onTransact(int,%20android.os.Parcel,%20android.os.Parcel,%20int) 的 `data` 参数接收的 `Parcel` 上执行的,该参数由从 `/dev/binder` `mmap` 的只读内存支持。写入会导致 `SIGSEGV` 另外值得注意的是,[我使用 `PackageParser`+`PooledStringWriter` 组合作为之前报告的一部分,例如 CVE-2023-21098](https://github.com/michalbednarski/TheLastBundleMismatch) # 完整流程 这是当你按下应用中的 "Do everything" 按钮时发生的情况 1. `RebootBackgroundRunner` 作为单独的进程启动,现在它只使用 [`setsid()`](https://man7.org/linux/man-pages/man2/setsid.2.html) 在用户空间重启中存活,之后将在后台等待 2. 分配新的 `PackageInstaller.Session` 并向其添加新的 `Checksum` 对象。该 `Checksum` 对象包含一个字节数组,其大小将在序列化期间导致整数溢出,一旦其数据被反序列化回来,系统将看到数据之前是 `Checksum` 负载的 `PackageInstaller.Session`-s。特别是,注入了两个会话 * 一个带有 `sessionStageDir="/data/system"` 和 `prepared="true"`(意味着 stage 目录已经准备好,不需要创建) * 一个带有 `sessionStageDir="/data/app/dropped_apk"` 和 `prepared="false"`(意味着目录将在第一次 `Session.openWrite()` 时创建) 3. 分配新的 `PackageInstaller.Session` 然后立即销毁。这触发系统将更新的内容写入 `install_sessions.xml` 4. 在短暂的延迟后,触发 `system_server` 崩溃 5. 在下一次 `system_server` 启动期间,读取 `install_sessions.xml` 文件,现在我们可以使用注入的 `PackageInstaller.Session`-s 6. `RebootBackgroundRunner` 在用户空间重启期间一直在后台等待,一旦它注意到系统已恢复并准备就绪,它就会执行接下来的步骤 7. 使用一个 `PackageInstaller.Session`,从 assets 中提取新的 APK 并将其写入 `/data/app/dropped_apk/base.apk` 8. 其他 session 用于读取 `/data/system/packages.xml`,该文件被修补以声明新丢弃的 APK 已经安装,并且用于它的证书之前用于 `android:sharedUserId="android.uid.system"` 并且仍然受此目的信任。更改后的文件被写入为 `/data/system/packages-backup.xml` 9. 触发另一次 `system_server` 崩溃 10. 当 `system_server` 在启动期间看到 `packages-backup.xml` 时,它认为原始 `packages.xml` 已损坏并改为使用备份 11. 由于系统已读取修改后的 `packages.xml`,刚刚丢弃的应用存在并从 [`ACTION_BOOT_COMPLETED`](https://developer.android.com/reference/android/content/Intent#ACTION_BOOT_COMPLETED) 启动自身。该新应用在 `system_server` 内运行,因为它的 `AndroidManifest.xml` 中有 `` 和 `` # `utils/` 中的脚本 连同 PoC 应用一起,还有包含少量脚本的 `utils` 目录 * `moveapk.sh` 将编译后的 APK 移动到注入器的 `assets` 中,在 `gradle :droppedapk:assembleRelease` 后运行 * `peeksessions.sh` 允许查看 `install_sessions.xml` 的当前内容(需要 Android 的 `eng`/`userdebug` 构建) * `wipesessions.sh` 清除任何存在的 `PackageInstaller.Session`-s 并重启系统(需要 Android 的 `eng`/`userdebug` 构建) # 琐事 不确定这是否相关,但在查看可能与 ABX 相关的错误的历史记录时(`cd frameworks/base ; git log -S ABX`),我发现了 ["Stop processing on IOException" 提交](https://android.googlesource.com/platform/frameworks/base/+/5112cfef2a2023a2629a426154547444593e9f9b%5E!/),其中**包含添加带有截断 ABX 文件的单元测试**。该提交是 ["Ignore malformed shortcuts"](https://android.googlesource.com/platform/frameworks/base/+/d5122bfaf18f1503e73c1a3a177a56d0f604a008%5E%21/) 的后续,后者在 [公告中被描述为 DoS](https://source.android.com/docs/security/bulletin/2022-12-01#framework)
标签:Android 14, Android安全, CVE-2024-34740, CyberChef, Java反射, JS文件枚举, Samsung S21, Shizuku, SSL Pinning绕过, System Server, 云资产清单, 共享UID, 包安装服务, 协议分析, 后渗透测试, 应用调试, 权限提升, 渗透测试框架, 移动安全工具, 系统提权, 绕过权限检查, 逆向工程, 防御