Sign In

ICS-Security Bundle

The artifacts that come out of a Virtual-SCADA run with ics_security_enabled. Each file is the real bytes the platform produces — none of it is template output.

Labelled data

ArtifactFormatWhat’s in it
scada_telemetryJSON arrayPer-tick rows with true_value, measured_value, reported_value. Attacked rows have labels.anomaly set to the MITRE technique id.
commandsJSON arrayOperator + supervisor + watchdog + attacker writes. Write-side attack rows carry ground_truth="control_attack" and a technique_id.
alarmsJSON arrayAlarms triggered by the attacked process state (high-high temperatures, pressure loss, interlock trips).
ics_securityNDJSONCampaign events: start / end windows, technique ids, target signals, and the effect parameters.

Wire captures

The traffic_pcapng artifact captures every packet produced during the attack window. Because the pcapng is real TCP/IP/Ethernet framing, it loads directly in Wireshark / Zeek / Suricata. Reconnaissance activity from T0846 shows up as real scan signatures; T0857 firmware-manipulation behaviour produces a corresponding firmware-load protocol exchange on the wire.

Using the bundle

from radmah_sdk import RadMah

sdk = RadMah(api_key="...", base_url="https://api.radmah.ai")

job = sdk.jobs.create(
    kind="simulate",
    engine="virtual_scada",
    seed=42,
    options={
        "description": (
            "11 kV distribution substation with two protective relays, one RTU, "
            "one breaker, one metering panel. Inject a command-manipulation "
            "campaign mid-run."
        ),
        "total_seconds": 120,
        "protocols": ["iec61850", "dnp3", "modbus"],
        "ics_security_enabled": True,
    },
)

# poll until succeeded
for art in sdk.list_artifacts(job.id):
    if art.name == "scada_telemetry":
        rows = json.loads(sdk.download_artifact(job.id, art.id))
        for r in rows:
            if r["labels"].get("anomaly", "none") != "none":
                # attacked row — feed to IDS training pipeline
                ...

Quality

The run is only released if the Validation Gate passes with attacks included — including the label_score dimension that specifically penalises runs where attacks were requested but no rows got labelled. See Validation Gate.