Lesson 12 / 16
Persistence Mechanisms
Ten persistence point types are counted as an inventory and six controls run across four endpoint classes: how many types each control closes, how many it only makes visible, which control cannot be applied in which endpoint class; the number of types closed in all four classes is zero, the number of types no control touches is one, and the cost of closing is 36 blocked legitimate items plus 52 manual-review items.
Published Updated
In this lesson
- Counts persistence point types as an inventory and separates the types each control closes from the types it makes visible
- Measures controls' applicability by endpoint class and derives the fleet average
- Derives three columns for each endpoint class: closed, only visible, and points no control touches
Contents
The previous lesson measured runs and showed that a run is transient: it ends and leaves only a record behind. What persists is the item that run writes on the endpoint. The topic therefore returns to where it began — the first lesson counted the points the types used; this lesson counts those points as an inventory on the endpoint and runs six controls across them.
The three columns measured are the course’s three columns. How many point types there are; how many each control closes, how many it only makes visible; what closing costs in legitimate work. A fourth distinction is added to this: a control cannot be applied in every endpoint class. No persistence-establishment method was written in this lesson; what is counted is the point itself and the control over it.
- EP31. Ten point types, legitimate item counts, six controls, and their applicability belong
to a fictional regional measurement network. What is measured is the model built with
node. - EP32. A control’s effect on a point type is one of three values: closes, makes visible, does not touch. Partial closing was not modeled; this makes the closed column look better than it is.
- EP33. Applicability is at the endpoint class level. In reality, some endpoints in the same class do not carry the control; drift measurement was the previous topic’s subject and is taken here as input.
- EP34. Cost was counted with two items: blocked legitimate items and work that falls to manual review. Ongoing maintenance load was not counted; had it been, the cost of visibility-granting controls would have grown.
- EP35. A control that closes a point can also log the same point; in the model, closing is counted first. The two columns are therefore disjoint: a type is either closed or only visible.
Inventory and Six Controls
// inventory.mjs — persistence point types, endpoint classes, and control effects; model // point type -> legitimate item count on one endpoint export const LEGIT = { 'startup-entry': 14, 'service-definition': 63, 'scheduled-task': 41, 'login-script': 6, 'load-path-hook': 3, 'event-subscription': 2, 'legitimate-binary-attach': 0, 'media-autorun': 1, 'application-extension': 27, 'boot-entry': 4, }; export const POINT = Object.keys(LEGIT); export const ENDPOINTS = { 'field laptop': 120, 'branch endpoint': 240, 'aggregator': 60, 'central server': 12 }; export const CLASS = Object.keys(ENDPOINTS); // control -> [effect map, applicable endpoint classes, blocked legitimate items, work that falls to manual review] export const CONTROL = { 'write-permission restriction': [ { 'login-script': 'closes', 'application-extension': 'closes', 'startup-entry': 'closes' }, ['branch endpoint', 'aggregator', 'central server'], 8, 4], 'application allowlist': [ { 'startup-entry': 'closes', 'service-definition': 'closes', 'scheduled-task': 'closes', 'media-autorun': 'closes', 'login-script': 'visible' }, ['branch endpoint', 'aggregator'], 22, 31], 'signed code requirement': [ { 'legitimate-binary-attach': 'closes', 'load-path-hook': 'closes', 'application-extension': 'visible' }, ['field laptop', 'branch endpoint', 'aggregator'], 6, 3], 'secure boot chain': [ { 'boot-entry': 'closes' }, ['field laptop', 'branch endpoint', 'central server'], 0, 2], 'baseline comparison': [ { 'startup-entry': 'visible', 'service-definition': 'visible', 'scheduled-task': 'visible', 'login-script': 'visible', 'load-path-hook': 'visible', 'application-extension': 'visible', 'media-autorun': 'visible' }, ['field laptop', 'branch endpoint', 'aggregator', 'central server'], 0, 12], 'process creation log': [ { 'startup-entry': 'visible', 'service-definition': 'visible', 'scheduled-task': 'visible', 'login-script': 'visible' }, ['field laptop', 'branch endpoint', 'aggregator', 'central server'], 0, 0], }; export const CONTROL_NAME = Object.keys(CONTROL); export const applies = (d, s) => CONTROL[d][1].includes(s); export function status(type, cls) { const effects = CONTROL_NAME.filter((d) => applies(d, cls)).map((d) => CONTROL[d][0][type]).filter(Boolean); return effects.includes('closes') ? 'closed' : effects.includes('visible') ? 'visible' : 'dark'; } export const size = (s) => ENDPOINTS[s]; export const TOTAL_ENDPOINTS = Object.values(ENDPOINTS).reduce((a, b) => a + b, 0);
// run1.mjs — point type inventory and the types each control closes / makes visible import { LEGIT, POINT, CONTROL, CONTROL_NAME, CLASS, TOTAL_ENDPOINTS } from './inventory.mjs'; const items = Object.values(LEGIT).reduce((a, b) => a + b, 0); console.log(`${POINT.length} persistence point types, ${items} legitimate items per endpoint, ${TOTAL_ENDPOINTS} endpoints\n`); console.log(`${'point type'.padEnd(26)} ${'legitimate items'.padStart(17)} ${'closing controls'.padStart(16)} ${'visible controls'.padStart(16)}`); for (const t of POINT) { const k = CONTROL_NAME.filter((d) => CONTROL[d][0][t] === 'closes'), g = CONTROL_NAME.filter((d) => CONTROL[d][0][t] === 'visible'); console.log(`${t.padEnd(26)} ${String(LEGIT[t]).padStart(17)} ${String(k.length).padStart(16)} ${String(g.length).padStart(16)}`); } console.log(`\n${'control'.padEnd(30)} ${'closes'.padStart(7)} ${'makes visible'.padStart(13)} ${'endpoint classes'.padStart(16)} ` + `${'blocked items'.padStart(16)} ${'manual review'.padStart(13)}`); for (const d of CONTROL_NAME) { const e = CONTROL[d][0]; console.log(`${d.padEnd(30)} ${String(Object.values(e).filter((x) => x === 'closes').length).padStart(7)} ` + `${String(Object.values(e).filter((x) => x === 'visible').length).padStart(13)} ` + `${`${CONTROL[d][1].length}/${CLASS.length}`.padStart(16)} ${String(CONTROL[d][2]).padStart(16)} ${String(CONTROL[d][3]).padStart(13)}`); } const none = POINT.filter((t) => !CONTROL_NAME.some((d) => CONTROL[d][0][t])); console.log(`\npoint types no control touches: ${none.length}/${POINT.length} (${none.join(', ') || '-'})`); console.log(`total blocked legitimate items: ${CONTROL_NAME.reduce((a, d) => a + CONTROL[d][2], 0)}; ` + `work that falls to manual review: ${CONTROL_NAME.reduce((a, d) => a + CONTROL[d][3], 0)}`);
10 persistence point types, 161 legitimate items per endpoint, 432 endpoints point type legitimate items closing controls visible controls startup-entry 14 2 2 service-definition 63 1 2 scheduled-task 41 1 2 login-script 6 1 3 load-path-hook 3 1 1 event-subscription 2 0 0 legitimate-binary-attach 0 1 0 media-autorun 1 1 1 application-extension 27 1 2 boot-entry 4 1 0 control closes makes visible endpoint classes blocked items manual review write-permission restriction 3 0 3/4 8 4 application allowlist 4 1 2/4 22 31 signed code requirement 2 1 3/4 6 3 secure boot chain 1 0 3/4 0 2 baseline comparison 0 7 4/4 0 12 process creation log 0 4 4/4 0 0 point types no control touches: 1/10 (event-subscription) total blocked legitimate items: 36; work that falls to manual review: 52
The lower table separates the two control families with a clean line. The four closing controls close ten point types in total and block thirty-six legitimate items in return. The two visibility controls close no type, make eleven types visible, and block not a single legitimate item. Their costs sit in different places: the closing controls’ cost is in blocked items, the visibility controls’ is in work that falls to manual review — and twelve of the total fifty-two-item manual-review load comes from a single visibility control.
The applicability column supplies the third distinction, and the closing controls are all weak here. Two controls are applicable in all four endpoint classes, and both of them give only visibility. None of the closing controls is applicable in all four classes; the narrowest, application allowlist, is applicable in only two. There is a tension between closing and spreading: the more a control closes, the fewer endpoints it can be applied in.
Two rows in the upper table also stand out. No control touches the persistent event subscription — neither a closing one nor a visibility one. One control closes the boot entry, but none makes it visible: on an endpoint where closing cannot be applied, that type stays entirely dark. The two most crowded point types, service definition and scheduled task, are each closed by one control and made visible by two.
Three Columns by Endpoint Class
// run2.mjs — closed / only-visible / dark point type count per endpoint class import { POINT, LEGIT, CLASS, CONTROL_NAME, CONTROL, status, size, TOTAL_ENDPOINTS } from './inventory.mjs'; console.log(`${'point type'.padEnd(26)} ${CLASS.map((s) => s.padStart(16)).join(' ')}`); for (const t of POINT) console.log(`${t.padEnd(26)} ${CLASS.map((s) => status(t, s).padStart(16)).join(' ')}`); console.log(`\n${'endpoint class'.padEnd(15)} ${'endpoints'.padStart(9)} ${'applied controls'.padStart(17)} ${'closed'.padStart(6)} ` + `${'only visible'.padStart(14)} ${'dark'.padStart(8)} ${'dark items'.padStart(14)}`); const count = (s, d) => POINT.filter((t) => status(t, s) === d); for (const s of CLASS) console.log(`${s.padEnd(15)} ${String(size(s)).padStart(9)} ${String(CONTROL_NAME.filter((d) => CONTROL[d][1].includes(s)).length).padStart(17)} ` + `${String(count(s, 'closed').length).padStart(6)} ${String(count(s, 'visible').length).padStart(14)} ` + `${String(count(s, 'dark').length).padStart(8)} ${String(count(s, 'dark').reduce((a, t) => a + LEGIT[t], 0)).padStart(14)}`); const weight = (d) => CLASS.reduce((a, s) => a + size(s) * count(s, d).length, 0) / TOTAL_ENDPOINTS; console.log(`${'fleet average'.padEnd(15)} ${String(TOTAL_ENDPOINTS).padStart(9)} ${''.padStart(17)} ` + `${weight('closed').toFixed(1).padStart(6)} ${weight('visible').toFixed(1).padStart(14)} ${weight('dark').toFixed(1).padStart(8)}`); const everywhere = POINT.filter((t) => CLASS.every((s) => status(t, s) === 'closed')); const neverClosed = POINT.filter((t) => CLASS.every((s) => status(t, s) !== 'closed')); console.log(`\npoint types closed in all four endpoint classes: ${everywhere.length}/${POINT.length} (${everywhere.join(', ') || '-'})`); console.log(`point types closed in no endpoint class : ${neverClosed.length}/${POINT.length} (${neverClosed.join(', ')})`); const darkest = CLASS.map((s) => [s, count(s, 'dark').length]).sort((a, b) => b[1] - a[1])[0]; console.log(`endpoint class carrying the most dark point types : ${darkest[0]} (${darkest[1]} types, ${size(darkest[0])} endpoints)`);
point type field laptop branch endpoint aggregator central server startup-entry visible closed closed closed service-definition visible closed closed visible scheduled-task visible closed closed visible login-script visible closed closed closed load-path-hook closed closed closed visible event-subscription dark dark dark dark legitimate-binary-attach closed closed closed dark media-autorun visible closed closed visible application-extension visible closed closed closed boot-entry closed closed dark closed endpoint class endpoints applied controls closed only visible dark dark items field laptop 120 4 3 6 1 2 branch endpoint 240 6 9 0 1 2 aggregator 60 5 8 0 2 6 central server 12 4 4 4 2 2 fleet average 432 7.1 1.8 1.2 point types closed in all four endpoint classes: 0/10 (-) point types closed in no endpoint class : 1/10 (event-subscription) endpoint class carrying the most dark point types : aggregator (2 types, 60 endpoints)
Zero is this table’s most important number. None of the ten point types is closed in all four endpoint classes. The fleet average is seven point one closed, one point eight only-visible, and one point two dark types; but the average summarizes this table wrongly, because the distribution splits sharply by class. Nine of the ten types are closed on branch endpoints, three on field laptops. The same inventory, the same control list, different applicability.
The field laptop column shows where closing gets stuck. Four controls are applicable, and six point types stay only visible. On these endpoints, the diversity of legitimate work makes application allowlist and write-permission restriction inapplicable; in return, these endpoints log the six types that cannot be closed. The measurement here gives a consequence, not a choice: where closing cannot be applied, the only thing left is visibility, and on these endpoints visibility carries all six types.
The dark column, in turn, counts the types that are neither closed nor made visible. In type count, two classes run neck and neck — aggregator and central server, both with two types — but the legitimate items left in the dark are highest at the aggregator. Two types are dark at aggregators: the persistent event subscription and the boot entry. The reason for the second is hardware — the secure boot chain is not applicable in this class, and no control makes that type visible. Six legitimate items sit in these two types, and no control looks at them. Central servers also have two dark types; that looks like a small number for twelve endpoints, but these twelve endpoints carry the most valuable data the course measures.
Cost at Fleet Scale
Cost counted per endpoint does not carry a decision; the number that carries the decision is that cost multiplied by the number of endpoints the control is applied on. The same model can also be run at fleet scale.
// run3.mjs — fleet-scale cost: covered endpoints, blocked items, and review load per control import { POINT, LEGIT, CLASS, CONTROL_NAME, CONTROL, status, size, TOTAL_ENDPOINTS } from './inventory.mjs'; console.log(`${'control'.padEnd(30)} ${'covered endpoints'.padStart(17)} ${'blocked items'.padStart(16)} ` + `${'review items'.padStart(12)} ${'closed pairs'.padStart(18)}`); let [blocked, review] = [0, 0]; for (const d of CONTROL_NAME) { const n = CONTROL[d][1].reduce((a, s) => a + size(s), 0); const closed = Object.entries(CONTROL[d][0]).filter(([, v]) => v === 'closes').length; blocked += n * CONTROL[d][2]; review += n * CONTROL[d][3]; console.log(`${d.padEnd(30)} ${String(n).padStart(17)} ${String(n * CONTROL[d][2]).padStart(16)} ` + `${String(n * CONTROL[d][3]).padStart(12)} ${String(n * closed).padStart(18)}`); } console.log(`${'total'.padEnd(30)} ${String(TOTAL_ENDPOINTS).padStart(17)} ${String(blocked).padStart(16)} ${String(review).padStart(12)}`); const pair = (d) => CLASS.reduce((a, s) => a + size(s) * POINT.filter((t) => status(t, s) === d).length, 0); const itemPair = (d) => CLASS.reduce((a, s) => a + size(s) * POINT.filter((t) => status(t, s) === d).reduce((x, t) => x + LEGIT[t], 0), 0); console.log(`\n${'column'.padEnd(16)} ${'point-endpoint pairs'.padStart(21)} ${'share'.padStart(6)} ${'legitimate items'.padStart(17)}`); const totalPairs = TOTAL_ENDPOINTS * POINT.length; for (const d of ['closed', 'visible', 'dark']) console.log(`${d.padEnd(16)} ${String(pair(d)).padStart(21)} ${`${(pair(d) * 100 / totalPairs).toFixed(1)}%`.padStart(6)} ` + `${String(itemPair(d)).padStart(17)}`); console.log(`${'total'.padEnd(16)} ${String(totalPairs).padStart(21)}`);
control covered endpoints blocked items review items closed pairs write-permission restriction 312 2496 1248 936 application allowlist 300 6600 9300 1200 signed code requirement 420 2520 1260 840 secure boot chain 372 0 744 372 baseline comparison 432 0 5184 0 process creation log 432 0 0 0 total 432 11616 17736 column point-endpoint pairs share legitimate items closed 3048 70.6% 48912 visible 768 17.8% 19536 dark 504 11.7% 1104 total 4320
The ranking changes at fleet scale. Application allowlist was the control that closed the most types per endpoint; at fleet scale it is also the most expensive: six thousand six hundred blocked items and nine thousand three hundred review items. Secure boot chain, by contrast, closes three hundred seventy-two point-endpoint pairs with zero blocked items. The difference between the two controls is the legitimate-item density of the point type they close: one touches the most crowded points on the endpoint, the other touches a point that is nearly empty.
The lower table gives the topic’s closing number. Four hundred thirty-two endpoints and ten point types make four thousand three hundred twenty point-endpoint pairs; of these, seventy point six percent are closed, seventeen point eight percent are only visible, and eleven point seven percent are dark. The legitimate items carried by the dark pairs are only eleven hundred four — meaning the points left in the dark are the sparsest points on the endpoint. This can read as good news, but the reverse is also true: a sparse point is the easiest point to notice when an item is added to it — if a record is watching. The definition of the dark column is exactly the absence of such a record.
Summary
- 10 persistence point types hold 161 legitimate items per endpoint. 4 closing controls close 10 types in total and block 36 legitimate items; 2 visibility controls close no type, make 11 types visible, and block 0 items.
- None of the closing controls is applicable in all 4 of the 4 endpoint classes; the two controls applicable in all four classes give only visibility. The more a control closes, the fewer endpoints it applies to.
- The number of point types closed in all 4 of the 4 endpoint classes is 0/10. 9 types are closed on branch endpoints, 3 on field laptops; the same inventory, different applicability.
- No control touches the persistent event subscription: neither a closing one nor a visibility one. One control closes the boot entry, none makes it visible — on an endpoint where closing cannot be applied, it stays entirely dark.
- The dark type count is equally highest in two classes (aggregator and central server, both with 2 types); the legitimate items left in the dark, however, are highest at the aggregator — 6 items, versus 2 at the central server. The fleet average is 7.1 closed, 1.8 only-visible, 1.2 dark.
- The total cost of closing is 36 blocked legitimate items and 52 manual-review items; of the 52, 31 come from a single control (application allowlist) and 12 from a single visibility control.
Next Step
This topic counted the layers of protection one by one across six lessons and wrote down what each layer misses: that closing persistence points could not stop seven of ten types, that there is no threshold that zeroes out both signature and behavior errors at once, that telemetry never saw two of eight points, that the host-based filter could not close legitimate peer-to-peer traffic, that living-off-the-land tools never showed up for a signature rule, and that no point is closed in all four endpoint classes at once. Each lesson left a missed set behind, and the same sentence was written every time: a point that cannot be closed must at least be made visible.
But visibility rested on an assumption, and that assumption was never tested. When a point was said to be “logged,” no one asked where that record sits, who can read it, how long it stays, and — most important — what proves it has not been tampered with. Only a record can show that what was missed was missed; if that record itself can be missed, nothing is left to measure. The next topic, System Logs, starts with this question: which sources hold the records on the endpoint, which of those sources sit somewhere someone standing at the endpoint can reach, and with what evidence a set of logs is said to be untampered with.
To keep your progress and take notes, Log in
My notes
Log in to take notes.