1.测试简述
SAE J1939是美国汽车工程协会(SAE)推荐标准,是一种广泛应用于商用车和新能源车中的网络应用。它包括物理层定义、数据链路层定义、应用层定义、网络层定义、故障诊断和网络管理。
CAN-J1939是SAE J1939的第三方开源库,它通过python语言实现了J1939协议,并持续在维护和更新中。从业人员使用CAN-J1939进行二次开发制作协议仿真器或自动化测试工具,完成在车辆制造过程中协议一致性测试、异常测试和功能测试。当前,CAN-J1939开源库支持CAN over Serial、USB2CAN、Socketcan、Vector等14种硬件接口,应用场景广泛。
本测试在SAE J1939最新开源库基础上实现协议发包功能,并通过模糊测试构造异常报文进行安全渗透攻击,最后发现该开源库存在内存越界漏洞,并给出了解决方法。
该漏洞能通过超长报文触发,并直接导致发包程序异常退出。
2.应用场景拓扑图
上位机安装python官网最新的python3.12.2版本,同时使用pip install can-j1939安装can-j1939最新包,版本是2.0.12。在上位机上,执行基于can-j1939的二次开发程序实现与OBD模拟器之间的J1939报文交互。
3.SAE J1939协议简介
SAEJ1939协议作为重型汽车应用通讯协议,对各类参数都进行了规定。SAEJ1939帧相当于CAN2.0报文中的一个PDU。CAN2.0使用扩展帧格式用29个标志符定义每一个PDU的含义和优先级。
如上图,CAN扩展帧中的29位就是J1939 PDU,PDU由PGN(参数组序号)组成。一个PGN是由一系列参数组成,每个参数有一个SPN(参数编号)。如下图PGN为 65262的参数组包括的参数,包含发动机冷却液温度、燃油温度、发动机机油温度等参数,不同的参数由SPN标识。
4.SAEJ1939报文收发
构造的J1939的报文:
j1939_data = [{‘cycle’: 1000, “pgn”: 65252,”dp”: 0, “pf”: 241, “ps”: 224, “p”: 5, “sa”: 0,”data”: [1, 2, 3, 4, 5, 6, 7, 8, 9]}]
pgn为65252,Dp为0,pf为241,ps为224,p是5,表示优先级,Pf大于240表示是广播发送。Data是数据。
执行程序:
同时使用Pcan-view能看到收包信息:
j1939_data 定义数据data长度超过了8个字节,所以分成了两个包发送:
01 01 02 03 04 05 06 07,第一个 01 表示包的序号是1。
02 08 09 FF FF FF FF FF,02表示包的序号是2,FF是填充的,因为不够8个字节。
20 09 00 02 FF DF FE 00 是回应报文。
5.模糊测试构造超长报文安全渗透
构造下面的j1939异常报文,data中包含超大值8888888888888888888888。
j1939_data = [{‘cycle’: 1000, “pgn”: 65252, “dp”: 0, “pf”: 241, “ps”: 224, “p”: 5, “sa”: 0,“data”: [1, 2, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9,3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4,5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5,6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6,7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7,8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8,9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9,3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9,666,777,8888888888888888888888]}]
执行程序,报异常出错。
变换一种报文构造方式,报文不包含超大值,但是让报文足够长。
在上述报文中让data数组中所有的元素都在1到9之间,这时加长报文长度,例如,data数组的长度达到180,也就是包含180个1到9之间的元素,执行程序仍然会出现上述错误。
6.异常分析&修改建议
异常分析:
通过代码跟踪分析下面这行代码有越界。
self.data = bytearray(data)。
这是安装目录site-packagescan下的文件message.py中一句代码。在执行该语句做了异常抛出,但没有对data做校验和检查。
继续分析J1939部分。
j1939electronic_control_unit.py
在上述文件中send_pgn函数也没有对data数据做检查和保护,就直接通过函数self.j1939_dll.send_pgn发送了数据,最后导致异常。
修改方法:
在J1939代码中增加对data的检查,并回显明确的错误提示。
在调用return self.j1939_dll.send_pgn之前,增加如下检查,判断data中是否存在大于255和小于0的数字,同时对data的长度作出限制。如果存在就直接打印错误并退出程序。
修改electronic_control_unit.py后,并再次编译执行程序,这时没有报异常和崩溃,并输出正常的错误信息。
除了上述修改方式之外,还可以在SAEJ 1939发送报文之前,丢弃上述异常报文,不直接退出程序,并通过输出日志记录异常报文。这样修改的好处在于不影响正常报文的转发。
该内存越界漏洞可以通过超大值异常报文或超长异常报文触发。漏洞一旦被触发,正常的业务报文收发将被终止,协议验证和功能测试结果无法保证,最终影响车辆产品质量。攻击者也可以利用这个漏洞,触发J1939应用场景中程序异常重启,并在重启过程中达到植入恶意代码或远程控制的目的。
我们北京驭安科技有限公司长期致力于车联网安全研究,在车载网络协议、开源代码和车载硬件的安全渗透方面有充足的积累,并一直乐于与业界分享和合作。
虽然我们前文给出了修复该漏洞的建议和方法,但实际上还远远不够,因为很多第三方开源库在异常测试、安全渗透测试上是不够充分的,会存在不少的BUG和漏洞。
在车联网具体应用中,我们要严格筛选第三方开源库,可以借此提高编码效率,但必须在功能、性能、异常和安全上充分测试,并对测试发现的问题深入分析,挖掘隐藏在开源库中的漏洞。这样有助于梳理车载J1939协议整个流程,识别薄弱环节和发现漏洞,最终提升车联网产品的质量。
欢迎交流与合作!
原文始发于微信公众号(驭安科技):车载J1939协议开源库CAN-J1939内存越界漏洞解析