I needed to find the address of malloc() in Cisco IOS. Incidentally, I also need to find free(), but I havent gotten around to doing that yet.
The first thing to note is ‘show mem’ on the IOS command line. It shows malloc chunk information, and is utterly useful; it gives you the address of each malloc chunk and its header information. The next thing to note is the IOS Exploitation presentation by FX in 2002 http://www.blackhat.com/presentations/bh-usa-02/bh-us-02-phenoelit-network.pdf. This presentation gives information on the malloc chunk itself, the header and the trailer. Also essential is dynamips http://www.ipflow.utc.fr/index.php/Cisco_7200_Simulator which I use for dynamic analysis to locate malloc.
Dynamips has a command vm_debug pmem_r32 (and r16, and w16, and w32) to print data at a physical address. I suspect IOS has a command to dump specific memory contents, but I don’t know what it is (can someone who knows IOS tell me if this is true or not?). I made some modifications to dynamips to do virtual address lookups. The problem with implementing read memory using a virtual address, is that virtual to physical address translation is done in software under MIPS in the event of a TLB miss.
In the FX presentation, and visible by looking at the malloc chunks in memory, we can see that the first 32bit word of a malloc chunk is a constant magic value of 0xab1234cd. At the end of a malloc chunk (following the malloc data) is the ‘redzone’, which is another constant value of 0xfd0110df.
Finding malloc’s address has been done before. http://www.lemuria.org/mirrors/lynn-cisco.pdf finds the address of malloc by its relation with an error string it uses. I do not believe his code is public however.
Attempt 1 that doesn’t give us results)
First, Load the IOS image in IDA (which requires unzipping the original image, and setting the e_machine type to MIPS in the resulting elf binary). Because MIPS doesnt allow you to do a 32bit immediate load, the loads are performed as two 16 bit loads. The logic we are to follow is that the malloc function writes the constant values to the malloc chunk, so finding those stores, gives us the malloc procedure. Searching for an immediate value of 0xab12 gives us a list of functions to work with. Then searching for 0xfd01 and finding a matching function gives us 1 function to work with.
Is this the function we are looking for? It seems like we followed the right approach, but this is not the code we are looking for. I included this approach, even though it did not work for me, because it seemed like a fairly reasonable approach.
Attempt 2) A dynamic approach
This approach works and gives us a malloc location in any IOS that runs inside dynamips.
If we track the MIPS store word, or sw instruction and look for a write of 0xab1234cd, we can tell when a malloc chunk is being created, and at what location in memory the chunk is at. So we instrument sw in dynamips, and have it keep track of the chunk address in an associative map.
We also keep track of the stack trace. I did this by instrumenting JAL/JALR and JR to build a stack implemented as a c++ list. JAL is jump and link, JALR is jump and link register. Its the MIPS call instruction. It makes a copy of the return address in the register $ra (return address) register. Then to return, JR, or jump register is called with a target of $ra.
Now when a function returns (JR $ra), we look at the return address (register $v0), and see if that address is in our map storing malloc chunks. But because we are looking for the user’s view of malloc without the prepended header, we believe the return address is actually the address of the malloc chunk plus an offset. Turns out that offset is 48 in ios 12.4. Having verified the return address is the address of the malloc buffer (chunk address + header), we take note of the current procedure on the stack frame and call this our malloc.
Also important, is that only the first function that returns our malloc buffer is malloc. Otherwise we could get functions that simply pass around the malloc buffer.
So running the code, low and behold gives a single address for the times we see the first malloc buffer being returned. Comparing the results with IOS’s show mem command validates our findings. A little more inspection, sees that register $a3 (argument 4) contains the size of the malloc buffer.
Interestingly, the size passed to malloc() is 4 less than as shown by the show mem command. Perhaps this extra 4 bytes is used internally for debugging.