汽车安全入门 07:Python 神器——python-can + Scapy + Caring Caribou 三件套

导语:上一讲把 SocketCAN + can-utils 这套 Linux 原生栈讲透了,今天切到 Python 工具链。Python 在车安全领域已经形成清晰的三层栈——底层是 python-can 做硬件抽象,中层是 Scapy 做协议构造,上层是 Caring Caribou 做安全测试。三件套组合起来能一站打 UDS 服务发现、DoIP 扫描、模糊测试,比单纯用 can-utils 命令行工具灵活得多。


一、为什么 Python 在车安全里必学

车安全的攻击面太多——CAN、CAN-FD、LIN、FlexRay、汽车以太网(DoIP、Some/IP)、UDS、XCP、TPMS(胎压监测系统)、IVN(车内联网)协议栈都不一样。用 C 写每个协议的解析器不现实,Python 的优势就在这里:动态语言 + 协议包生态成熟 + 跨平台,三件事凑齐就成了首选。

另外,所有红队 PoC 都倾向于 Python——它能快速验证假设,错了改一行就行,不用编译重链接。这一讲把 Phase 2 工具链最后一讲的三件套讲完,后面 Phase 3 的 Jeep Cherokee、Tesla 等经典案例会直接引用今天这套工具做实战。

Python 工具栈与硬件关系图

二、python-can:CAN 总线的硬件抽象层

python-can 由 hardbyte 维护,是 Python 生态里事实标准的 CAN 库。它的核心价值是把”硬件差异”藏到 backend(后端)层——同一份代码可以跑在 SocketCAN、PCAN(PEAK)、Vector、Kvaser、CANalyst-II、CANtact 等任意硬件上。

Bus 是这条类的核心抽象:

import can

# 通过 SocketCAN 后端连 vcan0 虚拟接口
bus = can.Bus(interface="socketcan", channel="vcan0", bitrate=500000)

# 发一帧
msg = can.Message(arbitration_id=0x123, data=[0xDE, 0xAD, 0xBE, 0xEF], is_extended_id=False)
bus.send(msg)

# 收一帧(超时 1 秒)
recv = bus.recv(timeout=1.0)
print(f"ID=0x{recv.arbitration_id:X}, DLC={recv.dlc}, DATA={recv.data.hex()}")

支持的 backend(部分):

  • socketcan — Linux 原生
  • pcan — PEAK PCAN 硬件
  • kvaser — Kvaser 硬件
  • vector — Vector CANalyzer/CANoe
  • virtual — 完全虚拟,不需硬件
  • udp_multicast / udp_socket — 把 CAN 流量走 UDP,跨机器调试用
  • ixxat / neovi — IXXAT 和 neoVI 硬件

常用功能:

  • Listener(监听器)——把 recv() 包成回调,异步处理不丢帧
  • Logger(记录器)——ASC(CANalyzer)、BLF(Vector)、MF4(ASAM)、TRC、CSV、SQLite 七种格式直接写盘
  • Notifier(通知器)——多 listener 订阅同一条总线
  • 周期发送——can.send_periodic() 设间隔自动发,用于模拟 ECU 节拍

版本要求是 Python 3.7+(4.0+)、3.8+(4.3+)、3.9+(4.6+),目前 4.x 主线跑得很稳。


三、Scapy:协议构造的瑞士军刀

Scapy 由 secdev 团队维护,是网络协议栈的事实标准 DSL(领域特定语言)。它在车安全领域的价值是用统一的 Python 语法描述任意协议包——从 Ethernet(以太网)到 CAN,从 TCP 到 UDS,语法完全一致。

CAN 协议包构造:

from scapy.all import *
from scapy.contrib.automotive import Ecu_State, UDS

load_contrib("automotive")  # 加载汽车贡献包

# 构造一帧 CAN 包
pkt = CAN(arbitration_id=0x7DF) / bytes([0x02, 0x01, 0x00])  # OBD-II 请求
# 0x7DF 是广播 ID,0x01 0x00 是 Service 01 PID 00 请求

# 发送并等待响应(默认 socketcan 接口 vcan0)
ans, unans = sr(pkt, timeout=2, iface="vcan0")
for s, r in ans:
    print(f"Recv: ID=0x{r.arbitration_id:X}, DATA={bytes(r).hex()}")

Scapy 的关键能力:

  • load_contrib(“automotive”)——加载汽车贡献包,提供 CAN、CAN-FD、UDS、OBD-II、Ecu_State 等高层协议对象
  • sr() / srp()——send & receive 模式,自动匹配请求和响应
  • sniff()——抓包并回调,等价于 tcpdump
  • hexdump() / raw()——包和字节流互转
  • 与 python-can 协同——Scapy 默认走 socketcan 后端,硬件切换靠 python-can

最大优势是协议无关性——同一个 sr() 函数既能发 TCP SYN 也能发 UDS 0x27 Seed-Key,对红队而言,写攻击脚本的认知负担最小。


四、Caring Caribou:车安全测试的 nmap

Caring Caribou(GitHub: CaringCaribou/caringcaribou)的定位直接——”a friendly automotive security exploration tool”,它把车安全测试做成类似 nmap 的模块化工具集:

模块功能
dump抓 CAN 流量到终端或文件
send命令行发原始帧,或 replay(重放)dump 文件
listener枚举总线上所有出现过的 arbitration ID(仲裁 ID)
fuzzer random随机 ID + 随机数据发包
fuzzer brute按位掩码(bit mask)暴力遍历某个 ID 下的可能值
udsUDS 服务发现(0x10 诊断会话控制、0x22 读 DID、0x27 安全访问等)
uds_fuzzUDS 模糊测试(基于 uds 模块的 fuzz(模糊测试)封装)
doipDoIP 扫描(UDP 发现 + TCP 连接握手)
xcpXCP 标定协议连接
dcm已弃用,旧版 DCM 接口
test跑内置测试套件

典型 UDS 扫描:

# 枚举所有仲裁 ID
caringcaribou listener

# 扫描 0x7DF(OBD-II 广播)下的 UDS 服务
caringcaribou uds services -arbuds 0x7DF

# 测试 ECU 是否支持 0x27 安全访问
caringcaribou uds security_access -arbuds 0x7E0

# 暴力遍历 0x100-0x1FF 范围 ID
caringcaribou fuzzer brute -bitmask 0x700 -start 0x100 -end 0x1FF

Caring Caribou 起源于瑞典 KTH 皇家理工学院的 HEAVENS(HEAling Vulnerabilities to ENhance Software Security and Safety)研究项目,本来是学术工具,但模块设计很工程化,已经被多家 OEM(整车厂)和 Tier 1(一级供应商)安全团队日常使用。

顺手做个三件套职责对比——红队拿到工具经常一脸蒙,这张表能让选型秒定:

关注点python-canscapycaring caribou
定位CAN 硬件抽象库多协议包构造器车安全测试 CLI(命令行工具)
后端硬件切换30+ 接口可配仅 socketcan仅 socketcan
协议对象抽象无CAN/UDS/Ecu_StateUDS/XCP/DoIP
学习曲线陡(API 多)平(DSL 友好)最平(开箱即用)
适合场景自研工具底座写自定义 PoC快速摸底目标

五、实战三连:UDS 发现 + DoIP 扫描 + 模糊测试

红队拿到 CAN 接口后,标准打法三步走:

第一步:listener 摸底——caringcaribou listener 启动一段时间,看哪些 ID 出现频率高、哪些 ID 只在上电时出现。出现频率高的 ID 优先分析。

第二步:UDS 服务发现——caringcaribou uds services -arbuds 0x7E0(0x7E0 是动力总成常见 ID)扫 ECU 支持哪些 UDS 子服务,重点看:

  • 0x27 安全访问是否启用
  • 0x2E 写 DID(Data Identifier,数据标识符)是否无鉴权
  • 0x31 例程控制能否调用未公开例程

第三步:模糊测试——caringcaribou fuzzer random 在工程车上永远不要跑,先在 HIL(Hardware-in-the-Loop,硬件在环)测试台架或车辆断开传动系统的状态下跑。模糊测试一定要有 watchdog(看门狗),发现异常立即停。


六、红队 4 大攻击场景

这三件套组合起来能做的事:

  • 服务发现——扫出整车 ECU 的诊断服务矩阵,画出攻击面地图
  • 凭证爆破——0x27 Seed-Key 算法的 Python 实现,结合 Scapy 跑字典
  • 协议模糊——自定义 UDS 数据包模板 + 变异策略,喂给 CanCat/UDSim 做离线 fuzz(模糊测试)
  • 持久化 PoC——Scapy 写一次攻击脚本,所有 ModBus 传测试用例都用同一套框架,便于工程化交付

真实攻防推荐链条:python-can 做收发底座(多硬件切换),Scapy 做协议层表达(高层协议对象),Caring Caribou 做工具层捷径(现成模块)。三层各管各的,不要混着用。


七、防御建议

  • 诊断端口接入——OBD-II 接口加身份认证或物理开关,非授权场景禁止调试协议访问
  • UDS 安全访问——0x27 强制 Seed-Key 算法,禁止默认 0x00/0x01 等弱种子
  • 日志审计——ECU 记录所有诊断会话,鉴权失败的请求要能回溯 IP/工具指纹
  • 模糊测试左移——OEM 在量产前用 Scapy + UDSim 做协议 fuzz(模糊测试),CI(持续集成)跑回归,发现问题比红队找到早

八、思考题

  1. python-can 的 Bus() 接口同时支持 socketcan 和 virtual 两种后端。如果你在开发 PoC 时用 vcan0 调试,部署到实车要改哪个参数?硬件切换要不要动业务代码?
  2. Scapy 的 sr() 自动匹配请求和响应,但 UDS 多帧传输(ISO-TP 协议)会把一个大包拆成多帧,sr() 还能正确匹配吗?需要用哪个 contrib 模块?
  3. Caring Caribou 的 fuzzer brute 按位掩码遍历 ID 范围。如果攻击面是 11-bit 标准帧的全 ID 空间(0x000-0x7FF),暴力遍历跑完要多久?为什么生产环境禁止直接跑?

下一篇 08 讲进入 Phase 3 的经典案例——Jeep Cherokee 2015 年 Miller/Valasek 那次著名远程攻击复盘。


九、素材出处

  • python-can:https://github.com/hardbyte/python-can
  • Scapy(secdev):https://github.com/secdev/scapy
  • Scapy 汽车贡献包文档:https://scapy.readthedocs.io/en/latest/layers/automotive.html
  • Caring Caribou:https://github.com/CaringCaribou/caringcaribou
  • HEAVENS 项目:https://research.kth.se/ics/heavens
  • awesome-vehicle-security 列表 #170、#172、#174

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容