Capture The Flag Shitty Add-On Writeup¶

A CTF writeup by Blake Burkhart, full source on GitHub

CTF Details¶

This IPython notebook is a solution for the Capture The Flag Shitty Add-On CTF by Uri Shaked. Read his blog post for full details of the CTF challenge.

For this CTF we have an ATtiny85 accessible over I²C. The full source code for the firmware is available on GitHub. Memory can be read and written via I²C, and it's possible to flash program memory through the main loop. We will need to interface with an LED, and read and write program memory. Along the way we will need to gain code execution to complete some of the steps.

There are a number of milestones required to solve the CTF:

The following resources will come in handy:

I tested this solution with a Raspberry Pi 2 directly connected to a Digispark clone (an ATtiny85 board). The "5v" input of the Digispark was powered from the Pi's 3.3v pin to ensure the Digsparks's outputs safely operate at 3.3v. The ctf-firmware-1.0.0.hex file was flashed to the Digispark.

The Pi must have I²C and SPI enabled in /boot/config.txt :

dtparam=i2c_arm=on,i2c_arm_baudrate=10000 dtparam=spi=on

I set the I²C baud rate to 10kHz to avoid errors. I think this may be due the fact that Raspberry Pis don't support clock stretching but the ATtiny85 and the TinyWire library may allow it. In other words, the slow ATtiny85 may not send all the data fast enough with the Pi's default 100kHz baud rate, set it to something slow like 10kHz. If you don't change the baud rate you will sometimes get OSError: [Errno 121] Remote I/O error and may see corrupted/failed reads or writes. At first I had 40kHz working, but it quit working some reason. I found 10kHz to be completely stable, but annoyingly slow.

If the AVR ISP interface is connected to the Pi's SPI pins, the Digispark can be flashed directly from the Pi with avrdude 's linuxspi protocol:

avrdude -v -pattiny85 -clinuxspi -b14400 -P /dev/spidev0.0 -U flash:w:ctf-firmware-1.0.0.hex avrdude -v -pattiny85 -clinuxspi -b14400 -P /dev/spidev0.0 -Uefuse:w:0xfe:m -Uhfuse:w:0xdf:m -Ulfuse:w:0xe2:m

Don't connect the Pi to the Digispark's I²C and SPI at the same time, they share pins and things will go wrong.

Here is a photo of how I connected my Pi to the Digispark. The yellow and green wires connect I²C from the Pi to the Digispark. The disconnected SPI wires can be temporarily connected to reflash the Digispark. The black wire from pin 25 of the Pi is used by avrdude for reset. And with Wi-Fi on the Pi I can walk around with my laptop and CTF with ease.

An AVR compiler will be needed for some later challenges. Make sure it is installed:

sudo apt install gcc-avr binutils-avr avr-libc

The smbus2 Python library will be used for I²C communication:

pip3 install smbus2

Review the source code for the CTF and you will find onI2CReceive() and onI2CRequest() handling I²C communication. During setup() the I²C address is configured to 0x23 and these functions are registered to run on receiving and on request for I²C data.

On an I²C read, two bytes are returned by the firmware: the lower byte of the 16-bit target address and the byte at that address in memory.

When writing, the first byte is used to set lower byte of the target address. The upper byte is always set to zero because only one byte is being written to a uint16_t sized variable. The remainder of the bytes are written to memory sequentially, starting at the target pointer address.

A write with zero data bytes may be performed before a read to set the target address of the read.