DEV Community

Victor Gutierrez Areyzaga
Victor Gutierrez Areyzaga

Posted on

The Service That Stored Nothing Sensitive But Still Became High Priority

I kept noticing a mismatch between how defenders prioritize assets and how attackers actually move through environments.

The standard model goes like this: classify your assets by what they contain — customer data, credentials, financial records — and allocate your hardening and monitoring effort proportionally. Crown jewels get the most attention. A health-check service with no sensitive data gets the minimum.

The problem is that attackers don't move through environments the way this model assumes. They often do not target the most valuable asset first. They target the most useful reachable path. And that path often runs through something that received less hardening, less monitoring, or less ownership precisely because it looked unimportant on its own.

So I built a small Docker lab to test whether the scoring model was the problem — and whether switching from value-indexed to path-indexed triage changed the answer for the same asset in the same environment.

It did. Significantly.


The core distinction

Before the lab: two ways of scoring an asset.

Value-indexed triage asks: what does this asset directly contain?

A database with customer PII scores high. A status service with no stored data scores low. The score reflects the asset's direct data classification.

Path-indexed triage asks: what can this asset help an attacker reach?

A status service with no stored data but with internal reach to a privileged API scores high — not because of what it holds, but because of where it sits in the environment. Its position in the graph is what matters.

The asset I wanted to test this against is what I started calling a corridor node: a service that appears low-priority under data classification but becomes high-priority because it sits between an exposed entry point and a sensitive downstream system.


The lab topology

Five services. One corridor node. One monitoring blind spot.

[public-web]          ← internet-facing entry point
     |
     v
[status-api]          ← corridor node
     |
     v
[internal-admin-api]  ← high-value control plane
     |
     v
[customer-db]         ← sensitive asset

[log-monitor]         ← collects logs from public-web and internal-admin-api
                        does NOT collect logs from status-api
Enter fullscreen mode Exit fullscreen mode

Each service is a minimal Python/Flask application. The Docker Compose networks enforce the topology structurally — not just in application code. public-web has no direct network path to internal-admin-api or customer-db. The only route is through status-api.


The corridor node: status-api

Under value-indexed triage, status-api scores low. It stores no customer data. It holds no credentials. It owns no business-sensitive records. It is an internal health-check service — uptime, version info, basic diagnostics.

The scoring model looks at what it contains and concludes: low priority. Minimal hardening. No monitoring assigned. Not included in the threat model.

Under path-indexed triage, status-api scores high — because of what it can reach.

It was connected to internal-admin-api during a diagnostic build. Engineering needed it to relay health checks and system status. The connection made sense at the time. It was never reviewed for security posture because the service itself was classified as low-priority. Why audit a health-check service?

That decision is what makes it a corridor node.


Running the proof

One request to public-web/status triggers the full chain:

curl http://localhost:8080/status
Enter fullscreen mode Exit fullscreen mode

The response:

{
  "source": "public-web",
  "diagnostic": {
    "source": "status-api",
    "system_check": {
      "source": "internal-admin-api",
      "records": [
        {"id": "C-001", "name": "Ana Torres", "email": "a.torres@example.com",
         "account_type": "premium", "balance": 14200.0},
        {"id": "C-003", "name": "Elena Voss", "email": "e.voss@example.com",
         "account_type": "premium", "balance": 98100.5},
        {"id": "C-005", "name": "Priya Nair", "email": "p.nair@example.com",
         "account_type": "enterprise", "balance": 340000.0},
        {"...": "additional records omitted for brevity"}
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Customer records — names, emails, account types, balances — returned through a single request to the internet-facing entry point. The external client never addressed internal-admin-api or customer-db directly. The downstream access was created by the application path itself, and that path ran through the asset the value-indexed model marked as lowest priority.


Finding 1 — Triage mismatch

The side-by-side comparison:

Asset Value-indexed Path-indexed Delta
customer-db CRITICAL CRITICAL none
internal-admin-api HIGH HIGH none
public-web MEDIUM MEDIUM none
status-api LOW HIGH ← mismatch

status-api was scored LOW because it stores nothing sensitive.

status-api scores HIGH because of where it sits in the path.

The mismatch is the finding. Same asset, same environment, different scoring model, different priority, different outcome. Under value-indexed triage, status-api receives minimal hardening and no monitoring. Under path-indexed triage, it enters the threat model immediately.


Finding 2 — Detection mismatch

This is the part that surprised me most when the lab ran.

After the request completes, log-monitor reports:

[public-web] — 1 event(s) logged
  GET /status from 172.23.0.1 — triggering diagnostic workflow

[internal-admin-api] — 1 event(s) logged
  GET /system-check — fetching records from customer-db

[status-api] — NOT IN MONITORING SCOPE
!! status-api activity is not captured
!! path reconstruction from public-web to internal-admin-api is impossible
!! an attacker traversing this corridor leaves no trace here
Enter fullscreen mode Exit fullscreen mode

Two events. One at the entry point, one at the control plane. Nothing in between.

An analyst reviewing this output would see a request come in and records go out — with no explanation of how they connected. The corridor hop is invisible. The path cannot be reconstructed after the fact.

This is the second failure. The first failure was the triage model that left status-api unmonitored. The second failure is what that decision costs during an investigation.

A control can fail twice: first when it allows a path, and again when monitoring cannot explain that path after it is used.


What this does not claim

This lab is an existence proof, not a prevalence claim.

It does not argue that corridor nodes are common in all environments. It proves that an asset can be mis-prioritized when classification ignores reachable downstream impact. One environment, one corridor node, one falsifiable claim.

The lab is also intentionally unsophisticated. There is no novel exploit technique here. No clever bypass. The attacker path is almost boring — a request triggers a diagnostic workflow that relays to an internal API. The point is not what the attacker did. The point is what the scoring model failed to notice.

This lab also does not compete with enterprise attack path management platforms. Tools in established categories like attack path management, continuous threat exposure management, and application dependency mapping address versions of this problem at scale. What this lab does differently is make the reasoning visible in a reproducible five-minute proof — small enough to run locally, clear enough to argue with.


Why this matters

Security triage is a resource allocation problem. Where you spend hardening effort, monitoring instrumentation, and ownership assignment is a prioritization decision. That decision is often influenced by value-indexed thinking — the more sensitive the data, the more attention the asset gets.

Attackers don't use this model. They use path-indexed thinking. They look for the weakest reachable node, not the most valuable one. And the weakest reachable node is often one that scored low on your triage model — because that low score is exactly why it received less hardening, less monitoring, and less ownership.

The mismatch between where defenders invest and where attackers move is not a resource problem. It is a prioritization model problem.

Switching the question from "what does this asset contain?" to "what can this asset help an attacker reach?" changes which assets demand attention — and changes it for the assets that are currently getting the least.


Limitations

  • The lab models application-layer logging, not network-layer traffic analysis. A network monitoring tool with full packet capture would see the status-api hop. The detection gap demonstrates a logging coverage failure, not an absolute invisibility claim.
  • log-monitor shares a network segment with internal-admin-api for log collection purposes. The lab does not model endpoint-level authorization on the monitoring plane — the corridor finding focuses on the public-web → status-api → internal-admin-api path.
  • One corridor node in one synthetic environment is an existence proof. The framework needs real-environment validation to make prevalence claims.

The repo

The full lab is on GitHub: github.com/rodrigo-areyzaga/corridor-lab

git clone https://github.com/rodrigo-areyzaga/corridor-lab
cd corridor-lab
docker compose up --build
Enter fullscreen mode Exit fullscreen mode

In a second terminal:

python triage-report.py
Enter fullscreen mode Exit fullscreen mode

The triage report runs both scoring models, triggers the live path, and walks through the falsifiability checks. The monitoring gap can be observed through the log-monitor output. The full chain takes under a minute to run from a cold start.


A note on how this was built

This lab was developed through an AI-assisted workflow. I used Claude and ChatGPT as pair-programming and review tools throughout — for implementation, for challenging claims, and for successive review passes before publication.

I'm noting that explicitly because it matters to how you read the work. The concept, the security framing, the constraint decisions, the local testing, and every accept/reject call on what went into the final lab were human-directed. The question that produced the lab — whether switching from value-indexed to path-indexed triage changes the answer for the same asset — was the creative act. The AI tools helped make that question executable.

This is also how a lot of real work gets done. Naming it seems more useful than obscuring it.


I'm publishing this as a question as much as a finding. If you've thought about this problem — or if you think the framework is wrong — I'd genuinely like to hear it.

The harder question, which the lab doesn't answer, is how you build the reachability map in a real environment efficiently enough to be useful. That's the next problem.

Top comments (0)