Lesson 21 / 21
Credential Theft
How long verification items stay in memory and the path controls close: 6 of 8 item types can be presented directly without knowing the password, 22 paths and 298,958 machine-hours of exposure, six controls closing 17 paths and dropping exposure to 1%, and the remaining 5 paths not closing under any control.
Published Updated
In this lesson
- Measures verification items by the product of time-in-memory and scope
- Explains conceptually why pass-the-hash works without knowing the password
- Calculates separately the paths controls close and the exposure that remains
Contents
The previous lesson counted escalation through configuration items: a membership, a permission, an account. There is one more item that stays silent behind the edge list. Moving from one account to another usually does not require knowing that account’s password, but rather getting hold of the value used to authenticate it; such a value sits in the memory of machines for every logged-in user.
This lesson measures those values on the defense axis: which item stays where for how long, how many items can be presented directly without knowing the password, and which control closes how many paths. No working step sequence, tool name, or command is written in this lesson; what is measured is not how the item is obtained, but what it opens assuming it has been obtained. Running such a test without written authorization and a defined scope is both illegal and unprofessional.
What Is Presented Is Not the Password
EI23: in most mechanisms the authentication input is not the password itself but a value derived from or produced with it; the verifying side expects that value. EI24: because of this, whoever gets hold of the value does not need to know the password — what is presented is already not the password. EI25: an item’s time in the machine is multiplied by its scope; defense is measured by one of removing the item, shortening its lifetime, or narrowing its scope.
EI24 is this lesson’s conceptual core and explains the entire class called pass-the-hash. The password is never stored anywhere; what is stored and passes over the network is a value derived from it. The verifying side compares the value presented to it with the value in its own record and accepts if they match. This is not a flaw but a design — done so the password does not pass over the network. But the same design also says this: whoever can present the derived value gets the same result as whoever knows the password. This is why changing the password renews the item, and it is also the only action to take for an account whose item was stolen.
Eight Item Types, Twenty-Two Paths
The inventory below is a model: the verification items sitting on machines after a session is opened, in the fictional regional measurement network organization. There is no real organization, real machine, or working step sequence.
// theft/item.mjs — verification items sitting on a machine after a session is opened // (model): there is no real organization, real machine, real item, or working step // sequence. No randomness. // EI23: in most mechanisms the authentication input is not the password itself but a value derived // from or produced with it; the verifying side expects that value. EI24: because of this, whoever // gets hold of the value does not need to know the password — what is presented is already not the password. // EI25: an item's time in the machine is multiplied by its scope; defense is measured by one // of removing the item, shortening its lifetime, or narrowing its scope. // Columns: life = the item's time on the machine (hours), scope = number of systems the item // can open, presentable = is it accepted when presented directly without knowing the password. export const ITEM = ` password-derived-verifier 720 13 yes ticket-granting-ticket 10 21 yes service-ticket 10 1 yes session-key 10 1 no application-session-cookie 72 1 yes cached-verifier 8 1 no renewal-ticket 168 21 yes service-account-secret 720 1 yes` .trim().split("\n").map((r) => { const p = r.trim().split(/\s+/); return { name: p[0], life: +p[1], scope: +p[2], presentable: p[3] === "yes" }; }); export const CLASS = [ { name: "branch-workstation", count: 96 }, { name: "server", count: 21 }, { name: "field-laptop", count: 140 }, { name: "meter-collector", count: 44 }, ]; // Which item is found on which class: each pair is a path. export const FOUND_ON = { "password-derived-verifier": ["branch-workstation", "server", "field-laptop"], "ticket-granting-ticket": ["branch-workstation", "server", "field-laptop"], "service-ticket": ["branch-workstation", "server", "field-laptop", "meter-collector"], "session-key": ["branch-workstation", "server", "field-laptop", "meter-collector"], "application-session-cookie": ["branch-workstation", "field-laptop"], "cached-verifier": ["branch-workstation", "field-laptop"], "renewal-ticket": ["branch-workstation", "field-laptop"], "service-account-secret": ["server", "meter-collector"], }; // Each control states which (item, class) pair it removes, by its own criterion. export const CONTROL = { "privileged-session-isolation": (k, s) => k.scope > 1 && s.name !== "server", "protected-account-group": (k) => k.name === "cached-verifier" || k.name === "password-derived-verifier", "local-admin-restriction": (k, s) => s.name === "branch-workstation" || s.name === "meter-collector", "logoff-enforcement": (k) => k.life > 24, "service-account-isolation": (k) => k.name === "service-account-secret", "origin-bound-factor": (k) => k.name === "application-session-cookie" || k.name === "renewal-ticket", };
// theft/tally.mjs — which item stays where for how long, how many items can be presented // without knowing the password, how many paths each control closes, and what exposure remains. import { ITEM, CLASS, FOUND_ON, CONTROL } from "./item.mjs"; const print = (g, ...s) => console.log(s.map((v, i) => (g[i] < 0 ? String(v).padEnd(-g[i]) : String(v).padStart(g[i]))).join("")); const classOf = (name) => CLASS.find((s) => s.name === name); const PATH = ITEM.flatMap((k) => FOUND_ON[k.name].map((s) => ({ k, s: classOf(s) }))); const exposure = (y) => y.reduce((a, x) => a + x.s.count * x.k.life, 0); const open = (closed = []) => PATH.filter((y) => !closed.some((d) => CONTROL[d](y.k, y.s))); console.log(`${ITEM.length} item types, ${CLASS.length} machine classes, ` + `${PATH.length} paths, ${CLASS.reduce((a, s) => a + s.count, 0)} machines`); console.log(`items presentable directly without knowing the password ` + `${ITEM.filter((k) => k.presentable).length}/${ITEM.length}; ` + `total exposure ${exposure(PATH)} machine-hours`); const A = [-28, 7, 8, 8, 8, 12]; console.log("\n1. per item: where, how long, how wide"); print(A, "item", "life", "scope", "class", "machine", "machine-hr"); for (const k of ITEM) { const y = PATH.filter((x) => x.k === k); print(A, k.name, k.life, k.scope, y.length, y.reduce((a, x) => a + x.s.count, 0), exposure(y)); } const B = [-30, 8, 12, 12, 10]; console.log("\n2. paths closed and exposure per control"); print(B, "control", "closed", "open paths", "open expo", "open %"); for (const d of Object.keys(CONTROL)) { const rest = open([d]); print(B, d, PATH.length - rest.length, rest.length, exposure(rest), `${(100 * exposure(rest) / exposure(PATH)).toFixed(0)}%`); } const C = [-30, 8, 9, 12, 10]; console.log("\n3. controls added cumulatively"); print(C, "added", "paths", "closed", "exposure", "open %"); const chosen = []; print(C, "none", PATH.length, 0, exposure(PATH), "100%"); for (const d of Object.keys(CONTROL)) { chosen.push(d); const rest = open(chosen); print(C, d, rest.length, PATH.length - rest.length, exposure(rest), `${(100 * exposure(rest) / exposure(PATH)).toFixed(0)}%`); } const D = [-26, -18, 8, 13]; console.log("\n4. paths remaining with all controls open"); const rest = open(Object.keys(CONTROL)); print(D, "item", "class", "machine", "machine-hr"); for (const y of rest) print(D, y.k.name, y.s.name, y.s.count, y.s.count * y.k.life); console.log(`remaining paths ${rest.length}/${PATH.length}, ` + `remaining exposure ${exposure(rest)}/${exposure(PATH)} machine-hours`);
8 item types, 4 machine classes, 22 paths, 301 machines items presentable directly without knowing the password 6/8; total exposure 298958 machine-hours 1. per item: where, how long, how wide item life scope class machine machine-hr password-derived-verifier 720 13 3 257 185040 ticket-granting-ticket 10 21 3 257 2570 service-ticket 10 1 4 301 3010 session-key 10 1 4 301 3010 application-session-cookie 72 1 2 236 16992 cached-verifier 8 1 2 236 1888 renewal-ticket 168 21 2 236 39648 service-account-secret 720 1 2 65 46800 2. paths closed and exposure per control control closed open paths open expo open % privileged-session-isolation 6 16 87030 29% protected-account-group 5 17 112030 37% local-admin-restriction 10 12 170590 57% logoff-enforcement 9 13 10478 4% service-account-isolation 2 20 252158 84% origin-bound-factor 4 18 242318 81% 3. controls added cumulatively added paths closed exposure open % none 22 0 298958 100% privileged-session-isolation 16 6 87030 29% protected-account-group 13 9 70022 23% local-admin-restriction 7 15 28630 10% logoff-enforcement 5 17 3430 1% service-account-isolation 5 17 3430 1% origin-bound-factor 5 17 3430 1% 4. paths remaining with all controls open item class machine machine-hr ticket-granting-ticket server 21 210 service-ticket server 21 210 service-ticket field-laptop 140 1400 session-key server 21 210 session-key field-laptop 140 1400 remaining paths 5/22, remaining exposure 3430/298958 machine-hours
The numbers are of the measurement class; their inputs are EI23–EI25 and the item inventory.
The Product of Life and Scope
The first table lays the eight items side by side. The password-derived verifier stays 720 hours, opens 13 systems, and is found on 257 machines across three classes: on its own it produces 185,040 machine-hours of exposure, 62% of the total. The ticket-granting ticket opens 21 systems but stays only 10 hours; its exposure is 2,570. Scope alone does not give a ranking; multiplied by lifetime, it does.
The renewal ticket ranks second: 168 hours, 21 systems, 39,648 machine-hours. The 72-hour session window measured in the fourth lesson and the 168-hour renewal ticket seen here are two views of the same setting — one written as revocation delay, the other as time-in-memory. The same number produces cost in two different columns.
The service account secret is found on only 65 machines but stays 720 hours and yields 46,800 machine-hours. The second lesson measured that breaking one of 21 service-account keys drops 214 principals; the 46,800 here is the counterpart of how long that item stays exposed on machines.
Six Controls, Seventeen Paths
The second table runs the controls one at a time. The local-admin restriction closes the most paths — 10 of 22 — but the remaining exposure is 57%, because the paths it closes are in classes hosting short-lived items. The logoff enforcement closes only 9 paths but drops exposure to 4%; the items it removes are the long-lived ones.
These two rows repeat one of the course’s rules: a number cannot be read without choosing a unit of measure. Ranked by path count, local-admin restriction is first; ranked by exposure, logoff enforcement is first. The two rankings give different defense priorities, and which one is correct depends on what the organization is measuring.
The third table adds all six cumulatively. The path count drops from 22 to 5, exposure from 298,958 to 3,430; the remaining ratio is 1%. The last two controls — service-account isolation and origin-bound factor — close no new paths, because the items they close have already been removed by the earlier controls. It is the same pattern seen in the seventh lesson: a control that is effective on its own can contribute zero in the cumulative list.
The Five Paths That Do Not Close
The fourth table lists what remains with all six controls open. Five paths remain, and all five are short-lived items on servers and field laptops: the ticket-granting ticket, the service ticket, and the session key. Total exposure is 3,430 machine-hours.
These five do not close, because removing them means removing the session itself. If a user has a session open on a server, that session’s key has to stay there; without a ticket, the service cannot be reached. This is where the course’s measured root falls shortest most plainly: the way to close the remaining paths is not a better control, it is shortening the session’s duration — and that also stops legitimate work.
Summary
- The authentication input is not the password itself but a value derived from it; 6 of 8 item types are accepted when presented directly without knowing the password.
- Exposure is the product of life and scope: the password-derived verifier alone produces 62% of the total 298,958 machine-hours with 720 hours and 257 machines.
- The control most effective by path count and the control most effective by exposure are not the same: one closes 10 paths and leaves 57%, the other closes 9 paths and leaves 4%.
- Six controls together close 17 paths and drop exposure to 1%; the last two controls contribute zero in the cumulative list.
- The remaining 5 paths do not close under any control, because removing them means removing the session itself.
Course Wrap-Up
Twenty-one lessons were measured with the same three columns. The table below lays each lesson’s trust root, the number of items that must be correct at the same time from root to use, and what drops if the root breaks, side by side.
| Lesson | Trust root | Items that must hold | What drops if the root breaks |
|---|---|---|---|
| Hashing, Encryption, and Encoding | operation–domain match | 2 secret items | 24,800 records |
| Hash Functions | lookup cost | 25,242 items | 12,502 items |
| Salt and Password Storage | password unpredictability | 2 factors | 340 records |
| Symmetric Encryption | shared key | 4 items | 12,401 ends |
| Asymmetric Encryption | private key secrecy | 12,401 secret items | 1 participant |
| Key Exchange | secret exponent | 3 items | 52,560 sessions |
| Digital Signatures | signing key | 6 items | 2,190 signatures |
| Certificates | audited fields | 8 items | 13 attempts |
| Certificate Authorities | root certificate | 14 items | 8,802 items |
| Certificate Revocation | publication freshness | 29.5-hour window | 271,877 connections |
| TLS Handshake | the server’s private key | 11 items | 5 presentations |
| Certificate Lifecycle | renewal automation | 4,397 items | 1,458 connections |
| End-to-End Encryption | the key at the end | 4 items | 172,200 records |
| Directory Services | the integrity of the central repository | 119 items | 646 items |
| Ticket-Based Authentication | the ticket-granting service’s key | 471 key items | 214 principals |
| Authentication for Network Access | shared secret | 480 items | 80 devices |
| Single Sign-On | signing key | 90 items | 1,562 connections |
| Multi-Factor Authentication | the bond at the registration moment | 4 items | 10 paths |
| Group Policies | policy repository | 12 settings | 295 machines |
| Privilege Escalation | inventory accuracy | 20 edges | 37 paths |
| Credential Theft | the verifier in memory | 8 item types | 22 paths |
The table’s common column is the second one. In no row does the root stand alone: the smallest asks for two, the largest for more than twelve thousand items, and most of the items are not cryptographic. An hour, a record, a distribution round, an inventory row — these items around the root determine where the break actually comes from.
Three topics show three different root classes. In the cryptography-fundamentals lessons the
root is a secret or a cost assumption: a secret exponent, a private key, the unpredictability
of a password. In the pki lessons the root is a decision: which authority is trusted, which
field is checked, how often revocation is published. In the enterprise-identity lessons the
root is an operational item: a repository record, a distribution round, an inventory row, a
moment of registration. Moving from plain mathematics toward operations does not make protecting the
root harder — it makes seeing it harder.
The third column’s magnitude follows this same order too. A secret exponent dropping opens 52,560 sessions and the event is obvious; a directory record getting corrupted drops 646 items and it is not even known that it happened. Three lessons in the course showed that the root itself is not enough: a signature does not prove who the signing key belongs to, a certificate does not guarantee revocation is learned, multi-factor authentication cannot verify its own moment of registration. In all three, what is missing is not inside the mechanism, it is around it.
The course’s rule is this: cryptography is not a secrecy tool, it is a method of reducing trust to a single root; whoever does not know where the root stands cannot protect it. The third column writes the price of that — 646 items, 8,802 items, 214 principals, 1,562 connections. These numbers do not come from the mechanism’s weakness but from the reduction’s success: because trust is concentrated in one place, that is where it breaks.
In this course the root was named every time and its chain counted. But under every root an unwritten assumption remained: the integrity of the machine the root stands on. A private key sits in a file, a ticket in a region of memory, a policy repository on a disk. If that machine is compromised, the whole chain — even if correctly set up, correctly renewed, correctly audited — stays valid in the attacker’s hands. The Endpoint and System Hardening course starts with this question.
To keep your progress and take notes, Log in
My notes
Log in to take notes.