Explotación de la memoria: vulnerabilidades de salida tardías, ciegas e ineludibles

0x01 Prefacio

En el campo de la seguridad informática, el peligro de una vulnerabilidad suele estar estrechamente relacionado con su amplitud y los posibles métodos de ataque. Hoy profundizaremos en una vulnerabilidad increíblemente peligrosa que existe en una función común llamada "salir" que se ejecuta cuando se cierra un programa. Ya sea en el sistema operativo o en la aplicación, "salir" es una función omnipresente, que generalmente se usa para salir del programa con elegancia. Pero esta ubicuidad también lo convierte en un objetivo potencial.

La amenaza de esta vulnerabilidad es que no sólo existe en varios programas, sino que también tiene múltiples métodos de ataque potenciales. Un atacante puede aprovechar esta vulnerabilidad para ejecutar código malicioso, obtener privilegios del sistema o realizar otras acciones maliciosas. Para comprender la amenaza de esta vulnerabilidad, debemos analizar en profundidad los principios detrás de ella y las diferentes formas de explotarla.

En este artículo, exploraremos los detalles de esta vulnerabilidad y analizaremos en detalle las dos formas principales de explotación: una es redirigir el flujo del programa a una función en la biblioteca libc y la otra es redirigir el flujo del programa a un código. segmento en el propio programa. Profundizaremos en la mecánica de ambos ataques y mostraremos un ejemplo de un exploit de la vida real.

"Blindless" es un tema del concurso WMCTF 2023. Aunque no es difícil, lleva mucho tiempo comprender y explotar las vulnerabilidades en profundidad. Este artículo resume los métodos de utilización de "exit_hook2libc" y "exit_hook2elf", con el objetivo de compartirlos con todos. La clave para esta pregunta es tener un conocimiento profundo de la función de "salida" que se ejecuta cuando el programa sale y cómo explotar la vulnerabilidad de diferentes maneras.

0x02 n posturas de exit_hook


La dirección base se coloca aquí para su referencia y se utiliza para calcular el desplazamiento de la instrucción.

salida_hook2libc

El primero es p &_rtld_global(ver la dirección), tiene un elemento rtld_lock_default_lock_recursivede suma rtld_lock_default_unlock_recursiveque se puede cambiar para llamar.

Tenga en cuenta que debe usar Docker o una máquina virtual; de lo contrario, estará en la cárcel si no tiene una tabla de símbolos.

Ejecutar p _rtld_global. Mira esas dos rtld_lock_default_lock_recursivesumas rtld_lock_default_unlock_recursive, son ellas dos. Podemos modificar su contenido y llamarlos como exithook (llamada directa). Copie lo siguiente p &xxxpara ver su dirección.


Tenga en cuenta que este programa se llama Xiaoshuai y el primer parámetro que llama es rdi.Sí _rtld_global+2312, podemos controlar sus parámetros para /bin/sh\x00hacer cosas malas (si rtld_lock_default_lock_recursivese puede cambiar system).


Entonces rtld_lock_default_unlock_recursiveel parámetro también es el desplazamiento de 2312.

Tenga en cuenta que 2312 es decimal.


Bien, modifiquemos estos dos lugares y podremos hacer lo que queramos, pero exit_hookaún no hemos terminado.

Y estrictamente hablando, esto no está completo exit_hook2libc. Si conoce la dirección de elf, puede volver completamente a la función de elf.

Para ayudar a los estudiantes de seguridad de redes a aprender, obtenga un conjunto completo de materiales de forma gratuita:
① Mapa mental de la ruta de crecimiento y aprendizaje de seguridad de redes
② Más de 60 kits de herramientas clásicos de seguridad de redes
③ Más de 100 informes de análisis SRC
④ Más de 150 programas electrónicos de tecnología práctica de defensa y ataques de seguridad de redes libros
⑤ La guía de examen de certificación CISSP más autorizada + banco de preguntas
⑥ Más de 1800 páginas del manual de habilidades prácticas de CTF
⑦ La última colección de preguntas de entrevistas de las principales empresas de seguridad de redes (incluidas las respuestas)
⑧ Guía de detección de seguridad del cliente de la aplicación (Android + IOS)

A continuación, hay algo aún más interesante: puede controlar la dirección en el programa (salto directo o salto de dirección indirecto).

salida_hook2elf

1.Llamada indirecta

Aquí, la primera es una llamada indirecta, es decir call qword ptr [寄存器], la instrucción es obtener la dirección de la memoria apuntada por la dirección del registro y luego llamar.

Para utilizar la llamada indirecta, podemos modificar su desplazamiento a cualquier función obtenida de la tabla y luego _rtld_global+2312usarlo con el parámetro rdi.

Por ejemplo, modifique _rtld_global+2312 a "/bin/sh\x00"


La dirección base y el desplazamiento de esto existen link_map, para que se pueda encontrar su dirección.


La depuración puede ver que obtendrá la dirección base elf de la memoria en esta dirección y luego obtendrá el desplazamiento a través de la dirección almacenada en la dirección link_map + 0x110. Podemos cambiar la dirección base o elegir cambiar el desplazamiento.
La dirección link_map +0x110 almacena el desplazamiento de la primera llamada indirecta.

Tenga en cuenta que la dirección almacenada tiene un desplazamiento -8, es decir, si desea cambiarla, debe cambiarla al destino -8.

2. Llama directamente

La dirección link_map +0xa8 es el desplazamiento donde se almacena la segunda llamada directa.

Tenga en cuenta que la dirección almacenada tiene un desplazamiento -8, es decir, si desea cambiarla, debe cambiarla al destino -8.

Si cambia el desplazamiento, será lo mejor y también puede formar directamente una cadena de llamadas. Pero si no hay compensación, sólo puedes cambiar la dirección base, es decir, de dónde p &lsale. Pero esto definitivamente dañará el primero call r14y hará imposible proceder con normalidad.

Pero descubrí que hay un lugar donde se puede saltarse el juicio call r14.

Aquí es donde test edx,edxestán edx y edx entre sí, dejando el bit de bandera. En pocas palabras, si es 0, no habrá salto. Si es 1, entonces salta.

En el ensamblaje x86, jelas instrucciones hacen:

  1. Compruebe si el indicador cero (ZF) está establecido en 1.
  2. Si la bandera cero se establece en 1, se realizará un salto a la ubicación de destino especificada.

Al retroceder se descubrió que era link_map+0x120la dirección tomada, es decir, si desea que sea 0 aquí, ¡simplemente apunte la dirección allí al lugar donde es 0! Sin embargo, también debe tenerse en cuenta que la dirección tomada aquí es +8, lo que significa que debemos cambiarla a la dirección de destino -8 para mejorarla. Simplemente busque la sección bss y similares aquí.

Después de completar esta operación, puede modificar la dirección base para lograr el efecto de cualquier llamada directa. Incluso si no hay fugas, puede volver directamente al programa (por ejemplo, hay una puerta trasera en esta pregunta). Si es así, ¡puedes hacer lo que quieras! (Igual que antes, si hay una fuga, realmente puedes hacer lo que quieras).

0x03 experiencia

Bueno, dado que esta pregunta tiene una función Brainfuck que puede ejecutar la escritura en cualquier dirección, los privilegios se pueden escalar según el exit_hook anterior.

from pwn import *


n2b = lambda x    : str(x).encode()
rv  = lambda x    : p.recv(x)
rl  = lambda     :p.recvline()
ru  = lambda s    : p.recvuntil(s)
sd  = lambda s    : p.send(s)
sl  = lambda s    : p.sendline(s)
sn  = lambda s    : sl(n2b(n))
sa  = lambda t, s : p.sendafter(t, s)
sla = lambda t, s : p.sendlineafter(t, s)
sna = lambda t, n : sla(t, n2b(n))
ia  = lambda      : p.interactive()
rop = lambda r    : flat([p64(x) for x in r])
uu64=lambda data :u64(data.ljust(8,b'\x00'))

while True:

        context(os='linux', arch='amd64', log_level='debug')
        p = process('./main')
        context.terminal = ['tmux','new-window' ,'-n','-c']
        #gdb.attach(p)
        sla('ze',b'-10')#分配到libc上(用mmap)
        sla('ze',b'256')

        pay = b'@'+p32(2148618432)#到ld的地址+0x2f190的偏移
        pay += b'@'+p32(2148618432)
        pay +=b'.' + b'\xb1'
        pay += b'>.' + b'\x7c'#使得加了偏移之后是后门函数地址
        pay += b'@'+p32(0x11f)#修改0x120的地址,指向0,跳过call r14
        pay +=b'.' + b'\x00'
        pay += b'q'
        sla('code\n',pay)

        re = p.recvrepeat(0.1)#一直接收直到有回显
        #如果是system的话可以发一个cat flag再这样
        #这是个很好的爆破方式,学习学习
        if re:
            print('pwned!get your flag here:',re)
            exit(0)
        p.close()

Supongo que te gusta

Origin blog.csdn.net/qq_38154820/article/details/133064271
Recomendado
Clasificación