Encontraron una vulnerabilidad en openSSH 9.1 que permite eludir malloc
Hace poco Qualys (una empresa de tecnología que se especializa en la seguridad en la nube) dio a conocer que ha encontrado una manera de eludir malloc y la protección sin dobles para iniciar un traspaso al código utilizando una vulnerabilidad en OpenSSH 9.1.
Hasta el momento se ha determinado que dicha vulnerabilidad es solo «teórica», ya que es poco probable que cree un exploit funcional. Al mismo tiempo, la posibilidad de crear un exploit que funcione sigue siendo una gran pregunta.
Sobre la vulnerabilidad se menciona que el truco para eludir las protecciones doble libre y uso después de libre de malloc es reasignar la memoria que estaba ocupada por options.kex_algorithms tan pronto como esté libre.
Desde el punto de vista de malloc, no se hace ningún intento de liberar, leer o escribir memoria que ya está libre; desde el punto de sshd, sin embargo, se produce un ataque de aliasing, ya que dos punteros diferentes a dos objetos diferentes se refieren a la misma porción de memoria, y una escritura en un objeto sobrescribe al otro objeto.
Esto abre un mundo de posibilidades.
Comenzamos nuestra investigación sobre el ratón de biblioteca de Debian (que usa el código de glibc
malloc), pero eventualmente cambiamos a OpenBSD 7.2, porque OpenBSD
malloc (a pesar de su programación muy defensiva) tiene dos características que
hágalo especialmente interesante para este error doble libre en particular:
La vulnerabilidad se debe a una doble liberación de un área de memoria en la etapa de preautenticación. Para crear condiciones para la vulnerabilidad, basta con cambiar el banner del cliente SSH a «SSH-2.0-FuTTYSH_9.1p1» (u otro cliente SSH antiguo) para lograr la configuración de «SSH_BUG_CURVE25519PAD» y «SSH_OLD_DHGEX» banderas Después de configurar estos indicadores, la memoria para el búfer «options.kex_algorithms» se libera dos veces.
Los investigadores de Qualys, en el curso de la manipulación de la vulnerabilidad, pudieron obtener control sobre el registro del procesador «% rip», que contiene un puntero a la siguiente instrucción que se ejecutará. La técnica de explotación desarrollada permite transferir el control a cualquier punto del espacio de direcciones del proceso sshd en un entorno OpenBSD 7.2 no actualizado, que se envía por defecto con OpenSSH 9.1.
Actualización rápida: pudimos obtener un control arbitrario del «rip» a través de este error (es decir, podemos saltar donde queramos en sshd’s espacio de direcciones) en una instalación sin parches de OpenBSD 7.2 (que se ejecuta
OpenSSH 9.1 por defecto). Esto no es de ninguna manera el final de la historia: este era solo el paso 1, omitir el malloc y las protecciones dobles.El siguiente paso que pueden o no ser factibles en absoluto, son:
– paso 2, ejecutar código arbitrario a pesar de ASLR, NX y ROP
protecciones (esto probablemente requerirá una fuga de información, ya sea
por el mismo error o por un error secundario);– paso 3, escapar de la caja de arena de sshd (a través de un error secundario, ya sea en
el proceso padre privilegiado o en el ataque reducido del núcleo
superficie).
Se observa que el prototipo propuesto es la implementación de solo la primera etapa del ataque: para crear un exploit que funcione, es necesario omitir los mecanismos de protección ASLR, NX y ROP, y salir del aislamiento de la zona de pruebas, lo cual es poco probable.
Resolver el problema de eludir ASLR, NX y ROP requiere obtener información sobre las direcciones, lo que se puede lograr identificando otra vulnerabilidad que conduce a la fuga de información. Un error en un proceso principal privilegiado o kernel puede ayudar a salir del entorno limitado.
Se menciona que la vulnerabilidad funciona de la siguiente manera:
- -Primero, se libera options.kex_algorithms en compat_kex_proposal(), fingiendo el cliente ssh es un antiguo cliente «FuTTY».
- -En segundo lugar, se reasigna el fragmento que estaba ocupado por options.kex_algorithms, con una estructura EVP_AES_KEY cuyo tamaño es de 264 bytes, seleccionando el cifrado «aes128-ctr» durante la fase de intercambio de claves. Esta reasignación ocurre con una probabilidad de ~1/32.
- – En tercer lugar, se libera (nuevamente) el fragmento que estaba ocupado por options.kex_algorithms (y ahora está ocupado por la estructura EVP_AES_KEY) en kex_assemble_names() (a través de mm_getpwnamallow()). Esto sucede si y solo si el primer byte del fragmento es ‘+’, ‘-‘ o ‘^’ (de lo contrario, kex_assemble_names() devuelve un error y se llama a fatal_fr()).
- – En cuarto lugar, se reasigna el fragmento que estaba ocupado por options.kex_algorithms (y todavía se hace referencia a él como una estructura EVP_AES_KEY ahora), con una cadena de 300 ‘A’ bytes, «authctxt->user» o «authctxt->style» durante la fase de autenticación. Esta reasignación, que efectivamente sobrescribe toda la estructura EVP_AES_KEY con bytes ‘A’, ocurre con una probabilidad de ~2/32.
- – Por último, se salta a 0x4141414141414141 cuando sshd llama a EVP_Cipher(), porque la estructura EVP_AES_KEY contiene un puntero de función que fue sobrescrito por nuestros bytes ‘A’ y que es llamado por CRYPTO_ctr128_encrypt_ctr32() (a través de EVP_Cipher()).
Finalmente si estás interesado en poder conocer más al respecto, puedes consultar los detalles en el siguiente enlace.