CVE-2018-9539: Vulnerabilidad use-after-free en servicio privilegiado de Android

Como parte de nuestra investigación de plataforma en Zimperium zLabs, recientemente descubrí una vulnerabilidad en un servicio privilegiado de Android llamado MediaCasService y lo informé a Google. Google lo designó como CVE-2018-9539 y lo parchó en el Actualización de seguridad de noviembre (nivel de parche 2018-11-01).

En esta publicación de blog, describiré los detalles técnicos de esta vulnerabilidad de uso posterior a la liberación, junto con algunos antecedentes y los detalles de la prueba de concepto que escribí que la activa. El enlace a la prueba de concepto completa está disponible al final de la publicación del blog.

El servicio de Android llamado MediaCasService (también conocido como android.hardware.cas) permite que las aplicaciones decodifiquen los flujos de medios protegidos. La comunicación entre las aplicaciones y MediaCasService se realiza principalmente a través de dos interfaces/objetos: Cas, que gestiona las claves (referencia: API Java de MediaCas), y Descrambler, que realiza la operación de descifrado real (referencia: API Java MediaDescrambler).

Debajo de la API de MediaCasService, las operaciones reales las realiza un complemento, que es una biblioteca que carga el servicio. En nuestro caso, el complemento relevante es el complemento ClearKey, cuya biblioteca es libclearkeycasplugin.so.

Para descifrar datos, las aplicaciones deben usar tanto el objeto Cas como el objeto Descrambler. El objeto Descrambler se usa para la operación real de descifrado, pero para hacerlo, debe vincularse a una sesión con una clave. Para administrar las sesiones y agregarles claves, se utiliza el objeto Cas.

Internamente, el complemento ClearKey administra las sesiones en el ClearKeySessionLibrary, que es esencialmente una tabla hash. Las claves son ID de sesión, mientras que los valores son los propios objetos de sesión. Las aplicaciones reciben los ID de sesión que pueden usar para hacer referencia a los objetos de sesión en el servicio.

Después de crear una sesión y adjuntarle una clave, las aplicaciones se encargan de vincularla a un objeto Descrambler. El objeto decodificador tiene un miembro llamado mCASSession, que es una referencia a su objeto de sesión y se utiliza en operaciones de decodificación. Si bien no hay obligación de hacerlo, una vez que una sesión de Descrambler se vincula con un objeto de sesión, una aplicación puede eliminar esa sesión de la biblioteca de sesiones. En ese caso, la única referencia al objeto de sesión será a través de la mCASSession del Descrambler.

Una nota importante es que las referencias a objetos de sesión se mantienen a través de punteros fuertes (sp clase). Por lo tanto, cada objeto de sesión tiene una cuenta de referencia, y una vez que esa cuenta de referencia llega a cero, se libera el objeto de sesión. Las referencias son a través de la biblioteca de sesión oa través de mCASSession de Descrambler.

Echemos un vistazo al método de decodificación de ClearKey:

Fragmento de frameworks/av/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp (fuente)

Como puede ver, el objeto de sesión al que hace referencia mCASSession se usa aquí para descifrar, pero su recuento de referencias no aumenta mientras se usa. Esto significa que es posible que la función de descifrado se ejecute con un objeto de sesión que se liberó, ya que su recuento de referencias se redujo a cero.

Esto permite que un atacante provoque un uso después de liberar (el objeto de sesión se usará después de que se libere) a través de una condición de carrera. Antes de ejecutar el descifrado, el atacante eliminaría la referencia al objeto de la sesión de la biblioteca de la sesión, dejando la mCASSession de Descrambler como la única referencia a la sesión. Entonces, el atacante ejecutaría el descifrado al mismo tiempo que configuraba la sesión del descifrador en otra sesión, lo que puede provocar una condición de carrera. Establecer una sesión diferente para Descrambler liberaría el objeto de la sesión original (su recuento de referencias se reduciría a cero); si esto sucede en medio de mCASSession->decrypt, entonces decrypt estaría usando un objeto de sesión liberado.

Antes de entrar en los detalles de la PoC, hay una nota sobre su efecto.

En este PoC, no se asigna nada en lugar del objeto de sesión liberado; simplemente dejamos que la función de descifrado use un objeto liberado. Uno de los miembros del objeto de sesión que utiliza el descifrado es un Bloqueo de teclas mque es esencialmente un mutex que descifra los intentos de bloquear:

Fragmento de frameworks/av/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp (fuente)

Como es de esperar, cuando se libera un objeto de sesión, se destruye el mutex de su mKeyLock. Por lo tanto, cuando se activa use-after-free, el descifrado intenta usar un mutex ya destruido.

Curiosamente, aquí es donde entra en juego un cambio reciente. Hasta Android 8.1, intentar usar un mutex destruido devolvía un error, que en este caso simplemente se ignoraba. Desde Android 9, intentar usar un mutex destruido da como resultado un abortoque bloquea el proceso:

Fragmento de bionic/libc/bionic/pthread_mutex.cpp (fuente)

Esto significa que, si bien el PoC siempre debe causar un uso después de la liberación, solo Android 9 tiene una forma de detectar si funcionó o no. En versiones anteriores, no hay ningún efecto perceptible. Por lo tanto, el PoC está destinado principalmente a ejecutarse en Android 9.

Después de cubrir el efecto de la PoC, aquí hay una descripción general de alto nivel de las acciones que realiza:

  1. Inicialice los objetos Cas y Descrambler.
  2. Utilice el objeto Cas para crear dos sesiones: Sesión 1 y sesión2. Se hará referencia a ambos desde la biblioteca de la sesión.
  3. Vincule session1 al objeto Descrambler y luego use el objeto Cas para eliminarlo de la biblioteca de sesiones. Ahora, session1 solo tiene una referencia del objeto Descrambler; su número de referencia es uno.
  4. Al mismo tiempo:
    • Ejecute varios subprocesos que realicen la decodificación a través del objeto Descrambler.
    • Establezca la sesión del objeto Descrambler en session2.
  5. Si la ejecución de descifrado en uno de los subprocesos no se devolvió, significa que el PoC fue exitoso y el servicio se bloqueó. Si no es así, vuelva a intentarlo desde el paso 2.

El código fuente completo para el PoC está disponible en GitHub .

Si tiene alguna pregunta, puede enviarme un mensaje privado en Twitter (@tamir_zb).

Deja un comentario