flawopen.com/Teardowns/cve-2024-32896-android-factory-reset-wipe-bypass

● CVE-2024-32896 · CVSS 7.8 · Alta
Investigación · FlawOpen

CVE-2024-32896: el restablecimiento de fábrica de Android podía interrumpirse antes de destruir las claves

CVE-2024-32896 (CVSS 7.8, explotada en ataques limitados y dirigidos): el restablecimiento de fábrica solo reiniciaba en modo recovery y dejaba intactas las claves de cifrado hasta que se ejecutaba el borrado, así que quien tuviera el teléfono podía interrumpir el reinicio y mantener los datos recuperables. La corrección elimina todas las claves del Keystore antes de reiniciar.

💡 Explicación en Lenguaje Sencillo (ELI5)

La norma de una empresa para un portátil perdido es enviarle la orden "triturar". El portátil camina entonces hasta la sala de la trituradora, donde se destruyen todos los archivos. Pero quien sostiene el portátil puede bloquear el pasillo por el camino, y los archivos no llegan a ninguna parte y siguen intactos. La solución es que, antes de salir, el portátil queme las únicas llaves de su archivador cerrado. Aunque el trayecto se interrumpa, nadie podrá volver a abrir el archivador.

Conceptos Clave y Términos

Restablecimiento de fábrica (--wipe_data)
Borrado pedido por el usuario, por una app de administración del dispositivo o MDM, o por un servicio de borrado remoto. RecoverySystemService.rebootRecoveryWithCommand() escribe el comando --wipe_data para recovery y reinicia.
Bloque de control del bootloader (BCB)
Pequeña partición (misc) donde Android deja instrucciones para el bootloader y el recovery; la escribe setupOrClearBcb().
FBE y la contraseña sintética
Cifrado basado en archivos. Las claves de los datos del usuario se derivan de una contraseña sintética cuyos blobs protectores están ligados a claves de KeyMint; destruir esas claves hace que los datos no se puedan descifrar.
KeyMint / Keystore
Servicio de claves respaldado por hardware. AndroidKeyStoreMaintenance.deleteAllKeys() pide a cada dispositivo KeyMint que borre todas sus claves.

Análisis de Causa Raíz

rebootRecoveryWithCommand() en RecoverySystemService.java gestionaba --wipe_data escribiendo el comando en el bloque de control del bootloader y reiniciando, confiando en que recovery borrara /data después. Hasta que ese borrado terminaba, las claves de KeyMint que protegen la contraseña sintética y las claves de cifrado DE y de metadatos seguían intactas. Interrumpir el reinicio, o impedir que el borrado se ejecutara, dejaba en su sitio los datos cifrados y sus claves: un error lógico en el orden de las operaciones. La corrección (AOSP 8b7b2c66, bug 324321147) llama a deleteSecrets(), que ejecuta AndroidKeyStoreMaintenance.deleteAllKeys(), antes del reinicio. Es la mitad del framework de Android de la corrección de firmware de Pixel registrada como CVE-2024-29748.

Flujo de Ataque Paso a Paso

Paso 1

Se pide un borrado

El propietario, una app de administración del dispositivo o MDM, o un servicio de borrado remoto solicita el restablecimiento de fábrica, que termina en rebootRecoveryWithCommand("--wipe_data ...").

Paso 2

Reinicio sin destruir nada

setupOrClearBcb() guarda el comando y pm.reboot(REBOOT_RECOVERY) reinicia el teléfono. En este punto las claves de cifrado siguen intactas.

Paso 3

El borrado se interrumpe

Alguien con posesión física detiene el reinicio antes de llegar a recovery, por ejemplo manteniendo pulsado Bajar volumen para caer en el bootloader, y el borrado nunca se ejecuta.

Paso 4

Los datos sobreviven al restablecimiento

Los datos cifrados del usuario y las claves que los protegen siguen en el dispositivo, que aún puede atacarse más tarde con herramientas forenses en lugar de haber quedado limpio.

Código Fuente: Vulnerable vs. Seguro

IMPLEMENTACIÓN VULNERABLE
// services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@Override // Binder call
public void rebootRecoveryWithCommand(String command) {
    if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
    synchronized (sRequestLock) {
        if (!setupOrClearBcb(true, command)) {
            return;
        }

        // BUG: for "--wipe_data" nothing is destroyed yet. The keys that
        // protect the user's encrypted data survive until recovery runs the
        // wipe. If the reboot is interrupted or the wipe is skipped, the data
        // is still recoverable.
        PowerManager pm = mInjector.getPowerManager();
        pm.reboot(PowerManager.REBOOT_RECOVERY);
    }
}
PARCHE SEGURO Y ROBUSTO
// services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
static final String RECOVERY_WIPE_DATA_COMMAND = "--wipe_data";

@Override // Binder call
public void rebootRecoveryWithCommand(String command) {
    if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");

    boolean isForcedWipe = command != null && command.contains(RECOVERY_WIPE_DATA_COMMAND);
    synchronized (sRequestLock) {
        if (!setupOrClearBcb(true, command)) {
            return;
        }

        // FIX: destroy the keys first. Deleting every KeyMint key (including
        // the synthetic-password protector keys and the keys protecting DE and
        // metadata encryption keys) makes FBE data unrecoverable even if the
        // wipe in recovery is interrupted or skipped.
        if (isForcedWipe) {
            deleteSecrets();
        }

        PowerManager pm = mInjector.getPowerManager();
        pm.reboot(PowerManager.REBOOT_RECOVERY);
    }
}

private static void deleteSecrets() {
    Slogf.w(TAG, "deleteSecrets");
    try {
        AndroidKeyStoreMaintenance.deleteAllKeys();
    } catch (android.security.KeyStoreException e) {
        Log.wtf(TAG, "Failed to delete all keys from keystore.", e);
    }
}

Lista de Verificación de Seguridad para Ingeniería

Fuentes