Descubrimiento de vulnerabilidades en bibliotecas de código abierto: análisis de CVE-2020-11863


Los proyectos de código abierto son los componentes básicos de cualquier proceso de desarrollo de application. Como indicamos en nuestro blog anterior, a medida que más y más productos utilizan código fuente abierto, el aumento de la superficie de ataque standard es inevitable, especialmente cuando el código fuente abierto no se audita antes de su uso. Por lo tanto, se recomienda probarlo a fondo en busca de posibles vulnerabilidades y colaborar con los desarrolladores para corregirlas, mitigando eventualmente los ataques. También indicamos que estábamos investigando bibliotecas de gráficos en Windows y Linux, informando múltiples vulnerabilidades en Windows GDI, así como en la biblioteca de gráficos vectoriales de Linux libEMF. Todavía estamos auditando muchas otras bibliotecas de gráficos de Linux, ya que son códigos heredados y no se han probado estrictamente antes.

En la parte 1 de esta serie de blogs, describimos en detalle la importancia de la investigación de código abierto, destacando las vulnerabilidades que informamos en la biblioteca libEMF. También destacamos la importancia de compilar el código con desinfectantes de memoria y cómo puede ayudar a detectar una variedad de errores de corrupción de memoria. En resumen, Handle Sanitizer (ASAN) intercepta las funciones de asignación / desasignación de memoria como malloc () / no cost () y llena la memoria con los respectivos bytes de llenado (malloc_fill_byte / free_fill_byte). También monitorea la lectura y escritura en estas ubicaciones de memoria, lo que ayuda a detectar accesos erróneos durante el tiempo de ejecución.

En este blog site, proporcionamos un análisis más detallado de una de las vulnerabilidades reportadas, CVE-2020-11863, que se debió al uso de memoria no inicializada. Esta vulnerabilidad está relacionada con CVE-2020-11865, un vector de objeto international fuera de los límites del acceso a la memoria en la función GlobalObject :: Locate () en libEMF. Sin embargo, la pila de llamadas fallidas resultó ser diferente, por lo que decidimos examinar esto más a fondo y producir este blog site de análisis profundo.

La información proporcionada por la ASAN fue suficiente para reproducir la falla de vulnerabilidad fuera del fuzzer. A partir de la información de ASAN, la vulnerabilidad parecía ser una desreferencia de puntero nulo, pero esta no era la causa raíz authentic, como veremos a continuación.

Al observar la pila de llamadas, parece que la aplicación se bloqueó mientras lanzaba dinámicamente el objeto, por lo que podría haber múltiples razones. Fuera de esas posibles razones que parecen probables, la aplicación intentó acceder al puntero de tabla digital inexistente o la dirección del objeto devuelta por la función era una dirección salvaje a la que se accedió cuando la aplicación falló. Al obtener más contexto sobre este bloqueo, encontramos un valor de registro interesante durante la depuración. A continuación se muestra el punto de caída en el desmontaje que indica el acceso a la memoria inexistente.

Si miramos el estado de los registros en el punto de caída, es particularmente interesante notar que el registro rdi tiene un valor inusual de 0xbebebebebebebebe. Queríamos profundizar un poco más para ver cómo este valor entró en el registro, lo que resultó en un acceso salvaje a la memoria. Como teníamos la fuente de la biblioteca, pudimos comprobar de inmediato qué significaba este registro en términos de acceso a los objetos en la memoria.

Refiriéndose a la documentación de Tackle Sanitizer, resulta que la ASAN escribe 0xbe a la memoria recién asignada de forma predeterminada, lo que básicamente significa que este valor de 64 bits se escribió pero la memoria no se inicializó. La ASAN llama a esto como el malloc_fill_byte. También hace lo mismo llenando la memoria con cost-free_fill_byte cuando se libera. Esto eventualmente ayuda a identificar errores de acceso a la memoria..

Esta naturaleza de la ASAN también se puede verificar en la fuente libsanitizer aquí. A continuación se muestra un extracto del archivo fuente.

Al observar el seguimiento de la pila en el punto de bloqueo como se muestra a continuación, el bloqueo se produjo en la función SelectObject (). Esta parte del código es responsable de procesar la estructura de registro EMR_SELECTOBJECT del archivo Enhanced Meta File (EMF) y el identificador del objeto gráfico pasado a la función es 0x80000018. Queremos investigar el flujo del código para verificar si esto es algo que proviene directamente del archivo EMF de entrada y puede ser controlado por un atacante.

En la función SelectObject (), mientras se procesa la estructura de registro EMR_SELECTOBJECT, el identificador del objeto GDI se pasa a GlobalObjects.find () como se muestra en el fragmento de código anterior, que a su vez accede al objeto de inventory vector enmascarando el bit de orden outstanding del identificador de objeto GDI y convirtiéndolo en el índice, finalmente devolviendo la referencia del objeto de inventory del vector de objeto utilizando el número de índice convertido. La enumeración de objetos de inventory especifica los índices de objetos gráficos lógicos predefinidos que se pueden utilizar en operaciones gráficas documentadas en el Documentación de MS. Por ejemplo, si el identificador del objeto es 0x8000018, se aplicará un AND con 0x7FFFFFFF, lo que dará como resultado 0x18, que se utilizará como índice del vector de objetos de stock world wide. Esta referencia de objeto de stock se convierte luego dinámicamente en el objeto de gráficos, después de lo cual la función miembro EMF :: GRAPHICSOBJECT getType () se llama para determinar el tipo de objeto gráfico y luego, más adelante en esta función, se vuelve a convertir en un objeto gráfico apropiado (BRUSH, PEN, FONT, PALETTE, EXTPEN), como se muestra en el siguiente fragmento de código.

EMF :: GRAPHICSOBJECT es la clase derivada de EMF :: Object y el diagrama de herencia de la clase EMF :: Object es como se muestra a continuación.

Sin embargo, como se mencionó anteriormente, estábamos interesados ​​en saber si el identificador del objeto, pasado como un argumento al SelectObject función, puede ser controlada por un atacante. Para poder obtener contexto sobre esto, veamos el formato del registro EMR_SELECTOBJECT como se muestra a continuación.

Como notamos aquí, ihObject es el entero sin signo de 4 bytes que especifica el índice de la enumeración de objetos de inventory. En este caso, las referencias de objetos de stock se mantienen en el vector de objetos globales. Aquí, el identificador de objeto de 0x80000018 implica que el índice 0x18 se utilizará para acceder al vector de objetos de inventory world-wide. Si, durante este tiempo, la longitud del vector de objeto es menor que 0x18 y la verificación de longitud no se realiza antes de acceder al vector de objeto, resultará en un acceso a la memoria fuera de los límites.

A continuación se muestra la representación visual del procesamiento del registro de metarchivo EMR_SELECTOBJECT.

Mientras depuramos este problema, habilitamos un punto de interrupción en GlobalObjects.locate () y continuamos hasta que tengamos el identificador de objeto 0x80000018 esencialmente, llegamos al punto donde se está procesando el registro EMR_SELECTOBJECT resaltado anteriormente. Como se muestra a continuación, el identificador del objeto se convierte en el índice (0x18 = 24) para acceder al vector de objeto de tamaño (0x16 = 22), lo que resulta en un acceso fuera de límites, que informamos como CVE-2020-11865.

Continuando con el código, ingresa a la biblioteca de vectores STL stl_vector.h que implementa la expansión dinámica de std :: vectors. Dado que el vector de objetos en este momento tiene solo 22 elementos, el vector STL expandirá el vector al tamaño indicado por el parámetro resaltado, accediendo al vector por índice pasado, y devolverá el valor en esa referencia de objeto, como se muestra en el siguiente fragmento de código, que resulta ser 0xbebebebebebebebe según lo rellenado por la ASAN.

El código united states el std: asignador para administrar la memoria vectorial que se utiliza principalmente para la asignación y desasignación de memoria. En un análisis más detallado, resulta que el valor devuelto, 0xbebebebebebebebe en este caso, es el puntero digital del objeto de stock inexistente, que se desreferencia durante la conversión dinámica, lo que resulta en un bloqueo.

Como se mencionó en nuestro blog anterior, las correcciones a la biblioteca se publicaron en una versión posterior, disponible aquí.

Conclusión

Si bien el uso de código de terceros en los productos ciertamente ahorra tiempo y aumenta la velocidad de desarrollo, potencialmente conlleva un aumento en el volumen de vulnerabilidades, especialmente cuando el código permanece sin auditar e integrado en los productos sin ninguna prueba. Es extremadamente crítico realizar pruebas de fuzz de las bibliotecas de código abierto utilizadas, lo que puede ayudar a descubrir vulnerabilidades más temprano en el ciclo de desarrollo y brinda la oportunidad de corregirlas antes de que se envíe el producto, mitigando así los ataques. Sin embargo, como enfatizamos en nuestro weblog anterior, es elementary fortalecer la colaboración entre los investigadores de vulnerabilidades y la comunidad de código abierto para continuar con las divulgaciones responsables, permitiendo que los mantenedores del código las aborden de manera oportuna.





Enlace a la noticia first