Because the bus uses Time-Division Multiple Acces (TDMA), the scheduling doesn’t allow receiving a message, changing certain bytes in the message, and then sending it out again at a random time.

To allow changing arbitrary data in a packet we have decided to modify individual bits in messages while they are transmitted over the bus.

Audi Q8 with Driver Assistance package

Audi Q8. The triangle on the windshield contains a camera looking at the road, the black squares next to the Audi logo are LIDARs to measure distance to other objects

For this project we used an Audi Q8 with Driver Assistance package. This includes adaptive cruise control and LKAS. The Q8 is very similar to other Audi and Volkswagen vehicles with FlexRay, except for the main difference which is that the Driver Assist ECU is not located together with the camera on the windshield. The Q8 has multiple cameras that are all routed to a Driver Assist ECU under the driver’s seat.

For this project, we decided to interrupt the FlexRay bus at the EPS side and place our proxy hardware between the EPS and the rest of the car. This way, we could make sure we could control everything that went into the EPS.

FlexRay Car Harness

The comma two devkit interfaces with a vehicle using a car harness. We built a proof-of-concept that is a drop-in replacement for the car harness which utilizes an FPGA, four FlexRay transceivers and four CAN transceivers which could be considered a FlexRay car harness.

Prototype of the Harness board with 4 FlexRay transceivers, 4 can transceivers and a Cyclone 10LP FPGA

Conventional microcontrollers usually have custom silicon to handle transmission and reception of CAN and FlexRay messages. They receive the whole message, check the CRC and put the result in some registers ready for the end-user to use them. However, this does not allow manipulation on the bit level as required for this attack. Therefore, we decided to use an FPGA and implement the PHY (Physical Layer) for FlexRay ourselves.

The FPGA acts as a gateway or bridge between FlexRay and CAN. You send CAN messages to the FPGA, which stores the data received and overwrites the appropriate FlexRay bits when the message is transmitted on the bus. The FlexRay transceivers are used to split the FlexRay bus, and then proxy the messages between the two sides (allowing modification of messages on the fly).

FlexRay topology including the FlexRay harness

Reverse engineering FlexRay messages

The first step is to record the FlexRay messages from the stock system in order for us to analyze the messages and find which bytes control the steering.

Recording the messages on the FlexRay bus does not yet require the FPGA. We simply hook the outputs of the FlexRay transceivers on the FlexRay harness to a logic analyzer. We used the Saleae Logic Pro 16 analyzer which can do 500 MS/s, but any logic analyzer that can do 100 MS/s should be more than sufficient.

Lane Keep Assist CAN message from VW Golf. Screenshot from cabana

Decoding FlexRay messages by hand would be a lot of work. Unfortunately, the Saleae software does not include a FlexRay decoder. You can, however, write your own decoder, so we authored and open sourced a FlexRay decoder for Saleae logic analyzers.

We went on several drives recording the vehicle in different states (manual steering, LKAS engaged vs. not engaged, LKAS steering left and right, etc…) and then combed through the data looking for the corresponding patterns. We found a frame with ID 65 that contained the LKAS steering control commands sent to the EPS. The steering data was in every fourth cycle and the last byte seems to be something like a sub-address. Interestingly, the data that we cared about very closely matched the CAN steering message HCA_01 already defined in opendbc for Volkswagen MQB vehicles.

Since openpilot already supports vehicles using this steering message, we know how to manipulate the bytes in the messages on the FlexRay bus to control the EPS even when LKAS is not active.

Modifying FlexRay messages

The first step is to split the bus electrically and proxy every message unchanged. In the FPGA we implemented a simple circuit that looks if there is data coming in from either side. Once the first bit comes in, we enable the transmitter on the other side and connect it to the receiver. After a frame is finished, there is a little bit of idle time on the bus and we can turn off the transmitter. Then this process starts from the beginning.

The following logic analyzer dump shows a packet coming from the car being forwarded to the EPS, then a packet from the EPS to the car, and then another packet from the car to the EPS.