---
title: "Affiliates Do Not Find Novel Paths. They Find Yours."
description: "Part three of testing the ransomware playbook: privilege escalation inside the domain uses a small, stable set of paths — the same ones already written up on this site — and the affiliate picks by reliability, not by cleverness."
date: 2025-12-12
author: Sevban Dönmez (@jankesec)
canonical: https://jankesec.com/posts/ransomware-playbook-03-privilege-escalation/
---

## Privilege escalation in 60 seconds

The domain is mapped. Something authenticated is sitting inside it with the rights of
whoever's credential got reused, and the next objective is the only one that matters:
become something that can write to everything.

This is the part of the playbook people expect to be sophisticated. It is not. The set of
paths in regular use is small, well documented, and years old — and every one of them has
already been covered on this site, because they are the same paths an assessment reports.

What is worth writing about is not the techniques. It is the selection criterion.

## They optimise for reliability, not for cleverness

An affiliate is working against a clock and cannot afford a path that fails halfway. That
constraint filters the option set hard, and it filters it differently than a tester's
curiosity does.

Three properties decide it:

**Does it need a human?** Anything that depends on an administrator logging in somewhere is
a waiting game. Anything that depends only on directory state is available immediately.

**Does it survive a reboot?** A path built on a token or a live session evaporates. A path
built on a permission does not.

**How loud is it?** Not zero — they accept noise. But they prefer noise that resembles
administration to noise that resembles an attack.

Ranked by those criteria, the list collapses to a handful, and it looks exactly like the
findings section of a competent internal report.

<figure class="diagram">
<svg viewBox="0 0 700 250" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Escalation paths ranked by reliability: ACL and delegation paths are deterministic and permanent, relay depends on coercion, credential theft from memory depends on someone being logged in.">
<text x="0" y="16" class="dg-muted">RANKED BY WHAT AN AFFILIATE ACTUALLY NEEDS</text>
<rect x="0" y="30" width="700" height="52" rx="8" class="dg-box-accent" />
<text x="16" y="52" class="dg-mono">ACL abuse · RBCD · Kerberoast</text>
<text x="16" y="70" class="dg-accent">DIRECTORY STATE ONLY — NO HUMAN, SURVIVES REBOOT, REPEATABLE</text>
<text x="470" y="52" class="dg-muted">PICKED FIRST</text>
<rect x="0" y="92" width="700" height="52" rx="8" class="dg-box" />
<text x="16" y="114" class="dg-mono">NTLM relay</text>
<text x="16" y="132" class="dg-muted">NEEDS A COERCIBLE HOST AND AN UNSIGNED DESTINATION</text>
<text x="470" y="114" class="dg-muted">PICKED IF PRESENT</text>
<rect x="0" y="154" width="700" height="52" rx="8" class="dg-box" />
<text x="16" y="176" class="dg-mono">Credentials in memory</text>
<text x="16" y="194" class="dg-crit">NEEDS SOMEONE LOGGED IN — EVAPORATES, AND EDR WATCHES IT</text>
<text x="470" y="176" class="dg-crit">LAST RESORT</text>
<line x1="0" y1="222" x2="700" y2="222" class="dg-line dg-dash" />
<text x="0" y="244" class="dg-crit">NOTE</text>
<text x="60" y="244" class="dg-mono">the order is the inverse of how these are usually prioritised in a report</text>
</svg>
<figcaption>The top row is deterministic: it depends on permissions that are already true and stay true. The bottom row is what most detection is tuned for, and it is the one an affiliate reaches for last.</figcaption>
</figure>

## The inversion that matters for reporting

Look at that ordering against how findings usually get triaged.

Credential dumping from memory is the one everybody has controls for. It is in the EDR
product, it is in the detection engineering backlog, it is the thing the tabletop exercise
was about. It is also the *last* thing an affiliate wants to do, because it needs a
privileged session to already exist on a box they already control.

The top row — a group that can write to a computer object, a service account with a
crackable password, an over-permissioned OU — has no product watching it. It is a state, not
an event. Nothing fires when a permission is used as designed.

So the estate is instrumented against the technique of last resort and blind to the ones
that get used first.

## The three you will actually find

Each of these has its own note on this site; this part is about how they sit together.

**Service account passwords.** Any user can request a service ticket for any SPN, so the only
control is the password behind the account. See
[Kerberoasting triage](/posts/kerberoasting-triage/) for the part that matters — which
tickets are worth the GPU time and which are a week wasted.

**Write access to computer objects.** The most reliable escalation in a modern domain, and
it is not a setting anybody misconfigured — it is a consequence of who holds `GenericWrite`.
[Delegation triage](/posts/delegation-triage/) covers why RBCD is the one you find and the
other two are the ones you were taught.

**Coerced authentication relayed somewhere unsigned.** Requires more of the environment to
line up, but when it lines up it is fast.
[SMB signing is on](/posts/ntlm-relay-triage/) covers why "signing is enforced" does not
close it.

The connection worth stating plainly: **the relay path and the delegation path are the same
finding.** A relay to LDAP writes an RBCD attribute. The prerequisite for the delegation
attack is write access to a computer object. One produces the other. Reporting them as two
unrelated items is how both end up half-fixed.

## Testing it as a chain, not as a list

The single most useful change to how this gets tested is to stop reporting escalation
findings individually and start reporting the shortest reliable chain from the starting
position.

```
# What does the position I have actually reach, using only durable edges?
MATCH (u {owned: true})
MATCH p = shortestPath(
  (u)-[:MemberOf|AdminTo|GenericAll|GenericWrite|WriteDacl|WriteOwner|
       AddAllowedToAct|AllowedToAct|ReadLAPSPassword|ReadGMSAPassword*1..]->
  (g:Group {name: 'DOMAIN ADMINS@LAB.LOCAL'})
)
RETURN p
```

Note what is missing from that edge list: `HasSession`. A chain that depends on somebody
being logged in is a chain that may not exist tomorrow — the same argument as in
[the note on edge shelf life](/posts/bloodhound-edge-triage/), applied to the report rather
than to the collection.

Run it from the position an affiliate would plausibly hold — a standard user, not your
domain admin test account — and the output is the finding.

## Evidence matrix

| Signal | What it proves | Negative control | Defender verification |
| --- | --- | --- | --- |
| Durable ACL, delegation, or service-account edge exists from the foothold | A deterministic escalation route is already encoded in configuration | Remove the earliest edge in a lab copy and recompute the path | Re-collect DACLs, delegation, and service ownership with nested membership expanded |
| Relay prerequisites compose from a coercible source to a writable target | The conditional route is operational, not a list of weak settings | Harden either source or destination and expect the chain to break | Track protocol-pair prerequisites instead of treating signing as one global control |
| Privileged credential appears only while a specific admin session exists | The route is transient and depends on timing | Log the administrator off and confirm the credential path disappears | Enforce tiered logon and correlate privileged sessions with endpoint exposure |
| Chain starts from the same standard-user position used in initial access | Severity reflects a plausible attacker starting point | Repeat from a principal without the required group/host access | Reproduce the full path under the least-privileged assumed foothold |

## The pattern I keep seeing

The permissions that make the top row work were granted for a reason that made sense and
then outlived it. A group given rights over an OU so somebody could join machines. A service
account created during a deployment with a password typed into a config file. An exemption
for an integration that could not do modern auth.

None of them were mistakes when they were made. All of them are load-bearing for an
escalation path now, and none of them will be found by scanning for vulnerabilities, because
none of them are vulnerabilities. They are decisions nobody revisited.

## What to hand the defenders

**The chain, with the weakest link named.** Not six findings — one path, and the single
permission whose removal breaks it. That is the thing that fits in a sprint.

**Say which controls do not apply.** If the chain never touches memory, say so explicitly.
Otherwise the response will be to tune the EDR policy, which changes nothing about the path
you just walked.

**Separate the state findings from the event findings.** Permissions and service account
passwords are state: they are fixed by changing configuration, and they stay fixed.
Credential exposure is an event: it is managed by detection. They belong to different teams
and different timelines, and merging them into one "privilege escalation" section guarantees
the slower half never happens.

---

*Next in this series: credential access and lateral movement — and what detection actually
catches once the affiliate stops needing new privileges.*