The short version
- A valid corporate SSO login reached the auxiliary cooling loop of Generator 3 in seven hops. Not one hop was a CVE: four were identity and trust decisions, two were network reachability, one was a protocol with no concept of authentication.
- The route lived in the seam between the identity team’s model and the OT team’s model. Both sides were covered and audited; neither could see the whole path.
- Severity came from the plant’s own derate table rather than CVSS: 43% capacity loss within eighteen minutes, which moved the finding past a queue of internet-facing medium CVEs.
- The route was cut at the OT boundary in nine days, and the finding closed only when the original path was re-run and denied three times out of three.
Map a power site properly and you do not get a report, you get a route. A standard corporate login, issued to a platform engineer who has never heard of the plant, reaching the auxiliary cooling loop of Generator 3 in seven hops. Nothing in that chain is broken. Everything in it is documented, ticketed, and owned.
The walkthrough below uses power-site-03, the reference environment we use to demonstrate Kybernao, and the finding it carries, KYB-004. The topology is synthetic. The shape is not. Identity reaching into control through a chain of individually reasonable decisions is the pattern this platform was built to surface.
The finding nobody scanned for
By every measure the site was in good shape. The historian was patched. The OT DMZ was segmented and documented. MFA was enforced on the VPN. An EDR agent ran on every Windows host, and the previous year's external penetration test had closed with no critical findings.
None of the seven hops was a CVE. Four were identity and trust decisions. Two were network reachability. One was a protocol that has no concept of authentication at all.
A vulnerability is a defect in a component. A path is a property of a system. You will not find the second by enumerating the first, no matter how good your enumeration gets.
This is the structural reason posture scores and attack paths disagree so often. A scanner asks each asset what is wrong with it. An operator needs to know what the assets can do to each other.
The route, hop by hop
Kybernao Atlas builds the terrain graph from the environment's own configuration: identity providers, cloud role assignments, firewall rule sets as exported from the running devices, switch and routing tables, controller project files, and passive OT observation. The route below is a walk through that graph, then reproduced end to end in the site's digital twin.
| Hop | Transition | What made it possible |
|---|---|---|
| 1 | Corporate SSO → cloud tenant | Standard member of eng-platform. Conditional access requires MFA at interactive sign-in, but not on token refresh or role assumption. |
| 2 | Cloud tenant → CI runner | That group inherits a role assignment on the build subscription through a nested group added fourteen months earlier for a datacenter migration, and never removed. |
| 3 | CI runner → OT jump host | The pipeline secret library holds an SSH key that is valid on the jump host. It was placed there so nightly historian backups could run unattended. |
| 4 | Jump host → historian | The jump host sits inside the OT DMZ and is permitted to the historian on 443 and 1433. The historian API returns controller tags without per-tag authorization. |
| 5 | Historian → engineering VLAN | The collector reaches back into the engineering VLAN on its data-collection port. The firewall rule is bidirectional because the vendor's installation guide asked for it. |
| 6 | Engineering VLAN → PLC-07 | Modbus/TCP 502 is permitted from the engineering VLAN, including function 16, write multiple registers. No session signing. No identity on the wire. |
| 7 | PLC-07 → cooling loop | Registers 40021–40024 are the setpoint block for the auxiliary cooling loop on Generator 3. |
Traversal time in the twin, from first authentication to a write landing on the register block: six minutes and forty seconds. No malware. No exploit. Native tooling at every step: a browser, a pipeline, an SSH client, a vendor API, and a Modbus library that ships in the standard library of two languages.
Scope note
Hops 1 through 5 are the kind that can be proven against a live environment at Level 2, one authorized action at a time. Hops 6 and 7, anything that puts a write in front of a controller, stay in the twin and never touch live equipment. That distinction is the subject of a separate article on execution levels.
Why seven correct decisions produce one wrong outcome
Every hop had an owner, a ticket, and a defensible reason at the time it was made. The nested group was a migration convenience with a note on it. The SSH key was an availability decision made after a backup job failed over a long weekend. The bidirectional rule came straight out of the vendor's install guide. The Modbus permit is how the engineers do their jobs.
No single owner could see hop 3 and hop 6 at the same time. The identity team's model of the world ends at the tenant boundary. The OT team's model begins at the DMZ. The path lives in the space between them, and that space is not a gap. Both sides are covered, audited, and confident. It is a seam: an overlap where two correct models disagree about what the other one guarantees.
Seams are where routes hide, and they are systematically invisible to programmes organized by domain. The only way we know to find them is to hold identity, cloud, network, control, and physical consequence in one graph and ask reachability questions across all of them at once.
Consequence is the severity
The interesting question about hop 7 is not the protocol. It is what the register block does.
Atlas carries a consequence edge from controllers to the physical functions they drive, populated from the site's own engineering documents. PLC-07 drives the auxiliary cooling loop for Generator 3. The derate table, which is a document the plant already owns rather than something Kybernao models, puts loss of auxiliary cooling at a 43% capacity reduction and a protective trip not long behind it under summer ambient conditions.
So the finding's severity is not "High, because Modbus is unauthenticated." It is "43% of 340 MW, during the week of the year when the reserve margin is thinnest." That framing is what moves an item. A consequence stated in megawatts does not need a scoring argument to get ahead of a queue of internet-facing medium CVEs.
What we changed, in the order that cut the route fastest
A seven-hop path does not need seven fixes. It needs the cheapest cut that holds, then the structural ones behind it.
- Cut hop 6 first, in days rather than weeks. A Guardian rule at the OT boundary denying unsigned Modbus writes originating from corporate address space, with signed-session enforcement required for the writes that remain legitimate.
- Then hop 3, over a few weeks. Key rotated, the backup job inverted to a pull model under a scoped service account, and the pipeline secret removed entirely.
- Then hop 2, a change-window item. The nested group unwound, and step-up authentication required on role assumption rather than only at sign-in.
- Hop 4 last, and it may never close on your schedule. Per-tag authorization in the historian is a vendor roadmap item. It is tracked, it is not blocking, and the route is already cut in two places ahead of it.
The boundary rule goes first because a controller like PLC-07 cannot be touched outside a maintenance window that may be months away, and because it has no authentication primitive to enable in the first place. You defend where you can act this week, and you write down where you could not.
rule block-unsigned-modbus-write
match proto=modbus fn=16 src=corp-vlan
dst plc-07 // aux cooling, Generator 3
- action allow
+ action deny log=guardian
+ require signed-session
verify twin=replay-set-004The rule was validated against the twin with the same replay set that reproduced the original path, before it was proposed to the operator. Guardian does not open a containment plan the twin has not already accepted.
The retest is the deliverable
A finding is not closed when a ticket is marked done. It is closed when the original route stops working, proven by running it again.
[09:42:31] Initiating retest for KYB-004
[09:42:31] Target: modbus://plc-07.ot.power-site-03 fn=16
[09:42:32] Attempt 1/3 unsigned write -> DENIED
[09:42:32] Attempt 2/3 replayed session -> DENIED
[09:42:33] Attempt 3/3 forged HMI origin -> DENIED
[09:42:33] All write vectors blocked. Sentinel observed 3/3.Two details matter more than the denials. The first is that Sentinel saw all three attempts: a control that blocks silently is a control you will not notice failing. The second is that the retest is scheduled to run again after the next change window, because the fix lives in a configuration file that people edit for good reasons. Every route we close is a claim with an expiry date.
Five questions for your own environment
You do not need our platform to start on these. You need somebody willing to hold two domains in their head at once for an afternoon.
- Which identities outside the OT trust boundary can, through any chain of group memberships or role assumptions, obtain a credential that is valid inside it?
- Which of your firewall rules are bidirectional because an installer asked, rather than because traffic needs to flow both ways?
- For every controller that drives a process with a derate or trip consequence, what is the shortest route from a corporate credential, and who has ever drawn it?
- When a finding is closed, who reruns the original route, and where is that result written down?
- If the WAN were down right now, which of the controls along that route would still enforce?
Mapping the terrain is the fast part, and the fixes are rarely the slow part either. What takes longest is getting the identity team and the control team to look at one picture together, which, in the end, is the entire product.