diff --git a/.github/workflows/verify-week2-literature-pcap.yml b/.github/workflows/verify-week2-literature-pcap.yml new file mode 100644 index 0000000..cd76554 --- /dev/null +++ b/.github/workflows/verify-week2-literature-pcap.yml @@ -0,0 +1,56 @@ +name: DARPA Week2 - PCAP corroboration of candidate attack intervals +on: + workflow_dispatch: + push: + paths: + - 'src/anomaly_detection/verify_week2_pcap.py' + - 'tests/test_week2_literature_intervals.py' + - 'data/ground_truth/week2_intervals_review.csv' + - '.github/workflows/verify-week2-literature-pcap.yml' +jobs: + audit: + runs-on: ubuntu-24.04 + timeout-minutes: 160 + env: + DEBIAN_FRONTEND: noninteractive + PYTHONUNBUFFERED: '1' + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: Setup TShark and tests + run: | + sudo apt-get update -qq + sudo DEBIAN_FRONTEND=noninteractive apt-get install -y tshark gzip + python -m pip install -e . + python -m unittest discover -s tests -v + - name: Download Wed/Thu/Fri INSIDE captures only + run: | + python - <<'PY' + from pathlib import Path + from urllib.request import Request, urlopen + from shutil import copyfileobj + from anomaly_detection.download import BASE + for day in ("wednesday", "thursday", "friday"): + path = Path("data/raw") / day / "inside.tcpdump.gz" + path.parent.mkdir(parents=True, exist_ok=True) + request = Request(f"{BASE}/{day}/inside.tcpdump.gz", + headers={"User-Agent": "DARPA-Research/0.1"}) + with urlopen(request, timeout=180) as reader, path.open("wb") as writer: + copyfileobj(reader, writer, length=1024*1024) + print(day, path.stat().st_size, flush=True) + PY + - name: Verify original gzip SHA-256 + run: | + set -euo pipefail + for day in wednesday thursday friday; do gzip -t "data/raw/$day/inside.tcpdump.gz"; done + grep -E 'data/raw/(wednesday|thursday|friday)/inside.tcpdump.gz$' reports/week2-1999/results/source_archive_sha256sums.txt | sha256sum -c - + - name: Audit candidate packet pairs and timestamps + run: python -m anomaly_detection.verify_week2_pcap --pcaps data/raw --out results/week2-pcap-validation + - uses: actions/upload-artifact@v4 + if: always() + with: + name: darpa-week2-candidate-pcap-audit + path: results/week2-pcap-validation/ + if-no-files-found: warn diff --git a/data/ground_truth/week2_intervals_review.csv b/data/ground_truth/week2_intervals_review.csv index 44e56cf..894d086 100644 --- a/data/ground_truth/week2_intervals_review.csv +++ b/data/ground_truth/week2_intervals_review.csv @@ -1 +1,16 @@ attack_id,label,start_utc,end_utc,review_status,evidence_url,reviewer,notes +2,attack,1999-03-08T13:50:11+00:00,1999-03-08T13:50:13+00:00,candidate,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,,Haag 2007 Table 4/Appendix B; secondary packet-mapped activity only; ICMP fragmented; observed end inclusive rounded up 1s; NOT complete MIT attack duration; manual PCAP cross-check required +5,attack,1999-03-08T20:57:07+00:00,1999-03-08T20:57:08+00:00,candidate,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,,Haag 2007 Table 4/Appendix B; secondary packet-mapped activity only; TCP single packet; observed end inclusive rounded up 1s; NOT complete MIT attack duration; manual PCAP cross-check required +7,attack,1999-03-09T00:09:06+00:00,1999-03-09T00:09:19+00:00,candidate,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,,Haag 2007 Table 4/Appendix B; secondary packet-mapped activity only; TCP FTP; observed end inclusive rounded up 1s; NOT complete MIT attack duration; manual PCAP cross-check required +8,attack,1999-03-09T13:44:13+00:00,1999-03-09T14:11:11+00:00,candidate,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,,Haag 2007 Table 4/Appendix B; secondary packet-mapped activity only; TCP FIN sweep; observed end inclusive rounded up 1s; NOT complete MIT attack duration; manual PCAP cross-check required +10,attack,1999-03-09T15:07:30+00:00,1999-03-09T15:09:31+00:00,candidate,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,,Haag 2007 Table 4/Appendix B; secondary packet-mapped activity only; TCP HTTP; observed end inclusive rounded up 1s; NOT complete MIT attack duration; manual PCAP cross-check required +17,attack,1999-03-10T17:02:18+00:00,1999-03-10T17:04:34+00:00,candidate,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,,Haag 2007 Table 4/Appendix B; secondary packet-mapped activity only; TCP SYN sweep; observed end inclusive rounded up 1s; NOT complete MIT attack duration; manual PCAP cross-check required; Stage5 protocol evidence exists +18,attack,1999-03-10T18:44:10+00:00,1999-03-10T18:54:07+00:00,candidate,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,,Haag 2007 Table 4/Appendix B; secondary packet-mapped activity only; TCP SMTP; observed end inclusive rounded up 1s; NOT complete MIT attack duration; manual PCAP cross-check required +22,attack,1999-03-11T04:56:00+00:00,1999-03-11T04:56:07+00:00,candidate,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,,Haag 2007 Table 4/Appendix B; secondary packet-mapped activity only; TCP HTTP; observed end inclusive rounded up 1s; NOT complete MIT attack duration; manual PCAP cross-check required +23,attack,1999-03-11T13:04:01+00:00,1999-03-11T13:04:09+00:00,candidate,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,,Haag 2007 Table 4/Appendix B; secondary packet-mapped activity only; TCP HTTP; observed end inclusive rounded up 1s; NOT complete MIT attack duration; manual PCAP cross-check required +26,attack,1999-03-11T16:03:51+00:00,1999-03-11T16:07:17+00:00,candidate,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,,Haag 2007 Table 4/Appendix B; secondary packet-mapped activity only; TCP SYN flood; observed end inclusive rounded up 1s; NOT complete MIT attack duration; manual PCAP cross-check required; Stage5 protocol evidence exists +29,attack,1999-03-11T20:46:46+00:00,1999-03-11T20:46:47+00:00,candidate,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,,Haag 2007 Table 4/Appendix B; secondary packet-mapped activity only; TCP single packet; observed end inclusive rounded up 1s; NOT complete MIT attack duration; manual PCAP cross-check required +35,attack,1999-03-12T14:18:11+00:00,1999-03-12T14:18:13+00:00,candidate,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,,Haag 2007 Table 4/Appendix B; secondary packet-mapped activity only; ICMP fragmented; observed end inclusive rounded up 1s; NOT complete MIT attack duration; manual PCAP cross-check required +36,attack,1999-03-12T16:20:10+00:00,1999-03-12T16:23:36+00:00,candidate,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,,Haag 2007 Table 4/Appendix B; secondary packet-mapped activity only; TCP SYN flood; observed end inclusive rounded up 1s; NOT complete MIT attack duration; manual PCAP cross-check required; Stage5 protocol evidence exists +37,attack,1999-03-12T17:40:09+00:00,1999-03-12T17:40:17+00:00,candidate,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,,Haag 2007 Table 4/Appendix B; secondary packet-mapped activity only; TCP HTTP; observed end inclusive rounded up 1s; NOT complete MIT attack duration; manual PCAP cross-check required +42,attack,1999-03-12T22:13:02+00:00,1999-03-12T22:25:05+00:00,candidate,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,,Haag 2007 Table 4/Appendix B; secondary packet-mapped activity only; TCP SYN sweep; observed end inclusive rounded up 1s; NOT complete MIT attack duration; manual PCAP cross-check required diff --git a/data/ground_truth/week2_literature_candidates.csv b/data/ground_truth/week2_literature_candidates.csv new file mode 100644 index 0000000..71085e3 --- /dev/null +++ b/data/ground_truth/week2_literature_candidates.csv @@ -0,0 +1,16 @@ +attack_id,attack,date_local,start_local,end_local,source,reported_protocol,review_status,verification_notes +2,pod,1999-03-08,08:50:11,08:50:12,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,ICMP fragmented,candidate,Secondary study of INSIDE packet traffic. First/last observed packet are not full attack boundaries +5,land,1999-03-08,15:57:07,15:57:07,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,TCP single packet,candidate,Secondary study of INSIDE packet traffic. First/last observed packet are not full attack boundaries +7,ps attack,1999-03-08,19:09:06,19:09:18,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,TCP FTP,candidate,Secondary study of INSIDE packet traffic. First/last observed packet are not full attack boundaries +8,portsweep,1999-03-09,08:44:13,09:11:10,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,TCP FIN sweep,candidate,Secondary study of INSIDE packet traffic. First/last observed packet are not full attack boundaries +10,back,1999-03-09,10:07:30,10:09:30,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,TCP HTTP,candidate,Secondary study of INSIDE packet traffic. First/last observed packet are not full attack boundaries +17,satan,1999-03-10,12:02:18,12:04:33,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,TCP SYN sweep,candidate,Secondary study of INSIDE packet traffic. First/last observed packet are not full attack boundaries +18,mailbomb,1999-03-10,13:44:10,13:54:06,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,TCP SMTP,candidate,Secondary study of INSIDE packet traffic. First/last observed packet are not full attack boundaries +22,crashiis,1999-03-10,23:56:00,23:56:06,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,TCP HTTP,candidate,Secondary study of INSIDE packet traffic. First/last observed packet are not full attack boundaries +23,crashiis,1999-03-11,08:04:01,08:04:08,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,TCP HTTP,candidate,Secondary study of INSIDE packet traffic. First/last observed packet are not full attack boundaries +26,neptune,1999-03-11,11:03:51,11:07:16,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,TCP SYN flood,candidate,Secondary study of INSIDE packet traffic. First/last observed packet are not full attack boundaries +29,land,1999-03-11,15:46:46,15:46:46,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,TCP single packet,candidate,Secondary study of INSIDE packet traffic. First/last observed packet are not full attack boundaries +35,pod,1999-03-12,09:18:11,09:18:12,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,ICMP fragmented,candidate,Secondary study of INSIDE packet traffic. First/last observed packet are not full attack boundaries +36,neptune,1999-03-12,11:20:10,11:23:35,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,TCP SYN flood,candidate,Secondary study of INSIDE packet traffic. First/last observed packet are not full attack boundaries +37,crashiis,1999-03-12,12:40:09,12:40:16,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,TCP HTTP,candidate,Secondary study of INSIDE packet traffic. First/last observed packet are not full attack boundaries +42,portsweep,1999-03-12,17:13:02,17:25:04,https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions,TCP SYN sweep,candidate,Secondary study of INSIDE packet traffic. First/last observed packet are not full attack boundaries diff --git a/docs/WEEK2_INTERVAL_LITERATURE_AUDIT.md b/docs/WEEK2_INTERVAL_LITERATURE_AUDIT.md new file mode 100644 index 0000000..a57cbe7 --- /dev/null +++ b/docs/WEEK2_INTERVAL_LITERATURE_AUDIT.md @@ -0,0 +1,75 @@ +# Fontes secundarias de intervalos — DARPA 1999 treino Week 2 + +O MIT lista **43 inicios oficiais** mas nao fornece duracoes completas nesta tabela: +https://archive.ll.mit.edu/ideval/docs/detections_1999.html + +Charles R. Haag, *An Artificial Immune System-Inspired Multiobjective Evolutionary +Algorithm with Application to the Detection of Distributed Computer Network +Intrusions*, AFIT/GCS/ENG/07-05 (2007), Tabela 4 e Apendice B: +https://www.researchgate.net/publication/221506722_An_Artificial_Immune_System-Inspired_Multiobjective_Evolutionary_Algorithm_with_Application_to_the_Detection_of_Distributed_Computer_Network_Intrusions + +O autor examinou 16 eventos dos 43 por filtragem de pacotes INSIDE usando +Ethereal (antecessor do Wireshark), com IP/protocolo, tempos e numeros de +pacote. Explicitou que a delimitacao pode ser imprecisa e nem todo o +ataque e visivel nos pacotes. A sua tabela e prova de **atividade suspeita +associada** a alguns eventos, NAO rotulacao oficial exaustiva. + +## Catalogo integrado + +Foram transcritos **15 eventos** como `candidate` em +`data/ground_truth/week2_intervals_review.csv` e mantidos em formato +de consulta em `data/ground_truth/week2_literature_candidates.csv`. +Convertidos de EST historico (UTC-05) para UTC com offset explicito. +Fim registrado como a marca de segundo observada +1s (fim exclusivo). +Esse arredondamento nao representa certeza de limite exato. +Os intervalos *nao* geram automaticamente is_attack=1. + +**O 16o evento, ID 25, foi explicitamente EXCLUIDO:** a Tabela 4 da +dissertacao posiciona um evento de portsweep por volta de 09:33 de +11/03, enquanto a tabela oficial do MIT situa o ID 25 às 10:50:11 +(portsweep) e o ID 24 às 09:33:17 (SATAN). O Apendice B tambem +apresenta identificacoes divergentes. E necessario reconciliar o +evento com pacote e destino antes de fornecer identificador confiavel. + +Outra limitacao: a dissertacao descreve `marx` e +`www.eyrie.af.mil` de modo inconsistente para alguns ataques; +nao presumir que sejam o mesmo alvo sem verificar IP e hora no PCAP. + +## Escopo da confirmacao por PCAP + +O novo comando `python -m anomaly_detection.verify_week2_pcap` +audita **somente os eventos 17/26/36**, cuja evidencia adicional +ja aparece em `reports/week2-1999/stage5-packet-evidence/`. +A tarefa aceita os arquivos INSIDE das capturas de quarta, quinta +e sexta-feira sem salvar pacotes individuais no repositorio, e +exporta metadados e limites observados do trafego TCP SYN entre +os enderecos indicados no estudo. + +O comando confirma atividade de rede e confronta a literatura, +**nao** confirma automaticamente malicia, duracao completa nem +rotulos de trafego benigno. O criterio de revisao nao e apenas +ter um IP ou estar proximo no tempo: e preciso identificar sessao, +protocolo/porta, origem, alvo, possivel trafego concorrente, +e evidencias de pacotes ou logs de exploracao. + +## Tráfego benigno + +O MIT declara como sem ataques as semanas **1 e 3** de treinamento: +https://www.ll.mit.edu/r-d/datasets/1999-darpa-intrusion-detection-evaluation-dataset + +Essas semanas podem formar um **controle negativo independente**, +avaliado separadamente para estimar alarmes em dados de fundo. +Isso nao autoriza rotular como benignos os minutos sem ataque +conhecido da **semana 2**. Portanto, sem intervalos benignos da +semana 2 validados, nao se calculam TP/FP/FN/TN globais da semana 2. + +## Proximos passos de revisao + +1. Reexecutar auditoria de PCAPs e confrontar timestamps/contagens com + Haag, MIT e a auditoria Stage5. Resolver discrepancias de nomes/IP. +2. Verificar os 15 intervalos individualmente e delimitar sessoes + atribuidas especificamente aos ataques; promover apenas trechos + revisados e justificados para `verified`. +3. Conseguir ground truth independente da ausencia de ataques para + os minutos benignos, nao extrapolar a partir dos 43 inicios. +4. Executar novamente `darpa-anomaly evaluate-confusion-week2`. diff --git a/src/anomaly_detection/verify_week2_pcap.py b/src/anomaly_detection/verify_week2_pcap.py new file mode 100644 index 0000000..9bf5b58 --- /dev/null +++ b/src/anomaly_detection/verify_week2_pcap.py @@ -0,0 +1,139 @@ +"""Corroborate candidate time spans with original INSIDE PCAP metadata. + +Never auto-promote candidates to verified ground truth. TCP SYN on an +expected source-target pair is evidence, not proof of an attack. +""" + +import argparse +import csv +import json +from collections import Counter +from datetime import datetime, timezone, timedelta +from pathlib import Path + +from .ground_truth_week2 import events_from_csv, intervals_from_csv, sha256 +from .part_b import read_series +from .packet_evidence import frames + +PAIRS = { + 17: ("wednesday", "204.97.153.43", "172.16.114.50"), + 26: ("thursday", "209.117.157.183", "172.16.114.207"), + 36: ("friday", "204.97.153.43", "172.16.114.50"), +} +SOURCE = ("https://www.researchgate.net/publication/" + "221506722_An_Artificial_Immune_System-Inspired_Multiobjective_" + "Evolutionary_Algorithm_with_Application_to_the_Detection_of_" + "Distributed_Computer_Network_Intrusions") + + +def spec_from_registry(official, registry): + candidates = intervals_from_csv(registry, events_from_csv(official)) + chosen = {} + for row in candidates: + event_id = row["attack_id"] + if event_id not in PAIRS: + continue + if event_id in chosen or row["status"] != "candidate" or row["url"] != SOURCE: + raise ValueError("Duplicate/non-candidate/non-literature input") + day, src, dst = PAIRS[event_id] + chosen[event_id] = dict(row, day=day, src=src, dst=dst) + if set(chosen) != set(PAIRS): + raise ValueError("Expected three independently documented candidates") + return chosen + + +def zero_stats(): + return {"pair_packets": 0, "syn_no_ack": 0, "syn_inside": 0, + "ports": set(), "first": None, "last": None} + + +def add(stats, spec, record): + epoch, _, source, target, protocol, port, _, flags = record + if protocol != "6" or source != spec["src"] or target != spec["dst"]: + return + t = datetime.fromtimestamp(float(epoch), timezone.utc) + if not spec["start"]-timedelta(minutes=2) <= t < spec["end"]+timedelta(minutes=2): + return + stats["pair_packets"] += 1 + bits = int(flags, 0) if flags else 0 + if not (bits & 0x02) or (bits & 0x10): + return + stats["syn_no_ack"] += 1 + if spec["start"] <= t < spec["end"]: + stats["syn_inside"] += 1 + if port: + stats["ports"].add(int(port)) + stats["first"] = t if stats["first"] is None else min(stats["first"], t) + stats["last"] = t if stats["last"] is None else max(stats["last"], t) + + +def run(pcaps, series, official, registry, out): + specs = spec_from_registry(official, registry) + expected = Counter() + for row in read_series(series): + expected[row["source_day"]] += int(row["packets"]) + stats = {event_id: zero_stats() for event_id in specs} + provenance = [] + for day in ("wednesday", "thursday", "friday"): + path = pcaps / day / "inside.tcpdump.gz" + if not path.is_file(): + raise FileNotFoundError(path) + count = 0 + for record in frames(path): + count += 1 + for event_id, spec in specs.items(): + if spec["day"] == day: + add(stats[event_id], spec, record) + if count != expected[day]: + raise ValueError(f"Capture {day} has {count} frames, expected {expected[day]}") + provenance.append({"day": day, "packets": count, "sha256": sha256(path)}) + print(f"[verified source integrity] {day}: {count} packets", flush=True) + rows = [] + for event_id, spec in sorted(specs.items()): + s = stats[event_id] + rows.append({ + "attack_id": event_id, "day": spec["day"], + "candidate_start": spec["start"].isoformat(), + "candidate_end_exclusive": spec["end"].isoformat(), + "source_ip_hypothesis": spec["src"], "target_ip_hypothesis": spec["dst"], + "matched_pair_packets_near": s["pair_packets"], + "matched_syn_no_ack_near": s["syn_no_ack"], + "matched_syn_no_ack_in_candidate": s["syn_inside"], + "unique_syn_destination_ports": len(s["ports"]), + "first_syn_utc": s["first"].isoformat() if s["first"] else "", + "last_syn_utc": s["last"].isoformat() if s["last"] else "", + "observed_metadata": "yes" if s["syn_no_ack"] else "no", + "ground_truth": "UNVERIFIED", + "evidence_source": SOURCE, + }) + out.mkdir(parents=True, exist_ok=True) + with (out / "pcap_candidate_evidence.csv").open("w", newline="", encoding="utf-8") as stream: + writer = csv.DictWriter(stream, fieldnames=list(rows[0])) + writer.writeheader() + writer.writerows(rows) + summary = { + "source_archives": provenance, + "candidate_attack_ids": sorted(specs), + "candidates_with_matching_syn_metadata": sum(r["observed_metadata"] == "yes" for r in rows), + "automatically_verified_attack_intervals": 0, + "automatically_verified_benign_intervals": 0, + "methodology": "source/target TCP SYN metadata only; manual attribution still required", + } + (out / "summary.json").write_text(json.dumps(summary, indent=2) + "\n", encoding="utf-8") + return summary + + +def main(): + p = argparse.ArgumentParser() + p.add_argument("--pcaps", type=Path, default=Path("data/raw")) + p.add_argument("--series", type=Path, default=Path("reports/week2-1999/data/traffic_60s.csv")) + p.add_argument("--official", type=Path, default=Path("data/ground_truth/darpa1999_week2_official_attack_starts.csv")) + p.add_argument("--intervals", type=Path, default=Path("data/ground_truth/week2_intervals_review.csv")) + p.add_argument("--out", type=Path, default=Path("results/week2-pcap-validation")) + a = p.parse_args() + print(json.dumps(run(a.pcaps, a.series, a.official, a.intervals, a.out), + indent=2, ensure_ascii=False)) + + +if __name__ == "__main__": + main() diff --git a/tests/test_week2_literature_intervals.py b/tests/test_week2_literature_intervals.py new file mode 100644 index 0000000..51c3878 --- /dev/null +++ b/tests/test_week2_literature_intervals.py @@ -0,0 +1,59 @@ +import unittest +from datetime import datetime, timedelta, timezone +from pathlib import Path + +from anomaly_detection.verify_week2_pcap import spec_from_registry, zero_stats, add +from anomaly_detection.ground_truth_week2 import ( + intervals_from_csv, events_from_csv, build_labels, +) +from anomaly_detection.part_b import read_series + + +class LiteratureCandidateTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.base = Path(__file__).resolve().parents[1] + cls.official = cls.base / "data/ground_truth/darpa1999_week2_official_attack_starts.csv" + cls.registry = cls.base / "data/ground_truth/week2_intervals_review.csv" + + def test_15_candidate_attacks_and_disputed_25_excluded(self): + rows = intervals_from_csv(self.registry, events_from_csv(self.official)) + self.assertEqual(len(rows), 15) + self.assertTrue(all(x["status"] == "candidate" for x in rows)) + self.assertNotIn(25, {x["attack_id"] for x in rows}) + + def test_cannot_generate_truth_from_candidates(self): + rows = read_series(self.base / "reports/week2-1999/data/traffic_60s.csv") + actual = build_labels( + rows, events_from_csv(self.official), + intervals_from_csv(self.registry, events_from_csv(self.official)), + ) + self.assertEqual(len(actual), 6424) + self.assertEqual(sum(x["is_attack"] != "" for x in actual), 0) + self.assertTrue(any(x["label_status"] == "candidate_attack" for x in actual)) + + def test_packet_cross_check_scoped_and_source_pinned(self): + specs = spec_from_registry(self.official, self.registry) + self.assertEqual(set(specs), {17, 26, 36}) + self.assertEqual(specs[26]["dst"], "172.16.114.207") + + def test_syn_packet_filter(self): + t = datetime(1999, 3, 11, 16, 5, tzinfo=timezone.utc) + spec = {"start": t-timedelta(minutes=1), "end": t+timedelta(minutes=1), + "src": "209.117.157.183", "dst": "172.16.114.207"} + def frame(flags, dst="172.16.114.207", when=t): + return [str(when.timestamp()), "64", "209.117.157.183", + dst, "6", "80", "", flags] + stats = zero_stats() + add(stats, spec, frame("0x0002")) + add(stats, spec, frame("0x0012")) + add(stats, spec, frame("0x0002", dst="172.16.114.99")) + add(stats, spec, frame("0x0002", when=t+timedelta(minutes=9))) + self.assertEqual(stats["pair_packets"], 2) + self.assertEqual(stats["syn_no_ack"], 1) + self.assertEqual(stats["syn_inside"], 1) + self.assertEqual(len(stats["ports"]), 1) + + +if __name__ == "__main__": + unittest.main()