Chaining: Where Real Severity Comes From
Individually-medium findings combine into a critical breach. Scanners score each in isolation and miss the chain. Here is how chaining works.
What this is
Vulnerability chaining is where two or more individually-medium findings combine into a critical breach. Each link looks unremarkable in a triage queue. The severity comes from the composition: the output of one flaw is exactly the key the next flaw needs, and the chain as a whole reaches something no single link could reach alone. Severity is emergent, which is precisely why triaging findings in isolation systematically under-rates risk.
This is the gap between what a scanner reports and what an attacker achieves. A scanner rates each finding on its own, against a fixed table, and misses the chain. A human assembles it.
Why it keeps happening
Severity scoring was built for a world of independent findings. You take a flaw, you rate its impact assuming nothing else is true, and you sort the queue. That model is convenient and it is wrong in the one situation that matters most, because real breaches are rarely one flaw. They are a sequence, and the impact of a sequence is not the maximum of its parts. It is the product.
A tool cannot reason about the sequence because it has no model of what a finding unlocks. It sees an open self-signup and marks it low, because signup is supposed to be open. It sees a missing ownership check and marks it medium, because plenty of apps have one somewhere it does not much matter. It has no way to notice that this particular signup produces the account that this particular missing check then fails to constrain. The two rows sit in the report a few lines apart, each correctly scored on its own, and the critical that they form together appears nowhere.
The consequence is a queue of mediums that a team reasonably deprioritizes in favor of the one high, while the actual critical is spread across three of the mediums nobody connected. This is not a failure of the scanner doing its job. It is the job being the wrong job.
How the attack works
The canonical shape, generic enough to teach from, is three links:
- An exposed environment variable leaks a credential or a piece of configuration. On its own, maybe a medium. It is a secret in the wrong place, but what it opens is not yet obvious.
- That credential grants cloud-storage access. The leaked variable turns out to be a key to a storage bucket. Still not a headline: it is one bucket, and buckets hold all kinds of unremarkable things.
- The bucket holds an unencrypted backup. And the backup is the whole database. Now the three innocuous links are a full data breach.
Exposed variable, plus storage access, plus unencrypted backup, equals breach. No single step is a critical. The severity is entirely in the arrows between the steps. An attacker does not experience the findings as a list. They experience them as a path, and they walk it from the first thing they can reach to the last thing worth taking.
The general move is always the same: the output of one flaw, an ID, a key, a bucket name, a valid session, becomes the input that satisfies the next flaw’s one precondition. Chaining is just noticing which findings hand each other exactly what they need.
What it looks like in the wild
We assemble these chains on nearly every engagement. Four show the shape clearly.
-
A customer-support AI startup had a social-login flow that trusted the client, which alone was a medium: an auth weakness, but bounded. Then it met sequential integer user IDs. The bypass let us act as a chosen user, and the sequential IDs meant we could choose every user in turn. Medium plus medium, and together they exposed the entire userbase. The full teardown is in The Social Login That Trusted the Client.
-
An AI lending platform allowed open self-signup, which is harmless on its own, that is what signup is. Then it met a missing ownership check on the loan-book records. The open signup produced a valid account, and the missing check failed to confine that account to its own data, so one bank could read another bank’s loan book. Neither flaw is a breach. The pair is. The full teardown is in One Signup, Another Bank’s Loan Book.
-
A contract-AI SaaS exposed an unauthenticated helper proxy, a minor-looking finding, the kind of internal convenience endpoint that reads a bit of data for the frontend. Except the proxy could be pointed at cloud metadata, and cloud metadata handed back credentials for the cloud project. A minor proxy became full cloud-project takeover, one request at a time. The full teardown is in The Helper Proxy That Owned the Cloud.
-
A healthcare startup had an open database read that, in isolation, leaked a set of identifiers. Not obviously catastrophic, until you noticed those identifiers were exactly the keys that unlocked a series of downstream endpoints that checked for a valid ID but never checked that the caller owned it. The first leak supplied the keys to every unauthenticated door behind it, and the chain unrolled from there.
The consistent theme: in each case a per-finding scanner would have produced a queue of mediums, and the actual critical lived in the space between them.
How to tell if you are exposed
A self-check aimed at the chain, not the finding:
- Read your last scan as a path, not a list. For each medium and low, ask one question: what does this give an attacker that another finding needs? An ID, a key, a session, a bucket name, an account. Draw the arrows.
- Look for the ingredients of the canonical chain. Any secret in the wrong place, any credential that grants storage access, any unencrypted backup. Each is a medium. Together they are the textbook breach.
- Test your own multi-step paths. Take a low-severity information leak and ask what the leaked information unlocks downstream. Take a weak auth finding and ask what a sequential or guessable ID would let it reach at scale.
- Assume one control fails. For each place where a single check is the only thing standing between a user and someone else’s data, ask what the impact is if that one check is bypassed. If the answer is “everything,” that control is load-bearing and the design has no depth.
How to fix it
The durable fixes are about the design and the process, not any single bug.
- Never triage findings in isolation. Model how a medium plus a medium becomes a critical. When you rate a finding, ask what it unlocks, and re-rate the pair, not just the part.
- Build defense in depth so no single control is load-bearing. Authorization on every object, not just at the front door. If one check is the only thing protecting the data, its bypass is a breach, and every check eventually gets bypassed.
- Assume an attacker will chain. Design as if the low-severity leak, the open signup, and the convenience proxy will all be found and connected, because that is what an attacker does. Close the arrows, not just the boxes.
- Use human-led testing for the assembly. A scanner scores each finding alone and is genuinely useful for coverage. It will not walk the path. The chain is assembled by a person who reasons about what each flaw hands the next, which is exactly why human-led testing beats an automated scanner that scores each finding in isolation.
The takeaway
Severity is emergent, so a queue of mediums can hide a critical that no single row reveals. Score the chain, not the finding, build depth so no one control is load-bearing, and assume the attacker will connect the dots your scanner rated separately.
Frequently asked
What is vulnerability chaining?
Why do automated scanners miss vulnerability chains?
Why does triaging findings in isolation under-rate risk?
How do you defend against exploit chains?
This is one finding from a harness that runs continuously. See how Greywatch finds, proves, and fixes them.
How it works →

