tyler

2026-06-2611 minsecurityautomotivethreat-modeling

Automotive threat model as code on Threagile

A tag-driven Threagile model of a composite BEV: 16 custom rules encoding automotive bus-security weaknesses and a multi-hop attack path analyzer.

The project is a reusable Threagile threat model of a generic composite (domain + zonal) battery-electric vehicle with SAE Level 3+ automation, 16 automotive-specific custom risk rules, and a multi-hop attack-path analyzer that maps every hop to Auto-ISAC Automotive Threat Matrix (ATM) and MITRE ATT&CK ICS technique IDs. It is a starting point you will need to adapt to your specific vehicle’s topology.

This doesn’t replace any existing method. STRIDE surfaces what kinds of threats apply to a component. An ISO 21434 TARA decides which threats are worth fixing. This automates the structural groundwork those methods assume you have already done: whether the architecture has the right controls in place before you enumerate scenarios.

Threagile briefly

Threagile (MIT, maintained by Christian Schneider) models a system as a YAML file: technical assets, communication links, trust boundaries, and data assets. Run it and it executes roughly 40 built-in risk rules, generates a data-flow diagram, a report PDF, and machine-readable risks.json and risks.xlsx. The model lives in version control. That is the practical difference from a TARA spreadsheet: a diff in code review can catch an unauthenticated internet-exposed asset before it ships.

Custom rules extend the built-in set via two paths: YAML risk-rule scripts evaluated with the cmd/script harness, and compiled Go .so plugins loaded via -custom-risk-rules-plugins. This project uses YAML. One caveat up front: Threagile’s production auto-loading of YAML risk-rule scripts during threagile analyze is unconfirmed upstream. The rules here are validated with the cmd/script harness, and any finding that must appear in the generated report is emitted as an individual_risk_categories block merged into the model. Both the auto-load feature and the harness path are real; this project just doesn’t rely on the former.

Tag-driven conventions and the default-insecure discipline

Threagile’s built-in rules key on standard fields: authentication, encryption, internet. The custom rules here key on a controlled tag vocabulary, so they apply to any vehicle model that follows the conventions, not just this one. That is the portability guarantee.

The vocabulary covers four groups. Domain and role tags (safety-critical, gateway, zone-controller, telematics, chassis, body, and others) identify what a component is and where it sits. In-vehicle bus tags (can, can-fd, lin, flexray, ethernet, some-ip, doip) carry the actual protocol since Threagile has no native automotive bus values. External and RF interface tags (cellular, bluetooth, uwb, gnss, ota, iso15118) mark the exposure surface. Capability and transport tags (secure-boot, tls-mutual, tls-server-only, distance-bounding, hsm, firmware-signing, removable-media) record security controls that are actually present. Every tag must be declared in tags_available: before use.

Two conventions drive everything else.

The first is default insecure. Every raw CAN, CAN-FD, LIN, FlexRay, and SENT link, and any Ethernet link without explicit MACsec, IPsec, or TLS, is modeled authentication: none, encryption: none. Classic CAN, LIN, and FlexRay define no authentication or encryption in their bus specifications (ISO 11898, ISO 17987, ISO 17458); that absence is the whole reason AUTOSAR SecOC exists. A link gets a real authentication value only where the control is actually designed in. The reason is false negatives: if links default to “secure” or authentication is left unset, rules fire only on the assets you remembered to mark. The assets you forgot are silent gaps. Default insecure means a forgotten asset fails loud.

Buses ride on tags because Threagile has no native CAN, LIN, or FlexRay protocol values. A raw fieldbus link uses protocol: binary and puts the real bus in the tags. A SecOC-protected CAN-FD link uses authentication: credentials (SecOC is represented that way: it provides authenticity via AES-CMAC plus a freshness value, not confidentiality). Here is what that looks like in the model:

# Raw CAN-FD, default insecure
To Steering ECU:
  target: steering-ecu
  description: CAN FD chassis commands
  protocol: binary
  authentication: none
  encryption: none
  tags: [can-fd]

# SecOC-protected CAN-FD: AES-CMAC + freshness, no confidentiality
To Brake ECU:
  target: brake-ecu
  description: CAN FD to brake-by-wire; SecOC (AES-CMAC + freshness)
  protocol: binary
  authentication: credentials # SecOC = "credentials" in Threagile
  encryption: none # SecOC gives authenticity, not confidentiality
  tags: [can-fd]

The second convention sets the analyzer’s source and sink sets. Internet- and RF-exposed assets (TCU, IVI system, V2X unit, Wi-Fi/Bluetooth controller, GNSS receiver, charge port) are marked internet: true. Safety-critical actuation targets (brake ECU, steering ECU, BMS, inverter, VCU, airbag ECU, ADAS compute, FlexRay actuator) carry the safety-critical tag. Those are the entry and target sets for the multi-hop analysis.

The 16 custom rules

Each rule encodes one automotive-specific weakness that Threagile’s built-in rules don’t cover. All 16 key on the tag vocabulary and link attributes, and each maps to a CWE and STRIDE category.

Rule IDFires whenReal weakness encoded
unauthenticated-safety-bus-linkcan/can-fd/lin/flexray link with auth: none reaches safety-criticalUnauthenticated fieldbus: actuation commands are spoofable
missing-secoc-on-safety-busfieldbus link to safety-critical asset whose auth is not SecOC (credentials)Broader SecOC gap: non-SecOC auth is also insufficient on a broadcast bus
cross-domain-link-no-filterexposed-domain source links directly to safety with no gateway/zone-controller between themDomain separation bypass: exposed component reaches safety without an authenticated filter
unauthenticated-gateway-bridgegateway/zone-controller originates an auth: none bridging linkGateway that authenticates nothing at the domain boundary
internet-exposed-ecu-unencryptedinternet: true asset tagged ecu/telematics/infotainment has encryption: noneCleartext traffic on an internet-exposed ECU
internet-exposed-ecu-no-secure-bootinternet: true compute asset lacks secure-boot tagNo verified boot chain: persistent firmware compromise becomes straightforward
reachable-unauthenticated-diagnosticsobd-ii/doip link has auth: noneUnauthenticated diagnostic access reachable from the network
reachable-debug-portphysical-tagged (JTAG/UART) link has auth: noneExposed hardware debug port without access control
unencrypted-ota-channelota-tagged link uses cleartext transportOTA channel susceptible to on-path modification
iso15118-server-only-tlsiso15118 link is not mutual TLS (no tls-mutual, no client-certificate)ISO 15118-2 server-only TLS: vehicle is not authenticated, relay attack surface remains
unauthenticated-someip-service-linksome-ip link has auth: noneUnauthenticated SOME/IP: service spoofing and on-path manipulation
safety-function-without-redundancysafety-critical asset is not redundant: trueSingle-point availability failure for a safety function
relay-vulnerable-passive-entryuwb/bluetooth link to body target lacks distance-boundingRelay attack on passive entry: crypto authentication alone does not stop it
unprotected-key-storageasset holding crypto-material data asset lacks hsm tagLong-term keys in readable flash: accessible to any firmware-level attacker
removable-media-ingressremovable-media link has auth: noneUnsigned USB/SD content: untrusted firmware or media ingress
unverified-firmware-updateasset holding ecu-firmware data asset lacks firmware-signing tagTransport TLS does not substitute for on-device image verification

missing-secoc-on-safety-bus is broader than unauthenticated-safety-bus-link. AUTOSAR SecOC (AUTOSAR R24-11 PRS SecOC Protocol) protects CAN, CAN-FD, and FlexRay messages with a symmetric AES-CMAC authenticator plus a freshness value, giving receivers verifiable authenticity and replay protection. It provides no confidentiality. A link carrying some other authentication method, like a session token, still cannot verify that actuation frames are authentic and fresh on a broadcast fieldbus. That is why missing-secoc-on-safety-bus flags any fieldbus-to-safety link that isn’t explicitly authentication: credentials (SecOC), not just unauthenticated ones.

relay-vulnerable-passive-entry ignores the link’s authentication value deliberately. Passive-entry relay attacks work by having two attackers forward the legitimate radio exchange between key and car. This defeats the proximity assumption regardless of authentication: the key is genuine, the challenge-response is correct, but the key is far away and the car doesn’t know it. The effective counter is a secure distance measurement: UWB time-of-flight distance bounding that proves the key is physically near. The rule fires on any short-range access link to the body domain that lacks the distance-bounding tag.

unverified-firmware-update encodes a specific claim: TLS on the OTA channel and on-device image verification are different controls. TLS gives you integrity of the channel. Uptane-style image verification (a Director Repository for per-vehicle instructions and an Image Repository for image metadata) gives you integrity of the payload after delivery, including off-path attacks where the attacker never touches the channel. You need both. The firmware-signing tag in the model marks only assets that actually perform on-device image verification.

cross-domain-link-no-filter catches direct exposed-to-safety shortcuts. A defensible E/E architecture forces traffic from exposed domains (connectivity, telematics, infotainment, V2X) through an authenticated gateway or zone controller before reaching the safety domain. A direct link from an IVI system to a chassis ECU collapses that separation. The rule fires when the source carries an exposure tag, the target carries a safety tag, and neither endpoint is tagged gateway or zone-controller.

Threagile to ATM and ATT&CK technique mapping

Each hop in an attack path carries both ATM technique IDs (ATM-Txxxx) and ATT&CK ICS IDs, embedded in the analyzer. The June 22 ATM post walks the matrix against a single TCU; this generalizes the mapping to multi-hop paths and automates it.

The mapping is at technique granularity, not tactic. That was deliberate: technique-level IDs let the path output feed straight into an ISO 21434 TARA attack-path analysis step without a manual mapping pass. The TARA’s feasibility rating stage needs to know which attack techniques are chained, at what granularity.

A representative path from the analyzer output:

TCU -> Central Gateway -> Chassis Zone Controller -> Brake ECU   [3 hops, weakest auth: none]
ATT&CK ICS: T0883 (Initial Access) -> T0866 -> T0867 -> T0831 (Manipulation of Control)
ATM:        ATM-T0012 -> ATM-T0051/ATM-T0052 -> ATM-T0067
realism:    corroborated (ATM-P0006 chained the entry and lateral-movement techniques)

The two lateral-movement hops are T0866 (Exploitation of Remote Services) and T0867 (Lateral Tool Transfer). Every technique ID here is a current ATT&CK ICS entry, verified against ATT&CK for ICS v19.1.

Each path also carries a realism weight: corroborated when a documented ATM campaign (ATM-Pxxxx) chained at least two of the path’s techniques, partially-corroborated, or theoretical. The weight is informational and doesn’t change severity, but it gives a TARA team a starting signal for which paths to analyze first.

The analyzer as payoff

A per-asset rule engine checks one asset at a time. It cannot find a three-hop path from a TCU to a brake ECU because there is no graph to traverse. Finding it means building the graph and traversing it.

The analyzer (scripts/attack_path_analyzer.py, Python + networkx) reads model/threagile.yaml, builds an attacker-reachability graph, and runs BFS from every internet: true entry asset toward every safety-critical target. The graph is undirected by default. That is a deliberate conservative assumption: attacks flow against the declared data-flow direction (down toward actuators), so following only declared data-flow edges would hide the dangerous paths. A --directed flag is available for cases where the model can assert true unidirectionality with confidence.

From the composite BEV model: 6 entry assets reach 8 crown jewels. The output is 48 attack-path risks and 6 chokepoint risks.

NodeCrown-jewel paths gated
Central Gateway4
Front Zone Controller3
Chassis Zone Controller3
Battery Management System / Vehicle Control Unit2 each

The central gateway gates four jewel paths. That means a gateway with mutual authentication and policy enforcement cuts more attack paths than any other single control in this topology.

The analyzer is external Python rather than a Go plugin, which avoids plugin ABI coupling and keeps networkx’s graph algorithms available. The only tie to Threagile is the YAML schema. The output goes back into the report as individual_risk_categories, so path findings and built-in rule findings appear in the same report.

Where this sits in the wider workflow

ISO 21434 TARA has an attack-path analysis step that sits between threat scenario identification and attack feasibility rating. The rules and analyzer are meant to feed that step: enumerate structural gaps (rules) and the multi-hop paths to crown jewels (analyzer), then hand those findings to feasibility rating.

This is still a work in progress. The conventions and rules generalize to any vehicle model that follows the tag vocabulary. What won’t generalize without work: the specific trust-boundary topology, which ATM campaigns corroborate which paths, and any rules that depend on OEM-specific subsystems. Treat this as scaffolding to build on, not a drop-in.

The repo is HomeAutobots/automotive-threagile. CONVENTIONS.md lists the full tag vocabulary, per-rule triggers, and run commands.

Further reading

← OlderThreat modeling an authenticated RAG chatbot for a bank