Skip to content

Leçon 3.6 · Triage statique· 45 min

Writing a Triage Report

Turn static triage into a report people can act on: verdict and confidence, ATT&CK-mapped capabilities, defanged IOCs, detections and next steps.

Cette leçon n’est disponible qu’en anglais pour le moment.

Objectifs

  • Structure a triage report around the decisions its readers must make, leading with the bottom line
  • State verdicts with explicit confidence levels and keep observed facts separate from inference
  • Map capabilities to MITRE ATT&CK technique IDs and present host and network IOCs in defanged form
  • Label reports for sharing with TLP and know when to export IOCs as STIX or MISP
  • Run a time-boxed triage and review your own report against a rubric

Triage is only finished when someone else can act on it. You may have identified the file, read its strings and imports, measured its entropy and written a YARA rule, but if that knowledge stays in your terminal history, the SOC still does not know whether to isolate the host, the detection team has no rule to deploy, and the next analyst will redo your work from scratch.

A triage report is the product of the first stage of the analysis funnel from What Is Malware Analysis?. It is short, it is written in hours rather than days, and it exists to support decisions. This lesson covers what goes into one, how to phrase what you know and what you only suspect, and how to share it. It ends with a complete example report and a timed lab.

A report exists to support decisions

Every triage report answers four questions, in this order of urgency:

  1. Is it malicious? Or benign, or potentially unwanted, or unknown.
  2. What does it do? Its capabilities, at the level of detail triage allows.
  3. What do we block and hunt for? IOCs and detection content.
  4. Does it need deeper analysis? And if so, which questions remain.

If a reader cannot find the answer to question 1 in the first ten seconds, the report has failed, however good its technical content.

Write for your readers

Different people read different parts of the same report:

ReaderWants to knowReads
SOC / incident response leadContain? Which hosts? How urgent?Summary, verdict, recommended actions
Detection engineerWhat to deploy, and how noisy it will beDetection content, IOCs, capabilities
Threat hunterWhat to search for in historical dataIOCs, ATT&CK techniques
CTI analystKnown family? Links to other activity?Identification, similarity hashes, notes
Reverse engineer (next stage)Where to start, what is already doneOpen questions, analyst notes, tooling
ManagementRisk and impact in plain languageSummary only

So the non-technical answer comes first, then progressively more technical sections, each usable on its own.

Anatomy of a triage report

Executive summary (BLUF)

Bottom line up front. Two to five sentences: what the file is, the verdict with its confidence, the one or two capabilities that matter, and the most important action. No tool names, no hex, no jargon a manager would have to look up. Write it last, put it first.

Verdict and confidence

One line each: the verdict (malicious, suspicious, potentially unwanted, benign, unknown), the family if identified, and the confidence. Then a short justification that points at the evidence sections below.

File identification

A table with everything needed to find this exact file again: file names seen, size, type, MD5, SHA-1, SHA-256, and the similarity hashes from Identifying and Hashing Files (imphash, ssdeep or TLSH). Add where the file came from (host, path, email, ticket), when it was collected, and the compile timestamp with a note that it can be forged.

Capabilities mapped to ATT&CK

List what the sample can do, each capability with its evidence and a MITRE ATT&CK technique ID such as T1140 or T1071.001. ATT&CK IDs give hunters and detection engineers a shared vocabulary, and let a CTI team compare this sample with others. Capa output from Reading Capabilities from Imports is a good starting point, but every mapping you publish is your claim: check it.

Map only what the evidence supports, and say what kind of evidence it is. An import of CreateRemoteThread is evidence of a capability, not proof the sample uses it.

Indicators of compromise

Split IOCs by where a defender will look for them:

  • Host indicators: hashes, file paths and names, mutexes, named pipes, registry keys, service names, scheduled task names, PDB paths.
  • Network indicators: domains, IPs, URLs, user-agents, URI patterns, TLS certificate or JA3/JA4 fingerprints.

Give each IOC a type, a value, and a short context note: "mutex created at startup, likely single-instance check" is far more useful than a bare string. Mark IOCs that were only seen statically, since they may be unused or decoys.

Defang network indicators so nobody clicks or auto-resolves them, and so mail filters and chat clients do not turn them into live links:

Live formDefanged form
http://update.example.com/gate.phphxxp://update[.]example[.]com/gate[.]php
https://cdn.example.nethxxps://cdn[.]example[.]net
203.0.113.7203.0.113[.]7
ops@example.orgops[@]example[.]org

Defang in prose and tables meant for humans; provide a machine-readable export (see below) with the original values for tools.

Detection content

The YARA rules from Writing Your First YARA Rules, plus anything else you produced: Sigma, EDR queries, Suricata signatures. For each rule, state what it was tested against (variants, goodware corpus) and its expected noise. An untested rule should say so.

Concrete, owned and ordered: block these hashes, deploy this rule in alert-only mode for a week, search for this mutex across the fleet, reimage this host. Match the actions to the verdict and confidence; a low-confidence verdict should not trigger fleet-wide blocking.

Open questions and next steps

What triage could not answer, and the analysis that would answer it: "The second-stage payload is encrypted in RT_RCDATA; dynamic analysis or reversing the decoder at 0x401A20 is needed to recover it." This section is the hand-off to the next stage of the funnel, and it keeps you honest about the limits of triage.

Analyst notes and tooling

Who analysed it, when, how long it took, which tools and versions were used, and anything odd you noticed but could not place. Tool versions matter: capa rule sets and YARA modules change, and a reader must be able to reproduce your results.

Saying how sure you are

"This is malware" and "this might be malware" lead to very different actions, so confidence must be explicit and use consistent words. A common convention, borrowed from intelligence analysis:

PhraseMeaningTypical basis
We assess with high confidenceJudgement is well supported; it could still be wrong, but that would be surprisingMultiple independent observations that agree; family confirmed by code, not just strings
We assess with moderate confidenceCredible and plausible, but not corroborated enough for high confidenceOne strong indicator, or several weaker ones
We assess with low confidencePlausible, but evidence is fragmentary or could have other explanationsA single string, a reputation hit, similarity only

Attach confidence to judgements, not facts. "The file imports WriteProcessMemory" needs no confidence level; it is observed. "The sample injects into other processes" is a judgement and does.

Observed versus inferred

The most common triage-report error is presenting an inference as an observation. Keep them visibly separate:

Observed (fact)Inferred (assessment)
The string LabAgent/1.0 (...) is present in .rdataThe sample probably uses this user-agent for HTTP traffic
No networking DLLs are importedNetwork functions, if any, are resolved dynamically or absent
Sections are named UPX0, UPX1, UPX2, and UPX1 has entropy 7.7The sample is packed with UPX
Its imphash matches three samples in our repositoryIt may share a build environment with them

Words like observed, present, imports, contains mark facts. Assess, likely, suggests, consistent with mark inference.

Tip: When static evidence contradicts an assumption, say so. "A user-agent string is present, but no networking imports were observed; the string may be unused or network APIs may be resolved at runtime" is a perfectly good triage finding, and it tells the next analyst exactly what to check.

Sharing: TLP, STIX and MISP

Label every report with the Traffic Light Protocol (TLP 2.0, maintained by FIRST) so recipients know how far they may pass it on:

LabelMay be shared with
TLP:REDNamed recipients only
TLP:AMBER+STRICTThe recipient's organisation only
TLP:AMBERThe recipient's organisation and its clients, on a need-to-know basis
TLP:GREENThe recipient's wider community, not publicly
TLP:CLEARAnyone, subject to copyright

Put the label at the top of the report and on any exported data. Internal reports about an ongoing incident are usually TLP:AMBER or stricter; a sanitised write-up of a commodity family may be TLP:CLEAR.

For machine consumption, IOCs travel as structured data rather than prose. STIX 2.1 (an OASIS standard) models indicators, malware, relationships and ATT&CK patterns as JSON objects, usually exchanged over TAXII. MISP is a widely deployed open-source sharing platform with its own event and attribute format and STIX import/export. Your report links to the event or bundle; the prose report stays the human-readable authority.

A report template

Copy this skeleton and fill it in. Every heading stays, even if the content is "none observed", so readers know you checked.

text
TLP:<LABEL>
# Triage report: <file name or family> (<report ID>)
Analyst: <name> | Date: <YYYY-MM-DD> | Time spent: <minutes>

## 1. Executive summary
<2-5 sentences, plain language, verdict + confidence + key action>

## 2. Verdict
Verdict: <malicious | suspicious | PUA | benign | unknown>
Family: <name or "unidentified">
Confidence: <low | moderate | high>
Rationale: <1-3 bullets pointing at sections 4-5>

## 3. File identification
| Field | Value |   (names, size, type, MD5, SHA-1, SHA-256, imphash,
                     ssdeep/TLSH, compile time, source, collection time)

## 4. Capabilities
| Capability | Evidence (observed / inferred) | ATT&CK |

## 5. Indicators of compromise
### Host   | Type | Value | Context |
### Network | Type | Value (defanged) | Context |

## 6. Detection content
<rules, what they were tested against, expected noise>

## 7. Recommended actions
<ordered, concrete, with owners>

## 8. Open questions and next steps

## 9. Analyst notes and tooling
<tools and versions, environment, anything unexplained>

Example report: LabImplant v1 (fictional)

Warning: Everything in this section is a fictional example written about the benign training program built in Writing Your First YARA Rules. There is no real incident, host, or threat actor. The hashes are from one build of that program; yours will differ.

TLP:CLEAR · Report TR-2026-0929-01 · Analyst: A. Analyst · 2026-09-29 · Time spent: 30 minutes

1. Executive summary

A file named lab_implant.exe, found in the malware-analysis training lab, was triaged statically. We assess with high confidence that it is not malicious: it is a training program that contains strings resembling implant artefacts (a custom user-agent, a mutex name, a named-pipe name and a configuration string) but has no networking, persistence or injection capability. No containment is required. A YARA rule has been produced so that lab copies are recognised and not escalated as real incidents.

2. Verdict

  • Verdict: Benign (training artefact)
  • Family: None; internal lab program "LabImplant"
  • Confidence: High
  • Rationale: The import table contains only C runtime and KERNEL32.dll functions linked by the mingw-w64 runtime (section 4); the implant-like strings are not referenced by any API that would use them; the code decodes a 5-byte blob to the text demo! and prints its strings.

3. File identification

FieldValue
File namelab_implant.exe
Size17,408 bytes
TypePE32+ executable (console), x86-64, stripped to external PDB
MD5de4d02e98a521e93dcfdb01c6a5025e9
SHA-1625d683cad6ccd138c7cdce4069ffa1bf9690988
SHA-256a603a2f93afc7ecdf9f88d5f6c05055b9cfcfa21074df4088ad527980b240495
Imphash3659d62105a06dba325698c81e1cd172 (shared by trivial mingw-w64 console programs; not distinctive)
Compile timestamp2026-09-29 13:21:24 UTC (header value; forgeable)
PDB namelab_implant.pdb
Sections10, standard mingw-w64 layout; .text entropy 5.88
PackerNone detected; whole-file entropy 4.71
SourceTraining lab build directory, collected 2026-09-29

4. Capabilities

CapabilityEvidenceTypeATT&CK
Decodes an embedded blob with a position-dependent XOR key (0x5A + i)Decoder loop in .text; 5-byte blob in .data decodes to demo!Observed (static)T1140 Deobfuscate/Decode Files or Information
Stores data in encoded formSame 5-byte blobObserved (static), trivial scaleT1027 Obfuscated Files or Information
HTTP communicationUser-agent string present; no WinINet, WinHTTP or Winsock imports; no GetProcAddress importNot supported by evidence(T1071.001 would apply if confirmed)
Single-instance check via mutexMutex name string present; no CreateMutex importNot supported by evidenceNone

VirtualProtect, VirtualQuery and Sleep appear in the import table. They are imported by the mingw-w64 runtime startup code, not by the program's own functions, and are not treated as evidence of capability.

5. Indicators of compromise

Host indicators

TypeValueContext
SHA-256a603a2f93afc7ecdf9f88d5f6c05055b9cfcfa21074df4088ad527980b240495This build only
Mutex name (string)Global\LabDemoMutexPresent as ASCII string; never created
Named pipe (string)\\.\pipe\lab-demo-pipeUTF-16LE string; never opened
Config markerLABCFG{interval=3600;jitter=20;mode=demo}Printed at runtime
PDB namelab_implant.pdbCodeView debug record

Network indicators

TypeValueContext
HTTP user-agentLabAgent/1.0 (Windows NT 10.0; lab-build)String only; no network capability observed
Domains, IPs, URLsNone observedChecked strings, FLOSS output and resources

6. Detection content

MAL_Win_LabImplant_Strings version 1.2 (full rule in the YARA lesson). Tested: matches the v1 (-O0) and v2 (-O2, version 1.1) builds; no matches in a scan of /usr/bin with the header check disabled; does not match the UPX-packed copy (SHA-256 4b956e927c41f7c4d9499840733f8cc44bad66b5893309d2d5d56d5dc92e9532), which is detected only by the generic SUSP_PE_FewSections_NoText hunting rule. Expected noise: none known.

  1. Lab team: add the rule to the lab scanner with the tag training, so lab copies are auto-closed rather than escalated.
  2. SOC: no action on production systems.
  3. Detection engineering: use this sample and rule as the regression test for the YARA test harness.

8. Open questions and next steps

  • Does any future build import networking or synchronisation APIs? If so, re-triage: the verdict rests on their absence.
  • The UPX-packed variant is not covered by the string rule. If packed builds are distributed, add a memory-scan deployment of the rule or a rule for the unpacked image.

9. Analyst notes and tooling

Static analysis only; the sample was not executed. Tools: file, strings, FLOSS, Python pefile, x86_64-w64-mingw32-objdump, YARA 4.5.4, Detect It Easy. Analysis host: isolated lab VM, no network.

Lab: a 30-minute timed triage

Take one of the binaries you built in this module: build/v2/lab_implant.exe, the UPX-packed lab_implant_upx.exe, or a variant from the hashing, strings, imports or packer labs. If you want an honest test, ask a colleague to pick one and rename it sample.bin. Start a timer and work through the checklist; stop at 30 minutes whatever the state, because triage is defined by its time box.

  1. Work through the checklist, writing findings straight into the template:

    MinutesStepToolsRecord in report
    0–3Hash and identify the file; note size and real typesha256sum, file, Detect It EasySection 3
    3–5Reputation lookup by hash only; check your own repositoryVirusTotal search, internal repoSection 3, notes
    5–9Headers and sections: timestamp, section names, sizes, entropypefile, PE-bearSection 3; packer yes/no
    9–12If packed: identify the packer, note it, decide whether to unpack nowDetect It Easy, upx -tSections 2 and 8
    12–17Strings: ASCII, UTF-16 and decoded, including stackstrings and XOR-encrypted strings; shortlist artefactsstrings, FLOSSSection 5 candidates
    17–21Imports and capabilities; separate runtime noisepefile, capaSection 4
    21–23Resources and overlaypefile, Resource HackerSections 4 and 5
    23–27Draft and test a YARA rule against the sample and a goodware folderyara -sSection 6
    27–30Verdict, confidence, summary, next stepsYour judgementSections 1, 2, 7, 8
  2. Fill every template heading. Use "none observed" rather than deleting a section.

  3. Label every statement in sections 4 and 5 as observed or inferred, and attach a confidence level to the verdict.

  4. Defang every network indicator, then swap reports with a colleague (or wait a day) and score it with the rubric below.

Self-review rubric. Score each line 0 (missing), 1 (partial) or 2 (done); aim for 16 or more out of 20.

#Criterion2 points means
1Bottom line up frontVerdict and main action are in the first two sentences, without jargon
2Explicit confidenceThe verdict uses low / moderate / high and the rationale supports that level
3Complete identificationAll three cryptographic hashes, one similarity hash, size, type, source
4Facts versus inferenceEvery capability is marked observed, inferred or not supported
5ATT&CK mappingIDs are correct, current and backed by cited evidence
6Useful IOCsSplit into host and network, each with type and context
7Defanging and TLPAll network IOCs defanged; TLP label at the top
8Tested detectionRule tested on the sample, a variant or packed copy, and a goodware set, with results stated
9Actionable recommendationsConcrete, ordered, proportionate to confidence
10Honest limitsOpen questions name what triage could not answer and how to answer it; tools and versions listed

Questions to answer: Did you finish in 30 minutes, and which step overran? Which of your capability claims would change if the sample turned out to resolve its imports at runtime? If you triaged the UPX-packed copy, what could you honestly say about its capabilities, and at what confidence? Would your executive summary make sense to someone who has never heard of a PE file?

Key takeaways

  • A triage report supports decisions: is it malicious, what does it do, what do we block and hunt, does it need deeper analysis.
  • Lead with the bottom line and a verdict with explicit confidence; order the rest from least to most technical so each reader finds their part.
  • Keep observed facts separate from inferences, and attach confidence levels to judgements, not to facts.
  • Map capabilities to ATT&CK only where evidence supports them, split IOCs into host and network, and defang every network indicator.
  • Include tested detection content, concrete recommended actions, open questions and tool versions, and label the report with TLP before sharing it or its STIX/MISP export.