Skip to content
academia.sh

Lesson 03 / 22

Attack Lifecycle

Building the course's unit of measure: writing the eight steps by their preconditions, removing any one of five environment conditions breaking the chain with certainty, reaching 48 items if the chain runs to the end unbroken while the expected item count is 20.14, the share that reaches the end never broken (0.2384) carrying more than half of the expected total, removing an environment condition yielding a certain gain that ranges from 20.14 to 1.67 items, and placing the same control item on each of the eight steps in turn producing an early-break gain that falls from 7.05 to 0.58 items.

Published Updated

In this lesson

  • Models the attack lifecycle through each step's precondition and the condition it produces
  • Distinguishes structural breaking from probabilistic breaking and calculates the expected item cost of each
  • Derives the early-break gain as a number: how many items moving the same control one step earlier saves
Contents

Two lessons measured the chain along two separate axes: the actor’s capability determined how many steps were passed, the step’s duration determined how many windows of opportunity were born. In both, the chain itself was taken as given — eight steps, a fixed order, each step following the one before it.

The chain’s real structure is not in its ordering but in its preconditions. What a step needs, and why the next step cannot happen if that need is not met. This lesson builds the chain by its preconditions and defines the course’s unit of measure: early-break gain, the number of items saved by moving the same defense one step earlier in the chain.

What a Step Needs

The structure below is a model; how a step is carried out is not written anywhere, only what it needs and what it makes available are written. The work is done on a fictional measurement network.

TM7: environment conditions are conditions the attacker does not produce and the defense can remove. TM8: each step is reachable once its preconditions are satisfied; if a control exists, it breaks the chain there with some catch probability. TM9: a step is reachable only if all its preconditions are satisfied; the first unsatisfied step structurally cuts the chain and none of the following steps are reachable.

// threat/lifecycle.mjs — the precondition model of the attack lifecycle: there is no running attack step
// here. Every step is written by "what it needs" (precondition) and "what it makes available" (produces);
// how the step is carried out is not written. There is no randomness; the expected value is calculated.

// TM7: environment conditions are conditions the attacker does not produce and the defense can remove.
export const ENVIRONMENT = ["externally-visible-item", "open-ingress-endpoint", "local-weak-configuration",
  "flat-network", "external-egress-path"];

// TM8: each step is reachable once its preconditions are satisfied; if a control exists, it breaks the chain
// there with a `catch` probability. `items` is the cumulative fictional item count reached once the step completes.
export const STEP = [
  { name: "recon", precondition: ["externally-visible-item"], produces: "target-list",
    control: null, catch: 0, items: 0 },
  { name: "initial-access", precondition: ["target-list", "open-ingress-endpoint"], produces: "valid-credential",
    control: "identity-log", catch: 0.15, items: 1 },
  { name: "execution", precondition: ["valid-credential"], produces: "code-execution",
    control: "process-log", catch: 0.25, items: 2 },
  { name: "persistence", precondition: ["code-execution"], produces: "persists-across-restart",
    control: "startup-inventory", catch: 0.2, items: 3 },
  { name: "privilege-escalation", precondition: ["code-execution", "local-weak-configuration"],
    produces: "admin-privilege", control: "privilege-change", catch: 0.15, items: 9 },
  { name: "lateral-movement", precondition: ["admin-privilege", "flat-network"], produces: "neighbor-access",
    control: null, catch: 0, items: 26 },
  { name: "collection", precondition: ["neighbor-access"], produces: "bulk-data",
    control: null, catch: 0, items: 41 },
  { name: "exfiltration", precondition: ["bulk-data", "external-egress-path"], produces: "impact",
    control: "egress-volume", catch: 0.45, items: 48 },
];

// TM9: a step is reachable only if all its preconditions are satisfied; the first unsatisfied
// step structurally cuts the chain and none of the following steps are reachable.
export function reachable(removed = []) {
  const satisfied = new Set(ENVIRONMENT.filter((k) => !removed.includes(k)));
  let n = 0;
  for (const s of STEP) {
    if (!s.precondition.every((k) => satisfied.has(k))) break;
    satisfied.add(s.produces);
    n++;
  }
  return n;
}

// Expected items: if the chain breaks at step i, the items reached up to that point are items(i-1).
// The break probability is the product of the earlier steps' miss probabilities and that step's catch.
export function expectedItems(removed = [], extra = {}) {
  const last = reachable(removed);
  let remaining = 1, total = 0;
  for (let i = 0; i < last; i++) {
    const p = 1 - (1 - STEP[i].catch) * (1 - (extra[STEP[i].name] || 0));
    total += remaining * p * (i === 0 ? 0 : STEP[i - 1].items);
    remaining *= 1 - p;
  }
  return total + remaining * (last === 0 ? 0 : STEP[last - 1].items);
}
// threat/gain.mjs — at which link the chain can be broken, and how many items breaking early gains.
import { ENVIRONMENT, STEP, reachable, expectedItems } from "./lifecycle.mjs";

const print = (w, ...s) => console.log(s.map((v, i) =>
  (w[i] < 0 ? String(v).padEnd(-w[i]) : String(v).padStart(w[i]))).join(""));
const BASELINE = expectedItems();
const uncontrolled = STEP.filter((s) => !s.control);

console.log(`${STEP.length} steps, ${ENVIRONMENT.length} environment conditions; ` +
  `${STEP.length - uncontrolled.length} steps have a control, ${uncontrolled.length} steps ` +
  `no control sees at all (${uncontrolled.map((s) => s.name).join(", ")}); ` +
  `items reached unbroken ${STEP.at(-1).items}, expected items ${BASELINE.toFixed(2)}`);

const A = [-23, -43, -26, 20, 7, 7];
console.log("\n1. what each step needs, what it makes available");
print(A, "step", "precondition", "produces", "control", "catch", "items");
for (const s of STEP)
  print(A, s.name, s.precondition.join(" + "), s.produces, s.control || "-",
    s.control ? s.catch.toFixed(2) : "-", s.items);

const B = [-26, -23, 14, 16, 7];
console.log("\n2. structural breaking: if one environment condition is removed");
print(B, "removed condition", "impossible step", "reached step", "expected items", "gain");
for (const k of ENVIRONMENT) {
  const n = reachable([k]), e = expectedItems([k]);
  print(B, k, STEP[n].name, n, e.toFixed(2), (BASELINE - e).toFixed(2));
}

const C = [-23, 7, 14, 13, 15, 14];
console.log("\n3. probabilistic breaking: at which step the chain breaks");
print(C, "step", "catch", "reaches here", "breaks here", "items by then", "contribution");
let remaining = 1, total = 0;
for (let i = 0; i < STEP.length; i++) {
  const p = STEP[i].catch, broken = remaining * p, before = i === 0 ? 0 : STEP[i - 1].items;
  total += broken * before;
  print(C, STEP[i].name, p.toFixed(2), remaining.toFixed(4), broken.toFixed(4), before,
    (broken * before).toFixed(3));
  remaining -= broken;
}
console.log(`share reaching the end unbroken ${remaining.toFixed(4)} x ${STEP.at(-1).items} items = ` +
  `${(remaining * STEP.at(-1).items).toFixed(3)}; total expected items ` +
  `${(total + remaining * STEP.at(-1).items).toFixed(2)}`);

const D = [-23, 10, 16, 8, 18];
console.log("\n4. early-break gain: where the same control item (catch 0.35) is placed");
print(D, "placed step", "existing", "expected items", "gain", "vs previous step");
let previousGain = null;
for (const s of STEP) {
  const e = expectedItems([], { [s.name]: 0.35 }), gain = BASELINE - e;
  print(D, s.name, s.control ? s.catch.toFixed(2) : "none", e.toFixed(2), gain.toFixed(2),
    previousGain === null ? "-" : (gain - previousGain).toFixed(2));
  previousGain = gain;
}
const best = STEP.map((s) => ({ name: s.name, g: BASELINE - expectedItems([], { [s.name]: 0.35 }) }))
  .sort((x, y) => y.g - x.g);
console.log(`highest gain ${best[0].name} (${best[0].g.toFixed(2)} items), lowest ` +
  `${best.at(-1).name} (${best.at(-1).g.toFixed(2)}); best of the three uncontrolled steps is ` +
  `${best.find((x) => uncontrolled.some((s) => s.name === x.name)).name}`);
8 steps, 5 environment conditions; 5 steps have a control, 3 steps no control sees at all (recon, lateral-movement, collection); items reached unbroken 48, expected items 20.14

1. what each step needs, what it makes available
step                   precondition                               produces                               control  catch  items
recon                  externally-visible-item                    target-list                                  -      -      0
initial-access         target-list + open-ingress-endpoint        valid-credential                  identity-log   0.15      1
execution              valid-credential                           code-execution                     process-log   0.25      2
persistence            code-execution                             persists-across-restart      startup-inventory   0.20      3
privilege-escalation   code-execution + local-weak-configuration  admin-privilege               privilege-change   0.15      9
lateral-movement       admin-privilege + flat-network             neighbor-access                              -      -     26
collection             neighbor-access                            bulk-data                                    -      -     41
exfiltration           bulk-data + external-egress-path           impact                           egress-volume   0.45     48

2. structural breaking: if one environment condition is removed
removed condition         impossible step          reached step  expected items   gain
externally-visible-item   recon                               0            0.00  20.14
open-ingress-endpoint     initial-access                      1            0.00  20.14
local-weak-configuration  privilege-escalation                4            2.00  18.14
flat-network              lateral-movement                    5            4.60  15.54
external-egress-path      exfiltration                        7           18.47   1.67

3. probabilistic breaking: at which step the chain breaks
step                     catch  reaches here  breaks here  items by then  contribution
recon                     0.00        1.0000       0.0000              0         0.000
initial-access            0.15        1.0000       0.1500              0         0.000
execution                 0.25        0.8500       0.2125              1         0.212
persistence               0.20        0.6375       0.1275              2         0.255
privilege-escalation      0.15        0.5100       0.0765              3         0.229
lateral-movement          0.00        0.4335       0.0000              9         0.000
collection                0.00        0.4335       0.0000             26         0.000
exfiltration              0.45        0.4335       0.1951             41         7.998
share reaching the end unbroken 0.2384 x 48 items = 11.444; total expected items 20.14

4. early-break gain: where the same control item (catch 0.35) is placed
placed step              existing  expected items    gain  vs previous step
recon                        none           13.09    7.05                 -
initial-access               0.15           13.09    7.05              0.00
execution                    0.25           13.39    6.75             -0.30
persistence                  0.20           13.61    6.53             -0.22
privilege-escalation         0.15           13.79    6.35             -0.18
lateral-movement             none           14.70    5.44             -0.91
collection                   none           17.28    2.86             -2.58
exfiltration                 0.45           19.56    0.58             -2.28
highest gain recon (7.05 items), lowest exfiltration (0.58); best of the three uncontrolled steps is recon

The numbers are in the measurement class; their inputs are TM7–TM9.

The Precondition Column Is the Chain Itself

The first table’s second column carries this lesson. Every step is written not by what it does but by what it requires to exist. The execution step requires a valid credential; the persistence step requires code execution; lateral movement requires both admin privilege and a flat network. Which method the step is carried out with never appears here and does not need to — the only information the defense side needs for its decision is the list of requirements.

The third column closes the match: every step produces a condition, and that condition sits in the next step’s precondition. The chain is therefore not an ordering but a dependency sequence. The order cannot be broken, because every step’s input condition comes from the one before it.

Preconditions are filled from two sources. Some are conditions the chain itself produces; the rest are environment conditions — the externally-visible item, the open ingress endpoint, the local weak configuration, the flat network, and the external egress path. The attacking side produces none of these five environment conditions; all of them are in the defense side’s hands. This distinction determines the whole of the next table.

Two Separate Forms of Breaking

The chain can be broken in two ways, and the two do not resemble each other as measures.

Structural breaking removes a precondition. The second table removes each of the five environment conditions in turn and writes which step becomes impossible. If the externally-visible item is removed, the recon step is not reachable and the chain never starts; if the open ingress endpoint is removed, initial access is not reachable. In both cases the expected items are 0.00 and the gain is the full 20.14 items. Removing the local weak configuration cuts the chain after the fourth step (2.00 items), removing the flat network after the fifth step (4.60), and removing the external egress path after the seventh step — expected items 18.47 and a gain of only 1.67.

The value of the same kind of move varies by a factor of twelve depending on where in the chain it is applied. Closing the external egress path has already let 41 items be lost, because it allows collection to complete.

Probabilistic breaking, on the other hand, leaves the step reachable and waits for a control to see it. The third table works this through: the share reaching each step, the share broken there, and the items reached by that point. The five controls’ catch rates lie between 0.15 and 0.45; none of these is certain. The share reaching the end of the chain never broken is 0.2384, and it alone carries 11.44 items — more than half of the expected 20.14 items. In a chain with five controls, the single largest item is the share that never breaks at all.

The expected item count is not a guess but a sum derived from the inputs: each row’s contribution is the product of the break share at that step and the items reached by that point, and the sum of the eight rows is 20.14. The model assumes two things — that the controls are independent of one another, and that a control stops the chain the moment it sees it. Both are optimistic assumptions; in a real setup, the time that passes between a warning being seen and being responded to grows the item count further. This is why the number 20.14 is not an upper bound but a lower bound.

Three Uncontrolled Steps

The third table’s most striking rows are lateral movement and collection. Both have a catch rate of 0.00, neither ever breaks the chain — yet together they take the chain’s item count from 3 to 41. 79% of the item growth happens on the two steps no control ever looks at.

This is the result of a distribution common on the defense side. Controls get placed on steps rich in records: identity logins, process launches, privilege changes. The lateral-movement and collection steps do not give rise to a new record type on their own; they run through privileges that already exist, over paths that already exist. A control follows the noise of an event; items accumulate on the silent steps.

The recon step is the third uncontrolled step, but it sits at the opposite end: it never raises the item count at all, because nothing has been touched yet. “Uncontrolled” is the same word; its consequence is two different things in two different places.

Early-Break Gain

The fourth table defines this course’s unit of measure. There is a single control item in hand — catch rate 0.35 — and it is placed on each of the eight steps in turn. Expected items are recalculated on every row; the gain is the difference from the baseline.

The gain sequence runs: 7.05, 7.05, 6.75, 6.53, 6.35, 5.44, 2.86, 0.58. The same control saves twelve times as many items at the start of the chain as at the end. The equality of the first two rows is structural: breaking at recon or at initial access gives the same result, because in neither case has any item been touched yet.

The last column gives the cost of a one-step delay separately. Over the chain’s first five steps, this cost is small — between 0.00 and 0.30. At lateral movement it is 0.91, at collection 2.58, at exfiltration 2.28. The loss is not evenly distributed: delaying is nearly free over the first five steps, then the cost jumps fivefold to 0.91 on the sixth, reaches 2.58 on the seventh, and stays at 2.28 on the eighth. The ordering is not a coincidence; the item count also jumps from 3 to 26 at exactly that point.

Among the three uncontrolled steps, the best choice is recon (7.05), the worst is collection (2.86). For a team with a single control item in hand, the decision is written right here: placing it on the earliest of the three never-looked-at steps saves 2.5 times as many items as placing it on the latest.

Even so, the fourth table’s highest row reaches only a third of the second table’s strongest row. The best control placement saves 7.05 items; the weakest structural break saves 1.67, but the strongest saves 20.14. Removing a precondition is certain; placing a control is probabilistic. The unit of measure is the same; the order of magnitude is not.

Summary

  • The attack lifecycle is written not by how steps are carried out but by what they need; every step produces a condition, and that condition is the next step’s precondition.
  • Five preconditions are environment conditions the attacker does not produce; removing one breaks the chain with certainty, and the gain ranges from 20.14 to 1.67 items — the same move is worth twelve times as much at the start of the chain.
  • The five controls’ catch rates lie between 0.15 and 0.45; the share reaching the end of the chain never broken is 0.2384 and carries 11.44 items, that is, more than half of the expected 20.14 items.
  • Three steps are steps no control sees at all; two of them (lateral movement, collection) take the item count from 3 to 41, that is, they carry 79% of the growth.
  • Early-break gain is this course’s unit of measure: placing the same control item on each of the eight steps in turn gives a gain sequence falling from 7.05 to 0.58 items.
  • The cost of breaking one step late is under 0.30 items for the first five steps, and 0.91, 2.58, and 2.28 items after the fifth; the cost jumps at exactly the point where the item count jumps from 3 to 26.

Next Step

This lesson measured eight steps and the single control set against each. In reality, a step is not matched against a single control; it needs as many controls as the number of distinct forms in which that step can be carried out. Lateral movement is one row here, yet underneath it there are many independent techniques, and a single control sees only a portion of them.

Works that classify adversary behavior are built exactly to fill this gap: steps are arranged as columns, techniques as rows, and a control is mapped to each cell. The next lesson builds this structure and asks a single question: as the coverage percentage rises, does the number of steps with no control at all fall, or does coverage only grow on the steps that were already seen.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ ↵ Esc navigate · open · close