© Aditya Gupta 2019
Aditya GuptaThe IoT Hacker's Handbookhttps://doi.org/10.1007/978-1-4842-4300-8_5

5. Exploitation Using I2C and SPI

Aditya Gupta1 
(1)
Walnut, CA, USA
 

In this chapter, we have a look at two of the other (apart from UART) most common serial protocols, namely I2C (pronounced I-2-C or I-square-C) and SPI, and see how they are useful for our security research and exploitation of IoT devices. Both SPI and I2C are useful bus protocols used for data communications between different components in an embedded device circuit. SPI and I2C have many similarities and a couple of differences in the way they function and how we interact with them.

We primarily use SPI and I2C exploitation techniques to dump contents (including firmware and other sensitive secrets) from a device’s flash chip, or write content (e.g., a malicious firmware image) to the flash chip, both of which are extremely useful techniques during a penetration test or while performing security research on an IoT device. However, because SPI and I2C are bus protocols, you will encounter them at many other places apart from just using them in Flash, such as Real Time Clocks (RTCs), LCDs, microcontrollers, analog-to-digital converters (ADCs), and so on. For this chapter, though, we focus on the underlying protocols and then look at how we could use these protocols to work with Flash and EEPROMs. We start with I2C and then move to SPI, understanding how we can interact with both and use them for our purposes.

I2C (Inter-Integrated Circuit)

Let’s start with a bit of background history on why I2C was created and how it evolved. I2C was developed in 1982, by Philips, to enable their chips to communicate and exchange data with other components. In the first version of I2C, the highest data transmission speed was 100 kbps with a 7-bit address, which then later improved to 400 kbps with a 10-bit address. At present, components using I2C can communicate with each other with a speed of up to 3.4 Mbps.

Looking at the technical aspects of I2C, it’s a multimaster protocol with only two wires required to enable data exchange—serial data (SDA) and serial clock (SCL). However, I2C is only half-duplex, which means it can only send or receive data at a given point of time.

Why Not SPI or UART

It might appear confusing at first: Why would someone use I2C instead of UART or SPI? Well, there are a couple of reasons.

The challenge with UART is the limitation of facilitating communication between only two devices at a given time. Additionally, as we have seen in the previous chapter, a UART packet structure includes a start and stop bit, which adds to the overall size of the data packet that is transferred, also affecting the speed of the entire process. Additionally, UART was originally intended to provide communication over large distances, interacting with external devices via cables. In contrast, I2C and SPI are meant for communicating with other peripherals located on the same circuit board.

SPI is another extremely popular protocol for data transfer between components. SPI has faster data transmission rates compared to I2C, but the only major downside that SPI has is the requirement of three pins for data transfer and one pin for Chip/Slave select, which increases the overall requirement of space while implementing the SPI protocol for data communication compared to I2C.

Serial Peripheral Interface

SPI is one of the other most popular communication protocols used in embedded devices. SPI is full-duplex (unlike I2C, which is half-duplex) and consists of three wires—SCK, MOSI, and MISO—and an additional chip select/slave select. In cases when there is no data to read, though, when there is a write happening, the slave should send dummy data to make the connection happen.

SPI was originally developed by Motorola to provide full-duplex synchronous serial communication between the master and slave devices. Unlike I2C, in SPI only one single master is controlling all the slaves and the master controls the clock for all the slaves. The overall implementation and standardization of SPI is pretty loosely defined and different manufacturers can modify the implementation in their own way, due to the lack of a strict standard. To understand the SPI communication for any given chip on the target device, the best way is to look up the data sheet and analyze how our target has implemented the SPI protocol for communication.

Understanding EEPROM

Both SPI and I2C are common protocols when it comes to talking about data storage via Electrically Erasable Programmable Read Only Memory (EEPROM). In this section, we look into EEPROM and understand the various pins in it, which will be useful while working with I2C and SPI.

Serial EEPROMs typically have eight pins, as listed in Table 5-1.
Table 5-1

Connections for Interacting with SPI EEPROM

Pin name

Function

#CS

Chip select

SCK

Serial data clock

MISO

Serial data input

MOSI

Serial data output

GND

Ground

VCC

Power supply

#WP

Write protect

#HOLD

Suspends serial input

Let’s look at each of the pins in detail and see what they mean.
  • Chip select: Because both SPI and I2C (and other protocols) usually have multiple slaves, it is required to be able to select one slave among others for any given action. The chip select pin helps exactly in that—helping select an EEPROM when the #CS is low. When a device is not selected, there will be no communication happening between the master and the slave, and the serial data output pin remains in a high impedance state.

  • Clock: The clock or the SCK (or CLK) pin determines with what speed the data exchange and communication should take place. The master is the one that determines the clock speed that the slaves must adhere to. However, in the case of I2C, the slaves can modify and slow down the clock if the clock speed selected by the master is too fast for the slaves. This process is also known as clock stretching.

  • MISO/MOSI: MISO and MOSI, as you might have expected, stand for master-in-slave-out and master-out-slave-in, respectively. Depending on who is sending data and who is receiving, the pins are used. In case of I2C, because it’s half-duplex, it can only either read or write data at a given point in time. However, in the case of SPI, both read and write data happens at the same time. If there is no data to be sent (in read or write), dummy data is sent.

  • Write protect: As the name suggests, this pin allows normal read/write operations when it is high. When the #WP pin is active low, all write operations are inhibited.

  • HOLD: When a device is selected and a serial sequence is underway, #HOLD can be used to pause the serial communication with the master device without resetting the serial sequence. To resume the serial sequence, the #HOLD pin should be made high while the SCK pin is low.

Also, as we have discussed, I2C works on two lines, namely SDA and SCL. The SDA line is for the data exchange, whereas the clock line, SCL, is controlled by the master and determines the speed at which the data exchange takes place. The master also holds the address and memory location of all the various slave devices that are used during any communication.

In I2C, unlike SPI, there can be multiple masters interacting with various slaves. That configuration is called a multimaster mode. You might wonder what would happen if two masters wanted to take control over an I2C bus at the same time. The answer is that whichever master pulls the SDA to LOW (0) will gain control of the bus; that is, zero rushes to win.

Exploiting I2C Security

Now that we have a good understanding of the foundational concepts of I2C and how the data transfer happens, let’s jump into how we can exploit the devices using the I2C protocol. By exploiting I2C here, I mean reading or writing data of the device using an I2C EEPROM in a real-world device.

For this section, you can choose any device that has a flash chip working on the I2C communication protocol. For the purposes of demonstration, I’ll be taking an example of an unnamed smart glucometer, which has a feature of saving health records of the user offline on the device. For the purpose of hands-on lab exercises, you can get any device with EEPROM working on I2C communication protocol such a as GY-521 breakout board or even any I2C chips from https://www.digikey.in/en/ptm/m/microchip-technology/ic-serial-eeprom and use an EEPROM adapter to connect to it. The connections would remain the same as mentioned in the upcoming sections, no matter which I2C EEPROM device you choose to go with.

In case of the smart glucometer that we have, it uses a MicroChip 24LC256 EEPROM chip, which works over the I2C communication protocol. Figure 5-1 shows an online data sheet for the specific I2C model.
../images/473264_1_En_5_Chapter/473264_1_En_5_Fig1_HTML.jpg
Figure 5-1

Data sheet of EEPROM

The first step for any analysis is finding the component name on the data sheet and looking it up online. Based on the data sheet of the I2C found on this device, which is a Microchip 256K I2C EEPROM arranged as 32K × 8 serial memory, we find out the pinouts of the EEPROM, as shown in Figure 5-2.
../images/473264_1_En_5_Chapter/473264_1_En_5_Fig2_HTML.jpg
Figure 5-2

Pinouts from the data sheet of EEPROM

Let’s go ahead and review what these individual pins mean in Table 5-2.
Table 5-2

Pin Explanations for I2C EEPROM

Pin

Description

A0

User-configurable address bit

A1

User-configurable address bit

A2

User-configurable address bit

VSS

Ground

VCC

1.7V to 5.5V (based on the model)

WP

Write protect (active low)

SCL

Serial clock

SCK

Serial data

Making Connections for I2C Exploitation with the Attify Badge

Once we have the previously mentioned information from the data sheet, we can now connect the EEPROM to our Attify Badge. You can either directly connect it to the Attify Badge by holding the EEPROM using a SOIC clip, or by removing the EEPROM from the device and soldering it on an EEPROM adapter corresponding to the packaging of EEPROM. Figure 5-3 displays how the connection will look between the EEPROM and the Attify Badge, with the Attify Badge plugged into our system.
../images/473264_1_En_5_Chapter/473264_1_En_5_Fig3_HTML.jpg
Figure 5-3

Attify Badge connections with EEPROM

To explain the connections further, here’s what is happening in Figure 5-3.
  • A0, A1, A2, and GND are connected to GND.

  • Vcc and WP are connected to 5V, as write protect is active low.

  • D1 and D2 of Attify Badge are connected, which is the SDA line.

  • D0 is connected to the I2C SCL (Clock) line.

Once we have made all the connections, let’s look at the script that we are going to use to read and write data from I2C EEPROM.

Understanding the Code

To work with I2C, we will use the script i2ceeprom.py by Craig Heffner located at https://github.com/devttys0/libmpsse/blob/master/src/examples/i2ceeprom.py .

Before actually running the script, let’s try to understand how the script works. This will also be useful if you want to modify the script for your own requirements. You’ll also need to modify the script a bit while working with different I2C EEPROMs with different configurations and speeds.

The script starts by mentioning the size of the EEPROM chip, which in this case is 32 KB followed by specifying the Read and Write commands. It also later specifies the speed to be 400 KHz as shown in our data sheet. Note that different I2C EEPROMs might have different speeds and you’ll need to modify this value to suit your target.
from mpsse import *
SIZE = 0x8000            # Size of EEPROM chip (32 KB)
WCMD = "xA0x00x00"    # Write start address command
RCMD = "xA1"            # Read command
FOUT = "eeprom.bin"      # Output file
      try:
      eeprom = MPSSE(I2C, FOUR_HUNDRED_KHZ)
print "%s initialized at %dHz (I2C)" % (eeprom.GetDescription(), eeprom.GetClock())
Next, we try to start the I2C clock by using eeprom.Start() and sending the Start command to initialize the EEPROM at the speed of 400 KHz.
eeprom = MPSSE(I2C, FOUR_HUNDRED_KHZ)
print "%s initialized at %dHz (I2C)" % (eeprom.GetDescription(), eeprom.GetClock())
       eeprom.Start()
       eeprom.Write(WCMD)
Now if we want to read data from the EEPROM, the script first checks if the EEPROM is available by checking for an ACK of start(). It then sends the Read command using eeprom.Write(RCMD) and sets the EEPROM to read mode. Once everything is set, it simply starts reading content from the EEPROM and saving it in data().
if eeprom.GetAck() == ACK:
            eeprom.Start()
            eeprom.Write(RCMD)
      if eeprom.GetAck() == ACK:
            data = eeprom.Read(SIZE)
            eeprom.SendNacks()
            eeprom.Read(1)
      else:
            raise Exception("Received read command NACK!")
else:
      raise Exception("Received write command NACK!")
eeprom.Stop()
Once the read operation is complete, we close the I2C connection and write the content to a file named EEPROM.bin.
open(FOUT, "wb").write(data)
print "Dumped %d bytes to %s" % (len(data), FOUT)
      eeprom.Close()
except Exception, e:
print "MPSSE failure:", e
Once we run the script after making the appropriate connections, we will see that the content from the EEPROM has been dumped to the file (see Figure 5-4).
../images/473264_1_En_5_Chapter/473264_1_En_5_Fig4_HTML.jpg
Figure 5-4

EEPROM content is dumped

Similarly, we can also write data to the I2C chip.

This is how we perform I2C analysis on any given device. To summarize, these are the steps involved in the process:
  • Open the device.

  • Identify the I2C chip on the PCB.

  • Note the component number printed on the I2C chip.

  • Look online for the data sheet to determine the pinouts.

  • Make the required connections.

  • Use the i2ceeprom.py script to read or write data to the I2C EEPROM.

Digging Deep into SPI

Now that we understand I2C and EEPROM, let’s dig deeper into how SPI works and how to interact with target devices using the SPI communication protocol.

An SPI master communicates with its slaves using four lines:
  • Serial clock (SCK).

  • Master-out-slave-in.

  • Master-in-slave–out.

  • Slave select (SS; active low, output from master) .

Out of these lines, the SCK, MISO, and MOSI pins are shared by slaves, whereas each SPI slave will have its own unique SS line. In SPI (unlike I2C) there can be only one master and multiple slaves. The master in SPI is the one in charge of the clock.

To give you a better perspective of what an SPI connection looks like, Figure 5-4 provides a diagram of SPI communication with a single master and multiple slaves. We discuss the individual pins later as explore SPI further.
../images/473264_1_En_5_Chapter/473264_1_En_5_Fig5_HTML.jpg
Figure 5-5

SPI master–slave configuration

The speed of SPI is not limited and that is why SPIs are typically faster than other protocols. Also, the fact that it is full-duplex makes it a better choice for device developers who want to utilize the speed.

How SPI Works

The master first configures the clock frequency according to the slave device’s clock frequency—typically up to a few MHz.

The fastest clock speed we can have in SPI is half the speed of the master clock. For instance, if the master runs at 32 MHz, the maximum serial clock rate can be up to 16 MHz.

To start communication, the master selects the slave device with a logic level 0 on the SS line. Remember that for every clock cycle, a full-duplex data transmission occurs.

The master initiates the communication by sending a bit on the MOSI line, which is read by the slave, whereas the slave sends a bit on the MISO line, which is read by the master. The most significant bit (MSB) is shifted out first and a new least significant bit (LSB) is shifted into the same register. Once the register bit has been shifted out and in, the master and slave have successfully exchanged the register value.

Reading and Writing from SPI EEPROM

To read and write data from or to an SPI EEPROM, we use a utility called spiflash.py available to download from https://github.com/devttys0/libmpsse/ .

Once you have cloned the repo, simply navigate to the folder src/examples/ where you will find the script spiflash.py, which is what we are going to use. Spiflash.py first defines several default values of the SPI protocol such as the read and write command used by most of the chips using SPI. Note that SPI is quite a flexible protocol, which means that developers can define their own custom values of read and write. In that case we need to modify the values shown in the following code.
#!/usr/bin/env python
from mpsse import *
from time import sleep
class SPIFlash(object):
    WCMD = "x02"       # SPI Write (0x02)
    RCMD = "x03"       # SPI Read  (0x03)
    WECMD = "x06"      # SPI write enable (0x06)
    CECMD = "xc7"      # SPI chip erase (0xC7)
    IDCMD = "x9f"      # SPI Chip ID (0x9F)
# Normal SPI chip ID length, in bytes
    ID_LENGTH = 3
# Normal SPI flash address length (24 bits, aka, 3 bytes)
    ADDRESS_LENGTH = 3
# SPI block size, writes must be done in multiples of this size
    BLOCK_SIZE = 256
# Page program time, in seconds
    PP_PERIOD = .025
Next, we define a default speed at which the script will interact with the target chip over SPI. In this case it is 15 MHz; however, we can change the speed using the -f parameter while running the script. The script then goes ahead and also sets the WP and HOLD pins as high in the _init_gpio section.
    def __init__(self, speed=FIFTEEN_MHZ):
        # Sanity check on the specified clock speed
        if not speed:
            speed = FIFTEEN_MHZ
        self.flash = MPSSE(SPI0, speed, MSB)
        self.chip = self.flash.GetDescription()
        self.speed = self.flash.GetClock()
        self._init_gpio()
    def _init_gpio(self):
        # Set the GPIOL0 and GPIOL1 pins high for connection to SPI flash WP and HOLD pins.
        self.flash.PinHigh(GPIOL0)
        self.flash.PinHigh(GPIOL1)
Next, we have the Read, Write, and Erase code blocks, in which the script connects to the target chip over SPI using the mpsse library and performs write, read, and erase operations using the flags provided during runtime, and defined earlier in the code (WCMD, RCMD, WECMD, RECMD).
def _addr2str(self, address):
            addr_str = ""
            for i in range(0, self.ADDRESS_LENGTH):
                    addr_str += chr((address >> (i*8)) & 0xFF)
            return addr_str[::-1]
    def Read(self, count, address=0):
        data = ''
        self.flash.Start()
        self.flash.Write(self.RCMD + self._addr2str(address))
        data = self.flash.Read(count)
        self.flash.Stop()
        return data
    def Write(self, data, address=0):
        count = 0
        while count < len(data):
            self.flash.Start()
                self.flash.Write(self.WECMD)
                self.flash.Stop()
            self.flash.Start()
            self.flash.Write(self.WCMD + self._addr2str(address) + data[address:address+self.BLOCK_SIZE])
            self.flash.Stop()
            sleep(self.PP_PERIOD)
            address += self.BLOCK_SIZE
            count += self.BLOCK_SIZE
    def Erase(self):
        self.flash.Start()
        self.flash.Write(self.WECMD)
        self.flash.Stop()
        self.flash.Start()
        self.flash.Write(self.CECMD)
        self.flash.Stop()
    def ChipID(self):
        self.flash.Start()
        self.flash.Write(self.IDCMD)
        chipid = self.flash.Read(self.ID_LENGTH)
        self.flash.Stop()
        return chipid
    def Close(self):
        self.flash.Close()
In the upcoming code section, the various flags that we can use with the code are mentioned as shown here.
    def usage():
        print ""
        print "Usage: %s [OPTIONS]" % sys.argv[0]
        print ""
        print " -r, --read=<file>      Read data from the chip to file"
        print " -w, --write=<file>     Write data from file to the chip"
        print " -s, --size=<int>       Set the size of data to read/write"
        print " -a, --address=<int>    Set the starting address for the read/write operation [0]"
        print " -f, --frequency=<int>  Set the SPI clock frequency, in hertz [15,000,000]"
        print " -i, --id               Read the chip ID"
        print " -v, --verify           Verify data that has been read/written"
        print " -e, --erase            Erase the entire chip"
        print " -p, --pin-mappings     Display a table of SPI flash to FTDI pin mappings"
        print " -h, --help             Show help"
        print ""
        sys.exit(1)

As you can see, we can set values such as specifying whether to read, write, or erase, as well as provide size to read and write, starting address to perform the operation, and custom clock frequency instead of the default 15 MHz. We also have an option of -v, which will verify if the data that has been written or read from the chip is the same as the original one.

Now that we are familiar with the script, we are ready to go ahead and try it on a target device. In my case, I have a Winbond SPI flash that I have removed from the PCB via desoldering. Once it is desoldered, we can then solder it to an EEPROM adapter (or reader). You could also directly read it while the chip is on the device by hooking miniprobes to the EEPROM or using a SOIC clip of a real-world IoT device without removing the chip from the device.

Figure 5-6 shows what our SPI flash looks like when soldered to the EEPROM adapter.
../images/473264_1_En_5_Chapter/473264_1_En_5_Fig6_HTML.png
Figure 5-6

Windbond SPI EEPROM

Let’s go ahead and get started with making all the necessary connections for SPI. To do this, we need to first understand the pinouts of our target SPI flash chip, which in our case is W25Q80DVSNIG.

If we look online for data sheets for this flash chip, we find the pinout mentioned in the data sheet, as shown in Figure 5-7.
../images/473264_1_En_5_Chapter/473264_1_En_5_Fig7_HTML.jpg
Figure 5-7

Flash chip pinouts

The next step would be to make the required connections using the Attify Badge or any supported FTDI-based hardware. To figure out where the numbers start for the pins in the real chip, compared to the one in the data sheet, notice the notch in the top left section of the chip and use it to count the pin numbers. Table 5-3 shows the pinouts of the Attify Badge for SPI.
Table 5-3

Connections for SPI Flash Read/Write Using Attify Badge

Pin on Attify Badge

Functionality during SPI communication

D0

SCK

D1

MISO

D2

MOSI

D3

CS

Now that we know the pinouts, these are the connections that we need to make to communicate using SPI:
  • Connect CLK to SCK (D0).

  • Connect MOSI/DO to MISO (D1).

  • Connect MISO/DI to MOSI (D2).

  • Connect CS to CS (D3).

  • Connect WP, HOLD, and Vcc to 3.3V.

  • Connect GND to GND.

One of the things that we need to note here is that the connections of MOSI and MISO will reverse if you are using another tool instead of Attify Badge (e.g., the Bus Pirate). This is because of the naming conventions of Attify Badge.

Once you have all the connections in place, we can now go ahead and run the spiflash.py script and try to read data from the SPI EEPROM. The syntax for spiflash.py is shown here.
python spiflash.py -s [size-to-dump] --read=[output-file-name]
strings [filename] / binwalk [filename]
As we can see in Figure 5-8, we have been able to successfully read the contents from the SPI flash EEPROM chip and store it on our local system.
../images/473264_1_En_5_Chapter/473264_1_En_5_Fig8_HTML.png
Figure 5-8

Dumping data from an EEPROM

This means that now given any device, you will be able to dump the content that the device has been storing in its EEPROM chip. Additionally, we can also write data to the chip as shown in Figure 5-9.
../images/473264_1_En_5_Chapter/473264_1_En_5_Fig9_HTML.jpg
Figure 5-9

Writing data to an EEPROM

This is extremely useful, as we can write a modified version of a device’s firmware if we are able to interact with the EEPROM flash chip over SPI.

Dumping Firmware Using SPI and Attify Badge

Let’s try it out on another device that has a complete firmware (in this case OpenWRT) and dump the firmware using the spiflash.py script and Attify Badge. The device that we are going to use in this case is a WRTNode shown in Figure 5-10. For the purpose of performing a hands-on exercise, you can either use the same device (WRTNode) or any other development board that has an SPI interface.
../images/473264_1_En_5_Chapter/473264_1_En_5_Fig10_HTML.png
Figure 5-10

WRTNode device that uses SPI protocol for reading/writing to and from flash chip

We can see that WRTNode has several pins and pads allowing us to connect and interact with it. We can look online for the data sheet of WRTNode because it is a popular development board (Figure 5-11).
../images/473264_1_En_5_Chapter/473264_1_En_5_Fig11_HTML.png
Figure 5-11

WRTNode pinouts: Note the SPI communication interface pinout at the bottom left

Now because we are already familiar with the SPI communication protocol and how to interact with devices using this protocol with Attify Badge, we can interact with WRTNode. In this case, if we read data from the flash chip, it will be the entire firmware, which we can then extract the file system from. Even though we cover firmware analysis and file system extraction in Chapter 7, I’ll show you briefly here the process of dumping firmware from a device using SPI.

Figure 5-12 shows a tabular view of the connections in case of WRTNode, which is the same as what we saw earlier.
../images/473264_1_En_5_Chapter/473264_1_En_5_Fig12_HTML.jpg
Figure 5-12

Connection of WRTNode and Attify Badge for SPI firmware dumping

Once you have made the connections, Figure 5-13 shows the final connection diagram for clarity.
../images/473264_1_En_5_Chapter/473264_1_En_5_Fig13_HTML.jpg
Figure 5-13

Connection of WRTNode and Attify Badge for SPI firmware dumping

After the connection, the next step would be the same as earlier, which is running spiflash.py and then specifying a large enough size so that the entire flash chip content gets dumped. Figure 5-14 shows the firmware dumping process.
../images/473264_1_En_5_Chapter/473264_1_En_5_Fig14_HTML.jpg
Figure 5-14

Dumping firmware from the WRTNode using Attify Badge

Finally, once we have the wrtnode-dump.bin we can run firmware analysis tools (which we cover later) such as Binwalk and get the entire original file system (Figure 5-15).
../images/473264_1_En_5_Chapter/473264_1_En_5_Fig15_HTML.jpg
Figure 5-15

Extracting file system from the firmware

This is how we apply the SPI exploitation skill sets on real-world devices to dump the entire firmware from the device.

Conclusion

In this chapter, we covered several topics including EEPROM, I2C, and SPI. We also had a look at how we can read and write data from the EEPROM using both I2C and SPI communication protocols. This knowledge would be extremely useful for you when you are pentesting a real-world device and want to look at the firmware, which would be stored in the EEPROM, or any sensitive information.

You can also modify a firmware image dumped from the EEPROM chip and write it back and analyze the device’s behavior.

In the next chapter, we start looking at one of the other most popular concepts in embedded device hacking, JTAG.

Often, you’ll find real-world commercial devices storing content such as secret keys, firmware, binaries, and interesting data pieces in its EEPROM flash, which with the knowledge gained from this chapter, can be exploited.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.149.168