Windows Buffer Overflow Primer
Understanding stack-based overflows by exploiting vulnerserver
Prerequisites:
Before getting into the weeds with exploit development, I highly recommend that you have a solid understanding of assembly (Stack, Registers, Calling convention, etc). If you are curious here are a few resources that I would recommend and should be enough to get you wrting exploits. I will not be covering any assembly as there are tons of posts surrounding basic assembly.
TLDR; I would learn x86/x64 assembly as there is a lot of overlap and you will most likely run into both architectures.
X86 Assembly Guide - Link
x64 Assembly Intro - Link
Intel Intro to x64 Assembly - Link
Journey to the Stack - Link
You will also need PyKd & Mona which can be found:
What is a Stack-based Overflow:
Wikipedia Definition: Stack-based buffer overflow occurs when a program writes to a memory address on the program's call stack outside of the intended data structure, which is usually a fixed-length buffer. Stack buffer overflow bugs are caused when a program writes more data to a buffer located on the stack than what is actually allocated for that buffer.
In lamin terms, you have an area designed for storage with a specific capacity limit. If that storage were to hold more than the allocated storage limit it would overflow...Pretty simple however, this is very simplified.
So it may not be the most accurate definition but should help you understand what a buffer overflow is.
Let us go one step further and show you an example program that has a buffer overflow:
This program creates two variables that hold a buffer with a size of 20. The program calls strncpy which will copy said input into the Password variable and will hold the value:
The call to gets (which takes user input) is where the program is vulnerable. This specific function is vulnerable as it does no checks on the size of the input. If you remember earlier our storage can only hold a certain amount of stuff. If we pass in more than 20 characters this will cause the program to crash.
I found a great illustration showing what the stack looks like if our input is within our buffer and what the stack looks like if our input exceeds the buffer
Exploiting Vulnserver - TRUN:
With all that out of the way let's try exploiting a vulnerable application. We'll start super easy and use a program that was made purposely vulnerable for people like us to practice. Vulnserver can be found on GitHub.
Running vulnserver we are presented with:
Connecting to the vulnserver using netcat we are presented with a greeting message:
Specifically for this blog post, we will be focusing on TRUN. Luckily for us, we do have the source code of this application. However, this is not always the case so be prepared.
Looking at the source we can indeed determine that TRUN function is vulnerable to a buffer overflow attack specifically the function strcpy
, which allows for input greater than the allocated buffer. This means input will start overflowing and spilling onto the stack, which allows us to control the stack and exploit the buffer overflow.
Fuzzing TRUN
Since I am a big smooth brained person, I am using the fuzzing script provided by @The_Keeb
Running this script will take a few minutes but eventually, the program will crash. Looking at our output we see that the fuzzing payload was 10'007 bytes long. We know now that if we provide 10'007 bytes into the input vulnserver crashes and dies. With this, we can being to craft our exploit.
Crashing Vulnserver
With vulnserver running, execute our python script. This should reliably crash the program.
This time load up a debugger of your choice I'll be using WinDbg. And crash vulnserver again
Now run our exploit code again. The program should crash however your debugger should catch it.
Looking at WinDbg we see some good information:
The memory region at the bottom right (showing ESP) has our payload in it (0x41 in ASCII is A)
The registers in the middle of the screen shows that EIP (Instruction Pointer) is
41414141
The command window on the bottom left of the screen shows an Access Violation
The overwrite of EIP (the 32-bit Instruction Pointer) is especially important. The EIP points to the next instruction to be executed, and it is currently pointing at AAAA. This shows the buffer overflow was successful and that we can change the next instruction that the program will execute.
The plan now will be to change the value of EIP to some instruction that will point the flow of execution to the stack (ESP), so that we can execute the custom shellcode that we have placed there.
Controlling EIP
For this next step, we will be using mona.py to help us out. In WinDbg command window enter:
!py mona pattern_create 10000
This will create a text file called pattern.txt, open that text file and paste the pattern into our exploit code. Our exploit should now look like this:
Replacing PATTERN with our pattern.txt file.
With our code updated restart vulnserver and run our exploit again.
Our debugger should catch it. Looking at our registers we are interested in the value of EIP. Take this value and use mona to calculate the offset.
!py mona pattern_offset <value>
This tells us that the string we see in EIP currently occurs in the cyclic pattern that is generated at position 2006.
This means if we cause a buffer overflow, at position 2006 we can enter an address and we will control the EIP. We can test this by changing our exploit code:
This will print 2006 A's, then 4 B's, and the rest C's
Re-running vulnserver and then running our exploit code we should see:
We now see that EIP is 42424242
which is "BBBB" in hex. We know now that we can control EIP and overwrite it.
Jumping to Stack Pointer
Now that we know we can control EIP we need to put something in that will take us to the shellcode we want to run.
To do this we can use a JMP ESP
instruction that will jump to the stack pointer, which is pointing to our payload within the stack.
We can do this with mona:
!py mona jmp -r esp
We can see several occurrences of JMP ESP, in this case, I am going to use the first one but I encourage you to try a different offset.
We need to take this address 0x625011af
and place it within EIP instead of BBBB
We need to remember to enter this value as little-endian, otherwise, our code will not work as intended
Our address 0x625011af
would look like 0xaf115062
To verify that our jump to the stack worked properly we can place a breakpoint right after. Which should pause execution.
We can do this with \xcc
Our code should now look like:
If we restart our vulnserver in WinDbg (CTRL+SHIFT+F5
)
Now run our exploit code.
WinDbg should catch our breakpoint and you can tell we hit our breakpoint because we see cc int 3
which is a breakpoint.
Before we just generate shellcode and slap it in we need to verify if there are any bad characters.
Finding Bad Characters
What are bad characters?
When generating shellcode we could have some bad characetrs. For example the null byte 0x00 is a bad character since it often gets interpreted as the end of the string and then the rest of our shellcode is lost.
To find bad characters we can of course use mona again.
!py mona bytearray
This will generate all possible byte characters.
We can now add this after our breakpoint like so:
Restart vulnserver and re-run our exploit. We should hit our breakpoint to stop execution.
We can see our JMP ESP
below that we can see our breakpoint \xcc
in the small red box. After which we see \x00
which is the first byte in our array. However, after we do not see the expected byte \x01
this means that our code has been mangled. When this happens it means the last byte that we can see that we input is a bad character. This means we can't use the byte \x00
in our exploit code.
With this we can edit out exploit to remove the byte \x00
This will be an iterativey process so it may take a few runs to find all the bad characters.
In this case if we update our code to remove byte \x00
and re-run our exploit we will see this:
We can see the full byte array this time all the way to byte \xff
Now that we have found all the bad characters we can now finally generate our shellcode.
Generating Shellcode
For my shellcode I am going to just use a basic calc generated from msfvenom
The command to do so would look the:
msfvenom -p windows/exec CMD=calc.exe -b '\x00' -f py
-p
is indicating the payload arcitecture
-b
is excluding byte \x00
in our shellcode
-f
is used to define output file type
Our final exploit code should look like this:
Re-runnig our exploit we should see calc pop up:
Summary
In this post we went over what a stack-based buffer overflow is. We then exploited a buffer overflow within Vulnserver specificaly the function TRUN.
Thats all folks, I hope you found this post interesting and informative. Stay tuned as I plan to release more exploit development guides both for myself for OSED prep and you as a reader can continue learning alongside with me!
Exploit code can be found on my GitHub: Here
Resources
Last updated