-
-
[原创]零成本入门车联网安全研究(二)
-
发表于: 2022-9-23 16:52 14151
-
本文是《零成本入门车联网安全研究》系列第二篇,第一篇简单介绍了下车内网络的架构,让大家对车内ECU组网的方式有了最基本的了解。本文则在车内网络的基础上,部署了真实的网络业务——基于以太网的车辆诊断服务,该服务基于doip(Diagnose On IP)协议栈实现通用诊断协议UDS。本文相比于第一篇文章更具可操作性与可玩性,可操作性的点在于大家根据文章介绍的步骤,可以搭建好自己的实验环境;可玩的点在于,本实验会在开源项目的基础上,加入可实际利用的协议漏洞,通过漏洞利用能够模拟远程控制车辆。此外,本文在描述实验步骤的同时,还会穿插介绍一些相关基础,包括:
- CAN网络基础
- UDS协议基础
- DoIP协议基础
- 远程诊断的实现原理
等等。为了帮助大家更好地开始,有必要先介绍下实验环境。本次实验的系统使用ubuntu20.04模拟车辆边缘节点,节点上运行DoIPServer,win10模拟DoIP诊断仪,诊断仪包含DoIPClient,其中win10与ubuntu组成可相互通信的局域网,即DoIP诊断仪可以通过以太网连接DoIPServer。车内CAN网络通过linux vcan实现,边缘节点、ICSim及车内UDS节点均连接到CAN网络,车内UDS节点上运行UDSServer,该Server能实现基于CAN网络的UDS诊断。整体网络架构如下图所示:
基于如上部署,DoIP诊断仪能够通过以太网连接边缘节点,通过发送诊断协议包的方式,直接对边缘节点执行远程诊断。另外,因为车内UDS节点也实现了UDS协议,因此DoIP诊断仪可以发送诊断协议包至边缘节点,由边缘节点执行DoIP转DoCAN,将基于DoIP的诊断协议包格式转换为基于CAN的诊断包格式,发送至车内UDS节点。
另外,由于ICSim未实现UDS协议,因此DoIP诊断仪的数据包理论上是不会被转发至ICSim,但我们在DoIPServer中插入了一个漏洞,将不合法的DoIP数据包转发至CAN网络,这样通过构造,便可以使用DoIP诊断仪远程发送控制报文至ICSim,实现远程车控。
关于实验环境就介绍这么多,接下来上手配置。首先在ubuntu上安装虚拟CAN网络的工具:
# 安装python can pip3 install python-can # 按照can-utils sudo apt-get install can-utils # 安装can驱动,设置can网络 sudo modprobe can sudo modprobe vcan sudo ip link add dev vcan0 type vcan sudo ip link set up vcan0
设置完CAN网络后,通过ifconfig查看状态:
配置ubuntu与windows10的通信环境,在同一个局域网可以相互ping通即可,ip地址不必完全按照本文配置。
其中ubuntu的网络配置如下:
windows10的网络配置如下:
安装ICSim依赖工具:
sudo apt-get install libsdl2-dev libsdl2-image-dev can-utils
下载ICSim源代码,make编译ICSim,运行ICSim:
git clone https://github.com/zombieCraig/ICSim.git cd ICSim make # 运行ICSim,绑定vcan0 ./icsim vcan0
下载DoIPServer与DoIPClient源码:
git clone https://gitlab.com/rohfle/doip-simulator.git
下载uds-server并编译:
https://github.com/zombieCraig/uds-server.git
启动uds-server并绑定到vcan0,uds-server用于模拟车内uds节点:
./uds-server vcan0
接下来对doipserver做一些修改,用来转发UDS报文给uds节点,以及插入漏洞代码:
修改一:
~/study/vehicle/doip-simulator$ git diff lib/server.py diff --git a/lib/server.py b/lib/server.py index 16f0248..6315479 100644 --- a/lib/server.py +++ b/lib/server.py @@ -20,6 +20,7 @@ import time from . import uds from . import doip +from . import utils from . import simulator as sim @@ -90,6 +91,22 @@ class DOIPServer(object): if not self.simulator.has_target_address(target_address): logger.error('Error: target_address 0x{:02x} is unknown'.format(target_address)) + + # send fake message to vcan0 + # print(utils.bytes_to_hex(userdata)) + data_len = len(userdata) + 1 + can_data = bytearray(data_len) + + + laddr = target_address + if target_address != 0x188 and userdata[0] == 0x22: + can_data[0] = 0x02 + can_data[1:] = userdata + else : + can_data[:] = userdata[1:] + utils.send2vcan0(bytes(can_data), target_address) + time.sleep(1) + response = doip.DiagnosticMessageNegativeAck(source_address, target_address,
修改二:
~/study/vehicle/doip-simulator$ git diff lib/utils.py diff --git a/lib/utils.py b/lib/utils.py index 427de2c..802e58f 100644 --- a/lib/utils.py +++ b/lib/utils.py @@ -15,6 +15,15 @@ """ import inspect +import can + + +def send2vcan0(can_data, laddr): + bus = can.Bus(channel='vcan0', interface='socketcan') + msg = can.Message(arbitration_id=laddr, data=can_data, is_extended_id=False) + bus.send(msg) + + def get_subclasses(mod, cls):
在doipclient的源码中,加入canid为0x188及0x710的报文配置,分别针对ICSim和UDS节点:
config = { 'datamap': { # target_address (hex) : { # identifier (hex) : tuple(label (str), key (str), parser (func)) # } 0x188: { # control ICSim 0x0100: ('Fake Msg', 'fake', parse_fake_msg), 0x0200: ('Fake Msg', 'fake', parse_fake_msg), }, 0x3300: { 0x3200: ('Dummy Accelerator', 'accelerator', parse_accelerator), 0x3230: ('Dummy Brake', 'brake', parse_brake_pressure), }, 0x3301: { 0x3250: ('Dummy Steering', 'steering', parse_steering_angle), }, 0x710: { # send to uds-server 0xF187: ('Fake Msg', 'fake', parse_fake_msg), } } }
修改完成之后,分别在ubuntu和windows10上将doipserver、doipclient运行起来:
# on ubuntu python3 doipserver.py # on windows10 python3 doipclient.py
此时,所有组件都开始工作。doipserver运行起来后会监听13400端口,等待doipclient连接。同时doipserver会周期性地发送广播帧向外报活,广播帧的内容主要包含车辆VIN码及边缘节点的逻辑地址,如下图所示:
广播节点逻辑地址的原因和UDS协议中的寻址方式有关系,UDS协议规定了两种寻址方式:功能寻址与物理寻址。功能寻址可以简单地理解为广播的形式,例如诊断仪发送一个广播帧询问有哪些节点存活着,它不针对指定的ECU;而物理寻址则是针对指定的ECU,因此每个ECU都会对应确定的逻辑地址,当诊断仪想要和指定的ECU进行通信时,就会采用物理寻址的方式,指定ECU的逻辑地址发起连接请求。
简单了解了UDS的寻址方式之后,我们就能明白Doipserver广播帧中携带逻辑地址的意义。
doipclient起来后首先接收广播帧,即协议中描述为“车辆发现”的动作,通过接收doipserver的广播帧,doipclient获得了server端的逻辑地址及车辆VIN码信息,接着连接13400端口,并发起激活路由的请求(Routing activation request)。UDS协议中,在执行针对某个ECU的诊断之前,首先要激活路由。接着发送具体的诊断报文“ReadDataByIdentifier”,“ReadDataByIdentifier”属于UDS的标准服务之一,诊断仪通过指定需要读取数据的Identifier,读取目标ECU中的对应数据:
通过调整doipclient的log级别,我们可以在终端输出doipclient接收到的数据,这些数据由边缘节点生成,包括accelerator、brake和steering的实时数据:
到目前为止,我们看到了doipclient连接doipserver的过程,并简单分析了doipclient与doipserver之间进行UDS诊断通信的过程。该过程仅包含了以太网的通信,接下来我们看看诊断报文转发到CAN网络的过程,首先上一张效果图:
上面的gif效果显示,ICSim周期性地接收到了左转向和右转向信号,说明doipclient的报文被成功转发到了vcan0,即我们成功地通过远程诊断仪实现了对车辆功能的控制。通过candump验证vcan0接收到的消息:
vcan0上接收到的消息包括ICSim左转向、ICSim右转向、VCDS gateway request及VCDS response。candump的结果显示了can消息中包含的canid,消息id(方括号中的内容),以及can报文的data部分。canid 0x188即ICSim的逻辑地址,数据01表示开启左转向灯,数据02表示开启右转向灯。VCDS的request和response包含两个不同的逻辑地址0x710、0x77A,因为当ECU节点作为发送方和接收方时,分别对应一个逻辑地址。uds-server接收到的请求数据如下:
通过调整doipserver的log级别,我们可以看到doipserver收发请求的完整过程,如下图所示:
实验至此就结束了,过程中涉及到的相关知识点均轻笔带过,本文仅作为大家学习的一个引子,提供一种可实验、可操作的入门方式,感兴趣的朋友可以继续全面深入地学习相关的知识。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
- [原创]零成本入门车联网安全研究(二) 14152
- 特斯拉攻击案例解读:硬件逆向分析 35551
- [原创]车载导航GPS欺骗 45072
- [原创]解锁一辆车的非“优雅”方式 32967
- [原创]零成本入门车联网安全研究 21050