原文始发于看雪(kxliping):零成本入门车联网安全研究
对车联网安全感兴趣,但却没有实车拿来练手,是一件很可惜的事情。为了解决这个问题,笔者结合自己的车联网安全实践经验,为大家搭建一个高度接近真实车辆的实验环境。这个环境使用了目前流行的多域架构,实现了包括VLAN、防火墙等真实的安全功能,供大家学习和参考。
域架构
对域架构的解释:
1)本实验环境实现了两个经典的通信域VCU和HU,VCU可以理解为整车控制域,而HU则是信息娱乐域,两个域通过以太网进行通信;
2)VCU域实现了车身CAN(BDCAN)和自动驾驶CAN(ASCAN),这两路CAN通过网关相连,用来模拟车身CAN报文发送至自动驾驶模块;
3)HU域实现了仪表CAN(ICCAN)与摄像头CAN(CAMCAN),它们分别与BDCAN、ASCAN通过以太网相连,第一路连接(BDCAN -> ICCAN)模拟车身数据在仪表盘上显示,第二路连接(CAMCAN -> ASCAN)模拟摄像头采集的数据传送给自动驾驶模块;
4)第一路连接与第二路连接通过VLAN进行隔离。
车辆功能
通过ICSim[1]模拟车辆功能,ICSim是一个开源的车辆仪表模拟器,该模拟器包含controls和ICSim两个模块,其中controls负责生成模拟的车辆数据,以CAN报文的方式发送给虚拟的CAN接口,ICSim从虚拟CAN接口读取CAN报文,并在仪表上更新对应零件的状态,如车速、车门状态等等。
(ICSim仪表和操作界面)
在我们这个架构中,ICSim和controls分别连接到两路不同的CAN上,其中ICSim连接到HU域的ICCAN,代表真实的仪表盘;controls连接到VCU域的BDCAN,代表真实的车身部件。VCU域与HU域通过以太网进行连接,controls产生的CAN报文通过以太网传送给ICSim。这种实现与真实的整车域架构及其接近,因为有些车型的仪表确实被划分到了HU域,以太网作为VCU与HU的通信协议也是当下比较流行的域架构。
通过如下方式创建vcanic和vcanbd:
1
2
3
4
5
|
sudo ip link add name vcanic type vcan sudo ip link set dev vcanic up sudo ip link add name vcanbd type vcan sudo ip link set dev vcanbd up |
接下来将ICSim与HU域的vcanic绑定,controls与VCU域的vcanbd绑定:
1
2
|
. /icsim vcanic . /controls vcanbd |
此时,代表车身零部件的controls产生的报文还不能顺利达到ICSim,因为vcanic与vcanbd两路CAN总线目前还未联通,我们将通过以太网的方式将这两路CAN连接起来,同时也将意味着VCU与HU的联通。
CAN-ETH通信
以太网的实现使用开源项目cannelloni[2],cannelloni的核心原理是通过UDP协议传送CAN报文,如下图所示:
(图片来自论文《Mapping CAN-to-ethernet communication channels within virtualized embedded environments》)
因为我们这个实验是在一台机器上进行的,所以我们将通过不同的端口区分VCU和HU这两个独立的通信域,结合划分广播域的需求,我们将通过vconfig创建带有vlan id的虚拟网卡。
VLAN划分广播域
考虑到通过以太网传输的报文类型包含了安全性要求比较高的ADAS(自动驾驶)报文,我们的想法是根据报文的不同安全等级来划分广播域,将普通控制报文和自动驾驶报文通过VLAN划分到不同的网络,相互之间不能访问。
首先需要在真实的网卡上创建带有vlan id的虚拟网卡:
1
2
|
$ sudo vconfig add enp0s31f6 20 $ sudo vconfig add enp0s31f6 30 |
创建结果如下:
1
2
3
4
5
6
|
$ ip link (此处省略不相关网卡) 5: enp0s31f6.20@enp0s31f6: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link /ether 70:85:c2:7f:31:ef brd ff:ff:ff:ff:ff:ff 6: enp0s31f6.30@enp0s31f6: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link /ether 70:85:c2:7f:31:ef brd ff:ff:ff:ff:ff:ff |
给网卡添加ip地址并启动网卡:
1
2
|
$ sudo ifconfig enp0s31f6.20 192.168.20.100 netmask 255.255.255.0 broadcast 192.168.20.255 up $ sudo ifconfig enp0s31f6.30 192.168.30.100 netmask 255.255.255.0 broadcast 192.168.30.255 up |
启动成功之后的效果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
$ ifconfig (此处省略无关网卡) enp0s31f6.20: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.20.100 netmask 255.255.255.0 broadcast 192.168.20.255 inet6 fe80::7285:c2ff:fe7f:31ef prefixlen 64 scopeid 0x20<link> ether 70:85:c2:7f:31:ef txqueuelen 1000 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 81 bytes 12838 (12.8 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 enp0s31f6.30: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.30.100 netmask 255.255.255.0 broadcast 192.168.30.255 inet6 fe80::7285:c2ff:fe7f:31ef prefixlen 64 scopeid 0x20<link> ether 70:85:c2:7f:31:ef txqueuelen 1000 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 81 bytes 12838 (12.8 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 |
测试以下两个VLAN的连通性:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
$ ping 192.168.20.100 -I enp0s31f6.30 PING 192.168.20.100 (192.168.20.100) from 192.168.30.100 enp0s31f6.30: 56(84) bytes of data. ^C --- 192.168.20.100 ping statistics --- 70 packets transmitted, 0 received, 100% packet loss, time 70637ms pipe 4 $ ping 192.168.30.100 -I enp0s31f6.20 PING 192.168.30.100 (192.168.30.100) from 192.168.20.100 enp0s31f6.20: 56(84) bytes of data. ^C --- 192.168.30.100 ping statistics --- 5 packets transmitted, 0 received, 100% packet loss, time 4100ms pipe 4 |
VLAN划分成功,接着我们使用cannelloni将BDCAN(vcanbd)和ICCAN(vcanic)连接起来:
1
2
3
4
5
6
7
8
|
$ cannelloni -I vcanbd -R 192.168.20.100 -r 20001 -l 20000 INFO:udpthread.cpp[INFO:146]:run:UDPThread up and running canthread.cpp[108]:run:CANThread up and running $ cannelloni -I vcanic -R 192.168.20.100 -r 20000 -l 20001 -p INFO:udpthread.cpp[146]:run:UDPThread up and running INFO:canthread.cpp[108]:run:CANThread up and running |
连通之后通过controls的操作,便可将CAN报文发送给ICSim,仪表盘也可以显示状态了。通过同样的方式将ASCAN和CAMCAN连接起来,VCU域与HU域的通信就完全打通了。在实际的通信场景下,我们可能会依据类似最小权限原则来配置业务能力,比如在VCU与HU的通信场景中,我们可能会认为HU域只需要从VCU接收车身CAN报文的能力,而不需要发送车身CAN报文的能力;相反,HU域产生的摄像头数据,需要经以太网传送给VCU的ADAS模块,此时HU域可能需要的是发送摄像头报文的能力,而不需要接收能力。为了满足这种需求,我们在实验中增加了网络防火墙。
防火墙
通过iptables规则分别在VCU域和HU域建立防火墙,过滤存在风险的以太网报文,以达到如下两个目的:
- 车身零部件产生的报文只能由VCU传给HU;
- 摄像头采集的报文只能由HU传递给VCU。
以HU为例,通过如下规则对报文的流向进行控制:
1
2
3
|
$ sudo iptables -A INPUT -d 192.168.20.100 -j LOG --log-prefix= '[hu-normal-in]' --log-ip-options $ sudo iptables -A INPUT -p udp -d 192.168.20.100 -s 192.168.20.100 --dport 20001 --sport 20000 -j ACCEPT $ sudo iptables -A INPUT -d 192.168.20.100 -j DROP |
到目前为止,VCU与HU的通信已经完全打通,并且我们加上了防火墙规则,但VCU内部的通信却还没有解决,因为ADAS模块可能也需要车身零部件的数据来做自动驾驶决策,所以我们需要通过网关将BDCAN与ASCAN连接起来。
CAN网关功能
使用cangw可以实现简单的CAN网关功能,连接车身CAN(BDCAN)和ADAS CAN(ADASCAN),车身产生的CAN报文通过网关路由至ADAS模块。(其实cangw和cannelloni的功能结合起来已经很接近真实的CAN网关,真实的CAN网关还支持LIN、Flexray等)
网关的配置方式如下:
1
2
|
$ sudo cangw -A -s vcanbd -d vcanas -e $ sudo cangw -A -s vcanas -d vcanbd -e |
cangw还支持配置报文过滤规则以及完整性校验,读者可以继续探索,摄像头模块与ADAS模块的功能也可继续完善。
参考
[1].GitHub – zombieCraig/ICSim: Instrument Cluster Simulator
[2].GitHub – mguentner/cannelloni: a SocketCAN over Ethernet tunnel
本文首发于本人的CSDN专栏“车联网安全杂货铺”,地址https://blog.csdn.net/andlee/article/details/121228330,转载请申明出处!