// HACKER NEWS — CYBERSECURITY
Reverse engineering my e-scooter and rewriting the firmware in Rust
Last year, I bought myself an Egret GT. It’s an e-scooter that touts a range of 100km and has very large tyres which makes driving it quite comfortable. To make sure you know that it’s a high-end e-scooter, it comes with a 320x480 LCD display used as a HUD, on which the speed, driving mode, battery level and range are displayed.
Now because I have to break tinker with everything I own, I eventually decided to start figuring out how this thing worked. I can’t remember exactly why, but it was possibly due to the fact that holding the ‘down’ button on the keypad while powering the scooter would cause it to enter a firmware update mode. If you clicked a button to exit this menu, you would enter the normal ‘driving’ mode, and would be able to use the scooter without entering the PIN. While I always secure the scooter with a reasonably good lock, this still irked me a bit.
The first thing I started on was the mobile app, which allows you to unlock the scooter remotely, change a few settings, and view the battery level. I won’t bore you with the process, but what I found from skimming through the bluetooth handlers of the app was the following:
Eventually I became bored at playing with the bluetooth interface and turned to the USB-C port on the display. The manufacturer states that this is just for charging phones, and after some testing with different devices I did conclude that if the data pins were connected, the display unit wouldn’t act as either a USB host or device. But I knew better, and ordered a USB-C breakout board. When this arrived, I plugged it in and probed each pin with an oscilloscope. To my surprise, two of the USB-C pins were being used as a CAN bus (which smells horribly noncompliant).
To sniff this can traffic, I threw together an abomination (pictured in Figure 2) using an ESP32-C6, a SN65HVD230, and a MCP2515^0.
I put together a quick program which initialised the CAN peripherals and logged every can message. Then I plugged my CAN logger into the scooter and recorded the messages during startup:
The CAN bus proved to be quite noisy, so to figure out what was going on I built a small tool using egui to show a plot of can messages against time. By plotting each can message as a dot with the y-axis as the can message ID, it becomes very easy to identify which messages are commands, responses, and periodic data.
Unfortunately at this point I still didn’t have a good idea which purpose each message had. But by sniffing the bus while running the scooter, I was able to quickly figure out which messages were used in communicating the throttle, driving mode, and motor speed:
0x300: Sent by the display to the controller. Contains the current driving mode (walk, eco, drive, sport), whether the headlight is on, and in walk mode contains a counter in the last nibble. Sending a message where the fourth byte is a5 instead of the usual 5a causes the controller to reset.
0x306: Sent by the display to the controller. Contains the throttle position, the blinker lights, and the speed limit of the scooter. The speed limit has no effect on the standard GT controller, but on the GTS it sets the speed limit to 25, 35, or 45km/h. For some reason the throttle level is transmitted as a 9 bit unsigned integer with the MSB being the first bit of the second byte.