In the first post of this series, I’d spent significant time getting to know the <redacted> device physically but I was now ready to start harassing the software. Initially, we glanced over the software using the default “stupid” credentials that we discovered during the surface mapping phase because process is vital when assessing an unknown device. Now with a decent understanding as to the functions each physical interface performs, we want to start to learn how the User Interface (UI) works and what we can accomplish with the tools we’re given.
After enumerating the function(s) of each physical surface, we learned there are multiple ways to reach the same UI. We can use serial, USB, or the web to connect to the device. While I’d determined that these roads all lead to the same place initially, the time had come to confirm this with some level of certainty. I connected a second monitor to my testing laptop (after all, screen real estate is critical) then connected to all 3 interfaces at once. The serial, USB, and web interfaces all granted us access with the default credentials previously discovered and while the terminal interfaces weren’t as pretty as the web interface, we were able to see the same functionality across the board.
Poking around the interfaces, it started to become apparent that this embedded device is some sort of networking component. I was able to deduce this based upon the functions that were available to the user logged in as an administrator. From configuring the upstream network connections, configuring and managing VLANs, and even configuring an SNMP agent, it was obvious this has something to do with sending/receiving data on the network to which it’s connected. Poking around even further, I found the ability to ping and trace the route to other devices on the network. What I didn’t understand with the observable operation of the device was HOW. How was authentication being managed? How was the device able to do these “networky things” without the stuff I’m so used to seeing on a system? If I’m an administrator, which I clearly am based upon my user name, I should have the ability to do all the things- or would I??
Thinking through the functionality observed, it made me think that while some functions of a “normal” machine (like my laptop running Linux for example) are available, the interface is some application that obfuscates the actual operating system underneath. What’s more, if there is some sort of normal operating system underneath the layer I’m able to access, it’s theoretically possible that there is some role above administrator- maybe a God mode or root? I decided that I needed to learn more about the architecture of the operating system.
I took the “stock” copy of the firmware I was provided and decided it would be pertinent to reverse engineer the software in order to determine what’s going on behind the curtain. Just as with the surface mapping of the device, I’ve found it valuable to be consistent when mapping the surfaces of the software. The first thing I do when analyzing any given software package is to see what I can see without any special tools. Do accomplish this I use one of my favorite Linux tools often overlooked and underestimated- strings.
The strings command is so simple it’s beautiful. Running this against a given file will extract any character strings that are found inside the package. I’ve seen this work against many different packages more often than it should. Extracting the strings can often provide some good insight into the workings of the software. Unfortunately, that wasn’t the case in this instance. After extracting the strings from this package, I was overwhelmed with a bunch of useless stuff. No user names, nothing that I was able to correlate with my known password, no file system information, nothing. Here we should come to an agreement that while it is an awesome tool, it’s not a silver bullet and not always useful.
But I wasn’t satisfied and wanted to push this further. At this point I’m convinced that there will be some benefit to understanding the software package completely. Fortunately I’d discovered a project that seemed like it was exactly what I was looking for. The Firmware Modification Toolkit (FMK) is a collection of scripts and tools that will take a given binary and performs several operations. The first step is to pull the binary apart and understand its components. In order to do this, FMK gets the total byte size of the firmware package. This is important because usually the end goal is to modify the original firmware, recompile the modified version, and reload the modified (likely malicious) firmware onto the target device. If the device is expecting a fixed size and we’re not within that expectation, we risk bricking the device- that’s not a good thing! Once the total size is known, FMK uses a common tool called binwalk which does some binary voodoo such as scanning for file, binary, or custom code signatures; plotting file entropy; performing hex dump and diff two given files, and ultimately extract signatures and file system data from the package. The binwalk portion of the FMK deconstruction process provided me with a lot of valuable information such as the memory addresses and type of the boot loader and uImage header as well as the type of compression and file system in play. FMK also determines the endianess in play which is necessary for reassembly, then seeks to determine the compression type used on the file system. There is some other checking based upon the file system type discovered such as looking for a block size if the target package uses squashFS. FMK keeps chugging away, determining the size of the header image before it, stopping short of the file system. Once the header is extracted, FMK tries to determine if there is a footer at the end of the image. This is done by grabbing the last 10 lines of a hexdump of the firmware image, excluding the last line, then reversing the line order. If a footer is discovered, it’s dumped but if not we keep going. Before actually getting to the extraction of the file system, FMK logs the pieces of the puzzle it’s learned to this point. The firmware size, header type and size, header image size and offset, among other tidbits are logged to a file which will be queried when we start reassembly.
Extraction of the file system is still a little cloudy to me as I continue learning thedifference in compression types. Currently (as of this writing) FMK only supports squashFS and CramFS. If the target firmware uses some other compression, then it will fail and quit. So the little gnomes inside my laptop work to decompress and extract the file system. Once they’re done digging, the gnomes toss the file system into the proper directories inside the fmk subdirectory, then a simple check looks to see if the expected subdirectories were created and populated- if not, it fails and quits. It was a gratifying feeling to see the subdirectories I’m used to seeing with a usual Linux build. I started browsing the contents of /bin, /dev, /etc, and more. When I found the shadow and password files, I knew I was on the right track and gratification washed over me.
Reverse engineering is a process. It takes a lot of intuition, a lot of determination, and a lot of learning the nature of the obstacles you’re working to overcome. Now that we’ve learned how to decompile the firmware and have something to work with, the question becomes “what can I do with it?”
We’ll answer that next time.