espilon-source/espilon_bot/components/mod_canbus/README.md
Eun0us 6d45770d98 epsilon: merge command system into core + add 5 new modules
Move command registry from components/command/ into components/core/.
New modules: mod_canbus, mod_honeypot, mod_fallback, mod_redteam, mod_ota.
Replace mod_proxy with tun_core (multiplexed SOCKS5 tunnel).
Kconfig extended with per-module settings and async worker config.
2026-02-28 20:07:59 +01:00

342 lines
12 KiB
Markdown

# CAN Bus Module (mod_canbus)
Automotive CAN bus offensive module for Espilon, built on the **MCP2515** SPI controller. Supports passive sniffing, frame injection, ISO-TP transport, UDS diagnostics, OBD-II decoding, fuzzing, and replay.
> **Authorization required**: CAN bus interaction with vehicles must be performed only on owned hardware or with explicit written authorization. Unauthorized access to vehicle networks is illegal.
---
## Table of Contents
- [Hardware Requirements](#hardware-requirements)
- [Wiring](#wiring)
- [Configuration](#configuration)
- [Architecture](#architecture)
- [Commands Reference](#commands-reference)
- [Core Commands](#core-commands)
- [UDS Diagnostic Commands](#uds-diagnostic-commands)
- [OBD-II Commands](#obd-ii-commands)
- [Fuzzing Commands](#fuzzing-commands)
- [Frame Format](#frame-format)
- [C3PO Integration](#c3po-integration)
- [Usage Examples](#usage-examples)
- [Troubleshooting](#troubleshooting)
---
## Hardware Requirements
| Component | Role | Cost |
|-----------|------|------|
| **MCP2515 module** | CAN 2.0B controller + TJA1050 transceiver | ~3 EUR |
| **ESP32** | Main MCU (any variant with SPI) | ~5 EUR |
Most MCP2515 modules sold online already integrate the TJA1050 CAN transceiver. Check the oscillator crystal on your module — common values are **8 MHz** and **16 MHz** (must match Kconfig `CANBUS_OSC_MHZ`).
---
## Wiring
Default GPIO mapping (configurable via `idf.py menuconfig`):
```
MCP2515 Module ESP32 (VSPI)
────────────── ────────────
VCC → 3.3V
GND → GND
CS → GPIO 5
MOSI (SI) → GPIO 23
MISO (SO) → GPIO 19
SCK → GPIO 18
INT → GPIO 4 (active low)
```
Connect **CAN_H** and **CAN_L** on the MCP2515 module to the target CAN bus. For OBD-II: pin 6 (CAN_H) and pin 14 (CAN_L).
---
## Configuration
Enable the module in `idf.py menuconfig` under **Modules > CAN Bus Module (MCP2515)**.
### Kconfig Options
| Option | Default | Description |
|--------|---------|-------------|
| `CONFIG_MODULE_CANBUS` | n | Enable the CAN bus module |
| `CANBUS_SPI_HOST` | 3 (VSPI) | SPI host: 2=HSPI, 3=VSPI |
| `CANBUS_PIN_MOSI` | 23 | SPI MOSI GPIO |
| `CANBUS_PIN_MISO` | 19 | SPI MISO GPIO |
| `CANBUS_PIN_SCK` | 18 | SPI SCK GPIO |
| `CANBUS_PIN_CS` | 5 | SPI Chip Select GPIO |
| `CANBUS_PIN_INT` | 4 | MCP2515 interrupt GPIO (active low) |
| `CANBUS_OSC_MHZ` | 8 | Oscillator frequency on MCP2515 module |
| `CANBUS_DEFAULT_BITRATE` | 500000 | Default bus speed (bps) |
| `CANBUS_SPI_CLOCK_HZ` | 10000000 | SPI clock (max 10 MHz) |
| `CANBUS_RECORD_BUFFER` | 512 | Frame ring buffer size (64-2048) |
| `CANBUS_ISO_TP` | y | ISO-TP transport layer (required for UDS/OBD) |
| `CANBUS_UDS` | y | UDS diagnostic services (requires ISO-TP) |
| `CANBUS_OBD` | y | OBD-II PID decoder (requires ISO-TP) |
| `CANBUS_FUZZ` | y | CAN fuzzing engine |
### Supported Bitrates
| Bitrate | Use Case | 8 MHz | 16 MHz |
|---------|----------|-------|--------|
| 1 Mbps | High-speed CAN | - | Yes |
| 500 kbps | Standard automotive | Yes | Yes |
| 250 kbps | J1939 (trucks) | Yes | Yes |
| 125 kbps | Low-speed CAN | Yes | Yes |
| 100 kbps | Diagnostic | Yes | Yes |
---
## Architecture
```
┌─────────────────────────────────────────────────────┐
│ cmd_canbus.c — C2 command handlers (27 cmds)│
│ ↕ │
│ canbus_uds.c — UDS (ISO 14229) services │
│ canbus_obd.c — OBD-II PID decoder │
│ canbus_fuzz.c — Fuzzing engine │
│ ↕ │
│ canbus_isotp.c — ISO-TP (ISO 15765-2) │
│ ↕ │
│ canbus_driver.c — MCP2515 SPI driver + RX task │
│ ↕ │
│ canbus_config.c — NVS persistence │
│ ↕ │
│ ESP-IDF SPI Master — Hardware SPI bus │
└─────────────────────────────────────────────────────┘
```
### File Manifest
| File | Lines | Layer |
|------|-------|-------|
| `canbus_driver.c/.h` | ~920 | MCP2515 SPI + RX/TX + ISR |
| `canbus_isotp.c/.h` | ~480 | Multi-frame CAN transport |
| `canbus_uds.c/.h` | ~440 | Automotive diagnostics |
| `canbus_obd.c/.h` | ~390 | OBD-II PID decode |
| `canbus_fuzz.c/.h` | ~390 | Fuzz testing engine |
| `canbus_config.c/.h` | ~360 | NVS persistence |
| `cmd_canbus.c/.h` | ~1360 | Command handlers + registration |
| **Total** | **~4350** | |
### NVS Persistence
Namespace: `"can_cfg"`
| Key | Type | Content |
|-----|------|---------|
| `bitrate` | i32 | Saved CAN speed |
| `osc_mhz` | u8 | Oscillator frequency |
| `sw_filters` | blob | Up to 16 software filter IDs |
| `ecus` | blob | Discovered UDS ECU IDs |
---
## Commands Reference
### Core Commands
| Command | Args | Async | Description |
|---------|------|-------|-------------|
| `can_start [bitrate] [mode]` | 0-2 | No | Init MCP2515, start bus. Mode: `normal` (default), `listen`, `loopback` |
| `can_stop` | 0 | No | Stop bus, set MCP2515 to config mode |
| `can_send <id_hex> <data_hex>` | 2 | No | Send a single frame. Ex: `can_send 0x7DF 0201000000000000` |
| `can_filter_add <id_hex>` | 1 | No | Add software filter (pass only matching IDs) |
| `can_filter_del <id_hex>` | 1 | No | Remove a software filter |
| `can_filter_list` | 0 | No | List active software filters |
| `can_filter_clear` | 0 | No | Clear all filters (accept everything) |
| `can_status` | 0 | No | Show bus state, config, RX/TX counters, error counters |
| `can_sniff [duration_s]` | 0-1 | **Yes** | Stream frames to C2 for N seconds (default: 10) |
| `can_record [duration_s]` | 0-1 | **Yes** | Record to local ring buffer for N seconds (default: 10) |
| `can_dump` | 0 | **Yes** | Send recorded buffer to C2 |
| `can_replay [speed_pct]` | 0-1 | **Yes** | Replay recorded buffer. 100=real-time, 0=max speed |
### UDS Diagnostic Commands
*Requires `CONFIG_CANBUS_UDS=y` (depends on ISO-TP)*
| Command | Args | Async | Description |
|---------|------|-------|-------------|
| `can_scan_ecu` | 0 | **Yes** | Discover ECUs: scans 0x7E0-0x7E7, 0x700-0x7DF |
| `can_uds <tx_id> <service_hex> [data_hex]` | 2-3 | **Yes** | Raw UDS request |
| `can_uds_session <tx_id> <type>` | 2 | No | DiagnosticSessionControl (1=default, 2=prog, 3=extended) |
| `can_uds_read <tx_id> <did_hex>` | 2 | **Yes** | ReadDataByIdentifier |
| `can_uds_dump <tx_id> <addr_hex> <size>` | 3 | **Yes** | ReadMemoryByAddress (streamed) |
| `can_uds_auth <tx_id> [level]` | 1-2 | **Yes** | SecurityAccess seed request |
### OBD-II Commands
*Requires `CONFIG_CANBUS_OBD=y` (depends on ISO-TP)*
| Command | Args | Async | Description |
|---------|------|-------|-------------|
| `can_obd <pid_hex>` | 1 | **Yes** | Query single PID, returns decoded value |
| `can_obd_vin` | 0 | **Yes** | Read Vehicle Identification Number |
| `can_obd_dtc` | 0 | **Yes** | Read Diagnostic Trouble Codes |
| `can_obd_supported` | 0 | **Yes** | List supported PIDs |
| `can_obd_monitor <pids> [interval_ms]` | 1-2 | **Yes** | Stream PIDs to C2 continuously |
| `can_obd_monitor_stop` | 0 | No | Stop monitoring |
### Fuzzing Commands
*Requires `CONFIG_CANBUS_FUZZ=y`*
| Command | Args | Async | Description |
|---------|------|-------|-------------|
| `can_fuzz_id [start] [end] [delay_ms]` | 0-3 | **Yes** | Iterate all CAN IDs with fixed payload |
| `can_fuzz_data <id_hex> [seed_hex] [delay_ms]` | 1-3 | **Yes** | Mutate data bytes for fixed ID |
| `can_fuzz_random [delay_ms] [count]` | 0-2 | **Yes** | Random ID + random data |
| `can_fuzz_stop` | 0 | No | Stop fuzzing |
---
## Frame Format
Frames streamed to C2 use the format:
```
CAN|<timestamp_ms>|<id_hex>|<dlc>|<data_hex>
```
**Example:**
```
CAN|1708000123456|0x123|8|DEADBEEF01020304
```
### Special Markers
| Marker | Meaning |
|--------|---------|
| `SNIFF_END` | End of sniff session |
| `DUMP_START\|<count>` | Beginning of frame dump |
| `DUMP_END` | End of frame dump |
| `UDS_RSP\|<rx_id>\|<hex>` | UDS response |
| `MEM_DUMP\|<addr>\|<size>` | Start of memory dump |
| `MEM\|<addr>\|<hex_data>` | Memory block |
| `MEM_DUMP_END` | End of memory dump |
| `ECU\|<tx_id>\|<rx_id>` | Discovered ECU |
---
## C3PO Integration
### REST API
CAN frames received from agents are stored in a server-side ring buffer (10,000 frames max).
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/can/frames` | GET | List frames. Params: `device_id`, `can_id`, `limit`, `offset` |
| `/api/can/stats` | GET | Frame stats. Params: `device_id` |
| `/api/can/frames/export` | GET | Download CSV. Params: `device_id` |
### TUI Commands
From the C3PO interactive TUI:
```
can stats [device_id] — Frame count, unique CAN IDs
can frames [device_id] [limit] — Display last N frames
can clear — Clear frame store
```
### Transport Integration
CAN frames arrive via `AGENT_DATA` messages with the `CAN|` prefix. The transport layer automatically parses and stores them in `CanStore`.
---
## Usage Examples
### Basic Sniffing (Listen-Only)
```
> can_start 500000 listen # Start in stealth mode (no ACK on bus)
> can_sniff 30 # Stream frames for 30 seconds
> can_stop
```
### Record and Replay
```
> can_start 500000 listen
> can_record 60 # Record for 60 seconds
> can_stop
> can_start 500000 normal # Switch to normal mode for TX
> can_replay 100 # Replay at real-time speed
```
### OBD-II Vehicle Diagnostics
```
> can_start 500000 # Standard automotive bitrate
> can_obd_supported # List what the car supports
> can_obd 0C # Engine RPM
> can_obd 0D # Vehicle speed (km/h)
> can_obd_vin # VIN number
> can_obd_dtc # Read trouble codes
> can_obd_monitor 0C,0D 500 # Stream RPM + speed every 500ms
```
### UDS ECU Exploration
```
> can_start 500000
> can_scan_ecu # Find ECUs on bus
> can_uds_session 0x7E0 3 # Extended session on ECU 0x7E0
> can_uds_read 0x7E0 F190 # Read VIN via DID
> can_uds_read 0x7E0 F191 # Hardware version
> can_uds_auth 0x7E0 1 # SecurityAccess level 1
> can_uds_dump 0x7E0 0x00000000 4096 # Dump 4KB from address 0
```
### Fuzzing (Isolated Bus Only!)
```
> can_start 500000
> can_fuzz_id 0x000 0x7FF 10 # Scan all standard IDs, 10ms delay
> can_fuzz_data 0x7E0 0000000000000000 5 # Mutate bytes on ECU ID
> can_fuzz_stop
```
---
## Troubleshooting
### MCP2515 not detected
- Verify wiring (CS, MOSI, MISO, SCK)
- Check `CANBUS_OSC_MHZ` matches the crystal on your module (8 vs 16 MHz)
- Try `can_start 500000 loopback` — if loopback works, wiring to the bus is the issue
### No frames received
- Confirm bus speed matches the target (500k for cars, 250k for trucks)
- Try `listen` mode first: `can_start 500000 listen`
- Check CAN_H / CAN_L connections and termination (120 ohm)
- Use `can_status` to check error counters — high RX errors indicate speed mismatch
### Bus-off state
- TEC exceeded 255 — the MCP2515 disconnected from the bus
- `can_stop` then `can_start` to reset
- Check for wiring issues or speed mismatch
### RX overflow
- Bus traffic exceeds processing speed
- Reduce bus load or add hardware filters: `can_filter_add <id>`
- Increase `CANBUS_RECORD_BUFFER` in menuconfig
### SPI communication errors
- Reduce `CANBUS_SPI_CLOCK_HZ` (try 8000000 or 4000000)
- Check for long wires or loose connections
- Ensure no other device shares the SPI bus