[Learning] CVE-2011-1591: Wireshark v1.4.4 RCE — SEH Overflow
Last Update:
Word Count:
Read Time:
Introduction
This article is part of my series: From Bug To Exploit.
In this article, I am going to introduce how to exploit Wireshark v1.4.4 with an SEH overflow.
While developing the exploit script, I encountered several issues, which I think are worth documenting since I learned some details that I had not discovered before.
Cause of the Vulnerability
Murmur: This was actually an example from a textbox I was reading. However, neither the book nor the official CVE advisories online provided a complete root-cause analysis of the vulnerability; instead, they just threw around magic numbers (
0x2323) out of nowhere. Since I couldn’t find any in-depth breakdown from other researchers, I decided to dive into the source code and analyze it myself in this article. Honestly, this is exactly why I started this series in the first place.Note: The vulnerable source code is available on my GitHub.
The root-cause is in the dissect_dect() method of epan/dissectors/packet-dect.c:
1 | |
The structure dect_bfield assigns a fixed memory space for Data[128]. However, the dissect_dect() method does not properly check the length of the data before copying it from pkt_ptr+8 into pkt_bfield.Data:
1 | |
This lack of boundary checking eventually causes a buffer overflow!
Murmur: I have to thank Exploit-DB for preserving the vulnerable source code, which saved me a lot of time.
Buffer Overflow
In this section, I am going to demonstrate how to exploit the application.
I chose to develop the exploit scripts with C++ because I think the struct data type provides a straightforward way to represent the memory layout.
Writing the struct layout for the packet is not that difficult. We can simply use the definitions from Wireshark:
1 | |
1 | |
Therefore, we can write our first exploit script to crash the application:
1 | |
Note: The
template.pcapis available on my GitHub.
Here, I want to explain this code in case my readers are not familiar with C++.
The #pragma pack directive forces the compiler to adjust the alignment of structure members, allowing us to control the structure layout and achieve and accurate 1-to-1 mapping between the structure and the raw binary data.
By default, compilers align data in memory based on the target architecture and ABI to optimize memory access.
If a struct contains a mixture of data types, such as uint16_t (2 bytes) and uint32_t (4 bytes), the compiler may insert additional bytes, known as padding, between or after fields to satisfy the required alignment. While this is useful for performace, it can cause problems when a structure is intended to represent a raw binary format.
The push saves (pushes) the compiler’s current default alignment state onto an internal stack so it can be restored later without affecting other parts of the code.
The value 1 packs all subsequent structures to a 1-byte alignment. This means that no additional padding is inserted between field. When one field ends, the next field begins immediately.
The pop restores (pops) the compiler’s previous alignment settings, returning to standard behavior for the rest of our codebase.
Without this approach:
1 | |
The compiler may insert additional padding depending on the alignment requirements of the structure. As a result, the structure’s memory layout may not match the binary layout expected by the .pcap format, causing problems when Wireshark attempts to parse the generated file.
In my textbook, however, the author implemented the alignment as follows:
1 | |
However, after checking the description of MSDN, I believe this is a misuse of __declspec(align(#)).
Original quote from MSDN: The
sizeofvalue for any structure is the offset of the final member, plus that member’s size, rounded up to the nearest multiple of the largest member alignment value or the whole structure alignment value, whichever is larger.__declspec(align(#))can only increase alignment restrictions.
Therefore, if we do not want any padding in a struct, then we should use #pragma pack(push, 1) instead of __declspec(align(1)).
Now, back to the exploitation.
As in the previous articles, we can use mona to find the exact offset of NSEH, which is 1239.
Then, we can demonstrate it with a DEADBEEF PoC script:
1 | |
Therefore, we can use shellcode with the same design as the previous article:
The completed exploit script can be implemented as follows:
1 | |
Other
In this section, I will discuss a few issues I encountered an present an alternative shellcode implementation.
While developing the NASM jump code, I saved first_jump.asm as follows:
1 | |
The exploit kept failing, and calc.exe did not show up.
After debuggin with Immunity Debugger, I found that the SEH value was not overwritten as anticipated. I then changed first_jump.asm to:
1 | |
Then calc.exe DID show up.
This produced a different opcode because NASM defaults to treating the source code as 16-bit (though this may depend on your environment and configuration). We need the [BITS 32] directive to specify a 32-bit program.
Another issue involved std::string. My original implementation looked like this:
1 | |
Once again, the SEH and NSEH values were not overwritten correctly. Switching all the data to std::vector resolved the problem. In short, I recommend using std::vector instead of std::string for raw binary payloads, much like using byte strings (b’\x12\x34\x56\x78’) in Python.
Finally, let’s look at another shellcode implementation.
In the previous article, I mentioned that SEH can be used if the stack does not have enough space. However, if there is sufficient space to perform an SEH overflow, a single jump is enough. The shellcode layout is shown below:
Therefore, the exploit script can be implemented like this:
1 | |
This achieves the exact same result!
Conclusion
In this article, I walked through an example of an SEH overflow by exploiting Wireshark v1.4.4.
I first investigated the root cause of CVE-2011-1591 by reviewing the vulnerable source code. I then discussed an alternative shellcode implementation and some of the hurdles I encountered along the way.
Honestly, studying the Wireshark v1.4.4 vulnerability taught me a great deal.
In the next article, I will dive into the Egg Hunter technique!
That’s all for this article. If you have any comments or suggestions, please feel free to leave them below.
THANKS FOR READING
I drew another drawing!