What They Meant to Do vs. What Happened: Modeling Plans and Executions in CTI
Most threat intelligence records outcomes. A sighting says APT-whoever used T1003.001 against DC01 at 14:02. That is useful, and it is also where the record stops. It doesn’t say what the operator tried first. It doesn’t say what failed, or why. It doesn’t say what they expected to happen, and it can’t tell you whether reality matched the expectation.
Those gaps matter in the places where CTI gets used hardest: adversary emulation, purple teaming, and incident reconstruction. In each of them the sequence and the misses are the intelligence. A credential dump that succeeded on the second tool tells you something the bare technique ID doesn’t.
This post walks through how STONEWORK, the open cyber-terrain ontology from cyberterrain.org (source on GitHub), models a plan, its execution, and the prediction that connects them. The whole thing is one small worked example: an operation that tries to dump credentials from LSASS memory (ATT&CK T1003.001).
The example is synthetic. The actor, the host, and the timestamps are made up for illustration. The technique and tools are public ATT&CK material.
The story
An operator wants credentials from a Windows endpoint. There are three well-known ways to dump LSASS memory, and the plan is to try them in order:
comsvcs.dllMiniDump, called throughrundll32procdump.exe- Mimikatz
What actually happened:
14:00:00 attempt comsvcs.dll ── blocked by EDR ──▶ fall back
14:00:15 attempt procdump.exe ── dump succeeds ──▶ credentials obtained
(Mimikatz never needed)
That’s a two-line incident timeline. The model has to capture what makes it more than that: there was a plan with branches, the plan was executed only in part, and the operator’s expectations were right once and wrong once.
Half one: the plan
STONEWORK separates what is intended from what occurred. The intended side is a stonework:OperationPlan: a set of steps and the transitions between them.
Two things about the plan are worth pointing out.
Three procedures, one technique. ATT&CK defines T1003.001 as a capability, meaning what adversaries may do and roughly how. It doesn’t prescribe one step sequence, because there isn’t one. Each of the three tools is a separate Procedure that implements the same technique. That keeps the “many ways to do one thing” fan-out queryable instead of flattening it into a single canonical script.
Branching is a first-class object. Each Transition is its own resource with a guard. Here is the fallback from comsvcs to procdump:
ex:t-comsvcs-to-procdump
a stonework:Transition ;
stonework:fromStep ex:step-attempt-comsvcs ;
stonework:guardVariable ex:var-comsvcs-outcome ;
stonework:guardLiteralValue "false"^^xsd:boolean ;
stonework:toStep ex:step-attempt-procdump .
Read it as: if the comsvcs attempt’s outcome is false, go to the procdump attempt. The success path is a sibling transition, guarded on the outcome being a SecurityCondition (usable credential material). Five transitions cover the whole plan, and only two of them fire in our trace.
Half two: the execution
What occurred is a set of stonework:Process individuals, each tied back to the plan step it performed with correspondsToStep:
ex:act-procdump-run
a stonework:Process ;
stonework:correspondsToStep ex:step-procdump-run ;
stonework:attributedTo ex:threat-actor ;
stonework:actsOn ex:rtproc-lsass ;
stonework:startedAtTime "2026-08-01T14:00:17Z"^^xsd:dateTime ;
stonework:endedAtTime "2026-08-01T14:00:45Z"^^xsd:dateTime ;
stonework:produces ex:cond-procdump-credential-material .
That is who did it, when, against what, and what it produced. The record of the failed attempt has the same shape. Its definition notes that the endpoint’s EDR agent terminated the process before a dump file was written, and it produces nothing.
The plan and the execution are different individuals. You can hold the same plan and record many executions of it: different actors, different networks, different outcomes.
The bridge: prediction versus outcome
This is the part I find most useful.
Each attempt in the plan has a Variable for its outcome, and a Hypothesis that predicts what that variable will resolve to:
ex:hyp-comsvcs-outcome
a stonework:Hypothesis ;
stonework:predictsVariable ex:var-comsvcs-outcome ;
stonework:predictedType stonework:SecurityCondition ;
stonework:confidence 60 ;
stonework:hasHypothesisStatus stonework:hypothesisStatusRefuted .
“comsvcs will yield credential material, 60% confidence.” Execution then binds the variable to what really happened, here the literal false, and the hypothesis is marked Refuted. The procdump hypothesis (75% confidence) is Confirmed.
Two details of this design are deliberate:
- Status is recorded, not inferred. You could work out “refuted” by noticing which transition fired. Storing it explicitly lets you query prediction quality directly.
- Absence is evidence. The Mimikatz outcome variable is declared and never bound. Nothing marks that branch as “skipped”. The missing binding is the record that it was never attempted.
What you can ask it
Because plans, steps, activities, and hypotheses are all addressable resources in one graph, questions that would take custom parsing elsewhere become short queries. These run as written against the example file.
Which techniques did this actor execute, and when? The link from an activity to a technique is a real property path (activity, then step, then procedure, then technique), not an ID tucked into a metadata field:
SELECT ?activity ?start ?technique WHERE {
?activity stonework:attributedTo ex:threat-actor ;
stonework:correspondsToStep/stonework:implements/stonework:implements ?technique ;
stonework:startedAtTime ?start .
} ORDER BY ?start
Result: both attempts, at 14:00:00 and 14:00:15, resolve to the same ATT&CK technique, T1003.001.
Which predictions turned out wrong?
SELECT ?hyp ?confidence WHERE {
?hyp a stonework:Hypothesis ;
stonework:hasHypothesisStatus stonework:hypothesisStatusRefuted ;
stonework:confidence ?confidence .
}
Result: hyp-comsvcs-outcome, at confidence 60.
Which parts of the plan never ran?
SELECT ?step WHERE {
ex:opplan-t1003-001 stonework:hasStep ?step .
?step stonework:hasOutputVariable ?var .
FILTER NOT EXISTS { ?var stonework:boundTo ?x }
FILTER NOT EXISTS { ?var stonework:boundToLiteral ?y }
}
Result: step-attempt-mimikatz.
Aggregate these over many executions and you get calibration data: how often an emulation team’s “this will work” holds, or which procedures a given defensive stack consistently turns into refuted predictions.
You don’t have to go this deep
Everything above is the deep path, and you should use it when the branching and the gap between expectation and result are what you’re modeling: emulation, forensic reconstruction, predicted-versus-actual analysis.
For a sighting or a report, use the lightweight path:
ex:act-cred-dump
a stonework:Process ;
stonework:executionOf cti-enc:attack-pattern--T1003.001 ;
stonework:attributedTo ex:apt-group ;
stonework:actsOn ex:host-dc01 ;
stonework:startedAtTime "2026-08-01T14:02:00Z"^^xsd:dateTime .
This actor performed T1003.001 against DC01 at this time. That is a complete, useful assertion. If you later learn the step-by-step story, you can add plan structure to it without retracting the executionOf triple. Going deeper never invalidates what you already recorded.
(One more note on scope: the shared graph is meant to hold abstract techniques and prose procedure examples. Executable-depth offensive plans like the one above belong in an enterprise or red-team graph, where the organization owns the dual-use call.)
What’s missing, and what comes next
The example has an adversary and a plan, but only one actor. Look back at the trace: comsvcs failed because something blocked it. The EDR agent that terminated the process is doing real work in this story, and right now it appears only as a sentence in a description.
In a complete model that EDR block is itself an execution: a defender’s detection or control, run as part of a plan of its own, attributed, timestamped, and testable against its own predictions (“this control should stop comsvcs-style LSASS dumps”). The same plan, execution, and prediction pattern applies unchanged, mirrored onto the defensive side. That is the next post.
Try it yourself
STONEWORK is developed in the open in the Cyber-Terrain-Ontology/stonework repository on GitHub. Everything in this post is there:
- The worked example, the full Turtle file behind every snippet above:
examples/operation-plan-t1003-001.ttl - Design notes on plans, executions, and hypotheses, including the full property list and a recipe for encoding another ATT&CK technique:
docs/plan-execution-model.md - Design notes on how deep to model a technique, covering the lightweight and deep paths and what belongs in a shared graph:
docs/technique-modeling.md
Load the example into any SPARQL engine and the queries above will run as written.
Feedback
Questions, corrections, and counterexamples are welcome. If you have a case this model handles badly, that is the most useful thing you can send us. Leave a comment below, or open an issue in the GitHub repository.