# Grid-API abuse, cured — the address is the inverter

> One stolen token shouldn't be able to dispatch a fleet it never owned. The address **is**
> the inverter — a routable, DNSSEC-anchored /128 bound to the `LFDI` it already carries,
> that no one can forge, so a command that can't prove the target DER's identity **fails**.
> Give every DER an identity it can prove — and an off-switch its own PKI never had.
> Additive to your DERMS, your OT sensors, and your SIEM.

The attacker mints a bearer against your aggregator's cloud API — or reuses one that's valid
across two platforms — enumerates every `LFDI` behind it, and pushes *legitimate-looking*
active-power setpoints, forced disconnects, or firmware from ordinary egress. Your IP
allowlists pass it; your DERMS can't tell it from the head-end; your logs record a
meaningless *last IP*. It works for one structural reason: your DER devices have no identity
they can prove, so the token authenticates a *claim*, never the inverter on the other end.

**We give them one.** The address **is** the inverter — a routable, DNSSEC-anchored **/128**
bound to the `LFDI` it already carries, that no one can forge, so a command that can't prove
the target DER's identity **fails** — and you can revoke it worldwide in a single call.
**Give every DER an identity it can prove — and an off-switch its own PKI never had.**

`whisper verify --trustless` — anchored at the IANA DNS root. Our own API is not in the trust path.

**The wound, in six numbers:**

- **~35k** — solar devices from 42 vendors sit exposed on the open internet
- **~195 GW** — of solar coordinated by one platform pair; a stolen OAuth/JWT token took over any account
- **<2%** — of inverters (~4.5 GW) modeled enough to force EU grid load-shedding at 49 Hz
- **~800** — PV monitors hijacked into Mirai, the first confirmed attack on PV generation
- **10+/yr** — solar-vendor vulns for 3 years — 80% high/critical, 32% CVSS 9.8–10
- **0** — revocation: IEEE 2030.5 device certs are life-long, no CRL, no OCSP

---

## The anatomy of a DER fleet takeover

**This is how a fleet you built gets dispatched by someone who never owned an inverter.**
No zero-day required. The *"many small devices, one control plane"* shape means a single
bearer token becomes authority over thousands of `EndDevice`s — the aggregator head-end and
its API used exactly as built, at fleet scale.

1. **01 · RECON — find the control plane.** An internet-exposed gateway (~35k on Shodan across 42 vendors) or, higher-value, the *vendor cloud / aggregator API* that fronts a whole fleet of DER.
2. **02 · TOKEN — mint or steal the bearer.** Hit an unauthenticated `/oauth2/token`, exploit a broken-authorization endpoint, or lift a hard-coded key. A valid OAuth/JWT bearer becomes *the* fleet authority — no per-device identity in the way.
3. **03 · ENUMERATE — one token, one fleet.** Iterate account and org IDs and the `LFDI`s of the `EndDevice`s behind them. "Many small devices, one control plane" becomes "one token, thousands of inverters."
4. **04 · COMMAND — legitimate-looking dispatch.** Push active-power setpoints, forced disconnect/reconnect, coordinated on/off, or malicious firmware. The token is valid; the egress is ordinary; the DERMS sees the head-end.
5. **05 · ROTATE — every last IP is disposable.** Hop across Amazon, Google and Azure, or a residential-proxy swarm, every few requests. Your SOC sees a fresh *last IP* and correlates nothing.
6. **06 · STRESS — swing the aggregate.** Synchronize the fleet to move real power. Research *models* `<2%` of inverters as grid-relevant — and the token stays valid until one vendor rotates it.

```
the shape — one stolen token, one control plane, a whole fleet, real power on the grid

  Stolen token ──01·02──▶  Aggregator head-end  ──03 enumerate──▶  thousands of EndDevices
  OAuth / JWT              one control plane        04 command       (one LFDI each)
  = fleet authority        /oauth2/token                                   │
                           05 · egress rotates                             │ coordinated setpoints
                           AWS·GCP·Azure + residential                     │ disconnect · firmware
                           — last IP is a decoy                            ▼
                                                              06 · Grid stress
                                                              <2% ≈ 4.5 GW → 49 Hz
                                                              (modeled, not observed)
```

The kill chain is a funnel: one stolen token, through one control plane, becomes coordinated
authority over a fleet of `EndDevice`s — and the grid-stress figure is a research *model*,
not an observed event. Every stage leans on the same missing thing: a per-device identity in
the command path.

Invisible at the network layer by design: a real operator is *one head-end to a fleet it
owns*; the abuser is *one token to a fleet it doesn't* — and every IP it ever shows you is
disposable. This is not hypothetical. A single cloud-platform pair coordinating **~195 GW**
of solar could be made to mint a token for any account and take it over; a bearer valid on
one platform proved *reusable across another*; **~800** monitoring devices were hijacked into
Mirai through one unauthenticated command-injection flaw — the first publicly confirmed
cyberattack on PV generation; and a longitudinal look at one vendor family's disclosures (the
`SUN:DOWN` class) found **10+** high/critical vulns a year for three years running. All
class-level — the pattern, not a name.

---

## Why detection always lags

**Stop detecting the abuse. Prove the identity.** 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 your aggregator — because, to your backend, it *is* one. The only
strictly-stronger move is to change what the command path trusts.

**Today · the command path trusts a bearer secret.** An OAuth/JWT token, an API key, a
session — whoever holds it can present it. That is the whole problem in one line: the
credential proves a *claim*, never *which DER* is on the other end, so a stolen one is
indistinguishable from the real head-end, and the source IP that *might* have narrowed it
down is disposable. The root cause has a name — `OWASP broken authorization / BOLA`:
authority is attached to a token, not to the machine it commands.

**Tomorrow · the command path authorizes a DER that proves itself.** Bind authority to an
identity the inverter *holds* and can demonstrate cryptographically — not a secret anyone can
copy. Now a dispatch either proves it is aimed at the DER it claims to command, or it has no
authority at all — *before* a single anomaly rule runs. Detection stops being the last line
of defense and becomes a bonus.

> **"A minted OAuth token or a leaked aggregator key passes every auth check — how do you
> catch abuse that *is* authenticated?"**
>
> You bind authority to the DER, not the bearer. A state-changing dispatch terminates
> mutually-authenticated to the *target inverter's* /128 — the device co-signs at the
> transport boundary — so a token can't reach an `LFDI` it can't cryptographically address. A
> request that passes auth but can't prove the identity never had authority in the first
> place, and `BOLA` loses its leverage: elevating to *any* account no longer reaches *any*
> inverter.

That identity already has a home on the network you run: an address. Here is how the DER's
own key becomes an address no stolen token can forge.

---

## The root-cause cure — bind the command path to the LFDI /128

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-anchored, **DANE-EE** pinned, RDAP/WHOIS-registered — re-derivable and verifiable by
anyone with `dig`. `whisper verify --trustless` checks it against the IANA root; *our own API
is not in the trust path.*

**Point it at the device.** Derive each inverter's — or each gateway's or EVSE's — /128 from
the public key behind the identifier it *already* carries: the IEEE **2030.5 LFDI** (itself a
SHA-256 of the device's X.509 cert), a SunSpec/Kyrio device cert, a secure element, or a TPM
— passing the **LFDI as the `device_id` domain separator**. The private key never leaves the
device; the address is a one-way function of its public half and that identifier. No
re-flashing the 25M+ inverters already in the field — you bind the identity they were born
with.

```
the dispatch checks the inverter, not the bearer

  DERMS head-end ──identity proven ✓──▶ ┌─────────────────────────┐ ──✓──▶ Setpoint applied
  proves the target /128                │ Dispatch / command API  │        to the real EndDevice
  mutual auth · DANE-EE                 │ authorizes on DER's /128 │        ✓ command lands
                                        │ DANE-EE 3 1 1 ·          │
  Stolen token   ──can't prove /128 ✗──▶│ transport boundary      │ ──✗──▶ Rejected
  valid bearer, no device key           │ complements 2030.5 ·     │        no identity behind token
  = claim, not a machine                │ never the bus           │        ✗ authenticates to nothing
                                        └─────────────────────────┘
                       op:revoke → the /128 is gone worldwide at DNS-TTL
```

State-changing dispatch terminates mutually-authenticated to the target DER's /128 — the
inverter co-signs at the transport boundary — so a stolen token can't reach an `LFDI` it
can't cryptographically address. One leaf key per identity; never a shared root. Whisper
anchors the device↔cloud boundary and never reaches into the IEC 62351 substation bus, the
DNP3 outstation, or the ISO 15118 charging handshake.

What becomes true the moment a DER holds one:

- **"One token → a whole fleet" becomes physically impossible.** You cannot present thousands of DER identities whose keys you don't hold. Every forgery is a DNSSEC/DANE inconsistency any verifier catches.
- **IP rotation becomes irrelevant.** Identity is not the source IP. The "last IP" was never the credential — so rotating it, across clouds or residential proxies, changes nothing.
- **Stolen tokens fail.** A minted or reused bearer with no device key behind it authenticates to nothing. The dispatch path checks the inverter, not the bearer.
- **One `revoke` kills a compromised inverter worldwide** at DNS-TTL speed: `dig -x` returns nothing; verify returns false. The revocation IEEE 2030.5's life-long certs never had.

> **"IEEE 2030.5 already gives every inverter a certificate. Why isn't that enough?"**
>
> Because it can't be revoked and no one outside the utility can verify it. The `LFDI` is a
> SHA-256 of the device's own certificate — a genuinely good key-derived name — but it lives
> in a private `SERCA→MCA→MICA` root, is allow-listed per-utility out-of-band, isn't
> addressable, and *"cannot be updated or revoked … CAs are not required to issue CRLs nor
> provide OCSP."* Whisper keeps the key-derived property and adds what it lacks: publicly
> verifiable, addressable, and revocable at DNS-TTL.

**Attaches to what you already ship — it does not replace it.** Whisper complements the
anchors you already trust — the IEEE 2030.5 / SunSpec / Kyrio device certificate and its
`LFDI`, IEC 62351 on the substation bus, OpenADR `VEN/VTN`, ISO 15118 Plug & Charge,
TPM/HSM/secure elements. It is the publicly verifiable, DNSSEC/DANE-anchored layer *on top*,
anchoring the device↔cloud boundary: no bespoke CA trust store to push to every inverter, and
revocation at DNS-TTL speed instead of a certificate that can never be revoked. You can even
**DANE-pin your existing 2030.5 head-end's certificate** to DNSSEC and cut single-CA trust
risk.

**A near-literal drop-in for the CSIP head-end you already run.** The `LFDI` is already the
primary key of the `EndDevice` resource in every CSIP / Rule 21 deployment — so binding it to
a /128 needs no new device provisioning. The same DANE pin anchors a **DERMS/VPP** head-end's
TLS, an **OpenADR 3.0** `VTN`'s HTTPS endpoint, or an **ISO 15118** `EVSE`/`SECC` offering —
where a per-device name becomes a stable, resolvable identity that a utility, an ISO, or a
peer can verify *outside* the vendor's cloud tenancy. Proposed integrations at the IP and
cloud boundary; Whisper never reaches into the closed control layer.

**The LFDI is the public fingerprint — the /128 is its cryptographic counterpart.** The
`LFDI` is a known, structured identifier flowing through every CSIP deployment; useful for
interoperability, but not a secret. The /128 is bound to the device's key *and* the `LFDI` —
so the `LFDI` alone yields nothing. You cannot go `LFDI` → /128 without the key, there is no
enumerable directory, and RDAP/reverse-DNS return the registry object, never the device's
whereabouts. Because the derivation is **tenant-bound**, the same device under two aggregators
yields two unrelated /128s — no one can link a unit across operators.

**Lifecycle, end to end — and nothing issued in the dark.** Factory key → in-life dispatch →
incident `revoke`. A module swap or repower re-keys to a new /128 and revokes the old one; a
decommission or a change of aggregator is one `revoke` and a re-register to the new owner.
Compromise one inverter and you've compromised *that inverter*, not the fleet — the takeover
failure mode is structurally removed. Every mint and every revoke lands in a public,
append-only Merkle transparency log, Ed25519-signed and Bitcoin-anchored via OpenTimestamps —
an auditable issuance trail for your regulator. *Honest status:* tamper-evident today;
independent witnessing is the next step. And you can **sign a DER's telemetry** to its /128 so
the ISO and market settlement trust the numbers came from the real device.

> **"Is a first-class `--lfdi` flag shipping today, or is this a roadmap slide?"**
>
> Shipped & live today: derive a /128 from the device's public key with its `LFDI` passed as
> `device_id` — DNSSEC + DANE-EE + RDAP, verifiable trustless from the IANA root, revocable in
> one call. A dedicated typed `--lfdi` CLI/API argument is on the roadmap; the control-plane
> call in the 60-second proof below runs as-is.

Maps to **NERC CIP-013 R1.2.3/R1.2.6** and **CIP-005 R3.1/R3.2** vendor-remote-access
controls, **EU NIS2 Art.21**, the **EU Network Code on Cybersecurity**, and **NISTIR 7628**
SG.IA/SG.AU device identification — delivered as a network primitive, not a compliance binder.
[See the compliance map →](/for-grid-security)

---

## The attribution companion — name whoever already got in

**Identity stops the next forgery. The graph names whoever already dispatched you.** You
won't re-key the whole fleet by Monday, and there is abuse in your logs right now. So the same
platform back-traces the operator behind the sessions you already logged — attribution that
*survives* the rotation, because it fingerprints the operator and the tooling, not the
ephemeral egress IP.

**The answer — the graph, not another rate-limit.** A live internet-infrastructure graph —
billions of nodes and tens of billions of relationships of fused BGP, DNS, WHOIS, TLS,
hosting and threat intelligence, answering in under 300 ms — fingerprints the *operator*, not
the IP. Two levers, kept honestly separate: for **cloud rotation** the graph clusters shared
ASN, hosting and certificate lineage into one infrastructure genealogy; for a
**residential-proxy swarm** — where a subscriber IP gives an infra graph nothing to grab — a
`JA4/JA3` client fingerprint travels with the *tooling* regardless of the exit and collapses
the swarm to one operator.

**And it's a question, not a signature.** Express fleet enumeration directly — *"one source
touching N distinct DER identities in a window"* — as read-only Cypher, and the graph returns
the operator with a reproducible evidence chain your OT SOC, your `PSIRT`, your auditors and a
regulator can replay. That's `LFDI`-enumeration and business-logic abuse caught by its *shape*
across the fleet, not by a pattern you had to know in advance.

```bash
# ask the graph the business-logic question directly — read-only Cypher (schema illustrative) over the public graph API
$ curl -s https://graph.whisper.security/api/query -H "X-API-Key: whisper_live_xxx" \
    -H 'content-type: application/json' -d '{"query":"MATCH (src)-[t:TOUCHED]->(d:DerIdentity)
    WHERE t.window = \"15m\" WITH src, count(DISTINCT d) AS lfdis
    WHERE lfdis > 50 RETURN src, lfdis ORDER BY lfdis DESC"}'
  operator <fingerprinted>   1 source → 3,412 distinct LFDIs / 15m
  egress:  AWS eu-central → GCP europe-w4 → Azure westeu   (collapsed to 1)
  ja4:     same tooling across 41 residential exits → 1 operator
  reproducible, replayable JSON evidence chain → your SIEM
```

> **"When a hijacked fleet phones home through rotating residential proxies and fresh cloud
> IPs, can you actually attribute it — or just rate-limit an IP and move on?"**
>
> Attribute it. Infrastructure genealogy collapses the cloud rotation; a JA4 client
> fingerprint collapses the residential swarm. The egress IP is the one thing we don't rely on
> — so the rotation that hides them from your OT SOC is exactly what the graph reads through.

**And before the command even lands:** `op:lookups` returns *who* resolved or RDAP-queried a
DER's identity — the `PTR`/`AAAA`/`TLSA` and RDAP accesses against its records. It's a
reconnaissance tripwire that catches an operator enumerating your fleet *in recon*, not a
post-mortem after the dispatch — the early warning the `LFDI`'s private registry never gave
you. The read-only graph verbs your analysts run — or your agent runs for them: `identify(ip)`
(who really operates a host, even behind a CDN) · `origins(prefix)` + `walk(node,depth)`
(cluster rotating IPs into one genealogy) · `history` / `watch` (a timeline and a standing
sentinel over a suspect operator). Every answer is reproducible, replayable JSON: the evidence
trail for an unauthorized-dispatch finding, not a screenshot.

Identity is the cure; the graph is how you clean up what got in before it, and catch the
operator who tries anyway. Detection made durable, on top of a root-cause fix.

---

## For the people who have to sign off

Your OT sensor sees *that* a DER is misbehaving. Whisper proves *who* commanded it — and
follows them when the IP rotates. The OT-detection incumbents are excellent at what's on your
network and whether it's behaving — necessary, and where that picture stops. Their device
identity is *observational*: inferred from behavior, scoped to the monitored network,
non-revocable off-box. Your DERMS *consumes* the manufacturer's 2030.5 certificate; it doesn't
mint identity and inherits the life-long, no-CRL/OCSP model. Whisper adds the two layers no one
else owns — exactly the two gaps the fleet-takeover attacks exploit.

| | OT detection | DERMS + 2030.5 PKI | Whisper |
|---|---|---|---|
| OT visibility & anomaly detection | ✓ | — | additive feed |
| **Publicly verifiable** device identity (DNS/DANE, not a private CA) | — | — | ✓ |
| Revocation at DNS-TTL, cross-operator | — | — | ✓ |
| Operator attribution across rotating egress | — | — | ✓ |

- **Feeds your SIEM and PSIRT.** The **Splunk** connector ships today — findings arrive as signed, replayable JSON mapped to CEF and ECS fields you can hand a regulator. **Microsoft Sentinel**, **OpenCTI**, and **STIX 2.1 over TAXII** export are *on the roadmap* — labeled honestly so you can plan against them.
- **Speaks your compliance language.** Enumerate and kill any vendor device's connection in one call — direct evidence for **CIP-005 R3.1/R3.2** (determine & disable active vendor remote access) and **CIP-013 R1.2.3/R1.2.6**. Also maps to NIS2 Art.21, the EU NCCS zero-trust/traceability, and NISTIR 7628 SG.IA/SG.AU — usable straight in an RSAW.
- **Additive & availability-safe.** It rides existing DNS/IPv6 and adds **no inline OT chokepoint**. If your head-end authorizes against the DANE/verify path, that plane is built to **fail open** — a Whisper outage never bricks an inverter; checks degrade to your existing anchors. Anycast on AS219419, no single node in the path.
- **One identity fabric, every vendor.** Derived from the key already in the device — no second PKI, no BOM cost, no re-flashing the fielded fleet. Whether it's an inverter, a battery gateway, or an `EVSE`, it's one verifiable /128 you and the ISO can both check.
- **Flat, predictable pricing.** Per-device/year and flat — not per-transaction, not usage-metered. Clear ROI: analyst-hours saved on disposable-IP correlation, one `revoke` instead of a fleet-wide reset. [See pricing →](/pricing)
- **A vendor that will still be here.** Real routable address space (AS219419), run by people who ran the internet's regional address registry and operated one of its root DNS servers. POC → pilot → enterprise, keyless to start.

[See the full comparison →](/compare)

---

## Prove it in 60 seconds — no account

Two tiers, by design. **No key:** anyone can verify a DER's identity, resolve it, and
back-trace a suspicious controller — trustless, anchored at the IANA root. **Your key:** bind
a device to the `LFDI` it carries, govern its egress, revoke it worldwide.

```bash
# keyless — re-derive and verify any inverter's identity, trustless
$ whisper verify --trustless 2a04:2a01:5e0::50c
  ✓ DNSSEC chain valid to the IANA root
  ✓ DANE-EE (TLSA 3 1 1) leaf matches the identity's key
  ✓ RDAP: registered under AS219419 · 2a04:2a01::/32
  identity: VERIFIED — and our own API was never trusted

# the address is the inverter — forward-confirmed reverse DNS names it
$ dig -x 2a04:2a01:5e0::50c +short
  lfdi-3f2504e0.der.example-vpp.whisper.online.

# who really operates a suspicious controller — the real graph API, a CALL whisper.identify()
$ 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(\"34.90.x.x\")"}'
  operator:  <fingerprinted> · seen across AWS / GCP / Azure
  residential swarm collapsed by JA4: same tooling, 41 exit IPs → 1 operator
```

```bash
# bind an inverter to the LFDI it already carries — one control-plane call
$ export WHISPER_API_KEY=whisper_live_xxx
$ curl -s https://graph.whisper.security/api/query -H "X-API-Key: $WHISPER_API_KEY" \
    -H 'content-type: application/json' --data @- <<'JSON'
{"query":"CALL whisper.agents({op:'connect', args:{tier:'wireguard',
   identity_public_key:'<base64 SPKI of the device key>',
   device_id:'3F2504E04F8911D39A0C0305E82C3301'}}) YIELD op, ok, result RETURN op, ok, result"}
JSON
  → identity 2a04:2a01:5e0::50c   DNSSEC + DANE-EE live   # device_id = the LFDI

# govern what the inverter may reach; see who enumerated it; then kill it if compromised
$ whisper policy set --default deny --allow derms.example-vpp.com,ota.example-vpp.com
$ curl -s https://whisper.online/ip/2a04:2a01:5e0::50c/lookups   # who checked this DER (recon tripwire)
$ whisper kill --revoke 2a04:2a01:5e0::50c   # worldwide, at DNS-TTL
```

[Secure your fleet →](https://console.whisper.security/sign-up) · [Read the docs](/docs)

---

## Give every DER an identity it can prove

The address is the inverter — routable, DNSSEC-anchored, bound to the `LFDI` it already
carries, revocable worldwide in one call. Keyless to try, one call to provision, one more to
revoke. The grid-API abuse that no rate-limit ever caught simply runs out of forgeries.

[Secure your fleet →](https://console.whisper.security/sign-up) · [For grid security →](/for-grid-security)

Or run `whisper verify --trustless` right now.

---

*Whisper for Energy — identity on the wire for distributed energy resources. AS219419 · 2a04:2a01::/32.
© viaGraph B.V. (dba Whisper Security).*
