Análisis de la vulnerabilidad de escalada de privilegios del servicio en primer plano CVE-2020-0108
1. Antecedentes de la vulnerabilidad
- En el parche de AOSP de 2020-08, se reveló una vulnerabilidad en la capa de framework AMS, con el identificador CVE-2020-0108 y calificada como High. Se trata de una vulnerabilidad lógica en el manejo de los servicios en primer plano en AMS; un atacante que la explote con éxito puede omitir la visualización de la notificación del servicio en primer plano y continuar ejecutándose en segundo plano. El ataque debe ser iniciado por una aplicación maliciosa local y no requiere interacción del usuario; si el usuario ha otorgado otros permisos a la aplicación, puede causar un daño mayor, como el rastreo continuo de la ubicación o la grabación silenciosa.
2. Detalles de la vulnerabilidad
- El servicio en primer plano es un concepto introducido por Google en Android 8.0. Dado que Android 8.0 no permite iniciar servicios en segundo plano desde el fondo, se diseñó el concepto de servicio en primer plano. Este tipo de servicio tiene una prioridad más alta y puede ejecutarse en segundo plano durante mucho tiempo, pero debe vincular una notificación dentro de los 5 segundos posteriores a su inicio; de lo contrario, será eliminado. En realidad, el servicio en primer plano sigue ejecutándose en "segundo plano", pero como está vinculado a una notificación visible para el usuario, Google lo denomina "servicio en primer plano".
- Esta vulnerabilidad tiene dos métodos de ataque, que corresponden a dos vulnerabilidades lógicas.
- La primera vulnerabilidad se encuentra en el método onNotificationError de NotificationManagerService, que no maneja correctamente las excepciones en la visualización de notificaciones.
// frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
@Override
public void onNotificationError(int callingUid, int callingPid, String pkg, String tag,
int id, int uid, int initialPid, String message, int userId) {
cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
REASON_ERROR, null);
}
- En este caso, después de que se inicia el servicio en primer plano, incluso si la notificación no se muestra correctamente, el servicio en primer plano no se termina. Por ejemplo, si el servicio en primer plano utiliza un diseño personalizado al crear la notificación y pasa un valor resID inexistente al construir el objeto RemoteViews, entonces, cuando NotificationManagerService analiza el diseño de la notificación, falla y lanza una excepción, invocando al método onNotificationError. Dado que el método onNotificationError solo llama al método cancelNotification para cancelar la notificación, sin terminar el servicio ni toda la aplicación, el servicio en primer plano continúa ejecutándose sin mostrar la notificación.
- La segunda vulnerabilidad se encuentra en el método postNotification de ServiceRecord, que no maneja correctamente las excepciones en la visualización de notificaciones, sino que lanza la excepción al programa del usuario.
// frameworks/base/services/core/java/com/android/server/am/ServiceRecord.java
public void postNotification() {
final int appUid = appInfo.uid;
final int appPid = app.pid;
if (foregroundId != 0 && foregroundNoti != null) {
//...
ams.mHandler.post(new Runnable() {
public void run() {
//...
try {
//...
} catch (RuntimeException e) {
Slog.w(TAG, "Error showing notification for service", e);
// If it gave us a garbage notification, it doesn't
// get to be foreground.
ams.setServiceForeground(instanceName, ServiceRecord.this,
0, null, 0, 0);
ams.crashApplication(appUid, appPid, localPackageName, -1,
"Bad notification for startForeground: " + e);
}
}
});
}
}
- En este caso, después de que se inicia el servicio en primer plano, si el programa del usuario captura la excepción del hilo principal, incluso si la notificación no se muestra correctamente, el servicio en primer plano no se termina. Por ejemplo, si el servicio en primer plano pasa un Channel ID no válido al crear la notificación, se lanzará una excepción al enviarla en el método postNotification de ServiceRecord. Durante el manejo de la excepción, solo se llama al método crashApplication de AMS para lanzar una excepción en el hilo principal de la aplicación; pero si la aplicación captura la excepción en el hilo principal, la aplicación no se bloquea. De este modo, el servicio en primer plano continúa ejecutándose sin mostrar la notificación.