[Learning] CVE-2011-2595: FotoSlate v4.0.146 RCE — SEH Overflow
Last Update:
Word Count:
Read Time:
Introduction
This article is part of my series: From Bug To Exploit.
In the previous articles, I demonstrated how to perform basic stack-based buffer overflow. In this article, however, I will demonstrate how an SEH overflow works.
What is SEH
SEH stands for Structured Exception Handling. It is a native Microsoft Windows mechanism designed to handle hardware and software exceptions robustly.
It allows applications to handle exceptions such as division by zero, invalid memory access, and access violations instead of terminating immediately. When an exception occurs, the operating system transfers control to a designed exception handler, which can handle the exception or terminate the process.
Under the Hood:
- Next SEH (NSEH) Record: A pointer to the next SEH record in the chain (a linked list).
- SEH Handler: A pointer to the function code responsible for handling the exception.
- The SEH Chain: Exception handlers are linked torgether through SEH records. When an exception occurs, Windows walks the chain and invokes the appropriate exception handler.
Why SEH?
In the early days of binary exploitation, standard buffer overflows aimed directly at overwriting the EIP value. Sometimes, however, there is not enough space to store the malicious data after filling the buffer with a large amount of junk data.
In this case, an attacker can use SEH overflow to overcome this limitation.
Attackers can fill the available space before the SEH structure with a NOP sled and their shellcode. They overwrite the SEH Handler to trigger a pop # pop # ret sequence. This instruction sequence redirects execution backward into the large, controlled buffer they created at a lower memory address.
Note: Stack pushes data from high addresses to low addresses. Some people might be confused because some debuggers display the stack in the opposite direction in their GUI.
Furthermore, SEH overflow can also be used to bypass certain modern protections, but I will discuss this in future posts.
SEH Overflow
Before starting to exploit the application, I want to explain the principle behind SEH overflow.
On Windows x86, SEH records are typically stored on the stack, allowing an attacker to overwrite both the NSEH and SEH Handler fields with a large amount of junk data.
In other words, once the SEH record is overwritten, the attacker can trigger an exception, causing Windows to process the SEH record and eventually redirect execution to the attacker’s controlled code.
Note: On Windows x86, the stack grows toward lower addresses. Therefore, higher addresses are considered the bottom of the stack, while lower addresses are considered the top.
Therefore, the payload layout should be structured as follows:
After exploitation, the memory layout of the stack will look like this:
When the SEH Handler is overwritten with an address pointing to a pop # pop # ret instruction sequence, it ultimately redirects the Instruction Pointer (EIP) back to the Next SEH (NSEH) address. This happens because executing two pop instructions increments the Stack Pointer (ESP) twice (by 4 bytes each time, moving it up to higher memory by two stack slots). When the subsequent RET instruction executes, it takes the address currently at the top of the stack (which is our NSEH pointer) and loads it directly into EIP. As a result, the code placed inside NSEH is executed, allowing us to jump backward into our earlier payload buffer.
However, because the NSEH slot provides only a tiny 4-byte window, the distance we can jump from there is extremely constrained. Since our shellcode can be quite large, we implemented a two-stage jump (double pivot) using the SECOND_JUMP code block. This dedicated area gives us 8 bytes of operational space, allowing for a much larger and more flexible jumping distance.
Furthermore, the preceding NOP Sled acts as a “mechanical funnel” for our alignment. It is inconvenient to calculate a jump that lands precisely on the exact start address of the shellcode. In fact, if the size or structure of the shellcode changes, we would be forced to re-calculate the exact destination offset for the SECOND_JUMP every single time (otherwise, unknown instructions will be executed). By jumping into a broad field of NOPs instead, the CPU can smoothly glide through the NOP-operational instructions until it safely arrives at and executes the shellcode.
Note: I call it “mechanical funnel” because it is much like the funnel on an aerial refueling receptacle that guides a slightly off-target probe into place, the NOP sled captures our imprecise jump and safely guides the CPU’s execution flow straight into the shellcode.
Exploit
In this section, I am going to perform an SEH overflow.
When FotoSlate parses a malicious .plp file with an excessively long payload in the id field, it triggers a buffer overflow due to improper boundary checking.
1 | |
The shellcode with junk data reaches the Next SEH (NSEH) pointer, immediately followed by 4 bytes to overwrite the SEH Handler.
First, I tried to inject a large amount of junk data:
1 | |
Attach the application to Immunity Debugger, and open evilfile.plp. The application then crashes:
We can also see that both the SEH and NSEH have been overwritten.
Now, we need to find the exact offsets. I did this with mona. If you are not familiar with it, you may refer to the previous article.
Therefore, we can find that the exact offsets to the SEH and the NSEH are 1816 and 1812, respectively.
As I mentioned before, the payload layout will look like this:
Therefore, I chose the offset 1812 instead of 1816. Here, I wrote a simple script to see if I could overwrite the NSEH:
1 | |
Open evilfile.plp again. Immunity Debugger demonstrates that I have successfully overwritten the value of NSEH!
Next, we need to find an address containing a pop # pop # ret instruction sequence, just like how we found jmp esp before.
We can use mona to find it:
1 | |
Here, I chose the last one (0x26345a1d). We will use this value to overwrite the SEH value. The execution flow will then jump to 0x26345a1d and execute the pop # pop # ret sequence.
Note: Always remember that your shellcode should not contain any bad characters. In the case, however, since the handler is located at the end of the payload, if a null byte (
\x00) is located at the end of this value (from the perspective of little-endian, the beginning of the address), it will not affect the execution flow.
After that, the value stored in NSEH (DEADBEEF) will be executed. Therefore, we need to change it to the first jump operation.
The find the opcode, we need to write assembly code and convert it into hexadecimal data.
There are several methods to do this:
mona- WinDbg
nasm
I am going to use the third method.
First, create a file and write the instructions into it:
1 | |
As I mentioned earlier, we only have a 4-byte space for NSEH. This means we can only use a simple instruction like this.
To obtain the opecode, compile it with nasm and obtain the opcode with xxd:
1 | |
The opecode is \xeb\xf6, which is only two bytes long. Therefore, we can use \x90 (nop) to pad it, making it 4 bytes long.
The next one is the second jump. We can obtain it in the same way:
1 | |
The hexadecimal 0x12c represents 300 in decimal. You can modify this value to fit your case.
1 | |
The opecode is \xe9\xcf\xfe\xff\xff, which is 5 bytes long. Therefore, we can use \x90 (nop) to pad it, making it 8 bytes long.
Finally, the completed exploit script can be implemented as follows:
1 | |
Note: The script and the
template.plpare available on my GitHub.
Conclusion
This article introduced the basic concepts of SEH overflow and demonstrated how to exploit FotoSlate v4.0.146.
The method is slightly harder than the classic buffer overflow. I spent hours understanding and exploiting it and found lots of things worth documenting.
The best way for me to learn is probably to go step-by-step!
This is the end of this article. If you have any comments or suggestions, please feel free to leave them below!
THANKS FOR READING
Recently, I have been learning how to draw (as a beginner), so I wanted to include one of my recent pieces at the end of this article. I hope you enjoy it.