> HooToo TM6 vulnerabilities
Part 3: In the course of reverse engineering the HooToo TM-06 Travel router, there were two interesting vulnerabilities discovered. Both are in the IOOS (vshttpd) web service. This is not shocking because the web service appears to be a custom implementation specific to the device. That’s not to say the developers weren’t good, rather it is that custom code tends to be the one that receives the least scrutiny. One vulnerability is a stack overflow. Another, a heap overflow. In this article we’ll see how to fully exploit the heap buffer overflow vulnerability.
There is a partial ASLR implemented on the device: the dynamic libraries and the stack move around. So far as I could tell, there were no other significant protections. Both the heap and the stack are writable and executable.
We found two buffer overflows. One is stack based (sprintf), which allows us to overwrite the return address on the executable stack. We were not able to exploit that vulnerability because it requires an information leak. However, it comes with a great attack vector via an XSRF. The other vulnerability is a heap overflow (strcpy) where we are able to overwrite a function pointer on the heap. We then leverage the fact that the heap is predictable, and does not move around, to build a full exploit with arbitrary binary code execution. Finally, we look at proposals on how to fix these vulnerabilities.
In this article we walk through vulnerabilities found in the webserver component of the HooToo Travel Mate 6 router. For background knowledge on the product and the various subcomponents please see the previous posts on Protecting the digital nomad (Part 1) and Reverse Engineering of an Embedded Webserver (Part 2).
The HooToo HT-TM06 webserver suffers from a potentially exploitable stack overflow. We say potentially because the memory corruption mitigations, as enforced by the OS, prevent full exploitation. However, given that, historically, claims of non-exploitability have had the tendency of being wrong, I prefer to make it a soft claim. The webserver executes as a privileged process on the router, so an attacker could gain privileged code execution via this vulnerability. In addition to running as a root user on the device the process listens to both internal and external interfaces.
CVE ID: CVE-2017-9026 Affected versions: HT-TM06 Firmware 2.000.030
This write up focuses on firmware 2.000.030 because at the time of analysis it was the latest version. However, due to the implementation style observed – abundance of sprintfs and other dangerous functions, it is believed that earlier versions will also be vulnerable as well.
ioos is a webserver responsible for handling the CGI content of the HT-TM06 web interface. Labeling itself vshttpd on HTTP responses, it responds to requests behind a lighttpd proxy. The function of ioos is to coordinate user sessions, authenticate users and reconfigure the system upon request. The webserver is configured to respond to *.csp requests such as GET /protocol.csp.
Upon closer analysis it was discovered that the ioos webserver has a stack overflow memory corruption vulnerability which can be triggered by an unauthenticated attacker. Most requests require an authentication token in the cookie to process, but the parameters abused by this vulnerability are processed before those checks are completed.
The trigger HTTP request looks as follows:
Notice that the parameter fname contains a very long string—longer than 256 bytes. That is what causes the overflow condition. Normally this string is just a few bytes.
A response to this request (without triggering the memory corruption) looks like this:
So, it becomes clear that the fname parameter is used to construct the XML response string. Looking at the disassembly we can see that the parameter value is used in the xml_add_elem function. This function appends a formatted string value to the response message string.
In the disassembly above we can see that the destination string is a stack based buffer: $sp + (0x238+var_110). Since sprintf is used instead of snprintf, there is nothing to prevent the function from writing past the buffer boundary.
Why is this issue so serious? Because it gives an unauthenticated attacker the ability to control the Program Counter. How? By allowing the attacker to change the function return address that is stored on the program stack which used to direct execution at the end of the function execution.
As mentioned above the sprintf allows the attacker to write past the buffer boundary. On the stack, the buffer occupies 256 bytes:
The destination buffer stops at 0x7f8e2078 and begins at 0x7f8e1f78 which the difference being 0x100 (256) as shown above. After the buffer, there are 8 bytes of local variables. The variables are followed by the saved return address ($ra register). This return address is used by the function before it returns. We can see the current value is 0x0043bb04 at location 0x7f8e2078 + 8.
After the sprintf call at location .text:005126A4, the same location on the stack looks like this:
The return address value has been changed to an attacker specified value which means that the program will jump to that address to continue execution resulting in a crash:
The crash is occurring because the location we send the Program Counter to does not contain any valid instructions. After non-exhaustive attempts at exploitation we found that the partial ASLR deployed on the device was enough to swart our attempts at exploiting this particular vulnerability. There are two constraints that we have to deal with:
- Due to the use of the sprintf function, we cannot have NULLs in the buffer. Not an uncommon constraint.
- Due to the use of the format string “</%s>”, the last character of the buffer must be a greater than sign.
The following scenarios were considered:
- Point the return address to some location is the main binary that would then jump to a buffer on the stack. The stack is executable (great!), however the program is located at a low address that starts with a zero. Due to the ending character in the format string, we cannot manage a zero character even though the architecture is little endian. Normally we’d be able to leverage the NULL that sprintf automatically places at the end of the C-String.
- Point the return address to some location in the heap. The heap is executable (great!). We are able to get a buffer into the heap (great!). The heap is at a location just above the main binary which has locations that start with a zero.
- Point the return address to some useful library instruction. I realize that the libraries actually move around between executions due to memory randomization.
- Point the return address directly to our buffer on the stack. The stack is located in high memory and moves around along with the libraries.
So, in order to exploit this we need to find a memory leak to get one of the last two scenarios to work. This is really unfortunate, we got thwarted by memory randomization and constraints on the format string.
Nonetheless, this vulnerability is particularly dangerous because, as you can see, it gets us really close to full exploitation. Also, if it works then it can be exploited without authentication and launched from the user’s web browser via a cross site request forgery attack. That is because the attack strings could be sent via a pure GET request. Such request can be embedded in some innocent looking page, an iframe or via an XSS attack of some unrelated website. In essence we would be able to exploit a user’s router through their browser – what a great attack vector!
The bug is trivial to fix. First option is to just use snprintf instead of sprintf:
Alternatively, one can also enable the use of stack canaries and recompile the original code. The program will still be remotely crashable, depending on how exceptions are handled, but it should prevent the attacker from directly controlling the program counter.
The HooToo HT-TM06 webserver suffers from an exploitable heap overflow vulnerability. The webserver executes as a privileged process on the router, so an attacker could again privileged code execution via this vulnerability.
CVE ID: CVE-2017-9025 Affected versions: HT-TM06 Firmware 2.000.030
This write up focuses on firmware 2.000.030 because at the time of analysis it was the latest version. However, due to the implementation style observed, it is believed that earlier versions will also be vulnerable.
ioos is the webserver responsible for handling the CGI content of the HT-TM06 web interface. Labeling itself vshttpd on HTTP responses, the server responds to requests behind a lighttpd proxy. The function of ioos is to coordinate user sessions, authenticate users and reconfigure the system upon request. The webserver is configured to respond to *.csp requests such as GET /protocol.csp.
Upon closer analysis it was discovered that the ioos webserver has a heap overflow memory corruption vulnerability which can be triggered by an unauthenticated attacker.
The trigger HTTP request looks as follows:
Notice that the cookie header contains a very long string – longer than 1024 bytes. That is what causes the overflow condition.
The above code shows the call to strcpy which copies the user supplied data to an internal cgi_tab data structure. The data structure is allocated on the heap with the string buffer, at most, 1028 bytes long. After the string buffer other data follows, specifically function pointers. This is a common pattern with data structures used in ioos.
The strcpy call is unbounded and so the unauthenticated user can supply any sized string to be copied into a fixed destination buffer.
Why is this issue so serious? Because it gives an unauthenticated attacker control of the Program Counter. How? By allowing the attacker to change the function pointers following a string buffer on the heap. Doing so the attacker can change program flow and potentially execute malware.
As previously mentioned the strcpy will write past the destination buffer end. Let’s see what happens under the microscope. We will set a breakpoint before and after the offending strcpy call.
Before the call, the parameters look like this:
The snippet above shows the destination and the source buffers. The source being controlled by the attacker, we see that the attacker is ready to supply a new pointer value. Now, let’s breakpoint just after the strcpy call.
The end of the original destination buffer now looks like this:
Notice that the addresses on the destination buffer match exactly and the pointer at 0x5ad730 + 4 has a new value 0x42ff4242 which is clearly invalid. If we let the program run we see that the program will use this address to look for instructions to execute:
Not good! The new pointer takes effect slightly further down the execution path from the strcpy overwrite:
The purpose of the above function is unknown but this is where the attacker gains control of the execution. This vulnerability is very much exploitable. What we notice is that the heap is allocated at low addresses and its structure is not randomized. This means that the heap allocations are at predictable addresses and the way that the server is implemented makes the malloc allocations predictable. It is probably due to the minimal implementation of the heap algorithms for the embedded system as well as the server’s single threaded model. So, we were able to insert a static address into our exploit buffer. Then we can point the program counter to our shellcode and take control of the device!
Unlike the stack overflow vulnerability, this one requires a more complex HTTP request. This means that the attack vector gets a little more complicated. However, since the device listens to all interfaces we can exploit the router directly from, say, a rogue WiFi router that it connects to.
The bug is trivial to fix. As a general rule strcpy is considered dangerous and should be avoided. Instead, use strncpy which accepts the size of the destination buffer as a parameter which would prevent such overwrites.
As an additional measure of protection, the function pointers on the heap should be XORed with an execution specific random integer. This way an attacker would not be able to overwrite them with useful values thereby preventing exploitation attempts even in the presence of overflow conditions.
Reporting to the vendor
The vulnerabilities were reported to the vendor on January 24, 2017 (stack overflow) and January 21, 2017 (heap overflow). The company quickly acknowledged their receipt and provided a new build with a patch on February 19, 2017. I found it interesting that HooToo has provided the update to me personally rather than making it generally available. Their download page was later updated on March 7, 2017. It is very important to take such vulnerabilities seriously and make patches generally available as quickly as possible.
Upon examining the change log, I only saw this line:
There are many reasons why an attacker might want to exploit these devices. One obvious reason is to gain access to the user’s information or manipulate user activity. Having malware on the router means that the attacker could potentially see unencrypted traffic, modify their DNS lookups to enable man-in-the-middle attacks or inject malicious content into traffic. For example, have the ability to inject iframes for browser exploitation or catch vulnerable update mechanisms .
Other reasons for leveraging the vulnerabilities in the travel routers are a bit more indirect. One is to obfuscate attack sources and make attribution harder by creating a jump point . Another is to use the router to infect other systems. A person using the travel router would likely be traveling a lot and touching many different networks from a position of some trust. These networks could include hotels, airbnb’s, private residences, cafes and enterprises. And so, having malware on the device would give an attacker a foothold inside another network.
Both of the aforementioned vulnerabilities are web based and require the attacker to have direct access via an IP address. The easiest way is via a user connected to the WiFi created by the device. However, another way is to go from the outside network. That is because the webserver doesn’t just listen on the internal network, it listens on all interfaces. And so, if an AirBnB network has been compromised then an attacker could discover the router as the client on the WiFi and launch attacks from there. There is already an example of malware for Android caught in the wild attacking host WiFi routers .
The stack overflow vulnerability presents another interesting opportunity for the attacker because it doesn’t require the attacker to be on the network along side the device. It has been previously reported  that the device is vulnerable to Cross Site Request Forgery attacks. Since the overflow is in processing of a GET request field, it means that the attacker just needs a user to open a page with a forged request to gain execution on the router. Now, that is an awesome attack vector!