Introduction
Attribution: This writeup documents my analysis of CVE-2016-5764, originally discovered by Umit Aksu (Exploit-DB #40651). The technical analysis, debugging process, and documentation presented here are my own educational exploration of this vulnerability.
Micro Focus Rumba is a terminal emulation suite designed for mainframe system connectivity. Among this suite of apps is the Rumba FTP client, our focus in this writeup. But before we get ahead of ourselves, what is an FTP client?
FTP
FTP, or File Transfer Protocol, is a standard network protocol used to transfer files between a client and server over a computer network. Think of this as a simple way to upload and download files between different systems on a network. The protocol operates on a client-server model, where the client initiates a connection to the server to request file transfers. The Rumba FTP client provides an easy to use interface that lets users manage file transfers.
Back in the late 2000s, Rumba FTP client 4.2 was released, and was famously used in IBM mainframes for encrypted file transfers. However, like many software applications of its time, it had its share of vulnerabilities. One such vulnerability is CVE-2016-5764, a stack-based buffer overflow that can be exploited remotely. Note that while the particular CVE we deal with here was published in 2016, there are traces of the exploit being discovered all the way back in 2010. So let's dive into how the exploit works.
Vulnerability Overview
CVE-2016-5764 Summary
The main vulnerability exploited here is something called a Buffer Overflow. This is one of the most common software vulnerabilities, where a program writes more memory that it is usually allowed access to. Think of it like overflowing a bucket of water, if you add more that what the bucket can hold, it'll lead to spillage. In software, this can be used to access memory from locations that users should not be able to access.
Let's look at a simple example to see a buffer overflow in action. Don't worry if you're not too familiar with the C syntax.
Buffer Overflow Demo
Try changing the input string length to see what happens when it exceeds the buffer size. Watch how it corrupts adjacent memory!
int main() {
char myBuffer[10]; // A buffer with an allocated size of 10 bytes
int mySecretValue = 123;
strcpy(myBuffer, "AAAAAAAAAAA"); // Copying 11 A's into our buffer
printf("%s\n", myBuffer);
printf("%d", mySecretValue);
}Modify Variables:
When we run this code, we notice that our secret value gets overwritten by 65, which is the ASCII code for "A". For a fixed buffer size of 10, we fill it with 11 A's, which causes the 11'th A to overflow and get written somewhere else in memory. It just so happens that variables are stored contiguously in memory on the stack, so we are able to access the variable stored right next to our buffer, which is our secret value. And voila, we are able to edit this with just one malformed input. Note that this works because strcpy in C does not do any length checking by default, so we can theoretically overwrite our buffer with a very long string.
Now let's get back to our exploit. Here are some setup details if you want to reproduce this on your own.
Setup
Environment Configuration
Target System:
- Operating System: Windows XP Professional SP3 (32-bit)
- Virtualization: VirtualBox
- Vulnerable Software: Rumba FTP Client 4.2
Tools Used:
- Static Analysis: Ghidra
- Dynamic Analysis: OllyDbg
- Exploit Server: Perl script
Vulnerability Analysis
Initial Crash Analysis
Let's start by triggering the vulnerability and seeing what happens. When we connect the Rumba FTP client to our malicious FTP server and send an overly long filename in the LIST response, the application crashes. Here's what the successful exploit looks like:
But before we get to spawning calc.exe, we need to understand why it crashes and how we can control execution.
Finding the Vulnerable Function
From an initial overview of the code, and the triggered exploit, we can guess that the crash happens when the LIST reponse is called on the GUI side of our app. The LIST FTP call just lists the contents of the remote directory we are connected to. So something happens when the app tries to read our long file name.
To dig further, we can attach a debugger to the application and trigger the crash. I use OllyDbg for this, but I would also recommend WinDbg as that is more comprehensive.
Here is the relevant exploit code for the following section:
my $junk = "\x77" x 1351; # Filler to reach SEH
my $nseh = "\xeb\x06\x90\x90"; # Jump forward 6 bytes
my $seh = pack('V', 0x1006E534); # POP POP RET gadget
my $nops = "\x90" x 50; # NOP sled
my $calcshell = "\cc..."; # Calculator shellcode
The exploit is also triggered when we send the following fake directory listing:
-rw-rw-r-- 1 1176 1176 1060 Apr 23 23:17 test(payload)\r\n\r\n
So we have a payload comprised of junk + nseh + seh + nops + calcshell. There is a lot going on here, but let's switch to the debugger to verify that we do call our exploit code. Firstly, we have a \cc at the start of our shellcode. This just sets a breakpoint, which is used to pause the execution when ran through a debugger. This lets us observe the program's memory right before we spawn our calculator. At crash time, we see the following register values:
We can see that we have overwritten the registers with our junk data (\77 is ASCII 'w') and the SEH handler with our address (we'll get into what this means in a bit). This confirms that we have control over the stack. But how do we know where to overwrite, and what to overwrite it with?
Another useful piece of information we can find using the debugger is the exact function that causes the crash. By looking at the call stack, we can see that the crash happens in a function called FUN_1000aed0 in the file FtpOcx.ocx. This is where we will focus our static analysis.
Static Analysis with Ghidra
Ghidra is an open-source reverse engineering tool developed by the NSA. We can use that to analyze the binary file FtpOcx.ocx and look at the decompiled code for our vulnerable function!
We start by loading the binary into Ghidra and navigating to the function FUN_1000aed0.
// Vulnerable code (decompiled using Ghidra)
void FUN_1000aed0(int param_1) {
wchar_t widePathBuffer[260]; // Fixed-size buffer
// Build temp file path
GetTempPathA(ansiPathBuffer, 0x104);
// Convert to wide-char
int len = lstrlenA(ansiPathBuffer);
MultiByteToWideChar(0, 0, ansiPathBuffer, -1, widePathBuffer, len + 1);
// No bounds checking string copy
wcscpy(widePathBuffer, parseFileName());
// Process folder
SHGetFileInfoA(...);
}
Here the important function to look at is wcscpy. This function copies a wide-character string from the source to the destination buffer. The problem here is that there is no bounds checking on the length of the string being copied, just like the strcpy example above. If the filename from the FTP server exceeds the size of widePathBuffer, which is 520 bytes (260 wide characters), it will overflow and overwrite adjacent stack memory. Let's look at the stack layout to see what happens when we overflow this buffer.
When we overflow
widePathBuffer, we can overwrite the saved pointers to SEH and nSEH stored above it on the stack. Let's understand what these do.
Structured Exception Handling (SEH) Overview
What is SEH?
In Windows, Structured Exception Handling (SEH) is a method for handing exceptions gracefully. Each thread maintains a chain of various exception handlers, and when the program crashes, it traverses this chain to find a suitable handler. If we can overwrite these handlers, we can redirect the program's execution flow to our own code when an exception occurs.
As we can see, each SEH record has a pointer to the handler function and to the next SEH record. These are added to the stack when there is a try-catch block in the function.
Why SEH Overwrite?
Some of you may be wondering, why can't we just overwrite where the return address of the function points to (EIP) and use that to call our code? Although that works in theory, there are a lot of protections in place to prevent that. Windows XP has stack canaries enabled, which is a simple way to detect if the stack has been corrupted during runtime. How this works is that the program adds a canary, which is a variable holding a fixed value, before the return pointer. If at any time this variable changes, the program knows that the stack has been corrupted and terminates execution. This makes it hard as we cannot simply overwrite the buffer with junk data. We will need to find the stack canary, which changes every time the program runs. This is hard and unreliable. So we look at a more reliable way of triggering the exploit through the SEH chain.
POP POP RET
We also notice in the code that we use a specific address 0x1006E534 for our SEH overwrite. This address points to a gadget in the ftplogic.dll file that contains the following assembly instructions:
POP EDI
POP ESI
RET
This is known as a POP POP RET gadget. When the exception occurs, Windows pushes exception records on to the stack. We want to remove that and just go to the location of nSEH. So calling POP twice removes the topmost two records from the stack, and RET transfers execution to the address stored in nSEH. This is why we set nSEH to a short jump instruction that jumps over the SEH pointer and lands in our NOP sled. A NOP is a "No Operation" instruction that does nothing and just moves to the next instruction. This gives us a safe landing zone before we execute our shellcode.
We can hardcode the address of this gadget in our code because DLL files are always loaded at fixed addresses in Windows. A large part of exploit development is finding appropriate gadgets that enable us to gain execution control. So looking at dll files is often a good start. In our case, the POP POP RET gadget is a standard gadget used in SEH overwrite exploits.
Shellcode Overview
What is Shellcode?
Shellcode is a small piece of code containing machine instructions that are used in exploits to gain unathorized control. Usually this consists of spawning a shell, hence the name. In our case, we use it to spawn a calculator when the program crashes. It is important that the shellcode can fit in our buffer space. Our code is of size ~300 bytes with the following structure:
-
Phase 1: Get current position in memory.
-
Phase 2: Find kernel32.dll base address to call windows library functions.
-
Phase 3: Find WinExec function address and call WinExec("calc.exe")
To make the shellcode position independent (so it can run anywhere on the stack), it uses relative addressing and saves the registers before execution. This is a complex topic in itself, and bypassing restrictions is also challenging, so I do recommend reading more about shellcode development if you're interested.
SEH Overwrite Exploitation Workflow
Now that we know how each part works, here's a brief recap of how the attack flow works:
- Attacker Setup: Deploy malicious FTP server with crafted responses
- Victim Connection: User opens Rumba FTP client and connects to our server (this could be automated or done through social engineering)
- Automatic Trigger: Rumba sends LIST command automatically
- Overflow: Server responds with malicious filename (>1,300 bytes)
- Stack Corruption: Buffer overflow overwrites SEH chain on the stack
- Exception Occurs: The overflow triggers an access violation
- SEH Hijack: Windows tries to handle exception using our corrupted SEH handler
- Code Execution: Control transfers to our shellcode
We have to note that the LIST command is called by Rumba only when the File List pane is opened in the GUI. When the app is opened in full screen, this is enabled by default so as soon as the user clicks connect, the exploit is triggered automatically. That's what makes this particularly dangerous, as it requires minimal user interaction. There are ways to get the user to automatically connect to the remote server as well.
Modern Mitigations
Why This Works on Windows XP
Windows XP lacks many modern security protections that would prevent overflows like these. These were introduced in later versions, so this exploit is not reproducible as is on modern Windows versions. Some mitigations include:
- ASLR (Address Space Layout Randomization): Randomizes memory addresses to prevent predictable locations.
- DEP (Data Execution Prevention): Marks certain memory regions as non-executable.
- SEHOP (SEH Overwrite Protection): Adds checks to the SEH chain to detect corruption.
The program can also be compiled with additional protections like SafeSEH, which works similarly to SEHOP. Ultimately, the exploit does boil down to the lack of basic checks in the code. While I couldn't find the exact fix that Micro Focus implemented and released in 2016 with Rumba FTP version 4.5, it's safe to assume that they added a length check before the buffer copy or used a fixed length copy wcscpy_s instead.
Reflection
I found this exploit to be a great exercise in understanding buffer overflow techniques and Windows internals. The reverse engineering process deals with a lot of moving parts and it's important to be able to break it into steps and verify that each part works as expected. While buffer overflows in theory are simple, we need to understand the memory layout of a program in detail in order to be able to exploit it successfully. It also led me to a deeper dive into Windows exception handling and exploit development, which differs a lot compared to Linux based systems. I also realized the importance of testing your code, as it's very easy to overlook bugs that are easy to fix but can cause major security issues if left unchecked. I would like to thank Professor Grenier at UMass for giving us an opportunity to explore this in detail for our Reverse Engineering course. I would also like to thank my teammate for collaborating on this project. I'm excited to use what I've learned here to explore more complex vulnerabilities in the future, and who knows, maybe even discover a few on my own as well :).
You can read our full detailed report here.
Responsible Disclosure Note
This vulnerability was disclosed in 2016 and patched in Rumba version 4.5. This writeup is for educational purposes only, demonstrating fundamental exploitation techniques on legacy software.
References
- zombiefx - Rumba FTP Client 4.2 PASV Buffer Overflow
- MITRE - CVE-2016-5764
- Umit Aksu - Rumba FTP Client 4.x Remote Stack Buffer Overflow
- Microsoft - Structured Exception Handling
- Microsoft - Understanding DEP as a mitigation technology
- FULLSHADE - Bypassing a Null Byte POP/POP/RET Sequence
- NSA - Ghidra Software Reverse Engineering Framework
- Oleh Yuschuk - OllyDbg
Full Paper: Download PDF