I was looking for some basic reversing fun to sharpen my skills when the “Shadow Brokers” dump happened. Among the tools leaked was an executable (ELF) named “catflap”. A leaked NSA tool… in ELF format… named “catflap”. How could I not reverse it?!
I fired up Radare2 and started reversing. I eventually got to the function at 0x08048a10 which appears to create an “evil buffer” to intentionally cause an overflow. Halfway through reversing the algorithm, I discovered that this function itself has a buffer overflow. There is an overflow in the overflow. Not just that, but it is the most trivial buffer overflow to exploit that I have ever seen.
I chose to go with public disclosure as I did not expect a response if I reached out to the vendor. Due to this vulnerability’s very low severity, I didn’t have time to think of a trendy name (sorry, there goes my media coverage). Do you think this deserves a CVE?
As a side note, I’m primarily calling this a “zero day” for comical value. I haven’t seen this specific vulnerability in catflap (let alone any vulnerability in catflap) mentioned elsewhere. Googling “catflap exploit” will not help, since catflap itself is an exploit.
Note: If someone else deserves some credit for this, let me know and I will attribute them their due.
Before we get into the exploit, let us consider why this vulnerability exists.
I posit there are three possibilities:
1. THE DEVELOPER WAS AN IDIOT
This overflow is very obvious, or at least it should be to any exploit developer. However, blaming a vuln on an incompetent dev is almost always wrong. The tools that have been leaked come from the Equation Group which is a threat actor named due to their affinity for difficult math and cryptography. Moreover, it is dangerous to consider an adversary an idiot (…but is the Equation Group an adversary?). It’s more likely that the dev simply did not care about how blatant the overflow is.
2. THE DEV DID NOT CARE
This is most probable. Someone likely whipped this exploit together in C real fast with little regard for bounds checking. The vulnerability occurs in the command line options. An attacker hoping to get RCE on the Equation Group would have to convince an Equation Group member to run catflap with strange parameters – parameters that look an awful lot like shellcode… If you can get the Equation Group to run one of their own secret tools with your shellcode, (where no shellcode should exit), then you deserve OP on every IRC channel (just hit alt+f4). The exploit vector is very weak, so why would an exploit developer waste time tracking down such bugs?
3. THE TOOL HAS A DUAL PURPOSE
I like this idea a lot, but this is really just wild speculation. On a compromised box, you would expect to find viruses and exploits; exploits to pivot into other boxes and a virus to maintain persistence. Both are treated very differently! So, what if catflap was both? Catflap can reliably load arbitrary shellcode via the overflow. This is just like a virus dropper but with every appearance of an exploit. When you find something that looks like an exploit, smells like an exploit, and runs like an exploit, this means you will probably be looking harder for a pivot attack on the network than an infection on the local machine.
While this is a neat idea, I really doubt this is the original purpose of catflap. Catflap apparently exploits a known overflow in /bin/login. It is probably an old exploit (it lacks NX) and was left around just in case. Other minor clues in the ELF indicate that the dev was not very careful about memory management, meaning it’s probably the second option. That said, catflap still has the potential to run arbitrary shellcode reliably, as we will see. So, if you find catflap on a compromised box, don’t get caught up thinking “stupid hacker, we don’t use Solaris”, or “OMG my precious Solaris!”. Consider that catflap could have been used as a virus to get persistence on the local box.
Alright, on to the good stuff. The function at 0x08048a10 appears to build an “evil buffer” to be used for the Solaris exploit. One of the parameters it accepts (ebp+0x10) is the user-supplied command to be run on the Solaris Machine. The user’s command is duplicated into the heap with strdup (@0x08048a3e). We will refer to the returned heap pointer cmd_dup. The loop at 0x8048c00 explodes the cmd_dup string on whitespace characters (0x20,0x09).
For clarity, the following is what that explode loop would look like in Python:
In C, it is not so simple, as the explode loop replaces each white space (0x20 and 0x09) with a null byte. The next iteration then double-checks that the next byte is not a whitespace character, then appends a pointer to that character in an array of type: char ** at ebp-0x228. Let’s call the ebp-0x228 array exploded_cmd. The exploded_cmd array is obviously on the stack (referenced from ebp), but the explode loop never checks the size of the array before writing to it. This means by providing a command string with a lot of space-separated characters, the exploit_cmd will fill the stack into higher memory locations eventually overwriting the saved return address.
After the loop finishes, a check is performed to see if the null pointer used to terminate the exploded_cmd array was clobbered. If so, the function prints “Command string has too many tokens” and returns.
I want catflap to play Nyan Cat in the terminal. That is my goal.
The exploit is incredibly easy and reliable. We can not directly control with what the return address is overwritten, but we don’t care. The return address will be overwritten with a pointer to our buffer! The buffer is already in the heap. Consequently, NX and ASLR would be bypassed if they were enabled. The check to determine if the end of the array got corrupted is a useless technique to mitigate the exploit. At the time of the check, the return address is already overwritten. Not only that, failing the check does not cause an exit(-1) but rather a return! This is like finding your stack canary dead but continuing down the mine anyway.
Hitting shellcode is easy, we just need the offset. The easiest way to find the offset is to just run the program with a big buffer containing unique characters separated by spaces, then see to where the program tries to return.
The following script is used (offset.py):
Start catflap in gdb and set a breakpoint on the function’s return (0x08049186):
In the “x/100bx $eip-30”, command we see that every even byte is incremented and every odd byte is null. This means we landed in the buffer. In the “x/3bx $eip” command, we see the byte we landed on was 0xa8. Our offset.py script started the bytes at 0x21 (to avoid white space) so we subtract 0xa8-0x21 to get 0x87. We need 0x87 characters separated by spaces, then our shellcode.
Let’s first double-check the offset with this code:
We hit the first “B” (0x42), so we just need to put shellcode there and we have code execution. Where do we get Nyan Cat shellcode? Metasploit of course! The nyancat.dakko.us server hosts telnet Nyan Cat, we just need to run the command “telnet nyancat.dakko.us”. Metasploit’s msfvenom has a “linux/x86/exec” payload that can execute the telnet command. I will use the ASCII encoder to make it a bit more pretty when we run it on the command line.
Note: Don’t forget about bad bytes. The null byte would actually be okay because we can just replace it with a 0x20 and the program will turn it into a 0x00. This would mess with offsets, though, so it’s best to avoid it.
And finally the big payoff:
Or, if you would want to try at home (however, don’t ever run commands someone tells you to):
To see it in action, check out the following asciinema: