docs(ttp): sync A.10 + rewrite §9 drift runbook + DEBT.md markers

Appendix A.10 corrected to match the post-2026-05-02-audit reality:
AbuseIPDB cat 7/13/16/17 land on their canonical AbuseIPDB names
(Phishing / VPN IP / SQL Injection / Spoofing); cats 4 and 10 carry
explicit "drop" annotations so the next reviewer sees the intent
rather than guessing. ThreatFox table re-keys on `threat_type` (the
canonical taxonomy field) and adds the `payload` and `cc_skimming`
rows. GreyNoise table promotes bare-malicious to a half-multiplier
emission of T1071.

§"Hard parts §9 Intel provider drift" replaces the prose handwave
with a runnable check: provider URLs, the ThreatFox curl invocation
that needs DECNET_THREATFOX_API_KEY, the rule_version + emits +
attack_catalog co-evolution rules, and the full chain of files to
exercise. Adds a "Ship-time audit log" subsection so future quarterly
runs have a known-good baseline to diff against.

DEBT.md item #1 records LAST_REVIEWED: 2026-05-02 / NEXT_REVIEW:
2026-08-02 and points at §9 for the runbook. DEBT.md item #3 (the
attacker.email.received producer) flags its gating premise as
potentially stale — ANTI noted SMTP honeypots already persist
received messages, contradicting the "no source row" claim that
deferred the wiring.
This commit is contained in:
2026-05-02 18:09:20 -04:00
parent f8dee596e5
commit 9a7d116351
2 changed files with 140 additions and 26 deletions

22
DEBT.md
View File

@@ -14,11 +14,22 @@ Feodo Tracker catalogues for new categories or classification changes.
Reconcile against `rules/ttp/R0054..R0058` (the intel-verdict rule Reconcile against `rules/ttp/R0054..R0058` (the intel-verdict rule
pack) and bump rule versions for any drift. See pack) and bump rule versions for any drift. See
`development/TTP_TAGGING.md` §"Hard parts §9 Intel provider drift" for `development/TTP_TAGGING.md` §"Hard parts §9 Intel provider drift" for
the operational rationale. the operational runbook.
Owner: TTP rule maintainer (currently ANTI). Owner: TTP rule maintainer (currently ANTI).
Cadence: every quarter, first week of the month. Cadence: every quarter, first week of the month.
Trigger: calendar reminder; no automated probe today. Trigger: rule YAML `next_review` markers (canonical), with a
calendar reminder as backup.
Last reviewed: **2026-05-02** (ship-time audit — see
`development/TTP_TAGGING.md` §9 "Ship-time audit log"; corrected
two AbuseIPDB code typos, expanded the R0054/R0055/R0057 emits
lists to cover the full predicate technique universe, repointed
ThreatFox dispatch from `ioc_type` to `threat_type`, wired the
`AttackerIntel.{abuseipdb_categories, greynoise_tags,
greynoise_name, feodo_malware_family, threatfox_*_types,
threatfox_malware_families}` columns + producer parsing).
Next review: **2026-08-02**.
## One-shot ## One-shot
@@ -45,6 +56,13 @@ collector persist log events, so there is no source row to fan out
on. See `development/TTP_TAGGING.md` §"Bus topics → Producer on. See `development/TTP_TAGGING.md` §"Bus topics → Producer
wiring" for the full producer audit. wiring" for the full producer audit.
**STALE PREMISE (2026-05-02):** ANTI noted during the intel audit
that the SMTP honeypots DO persist all received messages today.
Re-triage this entry — the gating premise above may no longer
hold and the producer wiring may be paydown-able directly. Map
the actual SMTP-receive persistence to `ReceivedEmail` (or its
extant analogue), then wire the publisher.
Trigger: SMTP-receive persistence model lands (a `ReceivedEmail` Trigger: SMTP-receive persistence model lands (a `ReceivedEmail`
SQLModel + ingest path). Wire the publisher in the same PR. SQLModel + ingest path). Wire the publisher in the same PR.
Owner: TBD. Owner: TBD.

View File

@@ -1509,9 +1509,82 @@ Mitigation:
AbuseIPDB category nobody has mapped yet is silently ignored AbuseIPDB category nobody has mapped yet is silently ignored
rather than tagged as some "generic abuse" technique. False rather than tagged as some "generic abuse" technique. False
silence is recoverable; false labels poison the SOC. silence is recoverable; false labels poison the SOC.
- **Quarterly review.** Add a note to DEBT.md to re-walk each - **Quarterly review.** Re-walk each provider's catalogue every
provider's category catalogue every quarter post-v1, until the quarter, tracked in `DEBT.md`. Each rule YAML carries its own
mapping tables stabilise. `last_reviewed` / `next_review` markers as the canonical record;
`DEBT.md` is just the calendar reminder.
#### Drift-check runbook
The audit takes ~20 minutes when nothing has drifted, longer when
mappings need updating. Order matters: the upstream catalogue is
authoritative, the lifter enum mirrors it, and the rule YAML
emits list MUST be a superset of every technique the predicate can
emit (the 2026-05-02 ship-time audit caught this exact cascade
silently dropping more than half of v1's intel tag emissions).
Per-provider source of truth:
| Provider | Category catalogue | Authentication |
|------------|---------------------------------------------------------------------|-----------------|
| AbuseIPDB | https://www.abuseipdb.com/categories | none (public) |
| GreyNoise | https://docs.greynoise.io/docs/understanding-greynoise-classifications + tags reference | Community endpoint returns `classification` + `name` only — tags require a non-Community plan |
| ThreatFox | `POST https://threatfox-api.abuse.ch/api/v1/` body `{"query":"types"}` | `Auth-Key` header (`DECNET_THREATFOX_API_KEY`) |
| Feodo | https://feodotracker.abuse.ch/blocklist/ — feed-driven, no enum | none (public) |
For each provider:
1. **Fetch the upstream catalogue** (URL above). For ThreatFox use
`curl -s -X POST https://threatfox-api.abuse.ch/api/v1/ -H "Auth-Key: $DECNET_THREATFOX_API_KEY" -H 'Content-Type: application/json' -d '{"query":"types"}'`.
2. **Diff against the lifter's enum** in
`decnet/ttp/impl/intel_lifter.py` (`_ABUSEIPDB_CATEGORY_TO_TECHNIQUES`,
`_GREYNOISE_TAG_TO_TECHNIQUES`,
`_THREATFOX_THREAT_TYPE_TO_TECHNIQUES`). Note also the AbuseIPDB
numeric codes — the v1 ship-time bug was a code-vs-name mix-up
(cat 10 was treated as DDoS when it's Web Spam), so confirm
names match codes when re-checking.
3. **For each new entry** the upstream introduces, decide whether
the signal warrants an ATT&CK technique. Default is "do not
map" — a category we cannot pin to a technique stays silent
rather than firing a generic tag.
4. **Drift found:**
- Update the lifter enum.
- If the new technique is not already in the rule's `emits`
list (`rules/ttp/R005{4..7}.yaml`), **add it there** — the
`_emit_filtered` machinery silently drops emits absent from
the YAML list. This is the cascade bug to watch for.
- If the new technique is not already in
`decnet/ttp/attack_catalog.py`, **add it there** — the
`test_every_rule_pack_technique_has_a_catalogue_entry` guard
fails the build otherwise.
- Bump `rule_version` on the affected YAML(s).
- Add a positive emit test in `tests/ttp/test_intel_lifter.py`.
- Commit with subject `feat(ttp): drift R0054-* — <provider> +
<category>`.
5. **No drift:** bump `last_reviewed` / `next_review` on each
touched rule YAML. No rule_version bump. Commit with subject
`chore(ttp): R0054-R0058 quarterly drift review (no changes)`.
The intel pipeline is end-to-end fragile — a working enum is
necessary but not sufficient. Pre-shipping any change here, run
`pytest tests/intel/ tests/ttp/ -k 'intel'` to exercise the full
provider → AttackerIntel → bus payload → IntelLifter → emit chain.
##### Ship-time audit log
- **2026-05-02** — initial v1 audit (commit forthcoming). Found
three classes of bug: (1) AbuseIPDB code typos (10/17 → 4/13)
in both code and design doc; (2) `emits`-list cascade silently
dropped majority of predicate techniques across R0054 / R0055 /
R0057; (3) ThreatFox keyed on `ioc_type` when the canonical
taxonomy field is `threat_type`. Also wired
`AttackerIntel.{abuseipdb_categories, greynoise_tags,
greynoise_name, feodo_malware_family, threatfox_*_types,
threatfox_malware_families}` columns and the matching producer
parsing — without these, R0054 / R0055 / R0057 fired zero tags
in production despite passing unit tests. Added
`last_reviewed` / `next_review` markers on all R0054R0058
YAMLs; next review 2026-08-02.
### 10. When to graduate from filesystem store to database store ### 10. When to graduate from filesystem store to database store
@@ -2010,11 +2083,23 @@ engines produced.
AbuseIPDB returns up to two categories per report plus an aggregate AbuseIPDB returns up to two categories per report plus an aggregate
abuse-confidence score (0100). Per-category mapping: abuse-confidence score (0100). Per-category mapping:
Mapping table (v2 — corrected by the 2026-05-02 ship-time audit;
AbuseIPDB code numbers verified against
https://www.abuseipdb.com/categories on review date):
| AbuseIPDB category | Tactic | Technique | Sub-tech | Conf | | AbuseIPDB category | Tactic | Technique | Sub-tech | Conf |
|----------------------------------------------|--------|-----------|----------|-------| |----------------------------------------------|--------|-----------|----------|-------|
| 5 — FTP Brute-Force | TA0006 | T1110 | (none) | H |
| 7 — Phishing | TA0001 | T1566 | (none) | M |
| 9 — Open Proxy | TA0011 | T1090 | (none) | M |
| 11 — Email Spam | TA0040 | T1496 | (none) | M |
| 11 — Email Spam (high score, ≥80) | TA0001 | T1566 | (none) | M |
| 13 — VPN IP | TA0011 | T1090 | (none) | M |
| 14 — Port Scan | TA0007 | T1046 | (none) | H | | 14 — Port Scan | TA0007 | T1046 | (none) | H |
| 14 — Port Scan | TA0043 | T1595 | T1595.001| H | | 14 — Port Scan | TA0043 | T1595 | T1595.001| H |
| 15 — Hacking | TA0001 | T1190 | (none) | M | | 15 — Hacking | TA0001 | T1190 | (none) | M |
| 16 — SQL Injection | TA0001 | T1190 | (none) | H |
| 17 — Spoofing (email-sender) | TA0001 | T1566 | (none) | M |
| 18 — Brute-Force | TA0006 | T1110 | (none) | H | | 18 — Brute-Force | TA0006 | T1110 | (none) | H |
| 18 + service=SSH | TA0006 | T1110 | T1110.001| H | | 18 + service=SSH | TA0006 | T1110 | T1110.001| H |
| 19 — Bad Web Bot | TA0043 | T1595 | T1595.002| M | | 19 — Bad Web Bot | TA0043 | T1595 | T1595.002| M |
@@ -2022,13 +2107,9 @@ abuse-confidence score (0100). Per-category mapping:
| 21 — Web App Attack | TA0001 | T1190 | (none) | H | | 21 — Web App Attack | TA0001 | T1190 | (none) | H |
| 22 — SSH | TA0006 | T1110 | (none) | M | | 22 — SSH | TA0006 | T1110 | (none) | M |
| 23 — IoT Targeted | TA0001 | T1190 | (none) | M | | 23 — IoT Targeted | TA0001 | T1190 | (none) | M |
| 11 — Email Spam | TA0040 | T1496 | (none) | M | | 1 / 2 / 3 / 6 / 8 / 12 — DNS / Fraud / Ping / VoIP / Blog Spam | (intentionally unmapped — low IP-layer signal) | | |
| 11Email Spam (high score, ≥80) | TA0001 | T1566 | (none) | M | | 4DDoS Attack | (drop — too muddy at IP layer for v0; revisit when service-aware) | | |
| 10 — DDoS | TA0040 | T1498 | (none) | L | | 10 — Web Spam | (drop — CMS / referer spam has no clean ATT&CK fit at the IP layer) | | |
| 5 — FTP Brute-Force | TA0006 | T1110 | (none) | H |
| 17 — VPN IP | TA0011 | T1090 | (none) | M |
| 9 — Open Proxy | TA0011 | T1090 | (none) | M |
| 4 — DDoS (untyped) | (drop — too muddy for v0) | |
Final tag confidence = listed band × `abuseipdb_score / 100`. Final tag confidence = listed band × `abuseipdb_score / 100`.
@@ -2036,7 +2117,7 @@ Final tag confidence = listed band × `abuseipdb_score / 100`.
| GreyNoise signal | Tactic | Technique | Sub-tech | Conf | | GreyNoise signal | Tactic | Technique | Sub-tech | Conf |
|----------------------------------------------|--------|-----------|-----------|-------| |----------------------------------------------|--------|-----------|-----------|-------|
| classification = "malicious" | (no tag alone — needs tag) | | | classification = "malicious" (no recognised tag) | TA0011 | T1071 | (none) | M (0.5×) |
| classification = "benign" | (no tag — confidence-decrement existing tags) | | classification = "benign" | (no tag — confidence-decrement existing tags) |
| classification = "scanner" | TA0043 | T1595 | T1595.002 | H | | classification = "scanner" | TA0043 | T1595 | T1595.002 | H |
| tag matches "tor_exit_node" | TA0011 | T1090 | T1090.003 | H | | tag matches "tor_exit_node" | TA0011 | T1090 | T1090.003 | H |
@@ -2045,11 +2126,15 @@ Final tag confidence = listed band × `abuseipdb_score / 100`.
| tag matches "ssh_bruteforcer" | TA0006 | T1110 | T1110.001 | H | | tag matches "ssh_bruteforcer" | TA0006 | T1110 | T1110.001 | H |
| tag matches "web_crawler" (non-Google) | TA0043 | T1595 | T1595.002 | M | | tag matches "web_crawler" (non-Google) | TA0043 | T1595 | T1595.002 | M |
Final confidence = listed band × 1.0 (GreyNoise has no per-verdict Final confidence = listed band × multiplier; the bare-malicious
score; classification is binary). Apply the "benign" decrement lane uses a 0.5× multiplier (audit decision 2026-05-02 — the
*only* to confidence-bumpable existing tags, never to identity- verdict is real but unspecific). All tag-driven lanes use 1.0×.
rollup or behavioral-lifter tags (those have independent
substantiation). The Community endpoint does not surface tags today; the tag-driven
emits become live only when an operator wires a non-Community
provider plan that does. Apply the "benign" decrement *only* to
confidence-bumpable existing tags, never to identity-rollup or
behavioral-lifter tags (those have independent substantiation).
#### abuse.ch Feodo Tracker #### abuse.ch Feodo Tracker
@@ -2065,21 +2150,32 @@ for ThreatFox where the IOC type genuinely varies.
#### abuse.ch ThreatFox #### abuse.ch ThreatFox
ThreatFox returns IOC type + malware family. Per-IOC-type mapping: ThreatFox returns a list of matches per queried IP. Each match has
a `threat_type` (the canonical taxonomy field, ∈ {`botnet_cc`,
`payload_delivery`, `payload`, `cc_skimming`}) and an `ioc_type`
(the indicator format — `url` / `domain` / `ip:port` / `md5_hash`
/ `sha256_hash` / `sha3_384_hash` / `sha1_hash` / `envelope_from`
/ `body_from`). The 2026-05-02 ship-time audit corrected v1's
inversion of these fields; ATT&CK dispatch keys on `threat_type`,
`ioc_type` is evidence-only.
| ThreatFox IOC type | Tactic | Technique | Sub-tech | Conf | Per-`threat_type` mapping (v2):
| ThreatFox threat_type | Tactic | Technique | Sub-tech | Conf |
|----------------------------------------------|--------|-----------|-----------|-------| |----------------------------------------------|--------|-----------|-----------|-------|
| `botnet_cc` | TA0011 | T1071 | T1071.001 | H | | `botnet_cc` | TA0011 | T1071 | T1071.001 | H |
| `botnet_cc` | TA0042 | T1588 | T1588.001 | H | | `botnet_cc` | TA0042 | T1588 | T1588.001 | H |
| `payload_delivery` | TA0011 | T1105 | (none) | H | | `payload_delivery` | TA0011 | T1105 | (none) | H |
| `payload_delivery` | TA0042 | T1588 | T1588.001 | H | | `payload_delivery` | TA0042 | T1588 | T1588.001 | H |
| `c2_server` | TA0011 | T1071 | T1071.001 | H | | `payload` | TA0042 | T1588 | T1588.001 | H |
| `download_url` | TA0011 | T1105 | (none) | H | | `cc_skimming` | TA0009 | T1056 | (none) | M |
Family name (e.g. "cobalt_strike", "sliver", "havoc", Family names (e.g. "Cobalt Strike", "Sliver", "Havoc",
"asyncrat") is carried in `evidence.malware_family` for downstream "AsyncRAT", "Emotet") are carried in
attribution. ThreatFox-derived tags carry the highest base `evidence.malware_families` for downstream attribution. The
confidence in v0 (0.95) — the IOC database is curated. `ioc_type` set rides in `evidence.ioc_types` for SIEM context.
ThreatFox-derived tags carry the highest base confidence in v0 —
the IOC database is curated.
--- ---