MAVLink协议与MavSec

2019/07/25 无人机

MAVLink(Micro Air Vehicle Link,微型空中飞行器链路通讯协议)是无人飞行器与地面站GCS之间通讯,以及无人飞行器之间通讯最常用的协议。它已经在PX4、APM、PIXHAWK和Parrot AR.Drone飞控平台上进行了大量测试。由于MAVLink协议是明文协议,因此可能遭遇恶意攻击,下面主要讲述关于在MAVLink上添加安全方案。

MAVLink协议的包格式

MAVLink协议的数据包格式由3部分组成,分别是头部、载荷、校验码。下图是MAVLink V1数据包各个位的定义:

Field name Index (Bytes) Purpose
STX 0 Denotes the start of frame transmission (v1.0: 0xFE)
LEN 1 length of payload (n)
SEQ 2 Each component counts up their send sequence. Allows for detection of packet loss.
SYS 3 Identification of the SENDING system. Allows to differentiate different systems on the same network.
COMP 4 Identification of the SENDING component. Allows to differentiate different components of the same system, e.g. the IMU and the autopilot.
MSG 5 Identification of the message - the id defines what the payload “means” and how it should be correctly decoded.
PAYLOAD 6 to (n+6) The data into the message, depends on the message id.
CRC (n+7) to (n+8) Check-sum of the entire packet, excluding the packet start sign (LSB to MSB)

下图总结了上面位定义:

而在MAVLink V2版本中,数据包的定义增加了新的位。分别是incompat_flagscompat_flags、变长的MSGID以及可选的签名。

Field name Index (Bytes) Purpose
STX 0 Denotes the start of frame transmission (v1.0: 0xFE)
LEN 1 length of payload (n)
incompat_flags 2 flags that must be understood
compat_flags 3 flags that can be ignored if not understood
SEQ 4 Each component counts up their send sequence. Allows for detection of packet loss.
SYS 5 Identification of the SENDING system. Allows to differentiate different systems on the same network.
COMP 6 Identification of the SENDING component. Allows to differentiate different components of the same system, e.g. the IMU and the autopilot.
MSG 7 8 9 Identification of the message - the id defines what the payload “means” and how it should be correctly decoded.
target sysid 10 Optional field for point-to-point messages, used for payload else
target compid 11 Optional field for point-to-point messages, used for payload else
PAYLOAD 12 to (n+12) The data into the message, depends on the message id.
CRC (n+13) to (n+14) Check-sum of the entire packet, excluding the packet start sign (LSB to MSB)
SIG (n+15) Signature which allows ensuring that the link is tamper-proof

MAVLink数据包类型

在MAVLink的源代码中,文件夹common包含了各种类型的MAVLink。下面这个网址是官方的数据包类型手册:

https://mavlink.io/en/messages/common.html

心跳包是在客户端和服务器间定时通知对方自己状态的一个自己定义的命令字,按照一定的时间间隔发送,类似于心跳,所以叫做心跳包。以MAVLink中最常见的心跳包为例:

fd 09 00 00 01 01 c8 00 00 00 00 00 00 00 04 00 d8 04 03 04 30

对该数据分为三部分:

  • 头部:fd 09 00 00 01 01 c8 00 00 00
  • 载荷:00 00 00 00 04 00 d8 04 03
  • 尾部:04 30

头部

  • STX位是0xfd,标志mavlink协议。
  • LEN位是0x09,载荷的长度为9。
  • SEQ位是0x01,该包的序列号是1。
  • SYS位是0x01,发送系统的ID。
  • COMP位是0xc8,发送单元的ID是200。
  • MSG位是0x00,表示心跳包。

载荷

心跳包的载荷分为6个部分,分别是用户模式、飞行器类型、飞控板类型、飞行模式、系统状态标志、协议版本。心跳包的函数定义如下:

uint16_t mavlink_msg_heartbeat_pack(
    uint8_t system_id,
    uint8_t component_id,
    mavlink_message_t* msg,
    uint8_t type,
    uint8_t autopilot,
    uint8_t base_mode,
    uint32_t custom_mode,
    uint8_t system_status
);

注意到协议版本不在参数例表内,因为该值固定为3。

尾部

尾部两个位分别是CRC的校验位。

MAVSEC

Azza Allouch等人发表论文,MAVSec: Securing the MAVLink Protocol forArdupilot/PX4 Unmanned Aerial Systems。这篇论文主要讨论了MAVLink协议中存在的安全问题,并在MAVLink协议的基础上分别使用AES-CTR、AES-CBC、RC4、ChaCha20四种对称加密算法对载荷进行加密,确保无人机与地面站之间的通信安全。

下图描述了在同一时间内使用不同的加密算法后发送数据包数量的比对,作者的结论是使用ChaCha20算法发送的数据包更多,相比于其他3种速度更快。

作者将实验的源代码上传到github,地址是https://github.com/aniskoubaa/mavsec。源代码包含两部分,第一部分是ardupilot飞控的源代码,第二部分是一个C语言的文件,文件名udpserver.c

复现论文中的实验

由于作者非常懒,README文件中的描述无法正常启动程序,下面是建议的启用方式(Ubuntu环境下):

  • 启动ardupilot软件仿真环境
# 安装相关配置环境
git clone https://github.com/aniskoubaa/mavsec.git
chmod -R 777 mavsec
cd mavsec/ardupilot/Tools/scripts
./install-prereqs-ubuntu.sh

# 配置环境变量
# 注意确保mavsec的文件目录在 $HOME/mavsec/ardupilot/Tools/autotest
# 使用“echo $HOME”命令查看$HOME环境变量的值。
export PATH = $PATH:$HOME/mavsec/ardupilot/Tools/autotest
export PATH = /usr/lib/ccache:$PATH
. ~/.bashrc

# 将build文件夹复制到/home/azzathesis目录下
# 该步骤不是必须,缺少该环节可能会引起错误
cd /home
mkdir azzathesis
cp /mavsec/ardupilot/build /home/azzathesis

# 启动SITL仿真环境
cd /mavsec/ardupilot/ArduCopter
sim_vehicle.py -w
  • 启动UDP服务器
# 因为udpserver.c编译所需的头文件在/mavsec/ardupilot/build/sitl/libraries/GCS_MAVLink/include/mavlink/v2.0下,所以将源代码复制到该目录下进行编译、运行

cd mavsec
cp updserver.c /mavsec/ardupilot/build/sitl/libraries/GCS_MAVLink/include/mavlink/v2.0
cd /mavsec/ardupilot/build/sitl/libraries/GCS_MAVLink/include/mavlink/v2.0
gcc -o UDPServ udpserver.c
./UDPServ 14550
  • 结果

此时UDP服务器每隔1秒钟会显示接收到的数据,此时数据包并没有经过加密。因为源代码中相关的加密函数都被注释掉了,所以需要手动修改udpserver.c文件与飞控中封装mavlink数据包的文件。这里不展开描述如何修改代码,需要注意的是在飞控中封装mavlink数据包的文件路径是:

/mavsec/ardupilot/build/sitl/libraries/GCS_MAVLink/include/mavlink/v2.0/mavlink_helpers.h

原理

MAVSEC所做的工作是在飞控对数据包进行封装的最后一个环节,即载入载荷的时候,对数据进行加密,然后再交给下一步封装、生成校验码,该行为由函数_mav_finalize_message_chan_send完成。飞控启动的时候会持续向本地环回地址的14550端口发送UDP数据包,此时UDP服务器接收该数据包并对载荷进行解密。

这就是整个实验过程的原理,整个通信过程是飞控到UDP服务器的单向通信,监听端口是14550。

问题

该篇论文以及源代码有几个严重问题。

  1. 作者以固定时间内发送数据包的数量、CPU和内存的占用率来评估这四种加密算法的效率。在源代码中其仅仅对发送心跳包做实验,也就是所有的通信数据包都是心跳包,我认为应该要对其他类型的数据包加入到实验环节中。

  2. 作者的源代码中的RC4算法的加解密实现错误。

    首先是严重批评其代码的书写格式混乱。然后注意到第1个for循环中,变量i被复用,导致生成的密钥流temp并没有全覆盖到每一个位。另外此处使用malloc创建动态数组也没有及时使用free函数销毁。下图是给出的改进方案:

  3. 作者的源代码中的AES-CBC算法的加密算法实现错误。

    第239、240行代码中,当数据长度不满足AES_BLOCK_SIZE的倍数时,将返回FALSE,此时应当要对数据进行填充,但是作者在调用该加密函数的时候忽略其返回值,也就是没有对数据进行填充处理而直接加密了。

综上所述,强烈建议不要自己去实现常见的密码算法,请使用官方的标准库。

MAVProxy

我们可以使用MAVProxy来仿真UAVs与GCS之间的通信。MAVProxy可以创建符合MAVLink协议的数据包,我们也可以设计独立的数据包类型。从github上下载MAVProxy的源代码开始,以发送一个心跳包为例分析其代码的运行过程。

预览整个过程

mavlink_msg_heartbeat_pack(1, 200, &msg, MAV_TYPE_HELICOPTER, MAV_AUTOPILOT_GENERIC, MAV_MODE_GUIDED_ARMED, 0, MAV_STATE_ACTIVE);
len = mavlink_msg_to_send_buffer(buf, &msg);
bytes_sent = sendto(sock, buf, len, 0, (struct sockaddr*)&gcAddr, sizeof(struct sockaddr_in));

第一步 创建心跳包

注意到其中的参数例表分别是填入心跳包所需要的6个参数,具体参见上述的心跳包描述。

mavlink_msg_heartbeat_pack(1, 200, &msg, MAV_TYPE_HELICOPTER, MAV_AUTOPILOT_GENERIC, MAV_MODE_GUIDED_ARMED, 0, MAV_STATE_ACTIVE);

整个过程的完整调用栈如下:

  • mavlink_msg_heartbeat_pack
    • 封装MSGID
    • mavlink_finalize_message
      • 封装MAVLINK_COMM_0
      • mavlink_finalize_message_chan
        • 封装STATUS
        • mavlink_finalize_message_buffer
          • 封装头部各个位
          • 封装载荷【加密可以在这个环节进行】
          • 封装校验码

第二步 将数据包传入buf数组

该函数返回心跳包的长度,并将心跳包值按位复制到buf数组中。

MAVLINK_HELPER uint16_t mavlink_msg_to_send_buffer(uint8_t *buf, const mavlink_message_t *msg)

除了使用加密方案,我们也可以在MAVLink上启用数字签名的认证方案,并拓展到批量认证的问题。下图是1对1的基于DSA的认证方案思路:

详细的内容待补充。

参考文献

  • MAVSec: Securing the MAVLink Protocol forArdupilot/PX4 Unmanned Aerial Systems
  • Empirical Analysis of MAVLink ProtocolVulnerability for Attacking UnmannedAerial Vehicles

Search

    Table of Contents