Whisper · Docs
Automotive

The fleet-API-abuse cure

A reverse-engineered companion-app API plus an access token that is portable to any IP is the entire whole-fleet-scraping problem. It has an OWASP name, BOLA, because the token authenticates a claim, never the machine on the other end.

Whisper closes it by making the address be the car: a routable IPv6 /128 derived from the key already sitting in the vehicle, DNSSEC-anchored and DANE-pinned, that no aggregator can forge and one call can revoke worldwide. A stolen session with no device key behind it authenticates to nothing. This page walks the abuse end-to-end, the reframe, and the exact live calls: keyless to verify, one keyed call to provision.

The abuse, at class level

It is not a breach. Your connected-vehicle API is used exactly as it was built, at fleet scale, by an operator who was never your customer.

The mechanics repeat across the industry. A third party reverse-engineers the companion app, increasingly with the help of an LLM, until it holds the exact calls the official app makes. It then authenticates the same ways the app does: a phished owner login, a static API-key header lifted from the app bundle, or an OAuth bearer token that travels to any IP. From there it is contract-less, low-and-slow polling: a request every few minutes, long-lived streams held open, indistinguishable from a real handset because it is the handset's protocol. Public reverse-engineered clients for a major European connected-car backend already circulate.

The root cause has a name: OWASP broken authentication / BOLA (Broken Object-Level Authorization, API1:2023). The token, key, or password authenticates a claim ("I am an authorized caller") but never proves which machine is on the other end. So a stolen credential is indistinguishable from the real one, and the source IP that might have narrowed it down is disposable.

Two properties make it invisible at the network layer. A legitimate driver is one IP to one car; the abuser is one operator to thousands, holding valid credentials. And the egress hops across clouds or a residential-proxy swarm every few requests, so a security operations center correlates a fresh, meaningless last IP and nothing else. Whole-fleet telematics resale is not hypothetical: two data brokers reached billions of harvested vehicle-data points, and litigation, before both collapsed in 2024, which makes this a GDPR and EU Data Act exposure, not only a security one.

The reframe: the address is the car

Detection will always be a step behind a credential that is genuinely valid. You can tune models forever and the abuser still looks exactly like a customer: to your backend, they are one. The strictly-stronger move is to change what the backend trusts.

Whisper has one primitive: the address is the identity. A routable IPv6 /128 out of 2a04:2a01::/32 (announced by AS219419), deterministically derived from a key, DNSSEC-signed to the IANA root, DANE-EE pinned (3 1 1), and RDAP-registered. Anyone can re-derive and verify it with dig.

Point it at the car. Whisper derives each vehicle's (or each ECU's) /128 from the hardware key it already holds: its public SubjectPublicKeyInfo from an IEEE 802.1AR IDevID, a TPM, or a secure element, with the VIN (and optionally an ECU serial) as the domain separator. The derivation is deterministic and tenant-bound: the same device key and VIN always yield the same /128, and the mapping is unlinkable across fleets to an outsider. The backend then authorizes on the car's pinned identity, not a stealable token: a request either proves it is the car it claims to be, before a single detection rule runs, or it has no authority at all.

The VIN is the public index: the /128 is its cryptographic counterpart. The VIN is on the windshield and guessable; that is what the attacker weaponizes. But the /128 derives from the in-device key salted by the VIN, so VIN alone yields nothing. You cannot go VIN → /128 without the key, there is no enumerable directory, and RDAP / reverse DNS return the registry object, never the car's whereabouts.

What changes

Nothing here is a new detection rule. Each row is an abuse technique that stops being possible, not one you catch after the fact.

The abuse today Why it dies under identity
One IP → thousands of cars You cannot present thousands of car-identities whose device keys you do not hold. Every forgery is a DNSSEC / DANE inconsistency any verifier catches with stock tools: dig -x names an identity whose AAAA and TLSA don't agree.
Rotate egress across clouds / residential proxies Identity is not the source IP. The last IP was never the credential, so rotating it, across AWS, GCP, Azure, or a proxy swarm, changes nothing about whether the caller can prove the car.
Replay a stolen token or dealer-portal session The credential has no device key behind it. State-changing commands terminate mutually-authenticated to the target car's /128 (the car co-signs), so a valid-looking token that can't prove the identity never had authority. BOLA / IDOR lose their leverage: reaching any account no longer reaches any car.
Blast radius on compromise One revoke tears down the /128, its PTR, and its DANE pin worldwide at DNS-TTL speed. No fleet-wide password reset, no CRL you hope every car fetched. Compromise one ECU and you have compromised that ECU, not the fleet.

Provision a vehicle identity

Provisioning is one control-plane call to POST https://graph.whisper.security/api/query with your X-API-Key header. You pass the vehicle's public key material (the base64 SubjectPublicKeyInfo of its IDevID / TPM / secure-element key) and its VIN; you get back the deterministic /128 and a WireGuard config to source the car's traffic from that address.

The call

CALL whisper.agents({op:'connect', args:{
  tier:'wireguard',
  identity_public_key:'<base64 SPKI of the vehicle key>',   # its 802.1AR IDevID / TPM / secure-element public key
  vin:'1HGCM82633A004352'
}}) YIELD op, ok, status, result, error
  RETURN op, ok, status, result, error

Over stock tools. jq builds the JSON body so the Cypher's own quotes never fight the shell:

# the public key only, the private key never leaves the vehicle's secure element
Q="CALL whisper.agents({op:'connect', args:{tier:'wireguard', \
   identity_public_key:'AAAAC3NzaC1lZDI1…SPKI', vin:'1HGCM82633A004352'}}) \
   YIELD op, ok, status, result, error RETURN op, ok, status, result, error"

curl -s https://graph.whisper.security/api/query \
  -H "X-API-Key: whisper_live_xxx" \
  -H "content-type: application/json" \
  --data "$(jq -nc --arg q "$Q" '{query:$q}')"

The response

# result carries the deterministic identity plus a ready-to-apply tunnel
address        2a04:2a01:1c0::c0de
fqdn           1c0000000000c0de.<tenant>.agents.whisper.online
ptr            1c0000000000c0de.<tenant>.agents.whisper.online
state          active                       # DNSSEC + DANE-EE (3 1 1) live at provision time
wireguard_config   [Interface] …            # source the car's traffic from its own /128

The car now has a name it can prove and an address it egresses from. Reverse DNS resolves the /128 to that identity, a TLSA record pins the leaf key, and RDAP registers the object under 2a04:2a01::/32: the full seven-proof chain, published atomically with the allocation.

Idempotency and errors

The call is deterministic and honest about conflicts: conservative in what it emits, liberal in what it accepts.

You send You get
The same device key + VIN again The same /128: idempotent, safe to retry, safe to run on every boot. No duplicate identities.
The same device key with a different VIN on your tenant 409 Conflict: a device key binds to exactly one VIN. The error.detail tells you which VIN it is already bound to.
A non-string vin (a number, an array, null) 400 with an actionable detail, never an opaque 500. Send the VIN as a string.

On the CLI: whisper create --register mints a generic agent identity, and whisper verify / whisper policy / whisper logs / whisper kill --revoke drive the rest. The vehicle-specific --vin flag is not shipped yet. Provision cars through the control-plane call above, which is live today, and drive them with the CLI verbs once allocated.

Revoke, worldwide

Decommission, resale, or a compromised ECU is one call. It's provable with the same stock tools that proved the identity existed, no Whisper software required.

# the control-plane op…
CALL whisper.agents({op:'revoke', args:{agent:'2a04:2a01:1c0::c0de'}})
# …or the CLI
whisper kill --revoke 2a04:2a01:1c0::c0de

# now prove it, worldwide, at DNS-TTL speed:
dig -x 2a04:2a01:1c0::c0de +short                       # -> nothing
curl -s https://whisper.online/verify-identity/2a04:2a01:1c0::c0de
# -> {"is_whisper_agent": false, ...}

Verify it: keyless, no account

The identity is public by design, so anyone (your PSIRT, a partner CPO, an auditor, a regulator) can check a car without your key and without taking Whisper's word for it. This is the keyless half of the two-tier surface: verify with no key, provision and govern with your key.

# no key, no account: re-derive and verify the car's identity, trustless to the IANA root
whisper verify --trustless 2a04:2a01:1c0::c0de

# or with only curl: the keyless full-chain verdict
curl -s https://whisper.online/verify-identity/2a04:2a01:1c0::c0de
# { "is_whisper_agent": true, "dane_ok": true, "jws_ok": true, "evidence": { ... } }

# the address IS the car: forward-confirmed reverse DNS names it
dig -x 2a04:2a01:1c0::c0de +short
# 1c0000000000c0de.<tenant>.agents.whisper.online.

# the registry object for the /128: RDAP, typed JSON
curl -s https://whisper.online/ip/2a04:2a01:1c0::c0de | jq '.handle, .parentHandle'
# "2A04:2A01:1C0::C0DE/128"
# "2A04:2A01::/32"

The --trustless flag is the point: nothing there calls back to Whisper's own API as an authority. The CLI re-derives the DNSSEC chain to the IANA root, on your machine, with your resolver. A regulator can verify a car outside your cloud's tenancy. Full mechanics: Verify an agent and DANE & TLSA.

Name what already got in

Identity stops the next forgery. It does not name the operator behind the sessions already in your logs. The same platform back-traces them, and the attribution survives the rotation because it fingerprints the operator's infrastructure, not the ephemeral egress IP.

Take a suspect egress IP straight from your SOC logs and ask the graph who really operates it. This runs read-only over the same public graph API, with your key:

# who really operates a host, even behind a CDN or a cloud front
curl -s https://graph.whisper.security/api/query \
  -H "X-API-Key: whisper_live_xxx" -H "content-type: application/json" \
  -d '{"query":"CALL whisper.identify(\"203.0.113.10\")"}'

The read-only verbs (identify, origins, walk, variants, history) run over that one endpoint against a live internet-infrastructure graph of fused BGP, DNS, WHOIS, TLS, hosting, and threat intelligence. Cloud rotation collapses through origins and walk, which cluster shared ASN, hosting, and certificate lineage into one infrastructure genealogy; history gives a timeline over a suspect operator. Every answer is reproducible, replayable JSON: the paper trail a GDPR or Data Act finding needs, not a screenshot.

These graph verbs are the API surface: you call the endpoint directly, as above. There is no whisper identify / graph / export CLI subcommand; the CLI covers the control plane (create, verify, policy, logs, kill), and the graph is the query API. See Graph & cognition.

Where it fits: standards, SIEM, integrations

Whisper is additive. It rides on top of the anchors you already ship and the SIEM you already run. It replaces none of them.

Compliance. The per-/128 egress logs and the attribution graph are ready-made UN R155 CSMS continuous-monitoring and forensics evidence; forge-proof per-vehicle identity and one-call revocation map to R156 SUMS and ISO/SAE 21434 TARA; findings tag to Auto-ISAC ATM tactics and techniques. And the EU Data Act (in force since 12 September 2025) multiplies the third parties calling your fleet APIs at the exact moment R155 obliges you to control them: each authorized party becomes one /128 plus DANE plus a who-accessed-what trail. The mapping lives in R155 · ISO 21434 · ATM.

Shipped vs roadmap. The Splunk, Microsoft Sentinel and OpenCTI connectors ship today; findings arrive as signed JSON mapped to CEF and ECS fields. Roadmap, labelled as such and not yet available: STIX 2.1 over TAXII export, and an Auto-ISAC ATM JSON export for machine-readable peer sharing.

Integrations (proposed, not vendor-endorsed). Whisper anchors the cloud and IP boundary, never the in-vehicle bus authenticator, the V2X air interface, or the charging plug:

And it is built to fail open: a Whisper outage never bricks a car; checks degrade to the anchors you already ship and connectivity is preserved.

Next