As mentioned in Chapter 22, the Internet of Things (or IoT, as used throughout this chapter) is expected to exceed 20 billion devices by 2020. Those devices will be part of our lives at home, on the job, in the hospital, and so on, while we are running, bathing, driving, watching TV, and even sleeping. Unfortunately, this explosion of devices also allows attackers to have more options to steal our information, and the most scary part is that it could allow them to cause physical damage to society—by hacking into a pacemaker and causing a heart attack, by controlling a car remotely and making it speed up until it crashes, by hijacking an airplane or ship, or by sending an overdose via an infusion pump, just to mention a few scenarios.
This chapter will help you prepare to respond to malware targeting IoT devices by having tools and techniques to dissect malware running on ARM or MIPS architectures in order to detect, stop, and hopefully prevent such attacks in our organizations.
In this chapter, we cover the following topics:
• Physical access to an IoT device
• Setting up the threat lab
• Dynamic analysis
• Reversing ARM and MIPS malware
Having physical access to the IoT device via a serial port is recommended for many reasons:
• Incident response Scenarios where a device is infected and the malware running inside needs to be analyzed, but the device is not accessible either from a local or remote console because it has been hijacked by ransomware.
• Penetration testing This is the most common scenario, where gaining physical access to the device’s console can give you root access if it’s not configured securely.
The section “Serial Interfaces” in Chapter 23 provides a great explanation of serial ports like UART and even provides a description of the JTAG interface. Refer to that chapter for more information on these topics. In this chapter, we focus on interacting with the RS-232 serial port because it is widely used to allow console access on many IoT devices.
Our discussion of the RS-232 serial port focuses on the information needed for an incident responder or penetration tester to be able to interact with the serial port. For a more complete overview of the RS-232, refer to the link provided in the “For Further Reading” section at the end of this chapter.
Back in its heyday, RS-232 was the standard communication port in personal computers; however, due to its low transmission speed (among other factors), it was eventually replaced by USB technology. However, in the IoT world, it is still a very common communication protocol used mainly to provide console access to medical, networking, entertainment, and industrial devices.
RS-232 can transmit or receive data synchronously and asynchronously, and it can operate in full duplex mode, exchanging data in both directions concurrently. The data is transmitted via voltage levels where a logical 1 (mark) is between –15 and –3 VDC and a logic 0 (space) is between +3 and +15 VDC. Figure 25-1 presents a typical example of an RS-232 waveform; the transmission begins with a “start” bit (logic 0), followed by the data bits enclosed between the least significant bit (LSB) and the most significant bit (MSB), and finishes with a “stop” bit (logic 1) to signal the end of the data. Although it’s not reflected in the figure, a parity bit can also be used or discarded in order to validate data integrity. Last but not least is the baud rate, which measures the number of bits transferred per second (bps). The most common values are standardized at 9600, 38400, 19200, 57600, and 115200 bps.
Figure 25-1 RS-232 waveform example
The most common RS-232 connectors are the DB-9 and DB-25. In this chapter, we focus on the DB-9 because it is the most common connector on IoT devices. Before the pinout details are listed, it is important for you to understand how these devices are classified, because a pinout is defined based on the device classification. There are two types of RS-232 devices: data terminal equipment (DTE) and data circuit-terminating equipment (DCE). Commonly, a DTE is tied to a computer and a DCE to a modem. However, modems are not so common anymore. So, the best way to identify whether you are dealing with a DCE or DTE is by measuring the voltage in the transmit pin, which in a DB-9 can be either pin 3 or pin 2. If you get a voltage between –15 and –3 VDC in pin 3, the device is a DTE. If you get that voltage on pin 2, you are dealing with a DCE device.
Why is it important to identify whether a device is a DCE or DTE? Because the pinout is different, as shown in Figure 25-2. Pay attention to the differences between pins 2 and 3, because a wrong connection will cause null communication with the serial port.
Figure 25-2 DTE and DCE DB-9 pinouts
Identifying the proper pinout is very important for interacting with the IoT device. Exercise 25-1 provides a practical example of this.
Here is the scenario: as an incident responder, you receive an IV pump infected with malware. Your mission, should you choose to accept it, is to extract the malware running inside. However, when you plug your serial cable into the device to get console access, you receive no response, even after trying different baud rates. Therefore, a more detailed analysis is needed. Following are the recommended steps to deal with such a scenario. Please keep in mind that this is an ongoing investigation with a real medical device at the time of this writing, so we are focusing on the knowledge needed to deal with this type of scenario, not teaching the full details of the resolution.
The very first step is to take the time needed to understand the hardware and serial protocol you are dealing with. This is normally done by disassembling the equipment, identifying the chipset in question, and finding on the Internet what is known as the datasheet, which is basically the technical specification of that particular device. On the datasheet, you want to find the pinout (in other words, how the pins in the chipset are being used). This exercise uses the RS-232 board from an IV pump. Figure 25-3 shows the front of the device (left) and the board itself (right). At this point, you do not know anything about the board yet. You can see it has an RJ-45 connector, which normally is used for Ethernet communication, and on the right side of the figure you can see the entire board and its components.
Figure 25-3 Serial device extracted
If you are new to the hardware-analysis world, and therefore are not familiar with the chipsets embedded in the board, you can just buy a cheap magnifying glass so you can easily see the small numbers on the chips. Then you can start Googling these numbers; normally, you will find a lot of information about the purpose of that hardware, such as whether it’s simply SDRAM, a microcontroller, a field-programmable gate array (FPGA), or (as in our case) a serial interface. It’s recommended that the magnifying glass come with clips because particularly when you need to solder something onto the printed circuit board (PCB), you need to have your hands free due to the precision required. Figure 25-4 shows using a magnifying glass to identify this device as a Maxim MAX3227E.
Figure 25-4 Looking at the device through a magnifying glass
A quick search on the Internet reveals the datasheet at www.ti.com/lit/ds/symlink/max3227e.pdf. Just by reading the description “3-V TO 5.5-V SINGLE CHANNEL RS-232 LINE DRIVER/RECEIVER,” it is clear that you have found your serial port and that it is using the standard RS-232 port, which means that the RJ-45 jack is not for Ethernet communication. Now, prepare a cup of coffee and get ready to read the entire specification, because it is very important to understand, in a general way, how this device works. For the purposes of this exercise, you need to identify the description of the pinout. What you want to find are the pins to transmit data, receive data, and the ground.
Now that you have identified the datasheet, you need to understand the pinout of the MAX3227E. Figure 25-5 shows the pinout taken from the datasheet.
Figure 25-5 MAX3227E pinout
Page 3 of the datasheet provides the description of the pins. You are interested in the following:
• GND (14) Ground
• RIN (8) RS-232 receiver input
• DOUT (13) RS-232 driver output
Now that you know the pins needed to interact with the device, it’s time to create your patch cable. You need to identify the corresponding RJ-45 pins, for which it’s normally easier to go by cable color rather than pin number. Here are the necessary steps to accomplish this:
1. Grab a standard Ethernet cable (the kind you would use to plug into a computer) and cut it so that you can get access to each wire separately.
2. Plug the RJ-45 connector into the device where the MAX3227E chip is embedded.
3. Get a multimeter and perform a connectivity test, as follows:
a. Turn the dial to Continuity Test mode (it looks like the Wi-Fi icon).
b. Touch the black and red test leads and make sure you hear a sound.
c. With the help of a magnifying glass, touch one test lead to pin 14 (ground) on the MAX3227E.
d. With the second test lead, start touching each wire of the Ethernet cable until you hear a sound. Write down the color of the wire. Conventionally, the wire is solid blue, but be sure to double-check.
e. Repeat the same process with pin 8 (RIN) and pin 13 (DOUT).
The mapping results are shown in the following table:
At this point, you have your patch cable ready to understand RS-232 communication coming out of the IV pump. Now you need to work with the other side of the communication, the PC, which will be using its DB-9 port. Therefore, you just need to map the previously identified wires with the corresponding DB-9 read, transmit, and ground pins, and you are set!
As you’ll recall, in every RS-232 communication, you need to identify the DCE and DTE components. Thus, your laptop will be acting as the DTE and the IV pump as the DCE. This is important because you will be using the DTE pinout (refer to Figure 25-2) to interface with the IV pump via RJ-45. The following table provides the final configuration.
Finally, now that all the mapping is done, it is time to connect all the components, as shown in Figure 25-6. Here’s the final configuration:
Figure 25-6 Final setup for RS-232 connectivity
IV pump RS-232 (MAX3227E) patch cable
RS-232 (DB-9 connector breakout)
The double (male and female) DB-9 interface is handy for these engagements because it allows you to easily manipulate every single wire. These devices are called a “DB-9 connector breakout” and can be found in many hardware stores at a decent price (less than $30 USD).
Now it’s time to check whether everything works correctly in the IV pump, so put the RS-232 PCB back into the medical device, connect all the wires as shown in Figure 25-6, and use a DB-9-male-to-USB cable to plug into your laptop. You can see the final setup in Figure 25-7.
Figure 25-7 Final test for IV pump
Without turning on the IV pump yet, and with the USB cable plugged into the laptop, fire up your Ubuntu virtual machine (VM) on VirtualBox in order to read from the serial port. You need to attach the USB device to the VM, which is done by going to the menu Devices | USB | FTDI FT232R USB UART [0600].
Depending on the USB you use, the name of the device will be different, but the rule of thumb is to always look for a new device you haven’t seen before in the list.
Now you need to make sure the USB device has been identified by Ubuntu by executing the lsusb command. The output should display the device you just saw in the menu in the previous step, plus other devices already present in the system. Here’s an example:
Now that you know the USB device is connected to your Ubuntu box, there are many ways to interact with it, depending on the client you are using. Here are three ways to identify your USB:
Looks like everything is set for your production test. However, you still do not know the serial communication configuration (in other words, the values of the baud rate as well as the parity and stop bits). There are three ways to approach this. The easiest method is to look for technical manuals about the device and try to find those parameters. The second option is to brute-force the values until you find the right ones. This should be fast because the available baud rates are around 5. The final option is to use a logic analyzer. You can think of this device as a sniffer for the serial port. It identifies pulses being sent and measures what is called the “frequency between those pulses,” which eventually can help you to identify the baud rate. It also helps to identify the serial port’s transmit and receive pins, like in this exercise. If you noticed, only two cables from the RJ-45 connector were used for this purpose. The Saleae logic analyzer is recommended for this task, although it is expensive. For this exercise, a logic analyzer was used to identify the parameters.
Let’s create a quick script to read from the serial port and see what you get. You can use Python’s “serial” module for this. Here is an example of a serial port reading. You can see that we are using the “/dev/ttyUSB0” device to interact with the port and are using a baud rate of 57600, which is taken from the logic analyzer:
Run this script and turn on the IV pump to see if you get something. Voilà! You start getting data—actually, the same data is sent every 2 seconds, which looks like a heartbeat or synchronization data. This confirms you have properly identified the receive pin! In Figure 25-8, you can see the test just run explained.
Figure 25-8 Receiving data from the IV pump serial port
What about the transmit pin? You need to see if you can send something and get a response. At the end of the day, if you want to break—sorry, to interact with—the IV pump, you need to be able to send data through the serial port.
To send data, you just need to use the following line of code. You also need to send back to the pump the same data you received to see if you get something different:
Success again! In Figure 25-9, you can see that after sending some bytes to the port, you get a different response (‘\xfd\x90\x00\x8d\xfc\x16’), which confirms two things: one, your transmit pin is properly working, and, two, the IV pump is reacting differently based on the data sent.
Figure 25-9 Transmitting data to the IV Pump serial port
At this point, I should thank John McMaster for his great contribution! His experience and equipment were invaluable to this exercise. Also, thanks to Zingbox, Inc., for supporting these kinds of efforts (that is, trying to identify issues with medical devices proactively and working with the affected vendors). Unfortunately, we cannot publish more details at this point because it is an ongoing investigation. However, at this stage, it is just about understanding the serial packets, playing or fuzzing them to see if we can accomplish unexpected behavior. If you are interested in the latest findings on this device, go to https://ics-cert.us-cert.gov/advisories/ICSMA-17-017-02A.
Traditional malware runs in Windows environments, and tools like sandboxes and virtual machines already support these systems transparently. However, when dealing with IoT malware, two different architectures are put into place that are far from being supported transparently. These two architectures are ARM and MIPS, and in this section, we discuss multiple options for emulating those environments in what we’ll call our “threat lab.” As a quick overview, here are the different ways to get a system up and running for the ARM and MIPS architectures:
• Use a QEMU (short for “Quick Emulator”). QEMU is an open source machine emulator and virtualizer. This is the most common way to emulate the ARM and MIPS architectures, and it’s very convenient for malware analysis because snapshots can be used to avoid permanent changes in the hard disk.
• Use a development platform such as BeagleBone or Raspberry Pi. Although not recommended for malware analysis, sometimes you need to run the malware in a real environment. The good news is that these boards can always be reimaged if needed.
• Use a sandbox such as the Cuckoo sandbox. Cuckoo can be customized to run ARM and MIPS because, behind the scenes, it uses QEMU for the emulation part. However, the results are still limited and far from the details you can obtain in Linux environments, especially in terms of process execution indicators. Also the Volatility plug-in is not supported. Still, it’s worth mentioning the effort done by the team at https://linux.huntingmalware.com/, who were able to make Cuckoo available.
In the next section, we use emulation through QEMU because it is the most stable and mature platform for emulation.
Before we start playing with ARM and MIPS, let’s quickly look at what these architectures are and how they work. ARM and MIPS are reduced instruction set computers (RISCs) whose instruction set architectures (ISAs) have sets of attributes that allow them to have lower cycles per instruction (CPI) and low operating voltages. This reduces energy consumption, allowing these architectures to run on small devices (so-called embedded devices) like a wristband with Wi-Fi capabilities or a finger ring with Bluetooth connectivity. This architecture is different from the complex instruction set computing (CISC) used by x86 processors found in the Windows and macOS X laptops used on a daily basis. These RISC architectures support 32-bit and 64-bit versions.
ARM offers a 16-bit-long instruction set called Thumb that is basically a compressed representation of the 32-bit instruction set, decompressed in real time, allowing for code size reduction that directly impacts application performance.
Although both architectures are commonly used in game consoles, like those from Nintendo and PlayStation, and in network equipment such as routers and residential gateways, there is one main distinction: ARM is the architecture of choice for mobile devices and is the most commonly supported in development boards like the BeagleBone and Raspberry Pi.
Although the binaries are wrapped in the ELF Linux binary format, the header of the binary will describe the machine and object file type. Figure 25-10 displays the ELF header. For a full understanding of the entire structure, refer to the great description from CMU provided in the “For Further Reading” section.
Figure 25-10 ELF binary header
By looking at the header, you can see that the first 4 bytes of the e_ident member (which has a size of 16 bytes) represent the magic number as \x7F\x45\x4c\x46, which is always the same across architectures. However, right after those 16 bytes, the e_type (2 bytes) and e_machine (2 bytes) display the type of object being loaded. We are interested in the number 2, which corresponds to an executable file, and the machine type, which for ARM is equal to 0x28 and for MIPS is equal to 0x08. In Figure 25-11, you can clearly see this; if you look at offset 16 (0x10 hexadecimal), you can see the fields just described.
Figure 25-11 ELF header machine identification
Also, it is important to understand that these architectures can support different instruction sets. For example, ARMEL supports the ARMv4 version (mainly used for compatibility issues), and ARMHF supports the ARMv7 platform (for the latest technologies). The endianness is also important to properly execute the binary; MIPS supports big-endian and MISEL little-endian.
More specific details are provided in the next sections for those architectures, while we dig more into their internals.
Following are the requirements to replicate the lab:
• Physical PC Referred to as P-PC in this lab, this can be your Windows, Linux, or macOS X machine where VirtualBox will be installed.
• Ubuntu 16.04 on VirtualBox Referred as Ubuntu-VM in this lab, this is the machine where QEMU will be installed to emulate IoT devices.
• QEMU ARM/MIPS Referred as QEMU-Guest in this lab, these are the machines emulating the ARM and MIPS environments.
• VNC client Used to connect to Ubuntu-VM during QEMU-Guest bootup (optional).
Multiple sites on the Internet describe methods for getting ARM and MIPS up and running. After a lot of tries, we found a great repository of pre-built VMs for ARM and MIPS at https://people.debian.org/%7Eaurel32/qemu/ (thanks to Aurelien Jarno for uploading these).
As you can see in the repository displayed in Figure 25-12, multiple directories represent the architectures supported, and inside each directory, all the necessary files and command lines are presented.
Figure 25-12 QEMU VMs repository
Download all the wheezy release binaries from the “armel” directory into your Ubuntu-VM. Also, make sure you configure the Ubuntu-VM in bridge mode.
NOTE The main difference between armel and armhf is the supported ARM versions: armel supports older versions and is very useful when dealing with legacy systems.
Now fire up the ARM system by executing the following command:
The monitor option provides a QEMU shell to interact with QEMU-Guest, which is useful to shut down the machine in case something goes wrong. We also add the vnc option, providing the IP address of the Ubuntu-VM to spawn a VNC Server instance on port 5901 by default. A VNC client will be needed to access it; no credentials are required, although those can be set via the QEMU shell. This is useful to watch the booting process and detect any problems during this phase. Finally, the redir option is used to connect to QEMU-Guest via SSH from Ubuntu-VM. This is done via port redirection because by default QEMU-Guest is configured as a NAT device and is not reachable directly. Therefore, we need to connect to local port 6666 on the Ubuntu-VM, which will be redirected to the QEMU-Guest machine on port 22 (SSH). Here’s the command to use:
The default username and password for the machine are both “root.” Figure 25-13 shows that the ARMEL system has started and we are already logged into the system.
Figure 25-13 Watching the boot process via a VNC client
Now it’s time to install all the tools needed during our malware analysis:
• GDBServer Allows us to perform remote debugging
• Tcpdump Allows us to capture network traffic
• Strace Allows us to record syscalls during malware execution
Before going any further, it is important to update the system by running the following command:
Note that those pre-built images always throw the following error:
In order to fix this, execute the following command:
Now, execute the update command again and it should work. Finally, the necessary tools are installed as follows:
Now that all the tools needed are installed, you are ready to run the malware. However, that is outside the scope of this exercise. See Lab 25-2 for those details. Note that you can follow exactly the same process to run other architectures such as MIPS through QEMU. You just need to download the proper files from the repository.
As mentioned earlier, multiple options are available to execute Windows-based malware in a monitored environment to extract indicators dynamically. Solutions such as Cuckoo Sandbox and Payload Security are two examples. However, when it comes to dynamic analysis on ARM or MIPS, there is still no solid solution—at least one that’s freely available. Therefore, we need to create our own environments. Lab 25-2 details the steps to follow to replicate an IoT malware known as Satori and extract network- and host-based indicators.
Before starting this lab, note that we will be using real malware found in the wild. Therefore, you are highly encouraged to run it in a virtual environment, totally segregated from your corporate or home network. It is also recommended that you make sure any inbound communications started from the guest VM to the host machine are rejected.
Sample MD5 to be used during this Lab = ad52832a507cede6521e11556f7cbb95
Fire up your armel emulated machine via QEMU, as previously described in Lab 25-1, but this time with a significant change: you need to add the option -snapshot to the command line to prevent the malware from causing permanent damage on the guest’s hard disk. With this option, the next time you reboot the machine, any modifications made by the malware will be gone, which perfectly fits our need during malware analysis. We do not want to reuse an infected host multiple times; it must be a fresh start for each iteration. You can monitor the booting process via VNC access. Once the login prompt appears, copy the malware (470.arm) to QEMU via the following scp command:
In case you forgot, the credentials are root/root. Once the file is copied successfully, log into the ARM machine and make sure the file is there. Then change the permissions to 755 and get ready for execution!
You will need another shell in order to run tcpdump to capture network traffic. You can start the network traffic capture with the following command:
We are basically saying to listen on network interface eth0 and only focus on the IP of the guest machine and only listen to TCP or UDP ports (to avoid ARP or other noisy traffic). Also, because we are dealing with Mirai-based malware, we want to avoid port 23, which is normally used for scanning the Internet and will quickly increase the size of our capture file with the same destination port. It is highly recommended that you avoid doing this on your local network because your IP might be banned by your ISP.
On another terminal, trace the execution of the malware by executing the following command. Probably the most important options are -f for following child processes (very important since Mirai-based malware uses fork to eventually hide from the parent process) and -o for storing the gathered information into a file.
Once strace and tcpdump are running, you can always monitor the size of those files and transfer the current captured file at intervals. Depending on the malware, it can be left running for minutes, hours, or even days. For this lab, after 5 minutes you can stop the malware execution (normally by shutting down the machine).
By taking a look at the captured traffic, you can identify the attacker’s host (C2) where the malware is trying to connect, as shown in Figure 25-14.
Figure 25-14 C2 used by the malware
If you want more details about the actions performed on that site, you can take a look at the satori.out output file, where you can easily identify a telnet connection to the resolved IP 177.67.82.48, as shown in Figure 25-15.
Figure 25-15 Telnet connection with the C2
The duration of this particular capture shows an infinite loop of the malware trying to get a response from the host, which is not available at the time of testing. That’s the normal behavior of Mirai-based malware, waiting to receive the command to be executed.
Feel free to play with the malware or execute other samples following the same approach. You will eventually need a strace output parser for a good-looking report.
PANDA is an open source platform for architecture-neutral dynamic analysis. Although it does not fully support MIPS and is in an early experimental phase, it is definitely a framework to keep an eye on. PANDA is being developed collaboratively among MIT Lincoln Laboratory, New York University (NYU), and Northeastern University. It is built on QEMU for emulation and adds the ability to record and replay malware executions multiple times, going through different plug-ins that allow for syscall monitoring, taint analysis, and so on. It also includes a mechanism to share functionality between plug-ins in order to avoid duplication of efforts. Unfortunately, we did not find a stable version during our ARM malware analysis, so we decided not to prepare a lab. However, be sure to keep an eye on PANDA and give it a try. If it can move into a stable release, it has the potential to be one of the must-have frameworks for IoT dynamic malware analysis.
PANDA can be built from its GitHub repository, which also includes a Docker image to simplify the installation process.
Sometimes the malware won’t run on a virtual machine or is heavily hardware dependent, or perhaps you are just reversing a piece of code of a Car infotainment. For those cases, a real device running an ARM system is highly recommended, and the BeagleBone Black board device is the way to go in this scenario: it cost less than $60 and you can always re-flash the firmware if it gets affected by the malware. This board runs on a Cortex-A8 ARM system, with USB, Ethernet, HDMI, and 2 x 46 pin headers for connectivity.
You just need to download the USB drivers for your computer, set up the SD card for booting, if needed (check the “For Further Reading” section for the Beaglebone Black Getting Started URL), and then just plug your BeagleBone device into your computer via USB, which should automatically assign you an IP in the 192.168.6.x or 192.168.7.x segment. Then you can use an SSH client to log into your ARM system that is waiting for you at the IP 192.168.7.2 or 192.168.6.2.
Reverse engineering is a skill that requires knowledge of assembly, which is the language used by the microprocessors. The goal is to understand how a program works without having the source code. Traditionally, most of the work is done on Intel microprocessors running Windows or Linux operating systems, but with the exponential growth of IoT devices, the need to understand ARM and MIPS architectures is mandatory. This section summarizes the key concepts needed to be able to reverse-engineer malware running on these architectures.
Before jumping into the IoT malware debugging, you need to understand key concepts that will make your life easier when reversing these type of threats. The good news is that the number of instructions in ARM/MIPS is significantly lower than in the x86 architecture and therefore it won’t be hard for you to get up to speed.
As usual, when you’re learning new architectures, understanding the calling convention is mandatory. You need to understand how the parameters are passed to the functions and where you get the response back.
Let’s create some quick ARM code and compile it to see the calling convention in action:
Now we just need to compile it with a cross-compiler:
NOTE A VM with cross-compilers and other useful tools like GDB and Objdump for ARM, MIPS, PowerPC, and many other architectures can be found at http://kozos.jp/vmimage/burning-asm.html. Note that this is the Japanese version.
Let’s look at the disassembly by running the following command:
Figure 25-16 shows the calling convention. We will refer to the assembly code based on the line numbers in the left column, starting with 0, 4, 8, c ... all the way to the line 40.
Figure 25-16 ARM calling convention
The numbers 0 to 3 ➊ are passed directly to the registers r0 to r3 in the assembly code. Look at the line numbers 2c, 20, 8, and 28 (left side) in Figure 25-16. The parameter with the value 4 is first moved to the register ip at line 14 and then stored in the stack via (stack pointer) register [sp] at line 1c. The same process is used for the parameter with the value 5; it is assigned at line 18 and then stored at [sp, #4] (stack pointer + 4) at line 30. Finally, for the last parameter with the value 6, its value is first calculated at line 24 by adding the current value of “ip = 4” (calculated at line 14), plus the current value of “r2 = 2” (assigned at line 8), for a total of 6. This is finally stored in the stack at offset [sp, #8] (stack pointer + 8) at line 34.
Now let’s do the same, but this time compile with MIPS toolchain (mips-elf-gcc). The result can be seen in Figure 25-17. Again, we will refer to the assembly code based on the line numbers along the left side.
Figure 25-17 MIPS calling convention
This time, the function parameters from 0 to 3 are passed directly via registers a0–a3 at lines 14, 18, 1c, and 20 (left side). Then, the parameter with the value 4 is assigned at line 4 and then stored at [stack pointer + 16]. Then, the parameter with the value 5 is assigned at line c and then stored at [stack pointer + 20] at line 28. Finally, the parameter with the value 6 is assigned at line 10 and then stored at [stack pointer + 24] at line 30.
Feel free to perform this same exercise for other architectures to validate the calling conventions.
Table 25-1 provides a pretty handy cheat sheet, where you can easily find the usage for common registers as well as operations that can aid in your reversing efforts.
Table 25-1 Multiarchitecture Reference
For full details for architecture-specific instructions, check the following URLs:
• ARM http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042f/IHI0042F_aapcs.pdf
• MIPS www.mrc.uidaho.edu/mrc/people/jff/digital/MIPSir.html
Here are the requirements for Lab 25-3:
• IDA Pro licensed
• Ubuntu 16.04 VM
• MIPS Qemu environment (see Lab 25-1)
• Okiru malware (MIPS 32-bit): okiru.mips (MD5: 7a38ee6ee15bd89d50161b3061b763ea)
Now that you are armed with an understanding of basic assembly instructions, we can start debugging malware.
NOTE The best recommendations during this type of effort are to perform static analysis via a disassembler like IDA Pro or radare2 and to use dynamic debugging with a tool like IDA Pro, Immunity Debugger (OllyDBG fork), or GDB.
This lab presents two approaches to debugging IoT malware: the quick approach via the QEMU stand-alone version, and the full system emulation via the QEMU system option. We will also discuss pros and cons of each approach.
The fast way to start debugging an IoT malware sample is by emulating the binary only and not the entire system. This has some limitations, due to the restricted environment, and because network communication and system checks might fail, but it’s definitely a great and fast start to get into the malware details.
To get this running, log into your Ubuntu VM (running VirtualBox in this case), copy the malware binary to the system (via SSH or drag and drop), and execute these commands in order:
Figure 25-18 shows that our instance has been started with QEMU, and thanks to the -g option, we have also spawned a GDBServer that has stopped the binary execution at the entry point and is waiting for a debugger to attach to it at TCP port 12345.
Figure 25-18 Launching a stand-alone emulation with QEMU
Now, on the system where IDA Pro is installed, open exactly the same okiru-mips binary just executed and then go to Debugger | Select Debugger | Remote GDB Debugger.
Select the Debugger | Process Options and fill out the options, making sure you enter the path of the binary as it appears in the Ubuntu VM (Debuggee), as well as the IP address and port, and then click OK (see Figure 25-19).
Figure 25-19 Debugger process options
Last but not least, set a breakpoint at the entry point of the program in IDA (by pressing F2 on the desired line) to make sure the malware execution stops at the beginning. Although that is the expected behavior, sometimes it fails and executes all the way to the end. This is simply a sanity check. To run it, click Debugger | Start Process.
A warning will prompt to make sure you know what you are doing. Click the Yes button. You should receive a message saying that there is a process already being debugged, which confirms we are heading in the right direction (see Figure 25-20).
Figure 25-20 Confirmation of a process already being debugged
If the process was successfully attached, you should receive a success message, as shown in Figure 25-21. If an error occurred, check the configured parameters.
Figure 25-21 Success confirmation
Finally, you can see IDA has stopped at the binary entry point at the address 0x400260 (see Figure 25-22). From here, you can start stepping into every function (by pressing F7) or stepping out of functions (by pressing F8).
Figure 25-22 Debugger breakpoint at the entry point
The limitation of this approach is that because the malware is not running in a full environment, it might fail during TCP/UDP connections or while trying to read/write at specific locations. To avoid this limitation, a full system emulation is recommended. For that, check the next section.
Now let’s run the malware via full QEMU emulation. In order to do this, you can install a GDBServer inside the QEMU VM and then follow the same process to attach to the IDA Pro Debugger remotely.
You can fire up your MIPS 32-bit system with the following command, taking into consideration the option -standalone, which makes sure there are no permanent changes in the image, and the redirection to port 12345, which will enable port redirection from the Ubuntu host machine port 12345 to QEMU VM port 12345, where GDBServer will be running:
Once the system is up and running, you can copy the malware over to QEMU and fire up GDBServer on port 12345 with the malware attached:
At this point, simply follow the same process described earlier to attach IDA Pro to the malware via GDBServer. The only difference is that the path on the MIPS system is /root/okiru-p.mips, so make this change accordingly.
Figure 25-23 shows the GDBServer receiving the connection from IDA Pro Remote Debugger (IP 192.168.1.185).
Figure 25-23 Remote debugging on full QEMU system
This time, because you have full emulation of the system, you can run iptables, tcpdump, strace, or any other tool to trace or restrict malware execution.
At this point, we have all the environments ready to analyze IoT malware targeting the ARM and MIPS architectures, so let’s perform an exercise for reversing these threats.
Looking at the sample being analyzed, you can see that it has been stripped of symbols. This is done to make the reverser’s life harder. Because we do not have function names, the time for analysis increases dramatically and sometimes isn’t even doable.
Fortunately, the syscalls are there, so either by creating an IDA Pro plug-in or by manually documenting the syscalls, you can start renaming those multiple functions.
Figure 25-24 shows a syscall to the service ID 0x104C (passed via the V0 register), which corresponds to the API getsockname.
Figure 25-24 Renaming syscalls on MIPS
You can find an excellent syscall reference for multiple architectures at https://w3challs.com/syscalls/. Kudos to whoever uploaded it.
When you’re dealing with syscalls in ARM, the syntax is different. In newer versions, the svc command is used, and the service ID is hardcoded for every call. Figure 25-25 shows an ARM version of the okiru malware (ad52832a507cede6521e11556f7cbb95) with the ID 0x900005, which in this case corresponds to the “open” function call.
Figure 25-25 Renaming syscalls on ARM
Once the syscalls are renamed, the entire binary will make more sense; from there, you can apply the same reversing process as used in Windows environments. One of the most important pieces of information is the IP or domain the malware is trying to reach. Let’s see how that looks in IoT (since it is Linux-based, there’s really no difference).
We put a breakpoint at 0x4065C4, and thanks to the syscall renaming, you know it corresponds to the “connect” function call (see Figure 25-26). Because of that, you can also now identify the parameters by looking at the definition:
Figure 25-26 Displaying the sockaddr structure in IDA Pro
Because you know that the second parameter passed via register A1 holds a sockaddr structure, you can ask IDA to show you the memory content of that register by right-clicking Memory Windows (lower-left window) and selecting “Synchronize with” -> A1, as displayed in the aforementioned figure. By looking at the sockaddr_in structure definition in the following code, you can see that the first parameter is the length of the structure, which is optional and is not being used in this case. You can also see that the second parameter is the sin_family, which has the number in memory equal to 00 02 (big-endian), corresponding to AF_INET. Then follows the port content, 00 35, which is the hexadecimal representation of 53 (domain port). Finally, you see the IP address, which corresponds to 08 08 08 08, a public domain IP address.
Although this specific connection is not related to the C2 server, it serves as an example of how to identify structures in memory and how to get the proper values out of it.
This chapter presented an approach to tackling challenges when dealing with RS-232 interfaces in medical devices—something very common when dealing with IoT hardware. The chapter then described the different ways to perform dynamic analysis on IoT malware and the setup needed to accomplish that. Last but not least, the labs in this chapter showed you how to perform remote debugging with IDA Pro on ARM and MIPS architectures, as well as how to perform basic reverse engineering.
BeagleBone Black http://beagleboard.org/getting-started
Development platforms:
BeagleBone Black https://beagleboard.org/black
Raspberry Pi https://www.raspberrypi.org/
ELF reference www.cs.cmu.edu/afs/cs/academic/class/15213-f00/docs/elf.pdf
Endianness https://en.wikipedia.org/wiki/Endianness
Logic analyzer https://www.saleae.com/
PANDA framework https://github.com/panda-re/panda
RS-232 serial port https://en.wikipedia.org/wiki/RS-232
Sandboxes:
Cuckoo https://cuckoosandbox.org/blog/cuckoo-sandbox-v2-rc1
Detux https://github.com/detuxsandbox/detux
Satori IoT malware http://blog.netlab.360.com/warning-satori-a-new-mirai-variant-is-spreading-in-worm-style-on-port-37215-and-52869-en/
VirtualBox https://www.virtualbox.org/wiki/Downloads
VirtualBox bridge mode configuration https://www.virtualbox.org/manual/ch06.xhtml#network_bridged