Thursday, August 13, 2015

Blackbox Reversing an Electric Skateboard Wireless Protocol

Recently at DEFCON 23 Richö Butts and I gave a talk about hacking electric skateboards. One of the skateboards, the Yuneec E-GO, uses a custom wireless protocol between its handheld remote and the board. We touched on how we reverse engineered the protocol during the talk, but I wanted to go into more depth on our methodology.

In short, this is the story of how we went from HackRF to skateboard jammer on Ubertooth. Read on for the gory details!

Finding the Signal

We theorized that the skateboard and remote used the 2.4 GHz band, which is well supported by HackRF One. Ordinarily one would use GNU Radio and a basic waterfall sink to look at spectrum, but GNU Radio can be a bit cumbersome.

Luckily we had a PortaPack to play with! The PortaPack sits on top of the HackRF and acts as a wideband spectrum analyzer (among other things). We tuned up to 2400 MHz and swept the spectrum in 20 MHz increments looking for our signal.

By holding the remote near the antenna, we could easily see short bursts occuring regularly at four frequencies: 2408 MHz, 2418 MHz, 2423 MHz, and 2463 MHz. Conveniently enough, by tuning the HackRF to 2416 MHz we could see from 2406 MHz to 2426 MHz, allowing us to capture the lower three frequencies at the same time.

Recovering Modulation Parameters

We next turned to GNU Radio and Baudline for doing further signal analysis. Using a very simple flowgraph, we could capture samples of the remote transmitting, save them to disk, and load them up into Baudline. Once we had loaded the capture into baudline, we could zoom in on individual transmissions and inspect them visually.

By looking at an individual transmission we could tell that the device uses a lower frequency to represent a 0 and a higher frequency a 1. This modulation mode is called frequency shift keying, or 2FSK. By measuring with Baudline, we could see that the lower frequency and upper frequency were around 300 kHz apart, giving a frequency offset of 150 kHz. In addition to discovering the modulation mode and frequency offset, we could also see that the transmissions included long sequences of 0's, meaning it was highly unlikely the device used encryption or data whitening.

Turning back to GNU Radio, we set out to create a flowgraph to demodulate 2FSK. This is somewhat complicated business, but thanks to Mike Ossmann's latest SDR tutorial video and an excellent blog post by Don C. Weber we were able to puzzle out some of the finer points of 2FSK demodulation. The workhorse is the Quadrature Demod block, which tracks changes in angle of a complex-valued signal. This somehow translates into changes in frequency, but this is the part where we admit that neither of us had any idea what we were doing with DSP (as evidenced by Richo's BSidesLV talk).

At this point we had turned our complex RF signal into a real-valued signal. I doubt it is the best tool for the job, but Audacity did a fine job dealing with this data. After importing the raw data into Audacity, we finally had something that resembled actual data.

At the beginning of the packet is the preamble. The signal alternates between 0 and 1 so that the receiving radio can lock on and recover the symbol rate. Following that is the first meaningful data, the access code (also called a sync word). This is a fixed-length value, usually 16, 24, or 32 bits long, which the receiving radio can synchronize on to differentiate the signal from noise. Finally the rest of the packet is the payload.

Zooming in on the signal, the first order of business was recovering the data rate. The preamble makes this convenient as it always alternates between 0 and 1. This allowed us to count the number of samples from a peak to a valley, which gave us samples/symbol (symbols being bits, since this was 2FSK). Dividing the samples/second by the samples/symbol gives symbols/second, the actual data rate.

In our case, we measured around 80 samples/symbol. Dividing our sample rate of 20 million samples/second by 80 gave a data rate of 250,000. This suggests a data rate of 250 kbit which is very commonly supported by off-the-shelf 2.4 GHz radios.

The final piece of the puzzle was the access code. To recover this value, we began the tedious process of counting bits. We measured the length (in samples) of each stretch of 1's and 0's using Audacity. By dividing these lengths by our samples/symbol, we could recover the number of bits in each stretch. After recovering around 12 bits of the access code we turned back to GNU Radio.

The Correlate Access Code block takes as input an access code and tags the data stream with a value of 0x02 or 0x03 (the actual data is one byte of 0x00 or 0x01 for each bit). We took the output of this block and dumped it to a file. To analyze this file, once again Don C. Weber has us covered with grc_bit_converter. This tool reads the tagged output, packs the bits into bytes, and dumps the data in ASCII hex. We we treated the first 32 bits of this output as a candidate access code.

Sniffing with Ubertooth

Armed with the frequencies the device transmitted on, modulation mode of 2FSK, frequency offset of 150 Hz, data rate of 250 kbit, and access code, we finally had enough information to use a narrowband transceiver to attempt to receive packets. Ubertooth makes an ideal platform to implement a sniffer, as its CC2400 is more than up to the task.

The details of tuning up the CC2400 are best left to code, but it was fairly straightforward to rig it up to tune to one of the known channels and dump packets over USB. This gave us our first samples of packet data.

time=1084328910 delta_t=1012.321200 ms freq=2418  
ff e1 92 13 01 d1 00 00 00 00 80 00 80 00 00 00 ff e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff bf

Of note is the 80 00 80 00 starting at the 11th byte of the data. By adjusting the speed control on the remote, these values varied linearly according to position of the speed control. It was thus clear at this point that the remote communicated in plaintext to the controller.

Hopping Along

The final order of business was figuring out the hop pattern and hop interval. As indicated in the above capture from the Ubertooth data, each packet was timestamped using Ubertooth's internal 100 ns clock. This gave us a high-resolution time source for measurements. Sitting on a single channel, we could calculate the delta between consecutive packets and plot these. The minimum value observed was 44 ms between packets, with all other values being multiples of 44 ms (accounting for false positives). The fact that we were seeing multiples of 44 ms indicates we were probably missing some packets, which is to be expected.

Last up was the hop pattern. Looking at the three lower channels in Baudline, it is fairly evident that the device hops between at least the visible three channels in order before returning to the initial channel. We measured the delta between the start of one packet and the start of the next packet to be 11 ms. There was a gap between the third visible channel and the initial channel of 22 ms, so it wasn't much of a stretch to deduce the device hopped to the fourth channel and then back to the first channel in that window. Thus the simple hopping pattern was: channel 1 -> channel 2 -> channel 3 -> channel 4 -> back to channel 1, with a delay of 11 ms between each channel.

Finally having figured out the hop interval and hop pattern, we had all the variables needed to completely follow the E-GO remote as it transmitted to the board. Implementing this in Ubertooth was again relatively straightforward, though the code was now somewhat complicated by a state machine.


Since the goal of the research was to attack the skateboards, we set out to create a jammer. In order to jam a narrowband communication such as this, it is sufficient to generate random noise during transmission that confuses the receiving device. The CC2400 has a special mode that produces pseudorandom data, so we made use of this.

The jamming code co-opts the connection following code we introduced to sniff connections. When the CC2400 detects the access code, instead of receiving the packet data we turn the radio into transmit mode and configure it to send pseudorandom data for around 2 ms. Although there is a delay of around 200 us to switch the radio into TX mode, enough of the packet data is obscured by noise that the board stops responding to the remote after around half a second.

For robustness, the jammer also includes a timeout. If no access code is detected after 11 ms plus a bit of slop, the Ubertooth begins transmitting anyway before hopping to the next channel and waiting for the access code again. Without this trick, the code would not reliably jam the connection.

Once the connection was jammed, the board immediately stopped driving the wheels and turned back into a regular skateboard. This is normally not an issue, as the rider would likely coast to a stop before anything bad happened. However, if the rider had been going downhill this attack would also disable the brakes, which could have obvious consequences.


One oddity that we noticed is that occasionally the board responds to packets with packets of its own. We haven't decoded the contents of these packets, but it is likely that they contain basic telemetry such as battery level. In the interest of open research, here is a link to a sample capture of the board and remote both transmitting on the same channel back-to-back.

Code for this has been pushed to Ubertooth master which you can grab from GitHub. Instructions for building and flashing firmware can be found in the wiki.

If you have an E-GO skateboard and an Ubertooth, give it a try and let me know how it goes!

As always, this was not possible without the help of many people. We would like to thank Jared Boone from ShareBrained Technology for his incredible patience assisting us with GNU Radio while he was in the midst of pushing out a product. We would also like to thank Don C. Weber for publishing an excellent walkthrough on decoding FSK with GNU Radio as well as Michael Ossmann for his incredibly awesome SDR tutorial video series. Finally, thanks to @safehex for winning the E-GO we used in this research at the BSidesLV charity auction.