Plan and execution of a credential-dumping operation (ATT&CK T1003.001): three planned attempts, the first blocked by EDR with its prediction refuted, the second succeeding with its prediction confirmed, the third never run.

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:

  1. comsvcs.dll MiniDump, called through rundll32
  2. procdump.exe
  3. 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:

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.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *