Linux Kernel ROP - ¿Regresando a la zona de usuario desde el contexto del kernel?

5

Tengo un módulo vulnerable de Kernel de Linux (32 bits), que puedo explotar con éxito, y he ganado privilegios con él. Básicamente, mi exploit usa una cadena de ROP para deshabilitar SMEP, y salta directamente a mi shellcode asignado en la zona de usuario. Mi código de shell en userland realiza una llamada a commit_creds(prepare_kernel_creds(0)); e intenta volver a mi código de usuario.

Ahora no entiendo cómo volver al modo de usuario desde el modo kernel. Varios artículos señalan que debo usar las instrucciones de ensamblado de iret para volver al modo usuario. Inserté claramente un iret después del código de shell, pero parece que no funciona.

Escribo en un archivo de dispositivo, y desde el rastreo de llamadas:

? vfs_write
? SyS_write
? do_fast_syscall_32
? entry_SYSENTER_32

Observo que esta es una llamada rápida al sistema, y debe regresar a través de la instrucción sysexit .

Ahora, ¿cómo vuelvo a userland sin entrar en pánico en el kernel? Necesito saber qué llamada debo realizar ( iret / sysexit ) y cómo realizarla de forma limpia.

(He revisado los manuales de Intel y un montón de otros recursos, pero nada me ha ayudado mucho hasta ahora).

    
pregunta Mukesh Sai Kumar 11.01.2018 - 13:27
fuente

2 respuestas

4

Terminé escribiendo mi shellcode de una manera diferente. Como no pude averiguar cómo regresar, dejé que el núcleo hiciera el trabajo pesado por mí, al regresar a la zona de usuario. La idea era ejecutar mi bit de escalada de privilegios y saltar al lugar donde se suponía que debía regresar la función vulnerable, con los registros y la pila arreglados.

Tan pronto como el kernel regresó de la función vulnerable (cuando no se desbordó), noté algo a través de gdb . (Las direcciones son imaginarias, pero de todos modos explica el concepto).

(gdb) x/i $eip
0xadd1: ret
(gdb) x/xw $esp
0xadd1: 0xadd2
(gdb) x/6i 0xadd2
0xadd2: add esp,0x40
0xadd3: pop ...
0xadd4: pop ...
0xadd5: pop ...
0xadd6: pop ...
0xadd7: ret

Por lo tanto, inmediatamente después de la devolución, 0x40 bytes de la pila no se utilizan y simplemente desaparecerán con la instrucción add esp . Por lo tanto, aprovechando este hecho, construí una cadena ROP (ya la había construido mientras escribía esta pregunta, su trabajo era deshabilitar SMEP y saltar a mi código de usuario del país), que tenía 24 bytes de longitud. 4 bytes sobrescribirían el EIP en el momento de la devolución, y los 20 restantes ( 0x14 ) lo seguirían directamente en la pila no utilizada. Esto nos deja con 0x2c bytes de pila no utilizada todavía.

Pero si regresamos a 0xadd2 , corremos el riesgo de perder un 0x14 bytes más de información valiosa del registro de la pila, y rellenar los registros con datos no válidos. Podríamos add 0x2c a esp en nuestro código de usuario de la zona del usuario, y saltar directamente a 0xadd3 , omitiendo la instrucción add real.

También observando desde la sesión de depuración, todo, excepto eax y ebx , se estaban restaurando correctamente. Como mi desbordamiento los había destruido a ambos, tuve que restaurarlos con valores similares a los casos en que la función hizo un retorno limpio. (Hacer esto fue simple: establecí un punto de interrupción en 0xadd2 y extraje los valores de info reg )

Por lo tanto, mi código de usuario final fue el siguiente:

Execute privesc payload -> add esp,0x2c -> register fixup -> jump to 0xadd3

Haciendo esto, la ruta del código se volvió perfectamente limpia, y el kernel hizo la tarea de saltar al modo usuario para mí.

    
respondido por el Mukesh Sai Kumar 22.01.2018 - 17:44
fuente
-1

Lea acerca de call_usermodehelper_setup () y call_usermodehelper_exec (). Usando estas llamadas puedes ejecutar código directamente desde el kernel.

Ejemplo:

enlace

    
respondido por el android_dev 11.01.2018 - 19:57
fuente

Lea otras preguntas en las etiquetas