publicado por Android Platform Hardening Team
En Android 11 continuamos aumentando la seguridad de la plataforma Android. Pasamos a valores predeterminados más seguros, pasamos a un asignador de memoria fortalecido y expandimos el uso de mitigaciones de compilación que defienden contra clases de vulnerabilidad y técnicas de explotación frustrantes.
Inicialización de memoria
Hemos activado formas de inicialización de memoria automática tanto en el espacio de usuario de Android 11 como en el kernel de Linux. Los errores de memoria no inicializados ocurren en C / C ++ cuando la memoria se usa sin haber sido inicializada a un valor seguro conocido. Estos tipos de errores pueden ser confusos e incluso el término "no inicializado" es engañoso. Sin inicializar, puede parecer que una variable tiene un valor aleatorio. En realidad no es accidental. Tiene cualquier valor previamente ingresado allí. Este valor puede ser predecible o incluso controlado por el atacante. Desafortunadamente, este comportamiento puede conducir a una vulnerabilidad grave como un error en la divulgación de información, como omisiones de ASLR o control de secuestro de flujo a través de una pila o un montón de spray. Otro posible efecto secundario del uso de valores no inicializados son las optimizaciones avanzadas del compilador que pueden transformar el código de manera impredecible, ya que esto se considera un comportamiento indefinido por los estándares C relevantes.
En la práctica, los usos de la memoria no inicializada son difíciles de detectar. Tales errores pueden permanecer sin ser detectados en la base del código durante años si la memoria se inicializa con un valor "seguro" la mayor parte del tiempo. Cuando la memoria no inicializada causa un error, a menudo es difícil identificar la fuente del error, especialmente si rara vez se activa.
Eliminar una clase completa de tales errores es mucho más efectivo que la búsqueda individual. La inicialización automática de las variables de la pila se basa en una función del compilador de Clang que le permite elegir inicializar las variables locales con cero o un patrón.
La inicialización a cero proporciona valores predeterminados más seguros para cadenas, punteros, índices y dimensiones. Las desventajas de zero init son valores predeterminados menos seguros para los valores de retorno y que exponen menos errores donde el código subyacente se basa en la inicialización cero. La inicialización del modelo tiende a exponer más errores y generalmente es más seguro para los valores de retorno y menos seguro para cadenas, punteros, índices y tamaños.
Inicialización del espacio del usuario:
La inicialización automática de la pila está habilitada en todo el espacio del usuario de Android. Durante el desarrollo de Android 11, inicialmente seleccionamos el modelo para descubrir los errores basados en zero init y luego cambiamos a zero-init después de unos meses para mayor seguridad. Los desarrolladores del sistema operativo de la plataforma pueden crear con `AUTO_PATTERN_INITIALIZE = true m`
si quieren ayuda para descubrir errores basados en cero init.
Inicialización del kernel:
La inicialización automática de pila y montón se ha fusionado recientemente en el kernel Linux ascendente. Pusimos estas funciones a disposición en versiones anteriores del kernel de Android, incluidas 4.14, 4.19 y 5.4. Estas características fuerzan la inicialización de variables locales y asignaciones de almacenamiento dinámico con valores conocidos que los atacantes no pueden controlar y que son inútiles cuando pierden. Ambas características conducen a una sobrecarga de rendimiento, pero también evitan comportamientos indefinidos que mejoran tanto la estabilidad como la seguridad.
Para la inicialización de la pila del kernel, hemos adoptado el CONFIG_INIT_STACK_ALL
de Linux ascendente. Actualmente se basa en la inicialización del modelo Clang para variables de pila, aunque esto está sujeto a cambios en el futuro.
La inicialización del montón está controlada por dos indicadores de inicio, init_on_alloc e init_on_free, y el primero borra los objetos del montón recién asignados con cero (piense s / kmalloc / kzalloc
a través del núcleo) y este último hace lo mismo antes de que se liberen los objetos (esto ayuda a reducir la duración de los datos confidenciales de seguridad). init_on_alloc
es mucho más compatible con el caché y tiene un menor impacto en el rendimiento (dentro del 2%), por lo que se eligió para proteger los núcleos de Android.
Shield es ahora el asignador nativo predeterminado de Android
En Android 11, Shield reemplaza a jemalloc como el asignador nativo predeterminado de Android. Shield es un asignador de memoria mejorado diseñado para ayudar a detectar y mitigar errores de corrupción de memoria en el montón, como:
Shield no evita por completo la explotación, pero agrega una serie de comprobaciones de integridad que son efectivas para fortalecer el montón contra algunos errores de corrupción de memoria.
También organiza proactivamente el montón de una manera que hace que sea más difícil explotar la corrupción de la memoria, reduciendo la previsibilidad de los modelos de asignación y separando las asignaciones por tamaño.
En nuestras pruebas internas, Scudo ya ha demostrado su valía al descubrir errores de seguridad y estabilidad previamente no detectados.
Búsqueda de errores de seguridad del montón en la memoria salvaje (GWP-ASan)
Android 11 presenta GWP-ASan, una herramienta de detección de seguridad del montón de memoria de producción integrada directamente en el asignador nativo de Shield. GWP-ASan detecta de manera probabilística y proporciona informes procesables para errores de seguridad de la memoria de almacenamiento dinámico cuando ocurren, funciona en procesos de 32 y 64 bits y está habilitado de forma predeterminada para procesos y aplicaciones del sistema.
GWP-ASan también está disponible para aplicaciones de desarrollador a través de una opción de una línea para AndroidManifest.xml de una aplicación, sin el soporte necesario para construir o recompilar bibliotecas predefinidas.
KASAN basado en la etiqueta de software
Continuando con la adopción de la extensión de codificación de memoria Arm (MTE) en Android, Android 11 incluye soporte para el núcleo HWASAN, también conocido como KASAN basado en etiqueta de software El espacio de usuario de HWASAN es compatible con Android 10.
KernelAddressSANitizer (KASAN) es un detector dinámico de errores de memoria diseñado para encontrar errores no vinculados y utilizables después del kernel de Linux. Su modo basado en etiquetas de software es una implementación de software del concepto de etiquetado de memoria para el núcleo. KASAN basado en etiquetas de software está disponible en los núcleos Android 4.14, 4.19 y 5.4 y se puede habilitar con la opción de configuración del núcleo CONFIG_KASAN_SW_TAGS. Actualmente, KASAN basado en etiquetas solo admite el etiquetado de memoria de placa; En el futuro se agregará soporte para otros tipos de memoria (como pilas y globales).
En comparación con el KASAN genérico, el KASAN basado en etiquetas tiene requisitos de memoria significativamente más bajos (consulte esta confirmación del núcleo para más detalles), lo que lo hace utilizable en dispositivos de prueba de alimentos para perros. Otro caso de uso para KASAN basado en etiquetas de software es probar la compatibilidad del código del núcleo existente con la codificación de la memoria. Dado que KASAN basado en etiquetas se basa en conceptos similares al futuro soporte MTE en el núcleo, asegurarse de que el código del núcleo funcione con KASAN basado en etiquetas facilitará la integración de MTE en el núcleo en el futuro.
Expandiendo las mitigaciones de compilación existentes
Continuamos expandiendo las mitigaciones de compilación que también se han implementado en versiones anteriores. Esto incluye agregar desinfectantes completos y limitados a algunas bibliotecas básicas que faltaban. Por ejemplo, la biblioteca de fuentes libminikin y la biblioteca de renderizado libui ahora se desinfectan. Hemos fortalecido la pila de NFC al implementar un desinfectante de desbordamiento entero y un desinfectante de límite en estos componentes.
Además de fuertes mitigaciones como desinfectantes, también continuamos expandiendo nuestro uso de CFI como mitigación de exploits. CFI se ha habilitado en el demonio de red de Android, en el solucionador de DNS y en más de nuestras bibliotecas básicas de javascript, como libv8 y PacProcessor.
La efectividad de nuestro sandbox de códec de software
Antes del lanzamiento de Android 10, anunciamos un nuevo entorno limitado limitado para códecs de software. Estamos realmente satisfechos con los resultados. Hasta ahora, Android 10 es la primera versión de Android después de las infames vulnerabilidades Stagefright en Android 5.0 con cero vulnerabilidades críticas en los marcos multimedia.
Gracias a Jeff Vander Stoep, Alexander Potapenko, Stephen Hines, Andrey Konovalov, Mitch Phillips, Ivan Lozano, Kostya Kortchinsky, Christopher Ferris, Cindy Zhou, Evgenii Stepanov, Kevin Deus, Peter Collingbourne, Elliott Hughes, Kees Cook y Ken Chen por su contribución a este post.