In the previous article in this “Professional IoT Hacking” series, we covered a couple ways to identify command injection vulnerabilities, but now we are going to go a little deeper. The attack surface will be similar — we are still targeting the web management interface of our target Vivotek FD1836 network camera, but the goal will be analyzing some of the 32-bit ARM binaries for memory corruption vulnerabilities. When decompiling or dissembling binaries, there are a few tools that rise to the top — IDA Pro and Binary Ninja. For statically hunting memory corruption in this blog post, we will be using IDA Pro.
Locating a stack overflow
During our initial investigation we identified the “set_getparam.cgi” binary as interesting for at least 3 reasons. 1) It can be accessed via the device web management interface without credentials being required. 2) It is widely used by the web management application to read and write settings. 3) It appears to be custom C/C++ code, which is always a good candidate for finding native memory corruption.
We can use IDA Pro to disassemble the file, but how will we locate a vulnerability, like a buffer overflow that leads to memory corruption? One way to approach this is to start looking for dangerous APIs calls like “strcpy,” “sprintf,” etc. By using the “Imports” tab in IDA Pro, we can see that the dangerous API “sprintf” is used in our target binary.
Next we need to configure IDA Pro to display cross-references, which will be helpful for locating where the dangerous “sprintf” API is used. We can do this by navigating to “Options > General > Cross-references” and setting the “Disassembly (graph)” value to 10.
Now back in the “Imports” tab, double-click the “sprintf” line, and then graph view should open with cross-references listed for where the function is used. Simply double-click each “CODE XREF” to discover each place “sprintf” is being used in the binary.
In this way, we were able to locate the exact location in our target binary where the dangerous “sprintf” API is being used. Both locations are shown in the screen capture below.
Suppose we want to audit the “sprintf” (on the right — there are two) to see if the destination buffer is large enough to hold the result of the expanded format string. As shown below, if we hover over “sprintf” we’ll see that “s,” which is set to register “R0” (with the “MOV R0, R6” instruction), which comes from “R6,” is the buffer we want to check the size of.
Higher up in the flow of assembly towards the beginning of the function, we see where “R6” is set (with the “MOV R6, SP, #0x1240+vlocal_4008” instruction). In IDA Pro, we can double-click any local variable to then view what the stack would look like if our binary was running in RAM.
After double-clicking the “vlocal_4008” variable, a stack view like the one below is shown. It should be noted that variables were renamed by selecting them and hitting the “N” key. Renaming variables can be helpful for remembering what they do later. Also, we collapsed the size of the pointer by selecting the variable and hitting the “A” key. This condenses the stack display and makes it easier to understand. By doing all this, we learn that our variable in question (the one renamed to “vlocal_4008”) is 4008 bytes long.
Now we hit the “Esc” key to exit out of the stack view and go back to where “R6” is being set in the assembly graph view. Looking down a little further, we see a subtract “0x3E” from the “R6” pointer, stored back to “R6.” Hmmm. What does it point to now?
As we can see, this process is cumbersome, takes quite a bit of time, requires a decent understand of how assembly works for a particular processor architecture, and can be error prone. Wouldn’t it be nice if we could turn the assembly directly to C code? It would be so much easier to analyze. For a hefty price tag, we can by purchasing the IDA Pro, hex-rays decompiling option (sold separately for each architecture). Because Binary Ninja doesn’t yet sell a decompiler, we still prefer IDA Pro for this type of reverse engineering work. With the decompiler installed, from the graph view of a function we simply hit the “TAB” key and we now see C pseudocode that is easier to understand.
We were close before, but after further stack investigation and renaming we now see that the destination buffer is a local stack buffer of size 4326 bytes. We have to figure out where the format arguments (like “v6” shown above) come from, but it quickly becomes clear how this new ability to read the code as C will greatly expedite our analysis. By looking further up in the function, we see that “v6” is set equal to the parameter “a2,” which is sent to the function as a parameter when called. The screen capture below shows this taking place in the C pseudocode.
Now we need to figure out where this function we renamed to “NETWORKING” is called. We called it “NETWORKING” because the function contains a socket API call. When it is called, what values are set for the parameters that are passed in? Let’s switch back the graph view, and follow one of the cross references to look at a calling function and see what parameter are passed in.
As can be seen below in a place where the “NETWORKING” function is called (we called this function “CALLS_NETWORKING”), the first argument is dynamic and used if present. If not, the function uses “root” as the value. Where does that value come from?
After much poking around, learning more about variables (and thus renaming variables), and tracing inputs from where functions are called from, and determining where results of function calls are used (all a very normal part of the reverse engineering process), we see that towards the bottom of the main function there is a flow like this. Data is passed into the program from the “GET_QUERY_STRING” function. What would happen if we could input like 4500 bytes? Let’s check the “GET_QUERY_STRING” function out first to see if reading 4500 bytes from a web request is possible.
Now looking inside the “GET_QUERY_STRING” function. Yep, looks like we should be able to pass 4500 bytes in via either a POST or GET web request. No limit seems to be specified for input.
Back in the “main” function we see that the results of the call to “GET_QUERY_STRING” are returned to the “data_from_web” variable, which is then set to a variable we named “heap_memory” and finally we see “heap_memory” passed into a call to the address of “function_pointer.” How do we determine where “function_pointer” points in this program we are analyzing? If we look at the code on line 63, we see that it references “&off_D00C” plus 4 or 5 bytes depending on the value of the variable “v9.” Let’s double-click “&off_D00C” to see where that goes.
As can be seen below, “off_D00C” plus a few bytes lands us in the function “CALLS_NETWORKING.” This is very interesting, because the function we named “CALLS_NETWORKING” calls the “NETWORKING” function, which then passes input to the vulnerable “sprintf” function we examined above.
So to sum up what we discovered, input seems to be passed into the program via the “GET_QUERY_STRING” function, into the “main” function, which is then passed into the “CALLS_NETWORKING” function, which then passes the input into the “NETWORKING” function, which then finally passes the input into a couple “sprintf” API calls. If we pass something like 4500 bytes to that vulnerable “sprintf” API call, it will overwrite the “vlocal_buff_4326” stack variable, resulting in a stack overflow!
In this post we covered loading a web server binary into IDA Pro, and briefly walked through looking at the disassembly, but finished up with using the hex-rays add-on to generate C pseudocode (decompiling) that allowed us to reverse engineer at a faster pace, and finally resulted in successfully tracing input through the application to locate a stack overflow. Next, we will look at verifying our stack overflow with dynamic testing, then explore hijacking control of executing code, which will then be followed up with building a ROP chain exploit to take full control of the Vivotek FD1836 network camera device. A great resource to learn more about reverse engineering is our Plural-sight training. You can also find VDA Labs teaching related classes at conferences like Blackhat and Derbycon.