Today, I played around with tampering with Flash memory. The target of my attack is a generously donated Netcomm N300 router – I was able to wrangle a hilarious web server memory leak in the unauthenticated web interface by tampering with Flash read operations. I will now describe how to do this.
Update: now with 100% more demo!
The target uses a Macronix MX15L12835FMI flash memory chip, in 16-pin SOP form.
I began my investigation in the morning by monitoring the regular operation of the chip. Broadly speaking, I noted that his flash memory wasn’t actually used too much: it was used during boot (presumably loading the entire OS), and then once again when the web admin panel is used (but only once, indicating it’s cached).
The Flash chip appeared to be used in single SPI mode, with a typical read command looking like this:
(This would be preceded by an 05 FF command – according to the datasheet, this reads out a status register, let’s set this aside for the sake of this attack). My initial goal was to tamper with a Flash read command to read something else from disk.
Given that the SPI commands are synchronous to a clock signal, my attack could also be synchronized to the same clock signal: I would count a number of rising edges on the clock, and at a given number, I would short the Slave In pin of the flash chip (pin 15 on the diagram above) to ground, modifying the Flash read command to read something else.
Zooming in for a closer look, we can see that data changes only occur on the falling edge of the clock signal, so this attack should work:
I started off at the device’s serial console, using cat /dev/mtdblock0 to trigger a Flash read. Without tampering it looks something like this:
For comparison, I also did cat /dev/mtdblock2:
I then wired up a transistor and an FPGA, which read the clock signal and controlled the transistor’s gate, grounding pin 15 temporarily for a number of clock cycles to corrupt only the address of a single read instruction:
I wrote a quick script to wait some clock cycles and ground the flash read operation, then ran cat /dev/mtdblock2 and monitored the resulting Flash commands via oscilloscope:
If you look closely, you can see the remnants of the original flash read operation to the right (the original command for /dev/mtdblock2 was 03 01 00 00). This was confirmed by the results of the cat /dev/mtdblock2 command:
Note that the command worked as intended, but the first block of /dev/mtdblock2 is the same as /dev/mtdblock0 above, indicating success.
From here, I attempted to repeat the work against the web server interface – if I could corrupt the loading of a resource from disk, I could theoretically make it read anything I wanted, leaking the firmware via web requests.
I quickly stumbled upon a setback in the form of SquashFS:
While I could read any block from physical Flash, I could not guarantee the stream would decompress correctly. Still, the web server seemed to work, but one of the images did not load correctly. Opening the web application in burp, I found the culprit:
This was the response to a valid request for /wireless_1.gif. I knew that this was not a valid GIF, but I didn’t know what it was: my hypothesis was that this is either memory from the web server, or a block from disk, with the first being more likely.
To test this, I idly browsed around the web application, and then sent a new request for /wireless_1.gif:
Lo and behold, the gif had magically changed itself whilst no further SPI traffic was observed, indicating that I had a memory leak (or more likely, a use after free?). No, “Ducky” was unfortunately not a part of the password for this system.
You can find the code I used for this attack here.
While this isn’t so useful against an IoT device with more holes than Swiss cheese, the impact of this is apparent – being able to arbitrarily load stored data from flash, without regard to OS-level access control (as it doesn’t know about the tampering), is a rather useful trick imo.