Skip to content

Evaluation

All numbers come from python scripts/run_benchmarks.py, run by the bench GitHub Actions workflow on the dataset snapshot in data/MANIFEST.json. Source of every number on this page: bench run 37092539472 on commit 2d7bacc (also printed at the top of the generated results below). Commands and expected values: Reproduce. Protocol details: ADR 0004.

Protocol in brief

  • Leave-report-out. Before a case is attributed, every group edge whose citations are a subset of the case's own citations is removed (A1-LF, R, G). Plain A1 is kept only as a leaky upper bound.
  • Per-report cases (R). One case per (group, cited report) with at least 3 techniques: 637 cases over 161 groups; 5-fold by report, and a temporal split: every citation dated 2022 or later is removed from the profiles (date from the citation's reference description "(YYYY, Month DD)", else from a year in its key) and the 219 cases from 2022+ reports are attributed. A sensitivity run also removes the 83 citations with no recoverable date.
  • Commitment rule. Baselines commit only on a unique top score (ties abstain); DRAGNET commits at MEDIUM+, and its LOW verdicts are counted separately as "wrong actor named (any grade)".
  • Uncertainty. Exact Clopper-Pearson intervals for proportions; bootstrap CIs for top-1 that resample the non-independent unit (threat groups for R, cases for the campaign sets, families for genetics, actors for G). Paired differences: sign-flip test, exact when at most 20 discordant units, otherwise Monte Carlo with B = 200,000 sign flips and p = (k+1)/(B+1) (never reported as 0); on R the test flips whole groups, matching the clustered CIs; Holm correction across methods.
  • Genetics (E2/E3, B3). Time split at 2024-01-01; collision filters and the TLSH radius use pre-cutoff or validation data only; results per family and with WannaCry excluded.
  • Published comparison (G). Guru, Moss & Kochenderfer (2025) adapted to ATT&CK human-curated per-report technique lists; an adaptation, not a reproduction (the paper picks the best of 10 weight matrices on validation; we report every split).

What the results say

  1. On leakage-controlled per-report cases DRAGNET beats every single-signal and TTP baseline on top-1: 5-fold 0.454 [0.401, 0.507] vs at most 0.310 (group-clustered Holm p = 5.0e-5, Monte Carlo with B = 200,000); temporal 0.137 [0.078, 0.214] vs at most 0.078 (Holm p <= 0.010).
  2. The gain comes from fusing techniques with software (+0.095 [0.064, 0.130] over software-only, p = 5.0e-6, B = 200,000) and from specificity weighting (+0.025 [0.003, 0.048], Holm p = 0.041).
  3. The ablation without the TTP-profile term scores higher on 5-fold top-1 (0.487 vs 0.454, not significant with group clustering: two-sided p = 0.088), and the temporal split does not decide it.
  4. On the 25 campaigns, with leakage removed, the edge over IOC correlation (+0.046 [-0.027, 0.147]) is not significant (exact p = 0.25).
  5. Tradecraft-only LOW verdicts are unreliable (A3 0/10; R-temporal LOW 18/40); MEDIUM verdicts are reliable (R 5-fold: 79/83, 0.95 [0.88, 0.99]).
  6. The false-flag rules matter only when a hard decoy anchor is planted (level 2: -0.23 [-0.37, -0.11]).
  7. Imphash and TLSH genetics are precise but rare anchors, mostly re-identify commodity families, and DRAGNET does not beat a plain lookup on them.

Per-report ablations

Figure 1. Per-report cases, 5-fold: top-1, coverage and selective accuracy of DRAGNET, its ablations and two baselines, with 95% CIs (top-1 group-cluster bootstrap, rates Clopper-Pearson).

A1-LF methods

Figure 2. The 25 campaigns with leave-report-out profiles: top-1, coverage and wrong commitments per method, with 95% CIs.

False-flag stress test

Figure 3. Confident decoy attributions at decoy levels 1 and 2, mean over 10 decoy seeds (whiskers: seed range), with the rules' level-2 effect and its case-clustered CI.

Reliability

Figure 4. Reliability of DRAGNET's top-hypothesis score on the temporal test set, raw and after the isotonic map fitted on pre-2022 cases (ECE with group-cluster bootstrap CIs).

Full generated results

Generated by python scripts/run_benchmarks.py; do not edit by hand.

Source: bench run 37092539472 on commit 2d7bacc (2026-10-03, Python 3.12.14, PYTHONHASHSEED=0).

Knowledge graph: MITRE ATT&CK Enterprise v19.2 - 176 groups, 1600 distinct signal keys, 117 groups with a MISP sponsor-state mapping.

Conventions. A baseline commits to an actor only when that actor is its unique top score (a tie at the top is an abstention); 'wrong if ties broken at random' scores the alternative convention in expectation. DRAGNET commits at MEDIUM or HIGH; 'wrong actor named (any grade)' also counts its LOW verdicts. Ranking metrics score ties in expectation.

A1-LF - ATT&CK campaigns, leave-report-out profiles (headline protocol)

Before each campaign is attributed, every group 'uses' edge whose citations are a subset of the campaign's own citations is removed (mean 3.680 edges per case). This prevents profiles written from the campaign's own reports from answering the question.

method n top-1 coverage selective acc. wrong actor named (any grade) wrong at MEDIUM+ / committed wrong if ties broken at random
dragnet 25 0.400 0.400 0.800 0.080 0.000 0.000
ttp-jaccard 25 0.120 0.960 0.125 0.840 0.840 0.840
ttp-cosine 25 0.080 0.960 0.083 0.880 0.880 0.880
ttp-binary-bayes 25 0.000 0.760 0.000 0.760 0.760 0.960
ioc-correlation 25 0.354 0.320 1.000 0.000 0.000 0.326
code-only 25 0.341 0.360 0.889 0.040 0.040 0.099

95% intervals (A1-LF): top-1 by case bootstrap; proportions exact Clopper-Pearson.

method top-1 coverage selective acc. wrong (any grade) wrong at MEDIUM+ / committed
dragnet 0.400 [0.200, 0.600] 0.400 [0.211, 0.613] 0.800 [0.444, 0.975] 0.080 [0.010, 0.260] 0.000 [0.000, 0.137]
ttp-jaccard 0.120 [0.000, 0.280] 0.960 [0.796, 0.999] 0.125 [0.027, 0.324] 0.840 [0.639, 0.955] 0.840 [0.639, 0.955]
ttp-cosine 0.080 [0.000, 0.200] 0.960 [0.796, 0.999] 0.083 [0.010, 0.270] 0.880 [0.688, 0.975] 0.880 [0.688, 0.975]
ttp-binary-bayes 0.000 [0.000, 0.000] 0.760 [0.549, 0.906] 0.000 [0.000, 0.176] 0.760 [0.549, 0.906] 0.760 [0.549, 0.906]
ioc-correlation 0.354 [0.180, 0.540] 0.320 [0.149, 0.535] 1.000 [0.631, 1.000] 0.000 [0.000, 0.137] 0.000 [0.000, 0.137]
code-only 0.341 [0.161, 0.523] 0.360 [0.180, 0.575] 0.889 [0.518, 0.997] 0.040 [0.001, 0.204] 0.040 [0.001, 0.204]

Paired top-1 difference, DRAGNET minus method (A1-LF): case bootstrap 95% CI. Sign-flip test (one-sided, DRAGNET better): exact when at most 20 discordant units, otherwise Monte Carlo with B = 200,000 sign flips, p = (k+1)/(B+1); Holm-adjusted across methods.

method diff 95% CI discordant cases p (one-sided) p (Holm) p (two-sided)
ttp-jaccard 0.280 [0.120, 0.480] 7 0.008 (exact) 0.047 0.016
ttp-cosine 0.320 [0.160, 0.520] 8 0.004 (exact) 0.027 0.008
ttp-binary-bayes 0.400 [0.200, 0.600] 10 9.8e-04 (exact) 0.008 0.002
ioc-correlation 0.046 [-0.027, 0.147] 4 0.250 (exact) 1.000 0.500
code-only 0.059 [-0.003, 0.159] 3 0.250 (exact) 1.000 0.500
dragnet-no-spec 0.040 [0.000, 0.120] 1 0.500 (exact) 1.000 1.000
dragnet-no-ttpsim 0.040 [0.000, 0.120] 1 0.500 (exact) 1.000 1.000
dragnet-no-ff 0.000 [0.000, 0.000] 0 1.000 (exact) 1.000 1.000

DRAGNET verdicts by stated grade (A1-LF), exact 95% CI:

grade n named correct accuracy [95% CI]
HIGH 0 0 n/a [n/a, n/a]
MEDIUM 3 3 1.000 [0.292, 1.000]
LOW 7 5 0.714 [0.290, 0.963]

Selective risk (error rate among the most confident cases) at fixed coverage, A1-LF; ties in confidence resolved in expectation. Lower is better.

method risk @20% risk @40% risk @60% risk @100% AURC
dragnet 0.000 0.200 0.333 0.600 0.281
ttp-jaccard 1.000 0.900 0.933 0.880 0.925
ttp-cosine 0.800 0.800 0.867 0.920 0.793
ttp-binary-bayes 1.000 1.000 1.000 1.000 1.000
ioc-correlation 0.200 0.300 0.411 0.646 0.367
code-only 0.200 0.150 0.431 0.659 0.367

Ablations (A1-LF)

method n top-1 coverage selective acc. wrong actor named (any grade) wrong at MEDIUM+ / committed wrong if ties broken at random
dragnet 25 0.400 0.400 0.800 0.080 0.000 0.000
dragnet-no-spec 25 0.360 0.440 0.818 0.080 0.000 0.000
dragnet-no-ttpsim 25 0.360 0.360 0.889 0.040 0.000 0.000
dragnet-no-ff 25 0.400 0.400 0.800 0.080 0.000 0.000

Ranking and calibration (A1-LF):

method in KG top-3 top-5 MRR Brier (top-1) ECE Brier (dist.)
dragnet 25 0.400 0.400 0.424 0.116 0.168 0.981
ttp-jaccard 25 0.160 0.160 0.172 0.116 0.102 0.990
ttp-cosine 25 0.200 0.240 0.184 0.076 0.061 0.987
ttp-binary-bayes 25 0.000 0.000 0.018 0.001 0.030 0.999
ioc-correlation 25 0.402 0.404 0.378 0.154 0.205 0.853
code-only 25 0.364 0.367 0.355 0.115 0.149 0.791

A1 - ATT&CK campaigns vs v19.2 group profiles (retrospective, leaky upper bound)

In 18 of 25 campaigns the true group's profile already lists at least one of the campaign's own software; 14 of DRAGNET's 14 correct named verdicts are among them. Read A1 as an upper bound; A1-LF is the controlled version.

method n top-1 coverage selective acc. wrong actor named (any grade) wrong at MEDIUM+ / committed wrong if ties broken at random
dragnet 25 0.680 0.600 0.933 0.040 0.000 0.000
ttp-jaccard 25 0.120 0.960 0.125 0.840 0.840 0.840
ttp-cosine 25 0.080 0.960 0.083 0.880 0.880 0.880
ttp-binary-bayes 25 0.000 0.760 0.000 0.760 0.760 0.960
ioc-correlation 25 0.560 0.520 1.000 0.000 0.000 0.280
code-only 25 0.540 0.560 0.929 0.040 0.040 0.060

95% intervals (A1): top-1 by case bootstrap; proportions exact Clopper-Pearson.

method top-1 coverage selective acc. wrong (any grade) wrong at MEDIUM+ / committed
dragnet 0.680 [0.480, 0.840] 0.600 [0.387, 0.789] 0.933 [0.681, 0.998] 0.040 [0.001, 0.204] 0.000 [0.000, 0.137]
ttp-jaccard 0.120 [0.000, 0.280] 0.960 [0.796, 0.999] 0.125 [0.027, 0.324] 0.840 [0.639, 0.955] 0.840 [0.639, 0.955]
ttp-cosine 0.080 [0.000, 0.200] 0.960 [0.796, 0.999] 0.083 [0.010, 0.270] 0.880 [0.688, 0.975] 0.880 [0.688, 0.975]
ttp-binary-bayes 0.000 [0.000, 0.000] 0.760 [0.549, 0.906] 0.000 [0.000, 0.176] 0.760 [0.549, 0.906] 0.760 [0.549, 0.906]
ioc-correlation 0.560 [0.381, 0.733] 0.520 [0.313, 0.722] 1.000 [0.753, 1.000] 0.000 [0.000, 0.137] 0.000 [0.000, 0.137]
code-only 0.540 [0.360, 0.720] 0.560 [0.349, 0.756] 0.929 [0.661, 0.998] 0.040 [0.001, 0.204] 0.040 [0.001, 0.204]

Paired top-1 difference, DRAGNET minus method (A1): case bootstrap 95% CI. Sign-flip test (one-sided, DRAGNET better): exact when at most 20 discordant units, otherwise Monte Carlo with B = 200,000 sign flips, p = (k+1)/(B+1); Holm-adjusted across methods.

method diff 95% CI discordant cases p (one-sided) p (Holm) p (two-sided)
ttp-jaccard 0.560 [0.360, 0.760] 14 6.1e-05 (exact) 3.7e-04 1.2e-04
ttp-cosine 0.600 [0.400, 0.760] 15 3.1e-05 (exact) 2.1e-04 6.1e-05
ttp-binary-bayes 0.680 [0.480, 0.840] 17 7.6e-06 (exact) 6.1e-05 1.5e-05
ioc-correlation 0.120 [0.019, 0.248] 6 0.062 (exact) 0.312 0.125
code-only 0.140 [0.020, 0.280] 4 0.062 (exact) 0.312 0.125
dragnet-no-spec 0.080 [0.000, 0.200] 2 0.250 (exact) 0.750 0.500
dragnet-no-ttpsim 0.080 [0.000, 0.200] 2 0.250 (exact) 0.750 0.500
dragnet-no-ff 0.000 [0.000, 0.000] 0 1.000 (exact) 1.000 1.000

DRAGNET verdicts by stated grade (A1), exact 95% CI:

grade n named correct accuracy [95% CI]
HIGH 0 0 n/a [n/a, n/a]
MEDIUM 5 5 1.000 [0.478, 1.000]
LOW 10 9 0.900 [0.555, 0.997]

Ablations (A1)

method n top-1 coverage selective acc. wrong actor named (any grade) wrong at MEDIUM+ / committed wrong if ties broken at random
dragnet 25 0.680 0.600 0.933 0.040 0.000 0.000
dragnet-no-spec 25 0.600 0.600 0.933 0.040 0.000 0.000
dragnet-no-ttpsim 25 0.600 0.560 0.929 0.040 0.000 0.000
dragnet-no-ff 25 0.680 0.600 0.933 0.040 0.000 0.000

Ranking and calibration (A1):

method in KG top-3 top-5 MRR Brier (top-1) ECE Brier (dist.)
dragnet 25 0.680 0.680 0.688 0.162 0.276 0.892
ttp-jaccard 25 0.160 0.160 0.169 0.116 0.102 0.990
ttp-cosine 25 0.160 0.200 0.166 0.076 0.061 0.987
ttp-binary-bayes 25 0.000 0.000 0.018 0.001 0.030 0.999
ioc-correlation 25 0.620 0.633 0.595 0.173 0.216 0.643
code-only 25 0.560 0.560 0.550 0.145 0.184 0.623

A2 - stale profiles: v19.2 campaigns vs ATT&CK v10.1 (2021-11-10) group profiles

ATT&CK v10.1 has no campaign objects, so this is not a temporal hold-out of campaigns: it measures attribution against older, thinner profiles in an open world (groups added after v10.1 are unknown to the graph). The 'later' rows restrict to the 12 campaigns whose recorded activity (first_seen) began after the v10.1 release.

method n top-1 coverage selective acc. wrong actor named (any grade) wrong at MEDIUM+ / committed wrong if ties broken at random out-of-KG abstain
dragnet 25 0.412 0.529 0.778 0.160 0.000 0.000 0.750
ttp-jaccard 25 0.235 0.941 0.250 0.800 0.800 0.800 0.000
ttp-cosine 25 0.235 0.941 0.250 0.800 0.800 0.800 0.000
ttp-binary-bayes 25 0.000 0.706 0.000 0.760 0.760 0.960 0.125
ioc-correlation 25 0.405 0.471 0.750 0.080 0.080 0.365 1.000
code-only 25 0.360 0.412 0.857 0.080 0.080 0.155 0.875

95% intervals (A2): top-1 by case bootstrap; proportions exact Clopper-Pearson.

method top-1 coverage selective acc. wrong (any grade) wrong at MEDIUM+ / committed
dragnet 0.412 [0.176, 0.647] 0.529 [0.278, 0.770] 0.778 [0.400, 0.972] 0.160 [0.045, 0.361] 0.000 [0.000, 0.137]
ttp-jaccard 0.235 [0.059, 0.471] 0.941 [0.713, 0.999] 0.250 [0.073, 0.524] 0.800 [0.593, 0.932] 0.800 [0.593, 0.932]
ttp-cosine 0.235 [0.059, 0.471] 0.941 [0.713, 0.999] 0.250 [0.073, 0.524] 0.800 [0.593, 0.932] 0.800 [0.593, 0.932]
ttp-binary-bayes 0.000 [0.000, 0.000] 0.706 [0.440, 0.897] 0.000 [0.000, 0.265] 0.760 [0.549, 0.906] 0.760 [0.549, 0.906]
ioc-correlation 0.405 [0.188, 0.631] 0.471 [0.230, 0.722] 0.750 [0.349, 0.968] 0.080 [0.010, 0.260] 0.080 [0.010, 0.260]
code-only 0.360 [0.129, 0.596] 0.412 [0.184, 0.671] 0.857 [0.421, 0.996] 0.080 [0.010, 0.260] 0.080 [0.010, 0.260]

DRAGNET verdicts by stated grade (A2), exact 95% CI:

grade n named correct accuracy [95% CI]
HIGH 0 0 n/a [n/a, n/a]
MEDIUM 4 4 1.000 [0.398, 1.000]
LOW 7 3 0.429 [0.099, 0.816]

A2-later (activity began after v10.1)

method n top-1 coverage selective acc. wrong actor named (any grade) wrong at MEDIUM+ / committed wrong if ties broken at random out-of-KG abstain
dragnet 12 0.143 0.143 1.000 0.000 0.000 0.000 1.000
ttp-jaccard 12 0.143 1.000 0.143 0.917 0.917 0.917 0.000
ttp-cosine 12 0.143 1.000 0.143 0.917 0.917 0.917 0.000
ttp-binary-bayes 12 0.000 0.714 0.000 0.750 0.750 1.000 0.200
ioc-correlation 12 0.214 0.286 0.500 0.083 0.083 0.292 1.000
code-only 12 0.152 0.286 0.500 0.083 0.083 0.161 1.000

95% intervals (A2-later): top-1 by case bootstrap; proportions exact Clopper-Pearson.

method top-1 coverage selective acc. wrong (any grade) wrong at MEDIUM+ / committed
dragnet 0.143 [0.000, 0.429] 0.143 [0.004, 0.579] 1.000 [0.025, 1.000] 0.000 [0.000, 0.265] 0.000 [0.000, 0.265]
ttp-jaccard 0.143 [0.000, 0.429] 1.000 [0.590, 1.000] 0.143 [0.004, 0.579] 0.917 [0.615, 0.998] 0.917 [0.615, 0.998]
ttp-cosine 0.143 [0.000, 0.429] 1.000 [0.590, 1.000] 0.143 [0.004, 0.579] 0.917 [0.615, 0.998] 0.917 [0.615, 0.998]
ttp-binary-bayes 0.000 [0.000, 0.000] 0.714 [0.290, 0.963] 0.000 [0.000, 0.522] 0.750 [0.428, 0.945] 0.750 [0.428, 0.945]
ioc-correlation 0.214 [0.000, 0.500] 0.286 [0.037, 0.710] 0.500 [0.013, 0.987] 0.083 [0.002, 0.385] 0.083 [0.002, 0.385]
code-only 0.152 [0.000, 0.438] 0.286 [0.037, 0.710] 0.500 [0.013, 0.987] 0.083 [0.002, 0.385] 0.083 [0.002, 0.385]

A3 - novel malware family attributed from its documented techniques only

method n top-1 coverage selective acc. wrong actor named (any grade) wrong at MEDIUM+ / committed wrong if ties broken at random
dragnet 411 0.063 0.024 0.000 0.024 0.000 0.000
ttp-jaccard 411 0.029 0.951 0.031 0.922 0.922 0.971
ttp-cosine 411 0.063 1.000 0.063 0.937 0.937 0.937
ttp-binary-bayes 411 0.000 0.842 0.000 0.842 0.842 1.000

95% intervals (A3, top-1 clustered by group): top-1 by cluster bootstrap; proportions exact Clopper-Pearson.

method top-1 coverage selective acc. wrong (any grade) wrong at MEDIUM+ / committed
dragnet 0.063 [0.036, 0.095] 0.024 [0.012, 0.044] 0.000 [0.000, 0.308] 0.024 [0.012, 0.044] 0.000 [0.000, 0.009]
ttp-jaccard 0.029 [0.011, 0.051] 0.951 [0.926, 0.970] 0.031 [0.016, 0.053] 0.922 [0.892, 0.946] 0.922 [0.892, 0.946]
ttp-cosine 0.063 [0.036, 0.095] 1.000 [0.991, 1.000] 0.063 [0.042, 0.091] 0.937 [0.909, 0.958] 0.937 [0.909, 0.958]
ttp-binary-bayes 0.000 [0.000, 0.000] 0.842 [0.803, 0.876] 0.000 [0.000, 0.011] 0.842 [0.803, 0.876] 0.842 [0.803, 0.876]

DRAGNET verdicts by stated grade (A3), exact 95% CI:

grade n named correct accuracy [95% CI]
HIGH 0 0 n/a [n/a, n/a]
MEDIUM 0 0 n/a [n/a, n/a]
LOW 10 0 0.000 [0.000, 0.308]

R - per-report ATT&CK cases (group x cited report)

637 cases over 161 groups: the techniques and software one cited report documents for one group (at least 3 techniques). Labels are ATT&CK's own attribution. k-fold: 5 folds by report; each fold's reports are removed from the profiles before its cases are attributed. Temporal: every citation dated 2022 or later is removed from the profiles, and the 219 cases from reports dated 2022 or later are attributed. Top-1 CIs are clustered by group.

Citation dating: of 1947 citations on 'uses' edges, 1852 are dated from their reference description '(YYYY, Month DD)', 12 only from a year in the citation key, and 83 have no recoverable date ('n.d.'). 52 citations whose key has no year are dated 2022+ by their description and held out (an earlier version dated by key only and kept them in the profiles); 26 keys carry a year that differs from the description. Held out: 547 citations (630 when undated citations are also held out, the sensitivity rows below).

R-kfold (n=637)

method n top-1 coverage selective acc. wrong actor named (any grade) wrong at MEDIUM+ / committed wrong if ties broken at random
dragnet 637 0.454 0.429 0.853 0.063 0.006 0.006
ttp-jaccard 637 0.126 0.936 0.133 0.812 0.812 0.874
ttp-cosine 637 0.218 0.983 0.222 0.765 0.765 0.782
ttp-binary-bayes 637 0.018 0.702 0.022 0.686 0.686 0.982
ioc-correlation 637 0.301 0.349 0.788 0.074 0.074 0.313
code-only 637 0.310 0.323 0.913 0.028 0.028 0.144
dragnet-ttp-only 637 0.218 0.096 0.525 0.046 0.000 0.000
dragnet-software-only 637 0.358 0.341 0.912 0.030 0.002 0.002
dragnet-no-ttpsim 637 0.487 0.336 0.921 0.027 0.002 0.002
dragnet-no-spec 637 0.429 0.498 0.703 0.148 0.003 0.003
dragnet-no-ff 637 0.454 0.435 0.848 0.066 0.006 0.006

95% intervals (R-kfold): top-1 by cluster bootstrap; proportions exact Clopper-Pearson.

method top-1 coverage selective acc. wrong (any grade) wrong at MEDIUM+ / committed
dragnet 0.454 [0.401, 0.507] 0.429 [0.390, 0.468] 0.853 [0.806, 0.893] 0.063 [0.045, 0.085] 0.006 [0.002, 0.016]
ttp-jaccard 0.126 [0.084, 0.180] 0.936 [0.914, 0.953] 0.133 [0.106, 0.162] 0.812 [0.779, 0.841] 0.812 [0.779, 0.841]
ttp-cosine 0.218 [0.160, 0.285] 0.983 [0.969, 0.991] 0.222 [0.190, 0.257] 0.765 [0.730, 0.797] 0.765 [0.730, 0.797]
ttp-binary-bayes 0.018 [0.008, 0.032] 0.702 [0.665, 0.737] 0.022 [0.011, 0.041] 0.686 [0.648, 0.722] 0.686 [0.648, 0.722]
ioc-correlation 0.301 [0.245, 0.356] 0.349 [0.311, 0.387] 0.788 [0.729, 0.840] 0.074 [0.055, 0.097] 0.074 [0.055, 0.097]
code-only 0.310 [0.251, 0.366] 0.323 [0.287, 0.361] 0.913 [0.865, 0.947] 0.028 [0.017, 0.044] 0.028 [0.017, 0.044]
dragnet-ttp-only 0.218 [0.160, 0.285] 0.096 [0.074, 0.121] 0.525 [0.393, 0.654] 0.046 [0.031, 0.065] 0.000 [0.000, 0.006]
dragnet-software-only 0.358 [0.302, 0.415] 0.341 [0.304, 0.379] 0.912 [0.867, 0.946] 0.030 [0.018, 0.046] 0.002 [0.000, 0.009]
dragnet-no-ttpsim 0.487 [0.423, 0.541] 0.336 [0.299, 0.374] 0.921 [0.876, 0.953] 0.027 [0.016, 0.042] 0.002 [0.000, 0.009]
dragnet-no-spec 0.429 [0.372, 0.486] 0.498 [0.458, 0.537] 0.703 [0.650, 0.753] 0.148 [0.121, 0.178] 0.003 [0.000, 0.011]
dragnet-no-ff 0.454 [0.401, 0.507] 0.435 [0.396, 0.474] 0.848 [0.801, 0.888] 0.066 [0.048, 0.088] 0.006 [0.002, 0.016]

Paired top-1 difference, DRAGNET minus method (R-kfold), clustered by threat group like the marginal CIs: group-cluster bootstrap 95% CI; the sign-flip test flips whole groups (per-group summed differences). Sign-flip test (one-sided, DRAGNET better): exact when at most 20 discordant units, otherwise Monte Carlo with B = 200,000 sign flips, p = (k+1)/(B+1); Holm-adjusted across methods. The last column is the case-level test (cases treated as independent), kept for reference.

method diff 95% CI (group cluster) discordant cases / groups p (one-sided) p (Holm) p (two-sided) case-level p (Holm)
ttp-jaccard 0.327 [0.282, 0.372] 218 / 81 5.0e-06 (MC, B = 200,000) 5.0e-05 5.0e-06 5.0e-06 (5.0e-05)
ttp-cosine 0.235 [0.186, 0.280] 158 / 67 5.0e-06 (MC, B = 200,000) 5.0e-05 5.0e-06 5.0e-06 (5.0e-05)
ttp-binary-bayes 0.436 [0.384, 0.489] 284 / 91 5.0e-06 (MC, B = 200,000) 5.0e-05 5.0e-06 5.0e-06 (5.0e-05)
ioc-correlation 0.153 [0.119, 0.192] 152 / 73 5.0e-06 (MC, B = 200,000) 5.0e-05 5.0e-06 5.0e-06 (5.0e-05)
code-only 0.144 [0.108, 0.184] 130 / 66 5.0e-06 (MC, B = 200,000) 5.0e-05 5.0e-06 5.0e-06 (5.0e-05)
dragnet-ttp-only 0.235 [0.186, 0.280] 158 / 67 5.0e-06 (MC, B = 200,000) 5.0e-05 5.0e-06 5.0e-06 (5.0e-05)
dragnet-software-only 0.095 [0.064, 0.130] 108 / 57 5.0e-06 (MC, B = 200,000) 5.0e-05 5.0e-06 5.0e-06 (5.0e-05)
dragnet-no-ttpsim -0.034 [-0.067, 0.004] 98 / 45 0.956 (MC, B = 200,000) 1.000 0.088 0.993 (1.000)
dragnet-no-spec 0.025 [0.003, 0.048] 38 / 28 0.014 (MC, B = 200,000) 0.041 0.028 0.007 (0.020)
dragnet-no-ff 0.000 [0.000, 0.000] 0 / 0 1.000 (exact) 1.000 1.000 1.000 (1.000)

DRAGNET verdicts by stated grade (R-kfold), exact 95% CI:

grade n named correct accuracy [95% CI]
HIGH 0 0 n/a [n/a, n/a]
MEDIUM 83 79 0.952 [0.881, 0.987]
LOW 190 154 0.811 [0.747, 0.864]

Selective risk (error rate among the most confident cases) at fixed coverage, R-kfold; ties in confidence resolved in expectation. Lower is better.

method risk @20% risk @40% risk @60% risk @100% AURC
dragnet 0.031 0.129 0.343 0.546 0.247
ttp-jaccard 0.827 0.833 0.849 0.874 0.824
ttp-cosine 0.677 0.722 0.743 0.782 0.708
ttp-binary-bayes 0.953 0.971 0.974 0.982 0.950
ioc-correlation 0.109 0.370 0.499 0.699 0.402
code-only 0.048 0.238 0.483 0.690 0.353
dragnet-ttp-only 0.701 0.729 0.741 0.782 0.727
dragnet-software-only 0.046 0.158 0.403 0.642 0.295
dragnet-no-ttpsim 0.024 0.125 0.322 0.513 0.229
dragnet-no-spec 0.252 0.310 0.393 0.571 0.350
dragnet-no-ff 0.031 0.129 0.343 0.546 0.247

Selective-risk differences, DRAGNET minus method (R-kfold); group-cluster bootstrap 95% CIs (1,000 resamples). Negative = DRAGNET has the lower risk.

method risk @20% diff [95% CI] AURC diff [95% CI]
ttp-jaccard -0.795 [-0.898, -0.674] -0.577 [-0.651, -0.497]
ttp-cosine -0.646 [-0.790, -0.441] -0.461 [-0.546, -0.368]
ttp-binary-bayes -0.921 [-0.967, -0.861] -0.703 [-0.766, -0.635]
ioc-correlation -0.077 [-0.262, -0.007] -0.154 [-0.208, -0.113]
code-only -0.016 [-0.065, 0.024] -0.106 [-0.150, -0.072]

R-temporal (n=219)

method n top-1 coverage selective acc. wrong actor named (any grade) wrong at MEDIUM+ / committed wrong if ties broken at random
dragnet 219 0.137 0.192 0.476 0.100 0.000 0.000
ttp-jaccard 219 0.050 0.932 0.049 0.886 0.886 0.950
ttp-cosine 219 0.078 1.000 0.078 0.922 0.922 0.922
ttp-binary-bayes 219 0.031 0.785 0.035 0.758 0.758 0.969
ioc-correlation 219 0.052 0.151 0.273 0.110 0.110 0.346
code-only 219 0.062 0.096 0.524 0.046 0.046 0.148
dragnet-ttp-only 219 0.078 0.055 0.417 0.032 0.000 0.000
dragnet-software-only 219 0.076 0.100 0.545 0.046 0.000 0.000
dragnet-no-ttpsim 219 0.126 0.100 0.591 0.041 0.000 0.000
dragnet-no-spec 219 0.123 0.274 0.317 0.187 0.005 0.005
dragnet-no-ff 219 0.137 0.192 0.476 0.100 0.000 0.000

95% intervals (R-temporal): top-1 by cluster bootstrap; proportions exact Clopper-Pearson.

method top-1 coverage selective acc. wrong (any grade) wrong at MEDIUM+ / committed
dragnet 0.137 [0.078, 0.214] 0.192 [0.142, 0.250] 0.476 [0.320, 0.636] 0.100 [0.064, 0.148] 0.000 [0.000, 0.017]
ttp-jaccard 0.050 [0.020, 0.091] 0.932 [0.890, 0.961] 0.049 [0.024, 0.088] 0.886 [0.836, 0.925] 0.886 [0.836, 0.925]
ttp-cosine 0.078 [0.034, 0.139] 1.000 [0.983, 1.000] 0.078 [0.046, 0.121] 0.922 [0.879, 0.954] 0.922 [0.879, 0.954]
ttp-binary-bayes 0.031 [0.000, 0.075] 0.785 [0.725, 0.838] 0.035 [0.013, 0.074] 0.758 [0.696, 0.813] 0.758 [0.696, 0.813]
ioc-correlation 0.052 [0.024, 0.090] 0.151 [0.106, 0.205] 0.273 [0.133, 0.455] 0.110 [0.071, 0.159] 0.110 [0.071, 0.159]
code-only 0.062 [0.027, 0.107] 0.096 [0.060, 0.143] 0.524 [0.298, 0.743] 0.046 [0.022, 0.082] 0.046 [0.022, 0.082]
dragnet-ttp-only 0.078 [0.034, 0.139] 0.055 [0.029, 0.094] 0.417 [0.152, 0.723] 0.032 [0.013, 0.065] 0.000 [0.000, 0.017]
dragnet-software-only 0.076 [0.038, 0.125] 0.100 [0.064, 0.148] 0.545 [0.322, 0.756] 0.046 [0.022, 0.082] 0.000 [0.000, 0.017]
dragnet-no-ttpsim 0.126 [0.069, 0.194] 0.100 [0.064, 0.148] 0.591 [0.364, 0.793] 0.041 [0.019, 0.077] 0.000 [0.000, 0.017]
dragnet-no-spec 0.123 [0.070, 0.193] 0.274 [0.216, 0.338] 0.317 [0.203, 0.450] 0.187 [0.138, 0.245] 0.005 [0.000, 0.025]
dragnet-no-ff 0.137 [0.078, 0.214] 0.192 [0.142, 0.250] 0.476 [0.320, 0.636] 0.100 [0.064, 0.148] 0.000 [0.000, 0.017]

Paired top-1 difference, DRAGNET minus method (R-temporal), clustered by threat group like the marginal CIs: group-cluster bootstrap 95% CI; the sign-flip test flips whole groups (per-group summed differences). Sign-flip test (one-sided, DRAGNET better): exact when at most 20 discordant units, otherwise Monte Carlo with B = 200,000 sign flips, p = (k+1)/(B+1); Holm-adjusted across methods. The last column is the case-level test (cases treated as independent), kept for reference.

method diff 95% CI (group cluster) discordant cases / groups p (one-sided) p (Holm) p (two-sided) case-level p (Holm)
ttp-jaccard 0.087 [0.045, 0.137] 21 / 16 4.6e-05 (exact) 4.6e-04 9.2e-05 5.0e-06 (5.0e-05)
ttp-cosine 0.059 [0.021, 0.108] 13 / 9 0.002 (exact) 0.010 0.004 1.2e-04 (6.1e-04)
ttp-binary-bayes 0.106 [0.051, 0.169] 27 / 18 5.3e-05 (exact) 4.8e-04 1.1e-04 5.0e-06 (5.0e-05)
ioc-correlation 0.085 [0.041, 0.144] 26 / 16 6.1e-05 (exact) 4.9e-04 1.2e-04 5.0e-06 (5.0e-05)
code-only 0.075 [0.029, 0.136] 26 / 16 7.3e-04 (exact) 0.004 0.001 3.0e-05 (2.1e-04)
dragnet-ttp-only 0.059 [0.021, 0.108] 13 / 9 0.002 (exact) 0.010 0.004 1.2e-04 (6.1e-04)
dragnet-software-only 0.061 [0.025, 0.110] 20 / 13 4.9e-04 (exact) 0.003 9.8e-04 3.1e-05 (2.1e-04)
dragnet-no-ttpsim 0.011 [-0.027, 0.055] 17 / 10 0.309 (exact) 0.750 0.617 0.262 (0.562)
dragnet-no-spec 0.014 [-0.009, 0.040] 5 / 4 0.250 (exact) 0.750 0.500 0.188 (0.562)
dragnet-no-ff 0.000 [0.000, 0.000] 0 / 0 1.000 (exact) 1.000 1.000 1.000 (1.000)

DRAGNET verdicts by stated grade (R-temporal), exact 95% CI:

grade n named correct accuracy [95% CI]
HIGH 0 0 n/a [n/a, n/a]
MEDIUM 2 2 1.000 [0.158, 1.000]
LOW 40 18 0.450 [0.293, 0.615]

Selective risk (error rate among the most confident cases) at fixed coverage, R-temporal; ties in confidence resolved in expectation. Lower is better.

method risk @20% risk @40% risk @60% risk @100% AURC
dragnet 0.523 0.739 0.802 0.863 0.692
ttp-jaccard 0.898 0.903 0.924 0.950 0.896
ttp-cosine 0.795 0.864 0.893 0.922 0.862
ttp-binary-bayes 0.909 0.932 0.952 0.969 0.931
ioc-correlation 0.798 0.871 0.914 0.948 0.858
code-only 0.693 0.846 0.897 0.938 0.814
dragnet-ttp-only 0.818 0.886 0.916 0.922 0.840
dragnet-software-only 0.648 0.810 0.872 0.924 0.771
dragnet-no-ttpsim 0.614 0.795 0.840 0.874 0.725
dragnet-no-spec 0.727 0.761 0.832 0.877 0.768
dragnet-no-ff 0.523 0.739 0.802 0.863 0.692

Selective-risk differences, DRAGNET minus method (R-temporal); group-cluster bootstrap 95% CIs (1,000 resamples). Negative = DRAGNET has the lower risk.

method risk @20% diff [95% CI] AURC diff [95% CI]
ttp-jaccard -0.375 [-0.543, -0.127] -0.203 [-0.314, -0.100]
ttp-cosine -0.273 [-0.457, -0.024] -0.169 [-0.286, -0.052]
ttp-binary-bayes -0.386 [-0.559, -0.154] -0.239 [-0.357, -0.130]
ioc-correlation -0.275 [-0.414, -0.086] -0.166 [-0.249, -0.082]
code-only -0.171 [-0.320, 0.026] -0.122 [-0.213, -0.041]

R-temporal sensitivity: undated citations also held out (n=219)

method n top-1 coverage selective acc. wrong actor named (any grade) wrong at MEDIUM+ / committed wrong if ties broken at random
dragnet 219 0.078 0.178 0.308 0.123 0.005 0.005
ttp-jaccard 219 0.021 0.927 0.020 0.909 0.909 0.979
ttp-cosine 219 0.032 1.000 0.032 0.968 0.968 0.968
ttp-binary-bayes 219 0.011 0.744 0.012 0.735 0.735 0.989
ioc-correlation 219 0.041 0.146 0.219 0.114 0.114 0.352
code-only 219 0.044 0.096 0.381 0.059 0.059 0.157
dragnet-ttp-only 219 0.032 0.064 0.071 0.059 0.000 0.000
dragnet-software-only 219 0.054 0.096 0.381 0.059 0.000 0.000
dragnet-no-ttpsim 219 0.098 0.091 0.450 0.050 0.000 0.000
dragnet-no-spec 219 0.078 0.269 0.220 0.210 0.005 0.005
dragnet-no-ff 219 0.078 0.178 0.308 0.123 0.005 0.005

95% intervals (R-temporal, undated held out): top-1 by cluster bootstrap; proportions exact Clopper-Pearson.

method top-1 coverage selective acc. wrong (any grade) wrong at MEDIUM+ / committed
dragnet 0.078 [0.035, 0.130] 0.178 [0.130, 0.235] 0.308 [0.170, 0.476] 0.123 [0.083, 0.174] 0.005 [0.000, 0.025]
ttp-jaccard 0.021 [0.004, 0.045] 0.927 [0.884, 0.958] 0.020 [0.005, 0.050] 0.909 [0.862, 0.943] 0.909 [0.862, 0.943]
ttp-cosine 0.032 [0.010, 0.063] 1.000 [0.983, 1.000] 0.032 [0.013, 0.065] 0.968 [0.935, 0.987] 0.968 [0.935, 0.987]
ttp-binary-bayes 0.011 [0.000, 0.039] 0.744 [0.681, 0.801] 0.012 [0.001, 0.044] 0.735 [0.671, 0.792] 0.735 [0.671, 0.792]
ioc-correlation 0.041 [0.013, 0.078] 0.146 [0.102, 0.200] 0.219 [0.093, 0.400] 0.114 [0.075, 0.164] 0.114 [0.075, 0.164]
code-only 0.044 [0.012, 0.084] 0.096 [0.060, 0.143] 0.381 [0.181, 0.616] 0.059 [0.032, 0.099] 0.059 [0.032, 0.099]
dragnet-ttp-only 0.032 [0.010, 0.063] 0.064 [0.035, 0.105] 0.071 [0.002, 0.339] 0.059 [0.032, 0.099] 0.000 [0.000, 0.017]
dragnet-software-only 0.054 [0.020, 0.096] 0.096 [0.060, 0.143] 0.381 [0.181, 0.616] 0.059 [0.032, 0.099] 0.000 [0.000, 0.017]
dragnet-no-ttpsim 0.098 [0.050, 0.155] 0.091 [0.057, 0.138] 0.450 [0.231, 0.685] 0.050 [0.025, 0.088] 0.000 [0.000, 0.017]
dragnet-no-spec 0.078 [0.035, 0.134] 0.269 [0.212, 0.333] 0.220 [0.123, 0.347] 0.210 [0.158, 0.270] 0.005 [0.000, 0.025]
dragnet-no-ff 0.078 [0.035, 0.130] 0.178 [0.130, 0.235] 0.308 [0.170, 0.476] 0.123 [0.083, 0.174] 0.005 [0.000, 0.025]

Paired top-1 difference, DRAGNET minus method (R-temporal, undated held out), clustered by threat group like the marginal CIs: group-cluster bootstrap 95% CI; the sign-flip test flips whole groups (per-group summed differences). Sign-flip test (one-sided, DRAGNET better): exact when at most 20 discordant units, otherwise Monte Carlo with B = 200,000 sign flips, p = (k+1)/(B+1); Holm-adjusted across methods. The last column is the case-level test (cases treated as independent), kept for reference.

method diff 95% CI (group cluster) discordant cases / groups p (one-sided) p (Holm) p (two-sided) case-level p (Holm)
ttp-jaccard 0.057 [0.022, 0.101] 13 / 9 0.002 (exact) 0.020 0.004 1.2e-04 (0.001)
ttp-cosine 0.046 [0.006, 0.094] 12 / 8 0.027 (exact) 0.137 0.055 0.003 (0.019)
ttp-binary-bayes 0.066 [0.011, 0.124] 20 / 12 0.010 (exact) 0.067 0.019 3.6e-04 (0.003)
ioc-correlation 0.037 [0.011, 0.071] 15 / 10 0.004 (exact) 0.035 0.008 9.8e-04 (0.008)
code-only 0.034 [0.011, 0.066] 14 / 10 0.004 (exact) 0.035 0.008 0.002 (0.014)
dragnet-ttp-only 0.046 [0.006, 0.094] 12 / 8 0.027 (exact) 0.137 0.055 0.003 (0.019)
dragnet-software-only 0.024 [0.005, 0.053] 12 / 8 0.016 (exact) 0.094 0.031 0.008 (0.031)
dragnet-no-ttpsim -0.020 [-0.055, 0.016] 17 / 10 0.883 (exact) 1.000 0.264 0.933 (1.000)
dragnet-no-spec 0.000 [-0.015, 0.011] 2 / 2 0.750 (exact) 1.000 1.000 0.750 (1.000)
dragnet-no-ff 0.000 [0.000, 0.000] 0 / 0 1.000 (exact) 1.000 1.000 1.000 (1.000)

DRAGNET verdicts by stated grade (R-temporal, undated held out), exact 95% CI:

grade n named correct accuracy [95% CI]
HIGH 0 0 n/a [n/a, n/a]
MEDIUM 1 0 0.000 [0.000, 0.975]
LOW 38 12 0.316 [0.175, 0.487]

Calibration of the top-hypothesis probability

Isotonic map fitted on 406 k-fold cases dated before 2022, evaluated on the 219 temporal test cases (CIs: group-cluster bootstrap, 1,000 resamples).

forecast ECE [95% CI] Brier [95% CI]
DRAGNET raw ACH score 0.157 [0.113, 0.210] 0.113 [0.095, 0.143]
DRAGNET isotonic 0.073 [0.055, 0.145] 0.100 [0.070, 0.145]
ttp-jaccard (normalised share, raw) 0.015 [0.004, 0.056] -
ttp-cosine (normalised share, raw) 0.040 [0.007, 0.102] -
ttp-binary-bayes (normalised share, raw) 0.028 [0.012, 0.058] -
ioc-correlation (normalised share, raw) 0.047 [0.027, 0.085] -
code-only (normalised share, raw) 0.046 [0.021, 0.085] -

G - comparison with Guru, Moss & Kochenderfer (2025)

Guru et al. (arXiv:2505.11547) attribute threat reports to 29 actors by scoring P(technique | actor), estimated from technique counts in training reports, against the techniques extracted from a test report (machine extraction with text-embedding-3-large over 727 reports); the metric is the mean rank of the true actor among the 29 (random = 15). Paper protocol: 70/20/10 split per actor; 10 weight matrices are trained, the best is selected on the validation split and its test mean rank is reported; the paper does not define its +/-. Their report corpus and extracted technique counts are not public in reusable form, so the paper's numbers cannot be reproduced exactly. Our adaptation keeps their scorer, actor set and 70/20/10 split per actor, but evaluates every one of 10 random splits on its test part (the 20% validation part is unused) and uses ATT&CK's human-curated per-report technique lists (261 reports). Human technique lists are cleaner than machine extraction, so the adaptation is an optimistic upper bound for their pipeline. DRAGNET and the other methods use graphs from which every validation/test report has been removed.

setting mean rank of true actor (of 29) 95% CI (pooled, actor cluster) top-1
paper: random 15.0 - -
paper: uniform prior 10.68 +/- 0.53 - -
paper: expert prior 7.75 +/- 0.09 - -
paper: HyDE + expert prior (best) 7.55 +/- 0.21 - -
paper artefact: released single test run 12.67 - -
ours: their scorer, uniform prior (ATT&CK technique lists) 8.718 +/- 0.549 [6.354, 11.770] 0.200
ours: their scorer, prior = training-report share (expert-prior proxy) 9.709 +/- 0.454 [6.998, 13.155] 0.173
ours: ttp-binary-bayes (simplified) 9.842 +/- 0.782 [7.758, 12.202] 0.112
ours: ttp-jaccard 8.891 +/- 0.888 [6.923, 11.182] 0.161
ours: DRAGNET, techniques only (= ttp-cosine ranking) 7.547 +/- 0.664 [5.525, 10.091] 0.279
ours: DRAGNET, software only 8.988 +/- 0.537 [7.178, 10.641] 0.376
ours: DRAGNET (techniques + software) 5.382 +/- 0.791 [3.630, 7.565] 0.533

Ours: mean +/- SD over the 10 random splits. The 95% CI pools the 330 (split, test report) rows and resamples whole actors, so it reflects actor-level variation; the splits reuse the same reports and are not independent samples.

Paired rank difference, method minus DRAGNET (positive = DRAGNET ranks the true actor higher), pooled rows, actor-cluster bootstrap CI; sign-flip over per-actor summed differences. Sign-flip test (one-sided, DRAGNET better): exact when at most 20 discordant units, otherwise Monte Carlo with B = 200,000 sign flips, p = (k+1)/(B+1); Holm-adjusted across methods.

method rank diff 95% CI splits where DRAGNET is lower p (one-sided) p (Holm)
guru-uniform 3.336 [1.839, 5.490] 10/10 1.5e-05 (MC, B = 200,000) 7.5e-05
guru-report-prior 4.327 [1.907, 7.325] 10/10 4.0e-04 (MC, B = 200,000) 0.001
ttp-binary-bayes 4.461 [2.013, 6.776] 10/10 5.9e-04 (MC, B = 200,000) 0.001
ttp-jaccard 3.509 [1.548, 5.543] 10/10 5.2e-04 (MC, B = 200,000) 0.001
dragnet-ttp-only 2.165 [0.982, 4.067] 10/10 5.0e-06 (MC, B = 200,000) 3.0e-05
dragnet-software-only 3.606 [2.080, 4.879] 10/10 7.0e-05 (MC, B = 200,000) 2.8e-04

B - larger case sets

B1 - every attributed ATT&CK campaign across releases and domains

Attributed campaigns per release: v12.1: 1, v13.1: 6, v14.1: 8, v15.1: 12, v16.1: 16, v17.1: 22, v18.1: 24, v19.2 (enterprise+ics+mobile): 26. The union over releases 12.1-19.2 and the Enterprise, ICS and Mobile domains has 26 attributed campaigns, 26 with evidence; new relative to A1: C0031. Campaigns are rarely removed from ATT&CK, so the union barely enlarges the set; the per-report cases (section R) are the real enlargement.

Leave-report-out, Enterprise+ICS+Mobile v19.2 graph:

method n top-1 coverage selective acc. wrong actor named (any grade) wrong at MEDIUM+ / committed wrong if ties broken at random
dragnet 26 0.423 0.462 0.750 0.115 0.000 0.000
ioc-correlation 26 0.378 0.346 1.000 0.000 0.000 0.276
code-only 26 0.367 0.385 0.900 0.038 0.038 0.095
ttp-jaccard 26 0.115 0.962 0.120 0.846 0.846 0.846

95% intervals (B1): top-1 by case bootstrap; proportions exact Clopper-Pearson.

method top-1 coverage selective acc. wrong (any grade) wrong at MEDIUM+ / committed
dragnet 0.423 [0.231, 0.615] 0.462 [0.266, 0.666] 0.750 [0.428, 0.945] 0.115 [0.024, 0.302] 0.000 [0.000, 0.132]
ioc-correlation 0.378 [0.205, 0.558] 0.346 [0.172, 0.557] 1.000 [0.664, 1.000] 0.000 [0.000, 0.132] 0.000 [0.000, 0.132]
code-only 0.367 [0.194, 0.558] 0.385 [0.202, 0.594] 0.900 [0.555, 0.997] 0.038 [0.001, 0.196] 0.038 [0.001, 0.196]
ttp-jaccard 0.115 [0.000, 0.269] 0.962 [0.804, 0.999] 0.120 [0.025, 0.312] 0.846 [0.651, 0.956] 0.846 [0.651, 0.956]

Paired top-1 difference, DRAGNET minus method (B1): case bootstrap 95% CI. Sign-flip test (one-sided, DRAGNET better): exact when at most 20 discordant units, otherwise Monte Carlo with B = 200,000 sign flips, p = (k+1)/(B+1); Holm-adjusted across methods.

method diff 95% CI discordant cases p (one-sided) p (Holm) p (two-sided)
ioc-correlation 0.045 [-0.026, 0.147] 3 0.250 (exact) 0.500 0.500
code-only 0.056 [-0.003, 0.154] 3 0.250 (exact) 0.500 0.500
ttp-jaccard 0.308 [0.154, 0.500] 8 0.004 (exact) 0.012 0.008

B2 - rolling origin: each campaign vs the newest release published before it was added

Release used: v12.1: 5, v13.1: 2, v14.1: 5, v15.1: 4, v16.1: 6, v17.1: 2, v18.1: 1.

method n top-1 coverage selective acc. wrong actor named (any grade) wrong at MEDIUM+ / committed wrong if ties broken at random out-of-KG abstain
dragnet 25 0.474 0.421 0.875 0.120 0.000 0.000 0.667
ioc-correlation 25 0.349 0.368 0.857 0.040 0.040 0.375 1.000
code-only 25 0.321 0.368 0.857 0.080 0.080 0.156 0.833

95% intervals (B2): top-1 by case bootstrap; proportions exact Clopper-Pearson.

method top-1 coverage selective acc. wrong (any grade) wrong at MEDIUM+ / committed
dragnet 0.474 [0.263, 0.684] 0.421 [0.203, 0.665] 0.875 [0.473, 0.997] 0.120 [0.025, 0.312] 0.000 [0.000, 0.137]
ioc-correlation 0.349 [0.162, 0.558] 0.368 [0.163, 0.616] 0.857 [0.421, 0.996] 0.040 [0.001, 0.204] 0.040 [0.001, 0.204]
code-only 0.321 [0.114, 0.531] 0.368 [0.163, 0.616] 0.857 [0.421, 0.996] 0.080 [0.010, 0.260] 0.080 [0.010, 0.260]

Paired top-1 difference, DRAGNET minus method (B2): case bootstrap 95% CI. Sign-flip test (one-sided, DRAGNET better): exact when at most 20 discordant units, otherwise Monte Carlo with B = 200,000 sign flips, p = (k+1)/(B+1); Holm-adjusted across methods.

method diff 95% CI discordant cases p (one-sided) p (Holm) p (two-sided)
ioc-correlation 0.125 [-0.018, 0.297] 4 0.125 (exact) 0.250 0.250
code-only 0.153 [0.000, 0.353] 4 0.125 (exact) 0.250 0.250

B3 - Malpedia-labelled abuse.ch cases

One case per Malpedia family with a single attributed actor and post-2024-01-01 abuse.ch metadata: up to 20 ThreatFox IOCs and 10 MalwareBazaar samples (imphash, TLSH), sampled with 5 seeds (means shown). Graph: ATT&CK v14.1 + pre-cutoff abuse.ch; labels come from Malpedia, a source the graph does not use. Label agreement Malpedia vs ATT&CK on families both attribute: 203/217.

artifacts only (129 cases, 80 with the actor in the graph):

method top-1 coverage selective acc. wrong (any grade) wrong at MEDIUM+ / committed out-of-KG abstain
dragnet 0.040 0.062 0.397 0.023 0.006 1.000
ioc-correlation 0.040 0.035 0.700 0.006 0.006 1.000
code-only 0.040 0.035 0.700 0.006 0.006 1.000
dragnet-infra-only 0.000 0.000 n/a 0.000 0.000 1.000
dragnet-genetics-only 0.040 0.062 0.397 0.023 0.006 1.000

95% CIs for the seed-0 sample of this variant (artifacts only): top-1 by case bootstrap, proportions exact Clopper-Pearson (the table above shows the 5-seed means).

method top-1 coverage selective acc. wrong (any grade)
dragnet [0.006, 0.085] [0.021, 0.140] [0.053, 0.853] [0.005, 0.066]
ioc-correlation [0.006, 0.085] [0.008, 0.106] [0.094, 0.992] [0.000, 0.042]
code-only [0.006, 0.085] [0.008, 0.106] [0.094, 0.992] [0.000, 0.042]
dragnet-infra-only [0.000, 0.000] [0.000, 0.045] [n/a, n/a] [0.000, 0.028]
dragnet-genetics-only [0.006, 0.085] [0.021, 0.140] [0.053, 0.853] [0.005, 0.066]

artifacts + family name (129 cases, 80 with the actor in the graph):

method top-1 coverage selective acc. wrong (any grade) wrong at MEDIUM+ / committed out-of-KG abstain
dragnet 0.272 0.278 0.856 0.025 0.006 1.000
ioc-correlation 0.272 0.250 0.950 0.008 0.008 1.000
code-only 0.272 0.250 0.950 0.008 0.008 1.000
dragnet-infra-only 0.000 0.000 n/a 0.000 0.000 1.000
dragnet-genetics-only 0.040 0.062 0.397 0.023 0.006 1.000

95% CIs for the seed-0 sample of this variant (artifacts + family name): top-1 by case bootstrap, proportions exact Clopper-Pearson (the table above shows the 5-seed means).

method top-1 coverage selective acc. wrong (any grade)
dragnet [0.178, 0.362] [0.181, 0.386] [0.651, 0.971] [0.005, 0.066]
ioc-correlation [0.178, 0.362] [0.160, 0.359] [0.751, 0.999] [0.000, 0.042]
code-only [0.178, 0.362] [0.160, 0.359] [0.751, 0.999] [0.000, 0.042]
dragnet-infra-only [0.000, 0.000] [0.000, 0.045] [n/a, n/a] [0.000, 0.028]
dragnet-genetics-only [0.006, 0.085] [0.021, 0.140] [0.053, 0.853] [0.005, 0.066]

C - curated real cases (demonstrations, not validation)

Rules R2 and R4 were designed from the Olympic Destroyer and Turla/OilRig patterns, and several cases depend on hand-written graph additions (e.g. WannaCry's code-reuse token), so these are demonstrations that the rules behave as designed, not independent evidence.

time-of-incident

case truth DRAGNET verdict truth rank false-flag indicators as designed ioc-correlation ttp-jaccard
bangladesh_bank_2016 APT38, Lazarus Group Lazarus Group (MEDIUM) 1 0 yes Lazarus Group Group5
commodity_tooling_generic (none) - (INSUFFICIENT) - 0 yes tie among 26 (abstain) Threat Group-1314
notpetya_2017 Sandworm Team Sandworm Team (MEDIUM) 1 0 yes Sandworm Team Blue Mockingbird
olympic_destroyer_2018 Sandworm Team - (INSUFFICIENT) 12 3 yes menuPass Play
sony_pictures_2014 Lazarus Group Lazarus Group (MEDIUM) 1 0 yes Lazarus Group Orangeworm
turla_oilrig_2019 Turla OilRig (LOW) 2 1 yes OilRig Tropic Trooper
wannacry_2017 Lazarus Group Lazarus Group (MEDIUM) 1 0 yes Lazarus Group Lotus Blossom

Behaved as designed: 7/7

retrospective

case truth DRAGNET verdict truth rank false-flag indicators as designed ioc-correlation ttp-jaccard
bangladesh_bank_2016 APT38, Lazarus Group Lazarus Group (MEDIUM) 1 0 yes Lazarus Group Group5
commodity_tooling_generic (none) - (INSUFFICIENT) - 0 yes tie among 26 (abstain) Threat Group-1314
notpetya_2017 Sandworm Team Sandworm Team (MEDIUM) 1 0 yes Sandworm Team Blue Mockingbird
olympic_destroyer_2018 Sandworm Team - (INSUFFICIENT) 3 4 yes tie among 2 (abstain) Play
sony_pictures_2014 Lazarus Group Lazarus Group (MEDIUM) 1 0 yes Lazarus Group Orangeworm
turla_oilrig_2019 Turla OilRig (LOW) 2 1 yes OilRig Tropic Trooper
wannacry_2017 Lazarus Group Lazarus Group (MEDIUM) 1 0 yes Lazarus Group Lotus Blossom

Behaved as designed: 7/7

D - false-flag stress test (planted decoy artifacts on A1 cases)

Decoys are drawn only from other sponsor states (the condition rules R3/R4 check), and the planted Rich header matches exactly one actor's simulated reference sample, so rule R1 fires by construction at level 1. Level 2 (a stolen exclusive decoy family) is the informative test.

False-flag indicators on clean A1 cases (false-alarm rate): 0.160 (4/25, exact 95% CI [0.045, 0.361])

level1 (seed 7)

method n decoy ranked #1 confidently attributed to decoy truth ranked #1 flagged withheld (<=LOW)
dragnet 25 0.440 0.000 0.520 1.000 1.000
ttp-jaccard 25 0.000 0.000 0.120 0.000 0.000
ttp-cosine 25 0.000 0.000 0.080 0.000 0.000
ttp-binary-bayes 25 0.000 0.000 0.000 0.000 0.000
ioc-correlation 25 0.541 0.440 0.267 0.000 0.000
code-only 25 0.000 0.000 0.540 0.000 0.000
dragnet-no-ff 25 0.440 0.000 0.520 0.000 0.800

DRAGNET confidently attributed to decoy, exact 95% CI: [0.000, 0.137].

Over 10 decoy seeds (same cases, different decoys): mean and range (min-max, not a CI):

  • dragnet: mean 0.000, range 0.000-0.000
  • ttp-jaccard: mean 0.000, range 0.000-0.000
  • ttp-cosine: mean 0.000, range 0.000-0.000
  • ttp-binary-bayes: mean 0.008, range 0.000-0.040
  • ioc-correlation: mean 0.476, range 0.440-0.560
  • code-only: mean 0.000, range 0.000-0.000
  • dragnet-no-ff: mean 0.000, range 0.000-0.000

Effect of the false-flag rules (no-ff minus full engine, confident decoy rate, all case x seed cells): 0.000, 95% CI [0.000, 0.000] (bootstrap clustered by case).

level2 (seed 7)

method n decoy ranked #1 confidently attributed to decoy truth ranked #1 flagged withheld (<=LOW)
dragnet 25 0.760 0.240 0.240 0.760 0.760
ttp-jaccard 25 0.000 0.000 0.120 0.000 0.000
ttp-cosine 25 0.000 0.000 0.080 0.000 0.000
ttp-binary-bayes 25 0.000 0.000 0.000 0.000 0.000
ioc-correlation 25 0.780 0.760 0.180 0.000 0.000
code-only 25 0.520 0.400 0.440 0.000 0.000
dragnet-no-ff 25 0.760 0.400 0.240 0.000 0.600

DRAGNET confidently attributed to decoy, exact 95% CI: [0.094, 0.451].

Over 10 decoy seeds (same cases, different decoys): mean and range (min-max, not a CI):

  • dragnet: mean 0.172, range 0.120-0.240
  • ttp-jaccard: mean 0.000, range 0.000-0.000
  • ttp-cosine: mean 0.000, range 0.000-0.000
  • ttp-binary-bayes: mean 0.008, range 0.000-0.040
  • ioc-correlation: mean 0.772, range 0.760-0.800
  • code-only: mean 0.400, range 0.400-0.400
  • dragnet-no-ff: mean 0.400, range 0.400-0.400

Effect of the false-flag rules (no-ff minus full engine, confident decoy rate, all case x seed cells): 0.228, 95% CI [0.108, 0.368] (bootstrap clustered by case).

E1 - ThreatFox export structure and IOC re-sighting

The export has 109234 rows and 108378 distinct IOC values; 0.992 of values occur in exactly one row, because ThreatFox records an IOC once (re-sightings do not create rows). The export therefore cannot measure IOC longevity, and no longevity conclusion is drawn.

Actor-specific-family IOCs: 2810 (types: {'url': 187, 'ip:port': 516, 'domain': 223, 'sha256_hash': 703, 'md5_hash': 841, 'sha1_hash': 340}); network IOCs (IP, domain): 739, spanning ['2020-12-15', '2026-10-02']. Split at the median network-IOC date (2026-06-30), 5 of 370 later network IOCs had an earlier row. Families are mostly commodity: WannaCryptor 462, Akira 406, Agent Tesla 304, Havoc 291, AgendaCrypt 194, CrossRAT 171.

E2 - MalwareBazaar imphash genetics (train < cutoff, test >= cutoff)

494204 labelled samples with imphash; 205246 in actor-specific ATT&CK families (train 191843, test 13403). The test samples share only 1139 distinct imphashes and are dominated by a few families (AgentTesla 8434, Amadey 1923, WannaCry 739, Loki 567, PureCrypter 324), so per-sample accuracy mostly measures re-identification of a few commodity families. CIs resample whole families.

variant method coverage selective acc. [family-cluster 95% CI] macro acc. over families families covered imphashes covered
collision-filtered DRAGNET (any grade) 0.078 0.918 [0.455, 0.999] 0.919 25 84
collision-filtered DRAGNET MEDIUM+ 0.064 0.998 [0.943, 1.000] 0.882 17 63
collision-filtered imphash lookup 0.064 0.998 [0.943, 1.000] 0.882 17 63
collision-filtered DRAGNET, WannaCry excluded 0.024 0.726 [0.308, 0.995] 0.915 24 78
collision-filtered DRAGNET MEDIUM+, WannaCry excluded 0.010 0.984 [0.889, 1.000] 0.875 16 57
unfiltered DRAGNET (any grade) 0.114 0.866 [0.590, 0.989] 0.790 29 111
unfiltered DRAGNET MEDIUM+ 0.092 0.912 [0.458, 0.999] 0.862 21 78
unfiltered imphash lookup 0.092 0.912 [0.458, 0.999] 0.862 21 78
unfiltered DRAGNET, WannaCry excluded 0.063 0.741 [0.448, 0.954] 0.782 28 105
unfiltered DRAGNET MEDIUM+, WannaCry excluded 0.040 0.783 [0.309, 0.998] 0.855 20 72

Share of collision-filtered MEDIUM+ verdicts that are WannaCry: 0.853.

E3 - MalwareBazaar TLSH genetics (train < cutoff, test >= cutoff)

Pre-cutoff TLSH digests of actor-specific families become fuzzy graph signals (collision filter: a digest whose radius-tau neighbourhood in a 100000-digest pre-cutoff sample spans more than 2 families is dropped). tau was chosen on a validation window ['2023-07-01', '2024-01-01'] (n=3000) by maximising correct minus twice wrong verdicts: chosen tau = 100 (tau 30: cov 0.005, acc 1.000, tau 50: cov 0.010, acc 0.806, tau 70: cov 0.014, acc 0.854, tau 100: cov 0.022, acc 0.851). Graph digests: 14376; test samples: 30643. Searches are exact (numpy); the stdlib banded index recovers 0.963 of the 1704 true neighbour pairs (within the chosen radius tau = 100) of 300 test queries. The shipped runtime default TLSH_TAU = 50 is more conservative than the evaluated tau = 100; at distance < 100 Oliver, Cheng & Chen (CTC 2013, Table II) report about a 6.43% file-pair false-positive rate.

method coverage selective acc. [family-cluster 95% CI] macro acc. over families families covered clusters
DRAGNET (any grade) 0.039 0.844 [0.461, 0.957] 0.592 37 37
DRAGNET MEDIUM+ 0.000 n/a [n/a, n/a] n/a 0 0
TLSH nearest-neighbour lookup 0.052 0.807 [0.464, 0.934] 0.605 30 30
DRAGNET, WannaCry excluded 0.017 0.648 [0.405, 0.809] 0.581 36 36

False alarms on 3000 post-cutoff samples of families with no actor mapping: an actor was named for 0.008 of them, at MEDIUM+ for 0.000.

F - accuracy vs public reporting depth (APTnotes, A1 cases)

An association, not a cause: APTnotes is dense for 2010-2018 and nearly empty afterwards, so '0 reports' partly means 'recently named actor'. Shown with ATT&CK-only aliases and with MISP aliases merged (which lets e.g. AppleJeus inherit Lazarus reports).

ATT&CK aliases only (Fisher exact p, 0 vs >0 reports: 0.007)

truth actor reporting n DRAGNET top-1
0 reports 11 0.364
1-5 reports 9 0.889
>5 reports 5 1.000

ATT&CK + MISP aliases (Fisher exact p, 0 vs >0 reports: 0.028)

truth actor reporting n DRAGNET top-1
0 reports 10 0.400
1-5 reports 2 1.000
>5 reports 13 0.846

K - APTMalware hashes in MalwareBazaar metadata (Kida & Olukoya 2023)

APTMalware (overview.csv, 4449 rows) lists 3719 distinct SHA-256s; 12 of them occur among the 1147952 rows of the MalwareBazaar metadata export used here (by group: APT 1 2, APT 10 3, APT 28 2, Energetic Bear 1, Equation Group 2, Gorgon Group 2). The fuzzy-hash study of Kida & Olukoya (IEEE Access 11:1148-1165, 2023, doi:10.1109/ACCESS.2022.3233403) needs the binaries, so its results cannot be matched from metadata. The export changes daily, so this count drifts.

Runtime of the last run: 1044.3 s (A1,A1LF,A2,A3,R,G,B,C,D,E1,E2,E3,F,K)