Introducing the CANHack toolkit
For the last few weeks we’ve been developing the CANHack
toolkit for CAN protocol hacking. It’s a portable
bit-banging library to emulate the minimal parts of the CAN protocol required
for hacking a CAN bus. This toolkit is a proof-of-concept to show how various attacks on the CAN bus can be done
purely in software if a microcontroller can be hijacked (we’ve not addressed how the hijacking
could occur: it might be as simple as reflashing the firmware on an OBD-II dongle that
an attacker owns, or something more complex like an exploit of a buffer overrun in a diagnostic
messaging stack).
In most situations a microcontroller’s on-chip CAN controller is connected to a CAN transceiver and through this
transceiver to the CAN bus. If the microcontroller can be hijacked then in general the pins allocated to the CAN
controller can be re-purposed as GPIO pins. The CANHack
toolkit has minimal assumptions about the environment, which
are implemented in target-specific code that wraps the library. The basic requirements are:
- Access to CAN RX and CAN TX as GPIO pins
- Access to a timer to read the elapsed clock cycles (typically a free-running counter)
- Interrupts are disabled around key operations (the code spins on the timer waiting for events)
This can be ported to pretty much anything fast enough to bit-bang CAN (a Cortex M0 at 48MHz is probably too slow, but an ESP32 should be fast enough). The only other hardware requirement is that a CAN transceiver is connected to two GPIO pins.
We’ve ported CANHack
first to the PyBoard v1.1. This is
a board using the STM32F405 microcontroller, which has a Cortex M4 CPU running at 168MHz. We’ve customized the
MicroPython
firmware to wrap the toolkit and make it available via a MicroPython API. MicroPython is a great
platform for CAN hacking: all kinds of scenarios can be scripted up. We’ve included the Python wrappers
in the CANHack respository.
The test rig is a card designed by Canis Automotive Labs that has a header 9-pin D-sub cable with standard CAN connector: this is the standard used by most bus analyzers and it makes it very easy to prototype a CAN bus using 10-way ribbon cable, with simple plugs at either end for bus termination.
The MicroPython API is provided as a class called CANHack
that allows a CAN frame to be defined and various attacks
to be mounted against traffic on the bus.
The toolkit is initialized with an instance of the CANHack
class:
1
>>> ch = CANHack()
This sets up the CAN TX pins and sets a default baud rate (500kbit/sec). The pins B8 and B9 are used (this is CAN1
on the PyBoard’s STM32F405). A different
CAN bit time can be selected with the bit_time
parameter:
1
>>> ch = CANHack(bit_time=CANHack.KBIT_125)
The parameters of the CAN frame are specified by calling set_frame
:
1
>>> ch.set_frame(0x14)
This call pre-computes the bitstream of the CAN frame (it has a C implementation very similar to the Python CAN calculator).
Identifiers default to standard IDs. Extended IDs can be set with the extended
parameter:
1
>>> ch.set_frame(0x14, extended=True)
Remote frames can be set by the remote
parameter.
Once the CAN frame parameters have been set the toolkit can be used to attack a frame matching the parameters. There
is also a method to transmit the frame on the bus, which is very useful to test that everything has been wired up
properly. The payload of the CAN frame can be set with the data
parameter, and the frame is set with the send_frame
method:
1
2
>>> ch.set_frame(0x14, data=bytes([1]))
>>> ch.send_frame()
This is the trace on the CAN RX and TX pins of the PyBoard:
The trace is produced by the Digilent Analog Discovery 2 USB scope (it has 8 channels of
digital inputs and the software has a CAN protocol decoder). Notice that the ACK field is transmitted dominant. The
CANHack
toolkit does this so that a protocol decoder on the bus with just the transmitter won’t treat this as an
ACK failure and expect the transmitter to send an error frame. In the test setup used to produce this there were
other boards connected to CAN that anyway asserted a dominant bit in the ACK field. But this illustrates the power of
bit-banging CAN: the protocol can be changed to do what we want.
The Bus-Off Attack is one of the simplest attacks on the CAN protocol. The idea is to target a specific device that’s transmitting and then generate error frames when that frame emerges so that the Transmit Error Counter (TEC) in the targeted device exceeds the bus-off threshold. Various follow-up attacks are then possible, including doing nothing (knocking a device off-line is a denial-of-service attack all by itself) and stepping in to emulate the missing device by spoofing its frames. The attack was published in 2016 by Kyong-Tak Cho and Kang G. Shin (PDF).
The attack can be run on the PyBoard with CANHack
by first setting a CAN frame with the ID we wish to attack:
1
>>> ch.set_frame(0x123)
This will attack a frame with standard ID 0x123. We mount the attack by calling the error_attack
method. This waits
to see a frame with the set ID and then injects an error frame. It will stimulate further errors by injecting
an error frame into the error delimiter, causing error recovery to repeat. The repeat
parameter indicates the number
of times an error frame is injected. For example:
1
>>> ch.error_attack(repeat=3)
results in this trace:
The transmitter will have seen a total of four errors while transmitting and its TEC will have increased by 31 (8 for each attack less 1 for a successful transmission). When it exceeds 255 it will go bus-off.
An attack of 32 repeats is sufficient to drive any transmitter bus off. For example:
1
>>> ch.error_attack(repeat=32)
gives this trace:
But attacking a transmitter directly in one go is not the best approach: receivers will have their Receive Error Counter (REC) incremented too and might be pushed error passive. It is probably best to attack a transmitter over time, incrementing TEC gradually so that the disruption to the bus is minimized (particularly if there is an intrusion detection system on the bus).
A CANHack toolkit demo video shows spoofing and bus-off attacks. It also shows the toolkit used for three new unpublished CAN protocol attacks:
- The Double Receive Attack
- The Freeze Doom Loop Attack
- The Janus Frame Attack
These new attacks will be written up in their own blog posts.
UPDATE: 22-01-2020
See also the CANT bit-banging CAN toolkit for the Nucleo-H743ZI2 board that also can do low level attacks on the CAN protocol (H/T @ehntoo).