No device? No worries 🙂
I. Requirements
- QEMU (https://www.qemu.org/)
- QEMU MIPSEL image installed & configured:
https://shadow-file.blogspot.com/2013/05/running-debian-mips-linux-in-qemu.html
- Binwalk
- Ghidra, IDA
- Keypatch (https://github.com/keystone-engine/keypatch)
Important: When dealing with old debian system, many libs are missing because it is EOL, I recommend upgrade your system from squeeze/wheezy to newest (current here Debian 10 – buster)
II. Emulator
Our system will look like this:
Step 1 – Finding the ROOTFS Folder in the firmware
First come to the camera page: https://support.dlink.com/ProductInfo.aspx?m=DCS-932L
Download the latest firmware (REV-B_2.18.01_06/12/2019) to the Ubuntu:
Unzip it, we got the firmware bin
Now let’s using binwalk to extract the firmware bin:
As you see binwalk will do the rest job for us (most of time), it detects the header and extract the content corresponding, we got folder _DCS-932L_B1_v2.18.01.bin.extracted
Still too much bin, we need to binwalk again
binwalk –e 50040
And again:
binwalk –e 3AC000
Here it is, we found the rootfs folder
Step 2 – Finding the Architecture & Endianness
We can find those informations by using file command on random binary within the rootfs:
As we can see the firmware architecture is MIPS, the Endian is little => We run the QEMU MIPSEL Image.
Step 3 – Copy the rootfs folder to QEMU MIPSEL
I’m now in QEMU:
First, from Ubuntu, we need to compress the folder:
Then send it to the QEMU MIPSEL:
Now from the mipsel, uncompress it:
Step 4 – chroot the ROOTFS
If the result like the image above, you’ve successful emulated the firmware J But we also need to run its service, mostly web service.
Step 5 – Finding the service command/binary
In general, this step requires you to figure which/where, reading some config code, etc. In this case, the DCS-932L Camera web server is /bin/alphapd binary.
Step 6 – Troubleshoot the problem
When Emulating, you will always encounter many problems, try troubleshoot one by one via its error dump. We run the bin:
Cannot open pid file? Seems it looking for pid file somewhere, let drag the binary into Ghidra to see how it works, come to main():
Yeah, that’s why, let create this file to pass the check
“waiting for nvram_daemon”? We can see it here
Maybe we missing the nvramd.pid too, let create one:
Ok, now we come to the “always happens problem” when emulating: the NVRAM. Non-volatile random-access memory (NVRAM) is random-access memory that is non-volatile. By default, emulator doesn’t have nvram, we need to fake it.
Luckily, there are many nvram emulators available, one of them is: libnvram
https://github.com/firmadyne/libnvram
clone the repository to QEMU MIPSEL, the emulator is pretty old that we need to modify a bit, delete line 338 to 342:
then make it:
We now got libnvram.so! Copy it to our main ROOTFS
The libnvram requires put in appropriate folder, we need to create some:
Now try again, using LD_PRELOAD to load the shared object before any other library (including the C runtime, libc.so)
Then /bin/alphapd, it works!
But we got new problem:
Unable to write ‘random state’? It means The RANDFILE and HOME environment variables are not set. We create an empty .rnd file and set the environment variable:
Run again:
Another error. We come to IDA and find this string, SHIFT + F12 -> xref the string
We can see that it does something in getSysInfoLong, it will get ip from gpio device interface, we are in emulator so we don’t have this interface, we also don’t care, just patch to return the other branch. Using keypatch:
We jump directly to inet_ntoa path, this function will check if IP input is incorrect => return to 0.0.0.0, so our server IP will located at 0.0.0.0. Now replace the original with our new alphapd, give it execute permission, and re-run the service again:
Cool, we able to start the service, let access it
It works! But when we try to access any section, it requires Authentication
Try admin/admin, admin/, it won’t work, let see the log in MIPSEL console:
Its because the value in nvram are not here, the nvram emulator can’t get it.
We back to the repository https://github.com/firmadyne/libnvram, in config.h, it defines the default path, value will be set to nvram, now we will go around the firmware and find the nvram configuration file, then edit the config.h, then recompile the libnvram.so:
We take the record which is not start with # and got the key pair struct to config.h, for example Platform=RT5350 => ENTRY(“Platform”, nvram_set, “RT5350”)
To login, we much fill two key:
ENTRY(“AdminID”, nvram_set, “admin”)
ENTRY(“AdminPassword”, nvram_set, “tsu123”)
Then we recompile the libnvram as above (remember to delete the nvram.o & libnvram.so in the folder first)
Put it in appropriate folder:
Run the service again:
Thanks @chung96vn for supporting.