In Extracting firmware with just a UBoot Shell we went over the process of extracting firmware from a device when we cannot download it from the manufacturers site. Even though we got the firmware, I still wanted to have the official firmware that was distributed by the manufacturer.
In this blog, I go over the process of reverse engineering the firmware updating functionality of the eWeLink FGHGF VG-IC-01 IP Camera so that we can get an official copy of the firmware.
Users can interact with their device through the mobile app that is available in both the Google Play store and iOS App store. I downloaded both apps but could not find any update features. This lack of user initiated updates led me to believe that updates are coordinated between the device and server directly. Having that in mind, I decided to look into the device's boot up process.
As a researcher I do not have access to the engineers that built the camera, which means that my next best move is going to be digging through the files we extracted from the camera in the previous blog. Somewhere in all of those files we should find the service that either initiates or at least installs firmware that is sent to the camera. If you want to follow along you can download the files for this blog by clicking on the link below.
If you check the files_in_memory directory you will see that there are a lot of files that were extracted when we dumped the memory through UBoot. That being said, a lot of these files are empty or corrupted. After manually reviewing the list of files 2 of them stuck out as good starting points: squashfs-root, and squashfs-root-0. Below I have included the list of files in both directories.
bin lib squashfs_init boot.sh linuxrc sys dev mnt tmp etc proc udhcpc filesystemversion sbin usr gm sd_upgrade.sh var init share
AVRecSch av.xml colinkwtg.sh lib AVRecorder avencode dbback res AlarmServer captive_server devctrl rtspd App.sh cfg drive script IOTCare colink factorytest sd ProcessGuard colink.sh font version
The first file that caught my eye was boot.sh. It sounds like a good name for a file that is called when the device is booted up. As you can see in the gist below, the home variable is set, the PATH environment variable is updated, a couple file system nodes are created, a shell script is copied into /usr/bin, and then the sd_upgrade.sh script is called on line 14.
After taking a look at sd_upgrade.sh I saw that the device checks to see if there is an SD card inserted, and then checks for the existence of a handful of files and runs a script at the end. I will dig more into this process in another blog, but for now it suffices to say that there is a way to do an offline update if we insert an SD card with the right files.
Going back to boot.sh we can see on line 17 that there is a call to a binary called netupdate that is stored in the /gm/bin directory that is called during boot up. The file's name is in line with our suspicion of an update occurring during boot up. Now that we have found this binary we are going to need to disassemble it to confirm that it reaches out for an update during the bootup process.
Reverse Engineering netupdate
In the squashfs-root/gm/bin/ directory we find the netupdate binary. If you are on a *nix machine and run the file command on the binary you will see that it is a 32-bit ARM binary.
››› file netupdate netupdate: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped
If you run the strings command on this binary and grep for "http" or "ftp" you will find that there are some occurrences of these sequences in this binary. From this output the two strings that stuck out the most were upgradeftp and /tmp/http.ko -n 14 -o /var http://%s/%s.
››› strings netupdate | grep -E 'http|ftp' --quit this ftp client program upgradeftp http:// https:// /lib/modules/http.ko cp /lib/modules/http.ko /tmp/http.ko chmod 777 /tmp/http.ko /tmp/http.ko -n 14 -o /var http://%s/%s http.ko
Although strings did provide us with promising results, we still do not have a URL which we can use to get the firmware. Unlike a bash script we cannot just open the file in a text editor, we are going to need a disassembler. For our disassembler we will use Ghidra.
After loading the binary into Ghidra I searched for the two strings that peaked our interest. When I searched for /tmp/http.ko -n 14 -o /var http://%s/%s using Ghidra's "search for strings" feature. I found a reference that was made by the function in the figure below. Note that this isn’t ARM and instead the C code that was provided to us by Ghdra’s decompiler.
In the figure you can see that the "http.ko" binary in format string on like 269 is issuing an HTTP request to a URL that is constructed based on some dynamic information. Aside from this string containing the promise of a command injection vulnerability if those parameters are user controlled, and the lack of HTTPS on that URL, it looks like you can figure out what the URL is by reversing the prior 268 lines in this function.
Going over the process of examining the decompiled code would make this blog drag on for far too long. Instead, I have provided the Python code to replicate the same process in the gist below. Note that the function above reads a file called "filesystemversion" that is in the device's root directory that contains all of the informat we need to know to get the firmware URL. If you want a copy of the firmware as it is provided by the manufacturer, you can run the gist and issue a GET request for the resulting URL.
Although we had the device’s firmware already, we were able to use what we had to get an official copy of the firmware. We reviewed some bash scripts that are called during boot, discovered some applications that were called by said scripts, reverse engineered those applications, and re-implemented the URL generating logic of the firmware updating service in Python. We also discovered a handful of possible avenues for us to get access to the file system after the camera is fully booted up.
In my next blog I will go over how I used SD updating functionality and the files stored in the firmware that I downloaded from the manufacturer to open a telnet listener on the device so that I could do active reconnaissance on the services that run on this device.