# 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](/docs/dane) 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:

```bash
# 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](/docs/verify) 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.

```bash
# 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.

```bash
# 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](/docs/verify) and [DANE & TLSA](/docs/dane).

## 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:

```bash
# 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](/docs/graph-api).

## 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](/docs/automotive-compliance).

> **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:

- **COVESA uProtocol / Eclipse SDV.** A `UUri` authority-name is, by spec, a domain name, a VIN, or an IPv6 address, so a Whisper name or `/128` slots in near-literally, with DANE anchoring the transport binding (Zenoh, SOME/IP, MQTT, or HTTP). Complements uProtocol's addressing; does not replace it.
- **AWS IoT FleetWise / IoT Core.** Your X.509 + mTLS stays; the cloud remains the CA. Whisper adds an *out-of-tenancy*, publicly verifiable identity anchored in DNSSEC / DANE, so a peer can verify a car outside that cloud. Complements IoT Core mTLS; does not replace it.
- **IEEE 802.1AR IDevID.** The cleanest bridge: derive the `/128` from the device's birth-certificate public key. The publicly verifiable, DNSSEC-anchored layer *on top* of the silicon identity you already stamp.
- **AUTOSAR Adaptive (SOME/IP, DDS).** The same DANE pin anchors a gateway's SOME/IP-over-TLS or DDS-Security endpoint certificate. Complements SecOC. Whisper never reaches into the SecOC bus authenticator.

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

- [Vehicle & ECU identity](/docs/vehicle-identity): how the `/128` is derived from the device key and VIN, in depth
- [Control plane](/docs/control-plane): the full `whisper.agents` op set the provisioning call belongs to
- [R155 · ISO 21434 · ATM](/docs/automotive-compliance): the clause-by-clause evidence mapping
