In this article we will be writing an exploit for a 32-bit Windows application vulnerable to Structured Exception Handler (SEH) overflows. While this type of exploit has been around for a long time, it is still applicable to modern systems.
This guide was written to run on a fresh install of Windows 10 Pro (either 32-bit or 64-bit should be fine) and, as such, you should follow along inside a Windows 10 virtual machine. This vulnerability has also been tested on Windows 7, however the offsets are the ones from the Windows 10 machine referenced in this article. The steps to recreate the exploit are exactly the same.
We will need a copy of X64dbg which you can download from the official website and a copy of the ERC plugin for X64dbg from here.Because the vulnerable application we will be working with is a 32-bit application, you will need to download either the 32-bit version of the plugin binaries or compile the plugin manually. Instructions for installing the plugin can be found on the Coalfire GitHub page.
If using Windows 7 and X64dbg with the plugin installed crashes and exits when starting, you may need to install .Net Framework 4.7.2, which can be downloaded here.
Finally, we will need a copy of the vulnerable application (R.3.4.4), which can be found here. In order to confirm everything is working, start X64dbg and select File -> Open, then navigate to where you installed R.3.4.4 and select the executable. Click through the breakpoints (there are many breakpoints to click through) and the R.3.4.4 GUI interface should pop up. Now in X64dbg’s terminal type:
You should see the following output:
An exception handler is a programming construct used to provide a structured way of handling both system- and application-level error conditions. Commonly they will look something like the code sample below:
Windows supplies a default exception handler for when an application has no exception handlers applicable to the associated error condition. When the Windows exception handler is called, the application will close and an error message similar to the one in the image below will be displayed:
Exception handlers are stored in the format of a linked list with the final element being the Windows default exception handler. This is represented by a pointer with the value 0xFFFFFFFF. Elements in the SEH chain prior to the Windows default exception handler are the exception handlers defined by the application.
Each element in the SEH chain (an SEH record) is 8 bytes in length consisting of two 4-byte pointers. The first points to the next SEH record and the second one points to the current SEH records exception handler:
When an exception occurs, the operating system will traverse the SEH chain to find a suitable exception handler to handle the exception. The values from this handler will then be pushed onto the stack at ESP+8.
Each process contains a Thread Environment Block (TEB), which can be useful to exploit developers and is pointed to by FS:[0].
The TEB contains information such as the following:
First element in the SEH list is located at FS:[0x00].
Address of the PEB (which contains a list of modules loaded by the application).
Address of the Thread Local Storage (TLS) array.
An image representation of the SEH chain can be seen below:
If you would like to view a collection of exception handlers under normal conditions, compile the code below into an executable using Visual Studio and then run it using X64dbg:
When navigating to the SEH tab you should see a number of exception handler records consisting of two 4-byte sequences each:
Confirming that the application is vulnerable to an SEH overflow requires us to pass a malicious input to the program and cause a crash. In order to create the malicious input, we will use the following Python program, which creates a file containing 3000 A’s:
Copy the contents of the file and move to the R.3.4.4 application, click Edit -> GUI preferences (if you are running Windows 10 at this point you will need to switch back to X64dbg and click through two more break points), then in the “GUI Preferences” window, paste the file contents into “Language for menus,” then click “OK.” A message box will appear giving an error message. Click through this and then switch back to X64dbg to examine the crash.
As in the first part in this series , the EIP register has been overwritten, indicating this application is also vulnerable to a standard buffer overflow (you can write an exploit for this type of vulnerability as well using this application if you wish). In this article, however, we are doing an SEH overflow and, if we navigate to X64dgb’s SEH tab, we can see that the first SEH record has been overwritten.
At this point we have confirmed that the application is vulnerable to an SEH overwrite and we can continue to write our exploit code.
In order to exploit an SEH overflow, we need to overwrite both parts of the SEH record. As you can see from the diagram above, an SEH record has two parts: a pointer to the next SEH record and a pointer to the current SEH records exception handler. As such, when you overwrite the pointer to the current exception handler, you have to overwrite the pointer to the next exception handler as well because the pointer to the next exception handler sits directly before the pointer to the current exception handler on the stack.
When an exception occurs, the application will go to the current SEH record and execute the handler. As such, when we overwrite the handler, we need to put a pointer to something that will take us to our shell code.
This is done by executing a POP, POP, RET instruction set. What this set does is POP 8 bytes off the top of the stack and then a returns execution to the top of the stack (POP 4 bytes off the stack, POP 4 bytes off the stack, RET execution to the top of the stack). This leaves the pointer to the next SEH record at the top of the stack.
As discussed earlier, if we overwrite an SEH handler we must overwrite the pointer to the next SEH record. Then, if we overwrite the next SEH record with a short jump instruction and some NOPs, we can jump over the SEH record on the stack and land in our payload buffer.
Now that we know we can overwrite the SEH record, we can start building a working exploit. As was the case in the previous episode of this series, we will be using the ERC plugin for X64dbg. So, let’s ensure we have all our files being generated in the correct place with the following commands:
If you are not using the same machine as last time, you may want to reassign the project author.
Now that we have assigned our working directory and set an author for the project, the next task is to identify how far into our string of A’s that the SEH record was overwritten. To identify this, we will generate a non-repeating pattern (NRP) and include it in our next buffer.
We can add this into our exploit code, so it looks like the following:
Run the Python program and copy the output into the copy buffer and pass it into the application again. It should cause a crash. Run the following command to find out how far into the pattern the SEH handler was overwritten:
The output should look like the following image. The output below indicates that the application is also vulnerable to a standard buffer overflow as was noted earlier:
The output of FindNRP indicates that after 1008 characters the SEH record was overwritten (this will be ~900 if you are on Windows 7). We will now test this by filling both the SEH handler pointer and next SEH record pointer with specific characters.
After providing the output to the application, the SEH tab should show the following results:
In the previous installment of this series we covered identifying bad characters. You can review that here if you need to. The process for this exploit, however, is exactly the same and we will not be covering it in this installment. The bad characters for this input are “\x00\x0A\x0D”.
Now that we have control over the SEH record, we need to find a pointer to a POP, POP, RET instruction set. We can do this with the following command:
Command:
Introduction
In this article we will cover the creation of an exploit for a 32-bit Windows application vulnerable to a buffer overflow using X64dbg and the associated ERC plugin. As this is the first article in this series, we will be looking at an exploit where we have a complete EIP overwrite and ESP points directly into our buffer. A basic knowledge of assembly and the Windows operating system will be useful, however, it is not a requirement.
This guide was written to run on a fresh install of Windows 7 (either 32-bit or 64-bit should be fine) and as such you should follow along inside a Windows 7 virtual machine. A Kali virtual machine will also be useful for payload generation using MSFVenom.
We will need a copy of X64dbg which you can download from . A copy of the ERC plugin for X64dbg as the vulnerable application we will be working with is a 32-bit application you will need to either download the or compile the plugin manually. Instructions for installing the plugin can be found on the .
Finally, we will need a copy of the vulnerable application (StreamRipper 2.6) which can be found .In order to confirm everything is working, start X64dbg, File --> Open --> Navigate to where you installed StreamRipper and select the executable. Click through the breakpoints and the interface should pop up. Now in X64dbg’s terminal type:
You should see the following output:
All processes use memory, regardless of what operating system (OS) they are running on. How that memory is managed is OS dependent; today we will be exploiting a Windows application and we are going to have a little primer on memory under Windows.
Processes do not access physical memory directly. Processes use virtual addresses which are translated by the CPU to a physical address when accessed. As such, multiple values can be stored at the same address (i.e., 0x12345678) while being in different processes as they will each refer to different physical memory addresses.
When a process is started in a Win32 environment, a virtual address is assigned to it. In a Win32 environment, the address range is 0x00000000 to 0xFFFFFFFF of which 0x00000000 to 0x7FFFFFFF is for userland processes and 0x7FFFFFFF to 0xFFFFFFFF is for kernel processes.
Each time a process calls a function, a stack frame is created. A stack frame stores things like the address to return to on completion of the function and the instructions to be carried out by the function.
The stack starts at a high address and proceeds to lower addresses as instructions are executed. 32-bit Intel CPUs use the ESP register to access the stack directly. ESP points to the top of the stack frame (the lowest addresses). Pushes will decrement ESP by 4 and POPs will increment ESP by 4.
The stack is a Last In First Out (LIFO) data structure, created and assigned to each thread in a process upon creation of that thread. When the thread is destroyed, the associated stack is also destroyed.
The stack is one part of the memory assigned to a specific process and is the structure within which the buffer overflow demonstrated in this article takes place. A more complete image detailing the Win32 process memory map can be seen below.
EAX: 32-bit general-purpose register with two common uses: to store the return value of a function and as a special register for certain calculations.
EBX: General-purpose register. It has no specific uses.
ECX: General-purpose register that is used as a loop counter.
EDX: Extension of the EAX register used for more complex calculations.
ESI: Source register, often used as a pointer to the input of an operation.
EDI: Destination register that is often used as a pointer to the result of an operation.
EBP: Base pointer, all functions and variables are at offsets of EBP.
ESP: Stack pointer, stores a pointer to the top of the stack.
EIP: Instruction pointer, EIP points at the instruction executed by the CPU.
In order to confirm the application is vulnerable to a buffer overflow, we will need to pass a malicious input to the program and cause a crash. We will use the following python program to create a file containing 1000 As.
Copy the content of the file to the copy buffer. In StreamRipper, double click on "Add" in the "Station/Song Section" and paste the output in "Song Pattern"
You should get the following crash. Notice the 41414141 in the EIP register. The character “A” in ASCII has the hex code 41 indicating that our input has overwritten the instruction pointer.
Now that we know we can overwrite the instruction pointer, we can start building a working exploit. To do this, we will be using the ERC plugin for X64dbg. The plugin creates a number of output files we will be using, so to begin with, let’s change the directory those files will be written to.
You can also set the name of the author which will be output into the files using the following command.
Now that we have assigned our working directory and set an author for the project, the next task is to identify how far into our string of As that EIP was overwritten. To identify this, we will generate a non-repeating pattern (NRP) and include it in our next buffer.
If you now look in your working directory, you should have a file named Pattern_Create_1.txt and the output from ERC should look something like the following image.
We can add this into our exploit code, so it looks like the following:
Run the python program and copy the output into the copy buffer and pass it into the application again. It should cause a crash. Run the following command to find out how far into the pattern EIP was overwritten.
The output should look like the following image. The output below indicates that the application is also vulnerable to a Structed Exception Handler (SEH) overflow, however, exploitation of this vulnerability is beyond the scope of this article.
The output of FindNRP indicates that after 256 characters EIP is overwritten. As such we will test this by providing a string of 256 As, 4 Bs and 740 Cs. If EIP is overwritten by 4 Bs, then we have confirmed that all our offsets are correct.
Our exploit code should now look like the following:
Which, after providing the string to the application, should produce the following crash:
In this context, bad characters are characters that alter our input string when it is parsed by the application. Common bad characters include things such as 0x00 (null) and 0x0D (carriage return), both of which are common string terminators.
In order to identify bad characters, we will create a string containing all 255 character combinations and then remove any that are malformed once in memory. In order to create the string, use the following command:
A text file will be created in the working directory (ByteArray_1.txt), containing the character codes that can be copied into the python exploit code and a .bin file which is what we will compare the content in memory with to identify differences.
We can now copy the bytearray into our exploit code, so it looks like the following:
Now, when we generate our string and pass it to the application, we view where the start of our buffer is by right clicking the ESP register and selecting “follow in dump” which identifies ESP points directly to the start of our string. Using the following command, we can identify which characters did not transfer properly into memory:
The following output identifies that numerous characters have not properly been transferred into memory. As such we should remove the first erroneous character and retry the steps again.
Repeat these steps until you have removed enough characters to get your input string into memory with no alterations like in the image below. At a minimum you will need to remove 0x00, 0x0A, and 0x0D.
Now that we have identified how far into memory our buffer overwrites EIP, and which characters must be removed from our input in order to have it correctly parsed into memory by the application, we can move on to the next step, redirecting the flow of execution into the buffer we control.
From when we were identifying bad characters, we know that ESP points directly at the start of our buffer, meaning if we can jump EIP to where ESP is pointing, we can start executing instructions we have injected into the process. The assembly we need to accomplish this is simply “jmp esp.” However, we need to find an instance of this instruction in the processes memory (don’t worry, there are many) which means we need the hexadecimal codes that represent this instruction. We find those using the following command:
The output should look like the following image:
Now, when searching for a pointer to a jmp esp instruction, we will need to identify a module that is consistently loaded at the same address and does not have any protections like ASLR on. As such, we can identify which modules are loaded by the processes and what protection mechanisms are enabled on them using the following command:
As we can see from the image, there are numerous options available to us that are suitable for our purposes. We can search modules excluding ones with things like ASLR, NXCompat (DEP), SafeSEH, and Rebase enabled using the following command.
As can be seen from the image there are many options available. For this instance, address 0x74302347 was chosen, replacing the Bs in our exploit code. Remember, when entering values into your exploit code, they will appear reversed in memory. As such, your exploit code will now look something like this:
If we pass this string into the application again and put a breakpoint at 0x74302347 (in X64dbg, right click in the CPU window and select “Go to” --> “Expression,” then paste the address and hit return, right click on the address and select “breakpoint” --> “Toggle” or press F2) we should see execution stop at our breakpoint.
Single stepping the instructions using F7 will lead us into our buffer of Cs confirming that we can redirect execution to an area of memory we can write too.
Now that we can redirect execution into a writeable area of memory, we can now generate our payload. For this example, we will be creating a basic payload which executes calc.exe using MSFVenom. This tool is part of the Metasploit Framework and can be found on any Kali distribution.
To add some stability to our exploit, instead of putting our payload at the very start of the buffer and possibly causing the exploit to fail (due to landing a few bytes into the payload), we will add a small NOP (no operation) sled to the start of our payload. A NOP sled is a number of “no operation” instructions where we expect execution to land. After the NOP sled, we can append our payload leading to exploit code looking a bit like the following:
Which, when passing the string into the application, causes the application to exit and the calc.exe to run.
In this article we have seen how to exploit a buffer overflow in a 32-bit Windows application with X64dbg and ERC using a basic EIP overwrite then a jmp esp to enter our buffer. Then we generated a payload using MSFVenom and added it to our exploit to demonstrate that we had code execution.
While this type of exploit is not new, applications vulnerable to this type of exploit are still being produced today, in part due to the wide variety of ways buffer overflows can occur. Due to this fact, an understanding of buffer overflows is of benefit to any computer security professional.
As usual within the security industry, the terms are made up and no one uses them consistently. Exploit development as it is used here is about the development of scripts or programs that can take advantage of (exploit) memory corruption vulnerabilities in software.
This is opposed to exploits that might take advantage of higher-level vulnerabilities such as those seen in general application security such as web-applications or mobile applications.
There are three things you need to know before getting started.
The C programming language. The thing about learning C is not that you're going to have to do a lot of programming in it, but rather in learning C you also gain a mental model of how software works. C doesn't attempt to hide most of the memory management from you, neither does it do magic like garbage collection in the background. It's important if you're going to learn about exploiting memory corruption vulnerabilities that you understand how software uses memory.
There is a really good blog post by Evan Millar You Can't Dig Upwards which is a defense of learning C as your first language. While I don't necessarily agree, he does an excellent job of describing why learning C matters, and how it enables you to understand the layers of abstraction in most software.
As a general rule of thumb, when you want to attack anything, the first step is to understand how it works.
An assembly language. This is a tricky one that newcomers sometimes take the wrong path on. You don't need to be able to program in assembly, and you shouldn't follow tutorials for programmers about writing assembly. Programmers can make use of compilers and assemblers to turn their hand-written assembly code into machine language (the code understood by the CPU). This gives access to things like named variables and labels, fake instructions, and other high-level concepts that are not reflected in the machine code.
What's more important is the ability to read what I'd call "raw" assembly. That is, take some compiled instructions, run them through a disassembler and read those instructions. This may be strange at first as you won't have the high-level type information and constructs you're used to, but it's important to understand what the CPU executes under the hood.
The most important concepts to understand for exploiting memory corruption issues are:
Registers and how they are used
Calling conventions for functions and syscalls
Memory segmentation
How high-level types translate to raw bytes. In assembly, there are no special "types", as it's all just bytes in memory or registers
Basic Linux Usage. Just get comfortable using a Linux terminal, a lot of the resources recommended here will involve Linux.
The classic question is what operating system to use. The truth is that it doesn't matter, use what you are comfortable with. You should learn to be comfortable with a Linux terminal, but for your host system don't worry about it.
This is a bit of a pet peeve of mine, but you don't gain much by starting on your target platform. A lot of the generic concepts apply no matter what operating system you're targeting. You start by learning your fundamentals and then you learn specific applications that are operating system specific. The idea of a write-primitive isn't unique to just Linux or Android exploitation, but what you actually write to turn your write-primitive into say a local privileges escalation will be OS-dependent. You can learn about various code-reuse attacks like return-oriented programming, the basic idea of which doesn't change, but you'll utilize it differently depending on the OS.
With that in mind, many of the resources that will be recommended are Linux-focused. There are a handful of reasons for that, from the ease of sharing a consistent Linux environment for training to the lack of smaller mitigations that exist on other platforms. Trust me, you'll have no problem transferring knowledge from one operating system to another.
In Nebula you are learning to think like an attacker and do research. This box isn't actually about exploit development, but more general application security. I like recommending it though because it forces you to start doing some research on topics you might not be familiar with to determine what the vulnerability being showcased is. It gives you enough information to get started. While this might feel annoying, this ability to research and digest information about a new topic is a huge part of exploit development. I spend more time reading documentation and other write-ups than I do writing exploit code. The ability to do research and persevere is immensely important.
You will find yourself going down dead-ends, doing research that doesn't pan out, thinking you're wasting hours of time, and that's okay. You need to learn to embrace that frustration as it's a key part of exploitation. Every dead end you go down doesn't help you immediately, but as you keep doing it you're building up a huge personal knowledge base that you'll eventually start drawing on as time goes on. Learn to enjoy the rabbit holes and don't worry about the wasted time.
Update (July 2022): Over the last year, as I've recommended resources, I've come to realize that a lot of bit-rot has occured with the OST course above. While I believe the content top-notch and it strikes a really good balance for beginners. Its quite a hassle to run the VM and work along with the course. So I've started recommending more segments from Pwn.College. While I don't entirely like the flow of pwn.college it has two huge benefits going for it. It has a ton of labs to practice on for every topic, and all the labs can be completed from within your browser inside its provided workspace. Making it far more accessible and usable than the OST course.
Introduction to Software Exploitation is a lab-driven course by Corey Kallenberg. As such, the labs are useful for learning fundamental concepts when it comes to exploitation, so when the labs come up in the videos, don't skip them, pause and do them yourself.
There are a few basic concepts that this course covers:
Shellcoding and Calling conventions
Buffer Overflows (stack and heap)
Arbitrary Write (format string attack)
Don't worry about the fact it uses a pretty old Linux distro, you're not going to be pulling off most of these attacks as they are in the course today. But the basic idea of a write-primitive, or overflowing a buffer and overwriting nearby data is still relevant.
This is a lab driven course from Arizona State University. It is a proper undergraduate course and taught by Zardus (Yan Shoshitaishvili) and kanak (Connor Nelson). You've got lectures on their Youtube channel, while the class is running, the classes are streamed live on Twitch, and the discord server is active. They've also been updating the course every year, so by the time you read it, it might be slightly different. As a course it is not quite a drop-in for the topics covered by the OST course it "replaces" here. The core topics to learn here would be:
Assembly and Shellcoding
Interacting with Software
Debugging
Memory Errors
While the entire course is valuable, you can consider stopping once the course gets into the topic of "Return Oriented Programming" (ROP) and doing the next couple of resources. Then returning to this course afterwards to learn ROP and some of the more advanced topics. The OST course assumes some basic knowledge of heap exploitation which isn't covered up to this point in Pwn College (its covered after ROP) but may be useful to know for some of the next resources.
Also, at least in 2020 and 2021 the course had a ton of repetitive feeling labs. There is some benefit to the repetition which I think is what they are going for, framing it kinda like a martial art and practicing the concepts, but don't feel too pressured to keep going on one topic if its getting really tedious and boring.
Now you're going to take the concepts you learned in the previous course and put them into practice a bit. You should start with the 32bit x86 version of Phoenix. Then move onto 64bit exploits on the AMD64 version of Phoenix.
Many of the challenges will be largely the same, but you'll start getting exposed to the differences between 32bit and 64bit x86 exploitation. There are some fundamental problems you'll run into. Again this will involve some of your own research as you learn about those differences.
So the type of exploitation covered so far is essentially the stuff we did in the early 2000s. Since that time, several exploit mitigations have been introduced. Three Four of which have attained significant popularity:
Stack Canaries/Cookies - This blocks you from overwriting the stored return address on the stack by placing a canary value on the stack before the saved return address. This canary is checked before returning and if it's modified then the program dies.
Data Execution Prevention (No-eXecute Bit) - In the previous sections, you wrote shellcode into memory and then jumped to it to get code execution. This is no longer possible on modern systems due to Data Execution Prevention, also known as DEP or NX. This mitigation ensures that a page can be Writable but not eXecutable, or eXecutable and not Writable.
Address Space Layout Randomization (ASLR) - Previously when you wrote your shellcode into memory you could do so in a roughly consistent location. Now the address space gets randomized, so even if you can control the program control flow, you don't know where direct it to.
Position Independent Executables/Code (PIE/PIC) - ASLR can easily randomize the location of some memory segments like the stack and heap. Moving segments containing code around requires that code be compiled as position independent. For awhile this left significant room for attackers to bypass ASLR simply by reusing code in shared libraries or within the main executable which wouldn't be randomized. Now almost all shared libraries will be compiled as PIC which severely reduces that attack surface, and sensitive executables such as those exposed on the network will be compiled with PIE.
Update (July 2022) - If you did Pwn College instead of OST then you should have already done this section and can go right on to the next resource :D
Pwn College is an awesome resource for more modern exploitation. In particular, I'm linking just a few of the lectures that cover dealing with some common mitigations. This module in particular you can probably skip the first three lectures. But the following lectures linked on the page are worth checking out:
Causes of Corruption 1 and 2
Stack Canary Mitigations
ASLR Mitigations
Causes of Disclosure
Shellcoding: Data Execution Prevention
If you're motivated there is a ton more content in pwn college to check out too.
Now that you've got a bit more knowledge about mitigations, it's time to put that into practice also. The Fusion box is also going to get you doing a bit more reverse engineering and testing for vulnerabilities than you had to do for Nebula or Phoenix. Its also going to introduce all of the above mitigations for you to play around with.
This is what I would consider the last of the beginner concepts, return-oriented programming (ROP). ROP is a very common exploitation technique, most exploits today tend to utilize ROP at some stage in the chain.
You'll probably find it easier to work through ROP Emporium on 32bit but do go back and do it on 64bit because things do change substantially with the different calling conventions.
Weird machines also touches on ROP, but it focuses more on how to think about ROP. It is more of a meta-topic I guess. A lot of recent exploit write-ups tend to talk about exploitation in terms of gadgets and primitives being obtained, but these terms won't be familiar to you if you're new to ROP. It's a bit of a mental shift away from talking about the specific exploit technique, so you need to familiarize yourself with it. You might just naturally get it, but I wanted to should out a couple of videos from LiveOverflow where he explains the concept:
This is a tricky concept to explain, but you'll start to understand it intuitively with experience. There is a solid paper on the topic of weird machines and exploitability which is also an interesting read.
So Nightmare has a ton of challenges for you to practice on. In particular, I want to call out the Heap Exploitation section. While heap exploitation is one of those areas that is particular to each operating system (and each heap implementation). I think there is significant value in learning about the ptmalloc2 allocator and its attacks. You might not find yourself using them, but at least for me, Malloc des-Maleficarum was a huge eye-opener for the creativity and art of exploitation.
What you gain by running through the heap exploitation is less about memorizing all the different techniques that have been found to attack ptmalloc but more just a sense of how you can creatively apply control of certain pieces of data. So I'd highly recommend running through the heap challenges on nightmare.
At this point, you have a lot of the basic concepts that you'll need to start looking at modern exploits, and hopefully the research skill to start discovering what you don't yet know you don't know.
We do plan to put out another part to this post covering how to bridge the gap from these CTFs and toy binaries to real-world exploitation soon.
For now, I'll just say that one of the biggest mistakes I see people make is they wait until they feel ready. Don't wait, just dive in and learn as you go. This is especially important when it comes to the process of discovering vulnerabilities (which I haven't touched on at all here) as a big part of that is building an intuition which just takes time.
Hello dear reader. If you have read the other articles in this series, welcome back! If not I encourage you to read the previous installments before proceeding with this post. This post covers a surprisingly useful technique in exploit development called Egg Hunters. In order to demonstrate how Egg Hunters function, we will write an exploit for a 32 bit Windows application vulnerable to a SEH overflow. However, due to how the application handles input, we will be required to use an Egg Hunter to locate our payload in memory move execution to it.
This guide was written to run on a fresh install of Windows 10 Pro (either 32-bit or 64-bit should be fine) and as such you should follow along inside a Windows 10 virtual machine. This vulnerability has also been tested on Windows 7; however, the offsets in this article are the ones from the Windows 10 machine and subsequently may differ on your Windows 7 installation. The steps to recreate the exploit are the same.
We will need a copy of X64dbg which you can download from the official website and a copy of the ERC plugin for X64dbg from here.If you already have a copy of X64dbg and the ERC plugin installed running “ERC --Update” will download and install the latest 32bit and 64 bit plugins for you. Since the vulnerable application we will be working with is a 32-bit application, you will need to either download the 32-bit version of the plugin binaries or compile the plugin manually. Instructions for installing the plugin can be found on the Coalfire GitHub page.
If you are using Windows 7 and X64dbg with the plugin installed and it crashes and exits when starting, you may need to install .Net Framework 4.7.2 which can be downloaded here.
Finally, we will need a copy of the vulnerable application (Base64 Decoder 1.1.2) which can be found here. In order to confirm everything is working, start X64dbg and select File -> Open, then navigate to where you installed B64dec.exe and select the executable. Click through the breakpoints and the b64dec GUI interface should pop up. Now in X64dbg’s terminal type:
Command: ERC –help
You should see the following output:
Generally, an Egg Hunter is the first stage of a multistage payload. It consists of a piece of code that scans memory for a specific pattern and moves execution to that location. The pattern is a 4 byte string referred to as an egg. The Egg Hunter searches for two instances of where one directly follows the other. As an example if your egg was “EGGS” the Egg Hunter would search for “EGGSEGGS” and move execution to that location.
Egg Hunters are commonly utilized in situations where there is very limited usable memory available to the exploit author. In short, Egg Hunters allow for a very small amount of shell code to be used to find a much larger piece of shell code somewhere else in memory.
Several Egg Hunters can be found online (there are even some prewritten ones provided by the ERC plugin) but for our purposes, we will create a very simple Egg Hunter from scratch so we can get a full understanding of how an Egg Hunter is constructed and executed.
This vulnerability relies on using the SEH overwrite technique discussed in the previous installment of this series. Therefore, the first thing required is to crash the program to ensure we are overwriting the SEH handler.
To begin, we will generate a file containing 700 A’s.
Then open the file and copy the contents and paste them into the search box of the b64dec.exe application and click decode.
Following the input of the malicious payload, the debugger should display a crash condition where the registers will look something like the following.
The crash does not immediately indicate that a vulnerability is present, EBP points into our malicious buffer however ESP appears to have been left as it was. From here we will check the SEH handlers to confirm at least one has been overwritten.
Navigating to the SEH tab we can see that the third SEH handler in the chain has been overwritten with our malicious buffer. If we can point this at a POP, POP, RET instruction set we can continue with exploitation of this vulnerability.
At this point, we have confirmed the vulnerability exists and that it appears to be exploitable. Now we can move on to developing an exploit.
We know that the application is vulnerable to an SEH overflow. Initially, we should set up our environment so all output files are generated in an easily accessible place.
Command: ERC --Config SetWorkingDirectory <C:\Wherever\you\are\working\from>
Now we should set an author so we know who is building the exploit.
Command: ERC --Config SetAuthor <You>
Now we must identify how far into our buffer the SEH overwrite occurs. For this, we will execute the following command to generate a pattern using ERC:
Command: ERC --pattern c 700
We can now add this into our exploit code either directly from the debugger or from the Pattern_Create_1.txt file in our working directory to give us exploit code that looks something like the following.
Now if we generate the crash-2.txt file and copy its contents into our vulnerable application we will encounter a crash. We can run the FindNRP command to identify how far through our buffer the SEH record was overwritten.
Command: ERC --FindNRP
The output of the FindNRP command above displays that the SEH register is overwritten after 620 characters in the malicious payload. As such we will now ensure that our tool output is correct by overwriting our SEH register with B’s and C’s. First we will need to hit the restart button to restart the process and prepare it for another malicious payload. The following exploit code should produce an overwrite of B’s and C’s over the SEH register.
The SEH register is overwritten with B’s and C’s as expected. In order to return us back to our exploit code we will need to find a POP, POP, RET instruction. For a full rundown of how an SEH overflow works, read the previous article in this series. To find a suitable pointer to a POP, POP, RET instruction set we will run the following command.
Command: ERC –SEH -ASLR -SafeSEH -Rebase -OSDLL -NXCompat
The output above shows most of the pointers available to us are prefixed with a 0x00 byte which for our previous exploit would have made them unsuitable. However we will have to use one here.
The additional flags passed here exclude modules from the search based on certain criteria. ASLR removes any modules that participate in address space layout randomization, SafeSEH removes dlls that support a SEH overflow protection mechanism (covered in the second installment of this series), Rebase removes DLLs that can be relocated at runtime, NXCompat removes modules that are DEP enabled and OSdll removes modules that are operating system dlls.
These flags persist through a session and are detailed in the help text of the ERC plugin. You will need to set them to your preference each time you restart the debugger.
The reason a 0x00 byte is commonly a problem in exploit development is that 0x00 is a string terminator in the C language which a lot of other languages are built on. Other commonly problematic bytes in exploit development are 0x0A (new line) and 0x0D (carriage return) as they are also usually interpreted as the end of a string.
This means we need to incorporate a null byte into our payload. We should identify if null bytes (and any other bytes) will cause our input string to be cut short or be modified. A full description of how to do this can be found in the first article of this series; however we have included the output of the process here:
The output shows that the instructions that will cause us problems (the omitted ones) are 0x00, 0x0A and 0x0D. (Shocking!) We can’t put a 0x00 in the middle of our payload as it will cut it short, meaning the overflow will never get triggered. However, we do need one in order to use our POP, POP, RET instructions.
We will try to put the 0x00 byte at the end of our payload to see if it makes it into memory unmodified. Our exploit code should now look something like this.
This gives us the following output when we view the SEH chain.
It looks like in the SEH chain the null byte is modified to 0x20, so this method will not be suitable. We will need another option. The next logical choice is to remove the byte altogether and see if the string terminator is written into the SEH chain after our buffer.
Our exploit code should now look like the below:
If we input this new string into our vulnerable application and then check the SEH tab, we have gotten our null byte into the SEH record.
Now we can use our POP, POP, RET instruction, but… we can’t write any data after our pointer to the POP, POP, RET instruction set, so we will not be able to just simply do a short jump over the SEH record into our payload like we did in the last exploit. This time we have 4 bytes to work with in the SEH record.
Our best option from here is a short jump backwards. This can be done because the operand of the short jump instruction is in two’s complement format. Which is the way computers use to represent integers. Basically it can be used to describe both positive and negative integers.
Say for example you have the value of 51 in binary: 00110011
And we want to know what 51 negative would be in binary we simply invert the 1’s and 0’s then add 1: 11001101
This allows us to jump back a maximum of 80 bytes using \xEB\x80. So let’s change our SEH overwrite to be the pointer to our POP, POP, RET instruction and see where we land with our jump backwards. Our exploit code should now look something like this:
When we pass the output into the application, a breakpoint should be placed at our POP, POP, RET instruction (0x00401E86) and wait to land there. We will have to pass through two exception handlers to get there so be prepared to press F11 twice and then you should be looking at something like the screenshot below.
Now we can single step through this, take our jump backwards and then land back into our buffer of A’s.
Since we have already established that we can jump back into a buffer we control, our exploit is almost complete. The only outstanding issue is that 80 bytes is simply not enough for us to inject most payloads into, so we will need to use a multistage payload.
As discussed at the start of this article we will be writing a custom egg hunter for this exercise. I would not recommend using it outside of this exercise because it is inferior to other freely available options.
Most Egg Hunters have mechanisms in them to handle errors and will already be optimized for speed because exhaustively searching memory is extremely time consuming. This Egg Hunter does not do those things, but it is simple and easy to understand which makes it perfect for this situation. Our Egg Hunter code is going to be this:
Let’s go over these instructions line by line.
MOV EDI, EBP: This instruction moves the value of EBP into the EDI register. EBP points to a location near to the start of our payload. Normally an egg hunter would search all memory for our string but due to the simplicity of this one we had to give it a starting point.
MOV EAX, 0x45524344: As discussed at the start of this article, Egg Hunters search for a byte string repeated twice. This instruction moves the value of our byte string (0x45524344 or “ERCD”) into the EAX register.
INC EDI: Increments EDI by 1 pointing it to the next address which will be checked for our egg.
CMP DWORD PTR DS:[EDI], EAX: Compare the DWORD pointed to by the EDI register to the value held in the EAX register. If the result is true (the values are the same) then the zero flag is set in the EFLAGS register.
JNE 0xF7: Jumps backwards 4 bytes to the INC EDI instruction if the zero flag is not set in the EFLAGS registers.
ADD EDI, 4: Moves EDI forward by 1 DWORD (4 bytes) after finding the first egg to confirm it is repeated directly afterwards.
CMP DWORD PTR DS:[EDI], EAX: Compare the DWORD pointed to by the EDI register to the value held in the EAX register. If the result is true (the values are the same) then the zero flag is set in the EFLAGS register. This is the second check and ensures that the EGG found is repeated.
JNE 0xF7: Jumps backwards 8 bytes to the INC EDI instruction if the zero flag is not set in the EFLAGS registers.
JMP EDI: If neither of the JNE instructions activated it is because the EGG was found twice in memory directly next to each other and as such a jump is now take to the location where they were found.
The instructions above indicate that regardless of where our payload is in memory (provided a lower address is moved into EDI - we used EBP in this instance but any value lower that the payload starting address will work) execution will be redirected to our payload.
Now that we have our SEH jumps in place and we have created our Egg Hunter, we can run the exploit again and ensure that execution is redirected to the location of our egg. We will replace the A’s (our initial padding) with 0x90’s and append our egg (“ERCD”) to the start of our payload for the egg hunter to find. Our exploit code should now look something like this:
When we inject this new payload into our vulnerable application and step through our breakpoints, we can see that execution is redirected to our egg.
Now that we have landed at our egg, we still need to generate a payload and add it to our exploit code. I used MSFVenom to generate a payload for this exploit.
Now our exploit code should look something like this:
And when we pass this string to our vulnerable application we should get the calculator application pop up.
In this installment, we covered exploiting a 32-bit Windows SEH overflow using the Egg Hunter technique with X64dbg and ERC. We then generated a payload with MSFVenom and added it to our exploit to demonstrate code execution. A large collection of Egg Hunters and other payloads can be found at www.exploit-db.com and further information on writing Egg Hunters can be found at various locations online.
If you have read the previous articles in this series, welcome back and keep reading. If not, I would encourage you to read those first before proceeding, as this article builds on concepts laid down in the previous installments. In this article, we will be covering a technique similar to the one in the second installment of this series but with the twist of the character encoding of the input being in Unicode. In order to demonstrate how to get around this impediment, we will be writing parts of the payload and doing some stack realignment manually.
This guide was written to run on a fresh install of Windows 10 Pro (either 32-bit or 64-bit should be fine) and as such you should follow along inside a Windows 10 virtual machine. This vulnerability has also been tested on Windows 7, however, the offsets in this article are the ones from the Windows 10 machine and subsequently may differ on your Windows 7 installation. The steps to recreate the exploit are exactly the same.
You will need a copy of X64dbg which can be downloaded from the official website and you will also need a copy of the ERC plugin for X64dbg from here. If you already have a copy of X64dbg and the ERC plugin installed, connecting to any process and running “ERC --Update” will download and install the latest 32-bit and 64-bit plugins, after which you can restart the debugger. As the vulnerable application we will be working with is a 32-bit application, it will be necessary to either download the 32-bit version of the plugin binaries or to compile the plugin manually. Instructions for installing the plugin can be found on the Coalfire GitHub page.
If you’re using Windows 7 and X64dbg with the plugin installed, and it crashes and exits when starting, you may need to install .Net Framework 4.7.2 which can be downloaded here. Finally, you will need a copy of the vulnerable application (Goldwave 5.70) which can be found here. In order to confirm everything is working, start X64dbg and select File -> Open, then navigate to where you installed Goldwave570.exe and select the executable. Click through the breakpoints and the Goldwave GUI interface should pop up. Now in X64dbg’s terminal type:
Command:
You should see the following output:
Unicode is a character encoding scheme. There are lots of languages with lots of characters that computers should ideally display. Unicode assigns each character a unique number, or code point.
Originally when 8-bit computers were the apex of our capability, ASCII was created to cover the dominant language in computing at the time which was English. ASCII mapped characters to numbers (originally at a maximum of 7 bits (127) but was later expanded to 8 bits to cover characters from other languages) and having 26 characters in the alphabet in both upper and lower case, numbers and punctuation worked quite well for a time.
However, over time the need arose to provide characters in all languages, and this simply could not be done with 255 bits. Thus, more character encodings were needed.
Unicode provides a solution to this problem by using a variable number of bytes per character. There are multiple UTF (Unicode Transformation Format) encodings, which all work in a similar manner. You choose a unit size, which for UTF-8 is 8 bits, for UTF-16 is 16 bits (UTF-16 is what Windows defines as “Unicode”), and for UTF-32 is 32 bits. The standard then defines a few of these bits as flags: if they're set, then the next unit in a sequence of units is to be considered part of the same character. If they're not set, this unit represents one character fully. Thus the most common (English) characters only occupy one byte in UTF-8 (two in UTF-16, 4 in UTF-32), but other language characters can occupy six bytes or more.
Now that Unicode has been defined as a multibyte character encoding, you need to know what to look for in our debugger. When an input string of “AAAA…” is used, it will no longer be shown as “41414141…” due to the fact that as mentioned above Windows predominantly uses UTF-16 as its Unicode standard, meaning even basic English characters will be two bytes long.
ASCII: A -> 41 ABC -> 414243
Unicode: A -> 0041 ABC -> 004100420043
Obviously, these additional null bytes will cause a problem, since any address we wish to overwrite EIP or our SEH registers with will need to contain null bytes. However, we do have some tools to help with those hurdles. More on those later.
This exploit begins with an SEH overwrite similar to the one covered in the second installment of this series. As such we will need to crash the program and confirm that the input we provided overwrites an SEH handler.
To begin, use the following python code to generate the input:
Run this and it will create a file, copy the contents of crash-1.txt to the clipboard. Open Goldwave app, select file then “Open URL” and paste the contents after http://. The application should crash and then you should be able to see what the Unicode input string looks like in memory.
As seen in the image above, the input is being read into memory. However, one byte is being left as is while the other byte absorbs the two null bytes. This is due to the fact that a null byte is the start of the add byte ptr instruction and 0x41 (inc ecx) does not take any values as arguments. As such, changing the values we put in the string can change how the instructions are interpreted in memory. Navigating to the SEH tab, you should see that the first handler has been overwritten with 00410041.
Now that we have confirmed the vulnerability exists, it is time to begin developing a full exploit.
At the present moment we know the application is vulnerable to an SEH overflow. We should initially set up our environment, so all our output files are generated in an easily accessible place.
Command:
Now we should set an author so we know who is building the exploit.
Command:
Now we must identify how far into our buffer the SEH overwrite occurs. For this we will execute the following command in order to generate a pattern using ERC:
Command:
We can now add this into our exploit code either directly from the debugger or from the Pattern_Create_1.txt file in our working directory to give us exploit code that looks something like the following.
With this, if we now generate the crash-2.txt file and copy its contents into our vulnerable application, we will encounter a crash. We can now run the FindNRP command in order to identify how far through our buffer the SEH record was overwritten. Appended to the FindNRP command is the -Unicode switch. This switch specifies that the character encoding is UTF-16 (the Windows Unicode default). It only needs to be specified once per debugging session. As such, all further commands will be in Unicode (where applicable) until the debugger is restarted.
Command:
The output of the FindNRP command above displays that the SEH register is overwritten after 1019 characters in the malicious payload. As such, we will now ensure that our tool output is correct by overwriting our SEH register with Bs and Cs. First, we will need to hit the restart button in order to restart the process and prepare it for another malicious payload. The following exploit code should produce an overwrite of Bs and Cs over the SEH register:
As you can see, the SEH register is overwritten with Bs and Cs as expected. Now, to return us back to our exploit code, we will need to find a POP, POP, RET instruction. For a full rundown of how an SEH overflow works, read the previous article in this series. In order to find a suitable pointer to a POP, POP, RET instruction set, we will run the following command. As the character encoding is set as Unicode only, Unicode compatible results will be returned.
Command:
As you can see, there are a number of options to choose from for POP, POP, RET instructions. Normally when carrying out an SEH overflow, we would execute a POP, POP, RET instruction set and then jump over them. However, this is not possible with Unicode due to the additional null bytes we must deal with. Therefore the address of the POP, POP, RET instruction must not cause the program to crash as we will need to step through it after executing the POP, POP, RET instructions.
Because of that, we decided to go with 004800b3. We can walk through these instructions without crashing the application. At this point, our exploit code should now look something like this:
We should now test our code and confirm that the POP, POP, RET does work and that we land where we expect to. To do this, place a breakpoint at the address of our POP, POP, RET instruction set:
With that test it is confirmed that we do land at our POP, POP, RET instruction set and this will return us to our SEH overwrite, which we can step through and this will land us in our payload. Normally this would be our mission complete. However, Unicode payloads are slightly more complex than a normal payload and some additional steps are required.
Under normal circumstances, a payload generated by MSFVenom or a similar tool will have a getPC (Program Counter) routine. However, there is no standard routine for Unicode Overflows. We can manually emulate this code by aligning one of our CPU registers to the address where our shell code begins.
To achieve this, we’ll have to create a Unicode NOP, since 0x90 will not work as the null bytes will crash the application. We need an instruction that—combined with a null byte or two—will not crash the application. For that purpose, use 0x71 and 0x75.
0x75 - Combined with a null byte before and after produces an “add byte ptr ds:[EBP], al” instruction. This does not cause a crash. Our registers indicate that EBP points into the address space of our application.
0x71 – Combined with a single trailing null byte produces a “JNO” (jump if overflow) instruction. This will only execute if the overflag flag (OF) is set, and as such, never activates during this exploit.
Now we will need to input these values between the instructions that we want to execute. We will use 0x75 between specific instructions and 0x71 as an NOP sled. Now all we need to do is align a register to the start of our payload and we have finished our exploit.
Note: A Unicode NOP sled does not technically need a separate instruction. It could simply be achieved using a combination of 0x75 and 0x90. However, we have added in 0x71 to demonstrate a second way of generating a Unicode NOP.
To do so, we will push the value of ESP onto the stack and then pop that value into EAX. Then add and subtract from EAX in order to get the value exactly where we want it. The final stack alignment instructions should look something like the following:
When read into memory, this will grab our unwanted null bytes and attach them all to the appropriate 0x75 bytes so they do not get in the way of what we are trying to do.
As we can see, our 0x004800B3 pointer has converted into harmless instructions and our Unicode NOPs have absorbed the unwanted null bytes. We then add 0x1000FF00 to EAX and subtract 0x1000EA00 from it in order to leave EAX with the value of 0x0019E664 or 1190 bytes past the current location of EIP.
This is where our second Unicode NOP comes into play. We will now fill the space between EIP and the address of our final payload with the second NOP (0x71). Remember, there is a null byte added with each character we inject so we only need half the number of characters as there are bytes between EIP and EAX.
Finally, all we have to do is create a payload and add it to our shell code and this should be completed.
In order to create our payload, we will once again be using MSFVenom. To do that, boot up an instance of Kali or wherever you keep a copy of MSFVenom, and run the following command to generate our payload:
Command:
After we copy this into our exploit code, it should look something like the following:
Now all we have to do is run the code, copy the contents of crash-6.txt to our copy buffer, and inject them into our application and watch calc.exe pop up for us.
For a substantial amount of time, it was believed that Unicode overflows were not exploitable and that they could only be used to cause a denial of service condition. However, in 2002, Chris Anley published a paper demonstrating this conclusion to be false. If you would like some further reading on this topic, I would suggest reading Building IA32 'Unicode-Proof' Shellcodes paper published in Phrack in 2003.