Consent Is a Record, Not a Checkbox
- Richard Nolan
- 8 hours ago
- 5 min read
Most authorized penetration tests are authorized in a PDF.
Somebody signs a scope document. Somebody else forwards it. A date range gets agreed in a meeting and written into a calendar invite. Then the testing happens in tooling that has never seen any of it. The scanner does not know what is in scope. The exploit framework does not know when the window closes. The operator knows, and the operator is trusted to remember.
That mostly works, because the operator is a person who read the document. It stops working the moment anything acts without a person watching it — and it was already thinner than it looked.
The gap is easy to state and hard to close. The authorization exists as prose. The system enforces nothing.
What a machine can actually check
If you want a system to refuse an action it has not been authorized to take, the authorization has to be something it can evaluate. Not a document it stores. A record it reads before every operation.
That means the record has to name, at minimum:
Scope — which hosts, as addresses or ranges. Not "the DMZ".
Exclusions — which hosts are never touched, whatever the scope says. These have to win. An exclusion that can lose to a scope entry is a comment.
Expiry — a timestamp after which the record authorizes nothing.
Permitted techniques, listed individually, each tagged with the tier it belongs to.
Approvers — an enrolled set, by identity, each with a credential the system can verify.
Quorum — how many of those approvers must sign, and per what.
Every one of those is a field rather than a paragraph. That is the entire point. A field can be checked in code before a packet leaves.
Expiry is not a formality
The field people leave out is the expiry, because in a document it feels like boilerplate. In a record it is the difference between an authorization and a standing permission.
Standing permissions are how offensive tooling goes wrong. Not dramatically — quietly. An engagement gets set up for a two-week test, the test finishes, and the engagement stays open because closing it was nobody's job. Six months later the scope is stale, two of the approvers have left, and the record still says yes.
Make expiry required and unset becomes impossible rather than merely discouraged. Past the timestamp the engagement reads as expired and nothing runs under it. There is no eternal authorization, because there is no way to express one.
The two-person rule, done properly
The phrase gets used loosely. Usually it means two people approved the engagement, once, at the start.
That is not a two-person rule. That is a two-person signup.
The version that holds is a quorum signing each operation. Two named approvers, enrolled in advance with credentials the system verifies, and two signatures for every exploit request — not for the engagement that contains it. The distinction matters because the engagement is a container. It might authorize forty operations across two weeks. Signing the container once delegates all forty to whoever holds the console.
Layer a time-boxed seal on top. The exploit tier stays sealed by default; a quorum unseals it for a fixed window — fifteen minutes, say — and it re-seals itself. The window is short enough that an operator who unseals it is committing to act now, in front of the people who signed, rather than banking an approval for later.
So a single exploit needs: an enrolled approver set, a quorum signing that operation, the tier unsealed inside its window, and a final confirm. Four independent conditions. No single operator satisfies all four alone, and that is by construction rather than by policy.
You cannot exploit what you have not proven
There is one more gate worth building, and it is the one that gets argued about.
Before a host can be exploited, it should already carry a recorded validation result — from a lower tier, against the same engagement. A T2 CVE validation that actually ran and actually landed.
The objection is that this is friction. It is. It is also the difference between testing a hypothesis and testing a rumour. A CVE on a list is a hypothesis: some scanner inferred a version string and matched it against a database. Firing an exploit at a host on that basis means the first real evidence you gather is the exploit itself, which is a poor place to discover you had the wrong host.
Requiring the prior validation, recorded against the same engagement, also closes an obvious hole: a validation from a different engagement should not unlock an exploit under this one. Otherwise the prerequisite becomes a lookup rather than a check.
Reversible by registration, not by promise
Tier 5 is where most platforms stop and hand you a warning dialog.
The alternative is to make reversibility a property of the technique, declared where the technique is registered, rather than a claim in the documentation. In practice that means a small set of destructive techniques that each prove an impact and then undo it:
Canary encryption (ATT&CK T1486) — encrypts a canary file, demonstrates the impact, decrypts it.
Service halt and restart (ATT&CK T1489) — stops a service, proves it stopped, brings it back.
Exfiltration beacon (ATT&CK T1041) — proves the channel exists and carries no data out of it.
Each maps to a real adversary behaviour. None leaves the host in a state it cannot be returned to.
One honest caveat, because the overclaim here is tempting. This is a property of the registered destructive techniques. It is not a claim that nothing on the platform can ever change a host. Tier 4 runs native and Metasploit exploits, and an exploit can leave a machine in a state it did not start in — that is what exploitation is. Anyone who tells you their offensive tooling is globally reversible is selling you something. The claim worth making is narrower and survives contact with a sceptical engineer: every technique registered as destructive is registered as reversible, and the ones that are not reversible are not registered.
Autonomy does not get its own rulebook
All of the above matters more, not less, once something can act on its own.
The usual failure is subtle. An autonomous component gets built, and because it is a component rather than a person it gets its own service account, its own standing permissions, its own quieter path through the controls. Nobody decides to weaken the model. It just turns out to be easier to give the agent a key than to make the agent queue at the same door.
The discipline is to make it queue. An agent that takes a job in plain language and runs the loop itself should run inside a named engagement, under a ceiling on the tier it may reach, with simulation as the default and the same quorum requirements above that ceiling. The same constraints an operator works under — not a looser set written for a machine on the grounds that the machine is faster.
If the autonomy needs an exception to be useful, the exception is the product, and it is worth being honest about that before shipping it.
Why bother
Because the buyers who can authorize offensive testing on their own estate — banks, defence, anyone running an air-gapped or heavily segmented network — are not asking whether you can fire an exploit. Everyone can fire an exploit.
They are asking what happens at the review afterwards. Who signed. What the authorization said at the moment it fired. Whether the host it hit was in scope, whether the technique was permitted, whether the consent had expired, and whether any of that can be reconstructed six months later without taking somebody's word for it.
A checkbox cannot answer those questions. A record can.
This is how authorization is built in SeeThrough, an evidence-first security intelligence platform that runs inside your own estate. Written by Richard Nolan, who spent twenty years on the recovery side of security before deciding the interesting problem was on the other end.
Comments