open atlas
↑ Back to track
Security Foundations SECF · 01 · 04

Attack trees and abuse cases: thinking like an attacker

Attack trees decompose one attacker goal into AND/OR sub-goals down to costed leaf attacks, and abuse cases write the evil user story beside the real one. Goal-centric where STRIDE is element-centric — reach for trees when you need to reason about the cheapest path.

SECF Middle ◷ 16 min
Level
FoundationsJuniorMiddleSenior

A security engineer drops a STRIDE table on the table: thirty rows, one per data-flow element, every threat category dutifully filled in. The room nods. Then someone asks the only question that matters — “so what’s the cheapest way an attacker actually steals a logged-in session?” — and the table goes quiet. STRIDE told you which elements could be attacked; it never connected them into a path with a price tag. The attacker doesn’t care about your element list. They have one goal, and they will walk whichever branch is cheapest. That branch — the one your element-by-element review never assembled — is an attack tree.

By the end of this lesson you’ll be able to decompose an attacker’s goal into an AND/OR attack tree, cost its leaves to find the cheapest path, and write abuse cases that bake the attacker’s intent into the requirements alongside the user story.

The attacker has a goal, not a checklist

STRIDE (the spoofing/tampering/repudiation/info-disclosure/DoS/elevation taxonomy you walk against a DFD) is element-centric: you stand at each box and arrow in the data-flow diagram and ask “what could go wrong here.” It is exhaustive and great for coverage — but it fragments the threat. The attacker never thinks in elements. They think in goals: “I want a valid admin session,” “I want to read another tenant’s data,” “I want to bypass payment.” An attack tree is the goal-centric complement: you write the attacker’s goal at the root and decompose downward until you reach concrete leaf attacks you can actually cost.

Bruce Schneier formalized the structure in 1999. The root node is the attacker’s objective. Every node below it is a sub-goal — a thing that, if achieved, contributes to its parent. You keep decomposing until the leaves are atomic actions: “phish the credential,” “replay a leaked token,” “exploit a known CVE in the auth library.” The value isn’t the diagram; it’s that once the tree exists, you can annotate every leaf and let the structure compute which whole-path attacks are feasible.

Reading AND vs OR (this is the whole game)

Every interior node is either an OR node or an AND node, and confusing them inverts your defense priorities.

  • An OR node means any one child achieves the parent. The sub-goals are alternative routes. To reach the parent, the attacker picks the cheapest, most feasible child — so the cost of an OR node is the minimum over its children. OR nodes are where attackers shop for the easy path.
  • An AND node means all children must be achieved together. The cost of an AND node is the sum of its children, and its feasibility is gated by the hardest child. AND nodes are where your defenses multiply — adding one more required step (a second factor, a signed nonce) raises the whole subtree’s cost.

This is the senior reflex: defenders want to turn ORs into ANDs. If “steal a session” currently has three cheap OR branches, you have three independent ways to lose. If you can force every path to also require something expensive — say, a device-bound credential the attacker can’t replay — you have converted alternatives into a conjunction, and the attacker now pays for the union, not the minimum.

Cost and feasibility on leaves can be anything you can reason about consistently: dollar cost, skill required, time, detectability, or a simple boolean “possible / impossible.” Even a coarse “cheap / moderate / expensive” labeling is enough to make the cheapest path jump out, and the cheapest feasible path is exactly the one you fix first.

A worked tree: “steal an authenticated session”

Walk the tree above as an attacker would, bottom-up. The root goal is obtain a valid authenticated session for a victim. It’s an OR — three independent ways to win:

  1. Steal the session token. Itself an OR: exfiltrate it via an XSS bug (token in localStorage, readable by any script), scrape it from a log or referrer header where it leaked, or pull it off a compromised device. Each leaf is cheap-to-moderate and single-step. This branch is usually the minimum, which is why it’s usually how real session theft happens.
  2. Hijack a live session. Ride an already-authenticated connection — network-position attacks, or a predictable session id you can guess. Moderate cost, often needs a privileged network position, so frequently more expensive than branch 1.
  3. Session fixation. An AND node: the attacker must both plant a session id they know and get the victim to authenticate under it. Two required steps means summed cost and the harder child (getting the victim to log in on your seeded session) gates feasibility — strictly more expensive than the one-step token theft.

Now read the structure: the attacker’s effective cost is the minimum over the OR root, and that minimum is almost always the cheapest leaf in branch 1. So your first fix targets that leaf — kill the XSS surface and stop putting tokens where script can read them. But notice what a single control does to the whole tree: bind the session to the device (a credential that can’t be replayed off-device) and you have raised the cost of every branch at once, because stolen-token-replay, hijack, and fixation all assumed a bearer token that works anywhere. That is the power of reading the tree top-down: one control placed at the root assumption beats three patches at the leaves.

Why this works

Why bother costing leaves at all, instead of just listing attacks? Because security is a budget problem, not a completeness problem. A list of twenty attacks tells you nothing about order; a tree with even rough cost labels tells you the attacker’s actual path is the single cheapest feasible leaf — and that your scarce remediation time belongs there, not on the scary-sounding-but-expensive attack nobody will bother with. Costing is what turns a threat catalog into a prioritized work queue. Get the labels roughly right and the ranking is robust even when the exact numbers are guesses.

Abuse cases: the evil user story, written beside the real one

Attack trees are an analysis tool. Abuse cases (and their close sibling, misuse cases) are a requirements tool — they get the attacker into the conversation while you’re still writing the feature, not after it ships.

The idea is disarmingly simple. For every user story you write, write the inverted story from the attacker’s point of view. The user story says “As a customer, I want to reset my password by email so I can regain access.” The abuse case says “As an attacker, I want to trigger password resets for arbitrary accounts and harvest the reset tokens, so I can take over accounts.” Now the requirement is no longer just “build password reset” — it’s “build password reset such that an attacker can’t enumerate accounts, can’t brute-force the token, and can’t flood a victim’s inbox.” The control (rate limiting, single-use high-entropy tokens, neutral responses that don’t reveal whether an account exists) becomes an acceptance criterion, not an afterthought.

A misuse case is the formal UML cousin: on the same diagram as your use cases, you draw the misuser and their misuse cases, linked to the use cases they threaten and the security use cases that mitigate them. The distinction is mostly notation — both encode the same move: name the hostile actor and their goal as a first-class artifact alongside the friendly one. The payoff is cultural as much as technical. A user story with a paired abuse case can’t quietly ship without someone deciding what to do about the abuse — the evil twin is right there in the backlog, demanding an answer.

When to reach for trees vs STRIDE vs abuse cases

These are complements, not competitors, and a senior picks by the question being asked:

  • Reach for STRIDE when you need coverage — you have a DFD and you want to be sure no element’s spoofing/tampering/etc. went unconsidered. It’s a breadth-first sweep of the system’s surface.
  • Reach for an attack tree when you need to reason about a specific goal and the cheapest path to it — “how does someone actually get admin?” — and especially when you want to prioritize defenses by cost or prove a control raises the attacker’s price across many paths.
  • Reach for abuse / misuse cases when you’re at the requirements stage and want the threat baked into acceptance criteria, so the control ships with the feature instead of being retrofitted after the pentest.

In practice they chain: STRIDE finds the threats element-by-element, an abuse case turns the scary ones into requirements, and an attack tree is what you draw when one goal (session theft, payment bypass) is important enough to deserve a costed, path-level analysis.

Pick the best fit

Your team has a finished data-flow diagram and wants to be confident no element's possible threats (spoofing, tampering, info disclosure, etc.) were overlooked before the next release. Which technique fits this question best?

Quiz

An interior node in an attack tree is an OR node with three children whose costs are cheap, moderate, and expensive. What is the cost the attacker effectively pays to achieve that node, and why?

Quiz

You're writing the user story 'As a customer, I want to reset my password by email.' What does pairing it with an abuse case actually buy you?

Order the steps

Order the steps of building an attack tree, from starting point to the result you act on:

  1. 1 Write the attacker's goal as the root node
  2. 2 Decompose it into OR/AND sub-goals
  3. 3 Keep decomposing until leaves are concrete, atomic attacks
  4. 4 Annotate each leaf with cost / feasibility
  5. 5 Read the minimum-cost feasible path and fix it first
Recall before you leave
  1. 01
    Explain the difference between an OR node and an AND node in an attack tree, how each is costed, and what 'defenders turn ORs into ANDs' means.
  2. 02
    What is an abuse case, how does it differ from a misuse case, and when do you reach for abuse cases versus an attack tree versus STRIDE?
Recap

An attack tree is the goal-centric complement to element-centric STRIDE: you root the attacker’s objective (“steal an authenticated session”), decompose it into sub-goals, and keep going until the leaves are concrete, costable attacks. Interior nodes are OR (any one child wins — cost is the minimum, so attackers shop the cheapest path) or AND (all children required — cost is the sum, gated by the hardest child). Annotating leaves with even rough cost makes the cheapest feasible path pop out — that’s what you fix first — and the strategic move is to add a control that every branch must also satisfy, turning ORs into ANDs and raising the price of the whole goal. Abuse cases bring the attacker into the requirements stage: write the evil user story beside the real one so controls become acceptance criteria, with misuse cases as the formal UML notation of the same move. Pick by question: STRIDE for breadth-first coverage of a DFD, an attack tree for the cheapest path to one goal, abuse cases to bake the threat into the spec. Next time you scope a sensitive feature, ask not “which elements could be attacked” but “what’s the attacker’s goal, and what’s the cheapest leaf that gets them there?”

Practice

Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.

recallapplystretch0 of 6 done
Connected lessons

Something unclear?

Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.

Apply this

Put this lesson to work on a real build.

shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.