
Android complete exploit chain that enables privilege escalation from a local untrusted app to root/kernel, combination of CVE-2026-49881 and CVE-2026-43284
Uma cadeia de exploração completa que permite escalonamento de privilégios de um aplicativo local não confiável para root/kernel. Ela não envolve corrupção de memória ou condições de corrida, portanto os atacantes não precisam realizar heap spraying complexo ou contornar mitigações contra vulnerabilidades de corrupção de memória como KASLR, MTE ou CFI, tornando esta cadeia de exploração uma taxa de sucesso de 100% em dispositivos vulneráveis.
Testado no Pixel 10 executando a versão oficial inicial do Android 17. Observe que não funciona no Pixel 6a e esse problema também pode ocorrer em outros dispositivos que executam árvores de kernel 6.1.xxx-android14 devido a outro bug nesses kernels.
Uso: Instale o aplicativo KernelSU, abra este aplicativo, clique em "Run userspace exploit" e depois em "Run kernel exploit and load KernelSU". Após uma exploração bem-sucedida, o KernelSU será ativado e você poderá usá-lo para conceder acesso root a outros aplicativos. Problema conhecido: Se você já executou o exploit do kernel e quiser executá-lo novamente, será necessário reiniciar o dispositivo.
Gravação de tela: clique aqui
A cadeia é composta por duas vulnerabilidades distintas: uma é um 0-day no serviço Telecom, enquanto a outra é um 1-day do kernel que foi divulgado há 3 meses. Mas AOSP e dispositivos Pixel (exceto aqueles que executam versões beta do QPR) permanecem vulneráveis no momento em que este texto foi escrito.
A primeira vulnerabilidade da cadeia é um bug lógico simples introduzido no Android 17. Ele se origina de uma mudança maluca, que adiciona o seguinte código a InCallController.java:
PackageManager packageManager = mContext.getPackageManager();
Context userContext = mContext.createContextAsUser(userHandle,
0 /* flags */);
PackageManager userPackageManager = userContext != null ?
userContext.getPackageManager() : packageManager;
List<ResolveInfo> entries;
entries = userPackageManager.queryIntentServices(
serviceIntent,
PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_COMPONENTS);
for (ResolveInfo entry : entries) {
ServiceInfo serviceInfo = entry.serviceInfo;
if (serviceInfo != null) {
boolean isMetaFlag = serviceInfo.metaData != null &&
serviceInfo.metaData.getBoolean(
"android.telecom.CLASS_EXISTENCE_CHECK", false);
if (isMetaFlag && !serviceClassExists(serviceInfo, userHandle)) {
continue;
}
}
}
O método serviceClassExists() relevante é definido da seguinte forma:
/**
* Verifies that the class for a given ServiceInfo exists within its package.
* This prevents a system crash if a service is declared in the manifest but its
* class was not included in the compiled code.
* @param serviceInfo The ServiceInfo of the service to check.
* @param userHandle The user under which to check for the service.
* @return {@code true} if the class exists, {@code false} otherwise.
*/
private boolean serviceClassExists(ServiceInfo serviceInfo, UserHandle userHandle) {
Log.i(this, "serviceClassExists check");
try {
Context packageContext = mContext.createPackageContextAsUser(
serviceInfo.packageName,
Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY, userHandle);
ClassLoader classLoader = packageContext.getClassLoader();
Class.forName(serviceInfo.name, false, classLoader);
return true;
} catch (NameNotFoundException | ClassNotFoundException e) {
Log.w(this, "Skipping InCallService: class not found for " + serviceInfo.name);
return false;
} catch (Exception e) {
Log.e(this, e, "Error checking for existence of " + serviceInfo.name);
return false;
}
}
Esta é a vulnerabilidade mais inacreditável que já vi. O código usa Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY para carregar código de um aplicativo arbitrário; Embora pareça haver algumas medidas destinadas a impedir a execução arbitrária de código, como passar false para Class.forName() para impedir a inicialização da classe, o aplicativo ainda pode declarar um AppComponentFactory personalizado que é invocado quando getClassLoader() é chamado.
Por outro lado, o bug existe em InCallController.java, que faz parte do pacote com.android.server.telecom em vez de com.android.phone. Vale a pena notar que o pacote declara android:sharedUserId="android.uid.system" e android:process="system" no AndroidManifest.xml, portanto ele é executado no processo system_server, um dos processos de userspace mais privilegiados do Android. Portanto, agora temos a capacidade de executar código Java arbitrário dentro do system_server.
É uma surpresa que até mesmo um engenheiro do Google possa cometer um erro tão grande na era da IA. Nós o encontramos e o reportamos à Equipe de Segurança do Android em 23 de julho de 2026. Eles nos disseram que era uma duplicata. O Google mudou o boletim de segurança mensal para lançamento trimestral, o que pode explicar por que a vulnerabilidade não foi corrigida 3 meses após o lançamento do Android 17.
A vulnerabilidade recebeu o CVE-2026-49881 e foi corrigida em setembro de 2026 por Remove serviceClassExists logic to address security vulnerability.
O primeiro bug nos permite escalar privilégios para system, mas ainda está longe do root. Um root completo requer pelo menos UID 0 e não ser restringido pelo SELinux.