Day 19 在 vcan0/ICSim 中送出一筆新建立的錯誤狀態 Frame,觀察接收端只依 Identifier 與 Data 採用內容時,另一個可傳送節點如何偽造相同訊息外觀。
但攻擊者不一定要理解每一個 bit 的語意,也不一定只送一筆 Frame。
假設一筆「車門解鎖」或診斷操作曾經合法出現在 Bus 上,攻擊者可能把它完整記錄後再次送出。
如果未受信任的節點持續建立高頻 Frame,其他訊息也可能反覆輸掉仲裁,延遲到超過應用程式允許的時間。
若攻擊者具有更低層的時序與 bit 控制能力,還可能利用 CAN 的錯誤處理,讓目標節點逐步累積傳送錯誤,最後進入 Bus-Off:
記錄過去的有效 Frame
→ Replay
→ 破壞訊息新鮮度
持續建立異常高頻 Frame
→ Flooding/DoS
→ 壓縮其他訊息的傳送機會
精準干擾目標節點的傳輸
→ Error Frame 與錯誤計數累積
→ 目標節點 Bus-Off
今天要把這三條路徑分清楚,並回答:格式正確的舊訊息為什麼仍可能有害,高頻流量如何影響可用性,而保護 CAN 的 Fault Confinement 又怎麼可能被轉成攻擊路徑?
讀完這篇文章,你應該能夠:
vcan 為什麼不能驗證實體 CAN 的 Error Frame 與 Bus-Offvcan0 中完成一次受控 Replay 與有限流量觀察
圖 1:Replay 重送過去的有效內容,Flooding 以異常頻率壓縮傳送機會,Bus-Off Attack 則利用錯誤處理讓目標節點退出通訊。三者需要的能力與證據不同
先用一張表建立邊界:
| 攻擊 | 攻擊者主要做什麼 | 直接破壞的目標 | 不應混淆的地方 |
|---|---|---|---|
| Replay | 再次送出先前觀察或記錄的有效 Frame | 新鮮度、狀態順序與操作時效 | Data 重複出現也可能只是正常週期訊息 |
| Flooding/DoS | 以異常頻率送出大量 Frame | Bus、Gateway 或接收端的可用性 | 高頻不一定等於 Bus 已達 100% 負載 |
| Error-based DoS | 反覆造成傳輸錯誤與重送 | 有效頻寬、通訊穩定性與節點狀態 | Error Frame 也可能來自線路或設定故障 |
| Bus-Off Attack | 利用 Fault Confinement 累積目標節點的傳送錯誤 | 特定節點的通訊能力 | 不是單純用小 ID Flood 就會讓目標 ECU Bus-Off |
共同前提仍和 Day 19 相同:攻擊者必須先到達目標 CAN 區段,並取得符合實體層與通訊設定的傳送能力。
文章不會因為 canplayer 或 cangen 能在 vcan0 執行,就省略 OBD、受入侵 ECU、外接設備與 Gateway 等前置攻擊路徑。
Replay Attack(重放攻擊)是把先前取得的有效資料,在不同時間或情境中再次送出。
假設一筆 Frame 原本在車輛靜止、使用者已授權時出現:
t = 10.0 s
合法 ECU 送出一筆狀態或操作訊息
→ ID 正確
→ DLC/Data 正確
→ CAN Controller 產生正確 CRC
t = 45.0 s
未受信任節點再次送出相同 ID/DLC/Data
→ Frame 格式仍然正確
→ 內容卻來自過去的情境
接收端若只檢查 ID、長度與資料格式,可能無法分辨第二筆是新的合法操作,還是被保存後重新送出的舊資料。
CRC 用來偵測 CAN Frame 在傳輸過程中是否出現特定錯誤。
Replay 節點重新傳送 Frame 時,CAN Controller 會替這次傳輸建立符合目前內容的低層 CRC。
即使攻擊者重播的是相同 Data,新的 Frame 仍可通過傳輸錯誤檢查。
所以:
CRC 正確
→ 這次 Frame 通過 CAN 傳輸錯誤檢查
CRC 正確
≠ 這筆資料是第一次出現
≠ 這筆資料仍符合目前車況
≠ 發送者具有現在執行操作的權限
不能。
CAN 裡有許多週期性狀態訊息。
車速維持不變、車門持續鎖定或開關狀態沒有變化時,連續多筆 Data 可能完全相同。
若 IDS 只用「相同 Payload 再次出現」判斷 Replay,正常週期訊息也可能被誤報。
要判斷 Replay,通常還要結合:
Rolling Counter 可以協助接收端發現重複、跳號或順序異常,但單靠一個會循環的小 Counter 仍有邊界。
例如 4-bit Counter 只會在 0 到 15 間循環。
接收端若只確認「和上一筆不相同」,舊值在 Counter 繞回後仍可能再次出現。
真正的防重放設計還要定義:
AUTOSAR SecOC 會把 Data Identifier、Authentic I-PDU 與完整 Freshness Value 納入 Authenticator 計算,再由接收端重建 Freshness 並驗證。
它處理的不只是「Payload 最後一個 byte 有沒有加一」,而是讓訊息內容、識別脈絡與新鮮度一起受到密碼學保護。
DoS 是 Denial of Service 的縮寫,也就是阻斷服務。
在 CAN 情境中,DoS 不一定表示整條 Bus 完全沒有任何 bit 流動。
只要重要訊息無法在要求時間內到達,應用功能就可能已經失去可用性。
例如:
重要狀態每 20 ms 應更新一次
→ 異常流量讓它延遲到 120 ms 才送出
→ 接收端觸發 Timeout 或使用過期資料
Bus 仍然「有流量」,卻不代表服務仍可用。
Day 05 已經知道,CAN 會在 Bus 空閒後開始下一輪仲裁。
同一格式下,數值較小的 Identifier 通常具有較高優先權。
如果一個未受信任節點持續準備低數值 ID:
所以 CAN Flooding 的關鍵不只是「每秒送幾筆」,還包括:
也不能直接下結論。
高 Bus Load 可能來自:
IDS 必須把流量變化和車輛狀態、允許的診斷工作、Error Counter 及來源區段一起分析。
只要看見高頻就立即阻擋,也可能誤傷原本要保護的可用性。
隨機 ID 會擴大 Acceptance Filter、Gateway Rule 與 Application Parser 的觀察範圍,卻不一定能一直取得較高仲裁順位。
指定低 ID 的高頻流量則可能更直接影響低優先權訊息等待時間。
若攻擊者使用合法系統已存在的 ID,頻率異常與來源區段會比「新 ID 出現」更重要。
因此 IDS 不應只維護一張 ID Allowlist,還要建立每個 ID 的週期、最大 Burst、DLC、方向與車況 Baseline。
CAN 的錯誤處理原本是可靠性設計。
節點會透過下列機制發現傳輸問題:
| 錯誤檢查 | 要發現的問題 |
|---|---|
| Bit Monitoring | 傳送端送出的 bit 和 Bus 上讀回的 bit 不一致 |
| Bit Stuffing Check | 需要 Stuff Bit 的區段違反連續 bit 規則 |
| CRC Check | 接收端重新計算的 CRC 和 Frame 內的 CRC 不一致 |
| Form Check | 固定格式欄位出現不允許的 bit |
| ACK Check | 傳送端沒有在 ACK Slot 讀到其他節點的確認 |
當節點偵測到錯誤時,會用 Error Flag 讓目前傳輸失敗,接著其他節點也能知道這筆 Frame 不應被當成有效資料。
傳送端之後通常會依協定重新嘗試傳送。
傳輸錯誤
→ Error Flag/Error Frame
→ 目前 Frame 中止
→ 錯誤計數依規則調整
→ 等 Bus 再次可用
→ 重新傳送
這種設計能防止不同節點各自接受不一致資料,但大量錯誤也會消耗原本可用來傳送有效 Frame 的時間。
Error Frame 沒有一般應用 Data,也不是攻擊者挑一個 CAN ID 後用 cansend 建立的普通訊息。
它由 CAN Controller 在偵測到協定錯誤時產生。
一般 SocketCAN Application 看到的 Linux CAN Error Message Frame,則是 Driver 將 Controller 與網路錯誤通知提供給使用者空間的表示方式。
兩者相關,卻不是「把線路上的 Error Frame 原封不動裝進一般 CAN Data」的意思。
不代表。
大量錯誤也可能來自:
資安監控應把 Error Event 當成需要調查的證據,而不是在沒有其他資料時直接標記為攻擊。
為了避免故障節點長期干擾整個網路,每個 CAN Controller 會維護兩項錯誤計數:
| Counter | 全名 | 主要反映的問題 |
|---|---|---|
| TEC | Transmit Error Counter | 本節點傳送時遇到的錯誤 |
| REC | Receive Error Counter | 本節點接收時遇到的錯誤 |
不同錯誤與例外會用不同規則增減 Counter。
常見簡化教材會寫成「Transmit Error 加 8、Receive Error 加 1、成功後減 1」,但完整規則包含 ACK Error、Error Passive 與其他例外,不能拿這句簡寫直接計算所有控制器何時 Bus-Off。
較安全的分析方式,是依適用的 ISO 11898-1 版本與 CAN Controller 文件讀取實際狀態及 Counter。

圖 2:TEC/REC 讓錯誤節點逐步降低干擾能力。只有 TEC 達到 Bus-Off 門檻時,節點才會退出 CAN 通訊;復原還要結合 Host Policy、故障排除與 Bus Idle 觀察
當 TEC 與 REC 都小於 128,節點處於 Error Active。
它可以正常參與通訊,偵測到錯誤時也能送出 Active Error Flag,讓其他節點中止目前 Frame。
名稱中的 Active 指錯誤狀態,不表示這顆 ECU 一定正在傳送應用資料。
當 TEC 或 REC 其中一項達到 128,節點進入 Error Passive。
它仍然可以傳送與接收,但偵測到錯誤時使用 Passive Error Flag,降低自己破壞其他節點正確傳輸的能力。
Error Passive 不是「完全離線」,也不是 Bus-Off 的另一個名稱。
當 TEC 達到 256,節點進入 Bus-Off。
此時 CAN Controller 不再正常參與 CAN 通訊,也不得繼續影響 Bus,避免一個持續發生傳送錯誤的節點拖垮整個網路。
控制器仍可能監看 Bus,以判斷是否符合復原條件;這不等於 ECU 已恢復正常收送 Application Frame。
要特別注意:REC 升高可以讓節點進入 Error Passive,但 Bus-Off 由 TEC 門檻決定。
這就是 Fault Confinement 的核心方向:越像錯誤來源的節點,越要降低它影響其他節點的能力。
Bus-Off 原本要隔離真正故障的傳送端。
2016 年的第一手研究進一步展示,一個已受入侵且能精準配合目標 Frame 時序的節點,可以刻意製造 bit mismatch,讓未受入侵的目標 ECU 認為自己的傳輸反覆出錯。
目標 ECU 的 TEC 因此逐步上升,最後進入 Bus-Off,不再正常參與 CAN 通訊。
這條路徑可以先概念化為:
受入侵節點先觀察目標 ECU 的週期傳輸
→ 在目標傳送時製造可預期的 bit-level 衝突
→ 目標 ECU 偵測 Bit Error
→ 目前 Frame 中止並出現 Error Event
→ 目標 TEC 依規則上升
→ 重複錯誤讓目標進入 Error Passive
→ TEC 達到門檻後進入 Bus-Off
這裡有四個不能省略的條件:
因此:
使用
cangen在vcan0送出大量隨機 Frame,不等於完成 Bus-Off Attack。
vcan 沒有 CAN_H/CAN_L、實體 bit timing、ACK、線路 Error Flag 與硬體 TEC/REC。
它可以呈現 SocketCAN Application 看見大量 Frame 時的流量現象,卻不能讓虛擬節點真的走過 Error Active、Error Passive 與 Bus-Off 狀態。
Linux SocketCAN 的實體 CAN Driver 可以提供手動 Restart,也能以 restart-ms 設定自動重啟延遲。
但「可以自動 Restart」不等於「遇到 Bus-Off 就應該無條件立刻恢復」。
受控復原至少要確認:
若節點在根因尚未排除時反覆自動回到 Bus,只會形成:
Bus-Off
→ 自動 Restart
→ 相同錯誤再次出現
→ 再次 Bus-Off
這種循環可能讓可用性與事件分析變得更差。
復原政策必須配合 ECU 功能、Safety Concept 與整車架構設計,不能只複製一個固定毫秒數。
| 觀察現象 | 最可能先影響哪一層 | 需要留下的證據 | 不能只靠什麼判斷 |
|---|---|---|---|
| 舊 Data 再次出現 | Application Freshness | Counter、時間、狀態與驗證結果 | Payload 相同 |
| 某個 ID 頻率異常升高 | 仲裁、Bus Load 與接收端負載 | Inter-arrival Time、ID、方向、Deadline | Frame 總數 |
| Error Event 增加 | Data Link 與 Physical Layer | Error Type、TEC/REC、線路與 Controller 狀態 | 有 Error Frame 就判定攻擊 |
| 單一 ECU 停止通訊 | ECU、Controller 或供電 | Bus-Off、Reset、DTC、電源與其他節點狀態 | 某個 ID 消失 |
| 整個區段大量訊息延遲 | Shared Bus Availability | Bus Load、仲裁、Error/Retransmission 與 Deadline | GUI 看起來卡住 |
這張表也顯示 IDS 為什麼不能只看 CAN ID。
頻率、錯誤狀態與車輛情境位於不同層次,需要在共同時間軸上分析。
vcan0 分析 Replay 與有限流量今天沿用 Day 07 與 Day 19 的環境:
SocketCAN → vcan0 → can-utils → ICSim/Controls
實作分成兩部分:
candump 記錄一次合法模擬操作,再用 canplayer 重播cangen 送出固定數量的測試 Frame,比較 Baseline 與有限 BurstBus-Off 不在 vcan 中實作。
它必須使用具有真實 CAN Controller、Transceiver、終端與完整安全隔離的測試台,並遵循控制器及整車的復原程序。
| 項目 | 本篇設定 |
|---|---|
| 主線概念 | Replay、新鮮度、頻率異常與可用性證據 |
| 隔離工具 | vcan0、ICSim、Controls、candump、canplayer 與 cangen |
| 傳送限制 | Replay 一次,Burst 固定 200 Frames,禁止無限循環 |
| 不做的事 | 不使用實體 can0,不建立 Error Frame,不嘗試讓 ECU Bus-Off |
| 輸出證據 | 來源 Log、Replay Log、Baseline/Burst Frame Count 與工具版本 |
先確認所有命令都會使用 vcan0:
ip -details link show vcan0
command -v candump canplayer cangen
若 vcan0 不存在,回到 Day 07 建立 Virtual CAN,不要把下面的介面名稱改成道路車輛使用的實體 CAN。
記錄 can-utils 套件版本:
dpkg-query -W can-utils
不同版本的參數與輸出可能改變,正式證據要和工具版本一起保存。
沿用 Day 19 的 Randomized Mode,先在 Terminal 1 啟動 ICSim:
cd ~/ICSim
./builddir/icsim -r vcan0
記下畫面中的實際 Seed。
在 Terminal 2 輸入相同 Seed,再啟動 Controls:
cd ~/ICSim
read -r -p "ICSim seed: " ICSIM_SEED
./builddir/controls -s "$ICSIM_SEED" -l 1 vcan0
這次仍要保存 Seed,因為 Randomized Mode 會改變 ID 與 Data byte 位置。
建立證據目錄:
mkdir -p ~/can-labs/day20
cd ~/can-labs/day20
在 Terminal 3 執行 15 秒記錄:
timeout 15s candump -L vcan0 | tee day20-replay-source.log
記錄期間只操作同一扇模擬車門:
等待 3 秒
→ 解鎖一次
→ 等待 3 秒
→ 再次鎖定
→ 其餘時間不做其他操作
完成後確認 Log:
wc -l day20-replay-source.log
head -n 3 day20-replay-source.log
tail -n 3 day20-replay-source.log
candump -L 會把適合 canplayer 使用的 Compact Log Format 寫到標準輸出,再由 tee 同時保存到檔案。
關閉 Controls,讓原本產生車況訊息的模擬 Sender 停止,但保留 ICSim 儀表視窗。
這一步能減少合法週期訊息和 Replay 混在一起,讓時間順序更容易觀察。
在新的 Terminal 開始記錄重播結果:
cd ~/can-labs/day20
timeout 20s candump -L vcan0 | tee day20-replay-observed.log
在另一個 Terminal 執行:
cd ~/can-labs/day20
canplayer -v -I day20-replay-source.log vcan0=vcan0
vcan0=vcan0 的意思是:把 Log 中原本從 vcan0 收到的 Frame,再送到目前的 vcan0。
canplayer 預設只處理輸入檔一次,並依 Log 時間關係重播。
本文不使用 -l i 無限循環,也不使用 -t 忽略時間後一次送完所有 Frame。
觀察:
day20-replay-observed.log 是否出現相同 ID/DLC/DataICSim 若再次採用舊資料,只能證明這個教學 Application 沒有防重放機制。
它不能證明真實 ECU、Gateway 或 SecOC Profile 會有相同行為。
沿用 Day 19 找到的候選 ID,輸入實際值:
cd ~/can-labs/day20
read -r -p "Target 11-bit CAN ID (hex): " TARGET_CAN_ID
grep " ${TARGET_CAN_ID}#" day20-replay-source.log
grep " ${TARGET_CAN_ID}#" day20-replay-observed.log
比較時不要只看 Data 是否相同,也要保留:
這些資料才能說明「同一組內容在不同時間再次出現」。
使用同一個 Seed 重新啟動 Controls,讓 ICSim 回到正常背景流量。
在 Terminal 記錄五秒 Baseline:
cd ~/can-labs/day20
timeout 5s candump -L vcan0 | tee day20-baseline.log
記錄期間不要操作油門、方向燈與車門,讓流量維持在相對穩定的狀態。
先在 Terminal 3 準備五秒記錄:
cd ~/can-labs/day20
timeout 5s candump -L vcan0 | tee day20-burst.log
接著在 Terminal 4 執行一次有限 Burst:
cangen vcan0 \
-g 1 \
-n 200 \
-I 100 \
-L 8 \
-D 0000000000000000
這個命令使用:
0x100
0x100 與 Data 只用於本次虛擬流量標記,不代表任何車款的真實訊息,也不應替換成真實車輛的控制 ID。
完成後比較:
wc -l day20-baseline.log day20-burst.log
grep -c ' 100#0000000000000000$' day20-burst.log
預期 day20-burst.log 會多出接近 200 筆具有相同 ID/Data 的紀錄,實際數量仍要看開始記錄與執行 cangen 的時間是否完整重疊。
對兩份 Log 分別執行:
awk '
{
t = $1
gsub(/[()]/, "", t)
if (count == 0) first = t
last = t
count++
}
END {
duration = last - first
rate = (count > 1 && duration > 0) ? (count - 1) / duration : 0
printf "frames=%d duration=%.6f s average=%.2f frames/s\n", \
count, duration, rate
}' day20-baseline.log
awk '
{
t = $1
gsub(/[()]/, "", t)
if (count == 0) first = t
last = t
count++
}
END {
duration = last - first
rate = (count > 1 && duration > 0) ? (count - 1) / duration : 0
printf "frames=%d duration=%.6f s average=%.2f frames/s\n", \
count, duration, rate
}' day20-burst.log
這個平均值只描述 SocketCAN Log 中觀察到的 Frame Rate。
它不是實體 CAN Bus Load,因為 vcan 沒有 Bit Rate、Arbitration、Bit Stuffing、CRC、ACK、Error Frame 與 Transceiver。
也不能因為 0x100 數值較小,就宣稱它在 vcan 中真的贏得實體仲裁。
最後保存:
| 證據 | 要回答的問題 |
|---|---|
| can-utils 版本與 ICSim Seed | 這次實驗使用哪一組工具及隨機配置? |
day20-replay-source.log |
哪些有效 Frame 被記錄?原始時間關係是什麼? |
day20-replay-observed.log |
舊 Frame 在什麼時間再次出現? |
| ICSim 重播前後畫面 | 教學 Application 是否再次採用舊資料? |
day20-baseline.log |
正常穩定操作時的觀察 Frame Rate 是多少? |
day20-burst.log |
有限 200-Frame Burst 如何改變數量與頻率? |
| 實驗限制 | 哪些現象因 vcan 缺少實體層而不能下結論? |
完成後關閉 ICSim、Controls 與所有監聽程式。
安全與法律提醒:
canplayer會重新送出 Log 中的 Frame,cangen則可以持續產生流量。本文把兩者限制在vcan0/ICSim,Replay 只執行一次,Burst 也固定為 200 Frames。不要把介面名稱、Log 或目標 ID 改成道路車輛、他人的 OBD 介面或未授權網路,也不要在車輛行駛時進行測試。
完成後可以合理確認:
candump 能保存 SocketCAN Frame 與時間戳記canplayer 能依 Log 將舊 Frame 再送入同一個虛擬介面cangen 的有限 Burst 能產生可量測的 ID 頻率異常不能證明:
vcan 上的 Frame Rate 等於實體 CAN Bus Load0x100 在這次實驗中真的執行線路仲裁cangen 能建立實體 Error Frame 或讓節點 Bus-Off所以今天的結論應寫成:
在隔離的
vcan0/ICSim 環境中,先前記錄的 Frame 可以被再次送入,有限 Burst 也能形成可觀察的頻率異常;實體 CAN 的仲裁、Bus Load、Error Frame 與 Bus-Off 仍需在授權測試台以硬體證據驗證。
如果後續在專用 CAN 測試台驗證故障與復原,不應只截一張「BUS-OFF」畫面。
至少要同步保存:
restart-ms/手動 Restart PolicyLinux 實體 CAN Driver 支援時,可以從 CAN Netdevice 狀態與 Error Message Frame 取得部分證據。
不過,欄位與能力仍取決於 Controller、Driver 與測試設備,不能只靠一條通用命令取代示波器、CAN Analyzer 與 ECU Log。
三條路徑需要不同控制,再由共同監控與復原串起來。
訊息 Authenticator 可以降低 Injection 與 Replay 風險,卻不會阻止攻擊者在更低層製造 bit error。
同樣地,Bus-Off Recovery 可以恢復節點通訊,卻不能修復仍然存在的線路故障或受入侵節點。
多層防護的目的,是讓某一層失守時,其他控制仍能限制影響並留下調查證據。
| 攻擊路徑階段 | 本篇新增證據 | 尚未證明的部分 |
|---|---|---|
| 記錄有效訊息 | day20-replay-source.log |
真實攻擊者如何取得被動觀察能力 |
| 再次送出 | 一次 canplayer 執行與 Replay Log |
真實 ECU 是否會接受相同內容 |
| 頻率異常 | 固定 200 Frames 的 cangen 紀錄 |
實體 Bus Load、仲裁延遲與功能 Timeout |
| Error Handling | 協定狀態與官方 Controller 文件 | 真實硬體上的 Error Type、TEC/REC 與線路證據 |
| Bus-Off | 研究中已知的 Fault Confinement 攻擊路徑 | 特定 ECU、Driver 與整車復原行為 |
| 可能損害 | 只觀察模擬狀態與流量變化 | Safety、營運、財務或隱私損害 |
這張表可以避免把三項不同層次的結論壓成一句「CAN 很容易被 DoS」。
Replay 要證明接收端採用了過期資料,Flooding 要證明重要服務超過 Deadline,Bus-Off 則要證明特定 Controller 的 TEC 與狀態真的走到退出通訊。
vcan 可以驗證 Replay 與 Frame Rate 異常,不能模擬實體仲裁、Error Frame、TEC/REC 與 Bus-Off今天看見 CAN 的舊訊息、高頻流量與錯誤處理如何影響新鮮度及可用性。
Day 21 將回到車外的 V2X,分析位置欺騙、假事件與 Sybil Attack:一個合法參與者如何宣告錯誤內容,又如何讓單一實體節點看起來像多台車。
candump 原始碼與參數說明
canplayer 原始碼與參數說明
cangen 原始碼與參數說明