-
Notifications
You must be signed in to change notification settings - Fork 1
CLI Non Interactive
IXF can be used without the interactive shell, passing commands directly on the command line. This enables scripting, CI/CD pipelines, automation, scheduled security scans, and one-liner penetration testing workflows — all without requiring a human to drive the interactive shell.
ixf <command> [args...] ixf <command1> <arg1> <command2> <arg2> ...
Commands are separated by spaces and processed sequentially. The IXF shell initializes, runs all commands in order, and exits. Module context is preserved across commands in the same invocation (i.e., after use, subsequent set and run commands target that module).
Installation check:
ixf --version # IndustrialXPL-Forge v1.0.13 ixf --help # Shows non-interactive syntax help
ixf stats
[*] Indexing modules...
[+] 1193 modules indexed.
[i] IXF Module Statistics — IndustrialXPL-Forge v1.0.13
Total: 1193 | Vendors: 150 | Protocols: 50 | MITRE: 96/103 (93%)
cve: 486 | exploits: 159 | creds: 34 | scanners: 31 | assessment: 18
ixf search CVE-2021-22681
[*] Indexing modules...
[+] 1193 modules indexed.
[*] Search results for: CVE-2021-22681
use cve/siemens/cve_2021_22681_s7_1200_hardcoded_key CRITICAL
[*] 1 result(s) found.
ixf use scanners/ics/modbus_detect set target 192.168.1.100 run[*] Indexing modules...
[+] 1193 modules indexed.
[*] Module loaded: Modbus TCP Device Detect
[*] target => 192.168.1.100
[SIMULATE MODE — no packets sent]
[i] What would happen:
Phase 1 [TCP Connect]: TCP to 192.168.1.100:502
Phase 2 [FC04 Probe]: Modbus FC04 Read Input Registers (Unit=1)
Phase 3 [Fingerprint]: Analyze response for device type
[i] Payload (hex): 00 01 00 00 00 06 01 04 00 00 00 0A
[i] MITRE: T0846 (Remote System Discovery)
ixf use scanners/ics/modbus_detect set target 192.168.1.100 check[*] Module loaded: Modbus TCP Device Detect
[*] target => 192.168.1.100
[*] Checking 192.168.1.100:502...
[+] VULNERABLE — Modbus device detected
[+] Device: Schneider Electric Modicon M340 (inferred)
ixf mitre-coverage
[*] Indexing modules...
MITRE ATT&CK for ICS Coverage
Initial Access (TA0108): 9/9 (100%)
Execution (TA0104): 8/9 (88%)
...
TOTAL: 96/103 (93%)
ixf mitre-list discovery
MITRE ATT&CK for ICS — Discovery Techniques (TA0102)
T0840 Network Connection Enumeration 2 modules
T0842 Network Sniffing 3 modules
T0846 Remote System Discovery 8 modules
...
ixf ttp T0843 192.168.1.100
[*] TTP T0843 (Program Download) — 5 modules — simulate=True
[*] Running module 1/5: cve/siemens/cve_2021_22681_s7_1200_hardcoded_key
[SIMULATE] CVE-2021-22681 ...
[*] Running module 2/5: cve/siemens/cve_2022_38465_s7_global_key
[SIMULATE] CVE-2022-38465 ...
[+] T0843 sweep complete: 5 modules (simulate)
ixf vendors japan
Vendors (7 results — japan)
Omron 12 CVE | 3 Cred
Mitsubishi 8 CVE | 1 Cred
Yokogawa 5 CVE | 1 Cred
...
ixf protocols
Protocol Coverage (50 protocols)
MODBUS TCP 502/TCP 18 modules
Siemens S7comm 102/TCP 8 modules
...
ixf use scanners/ics/modbus_detect set target 192.168.1.100 run report json[*] Module loaded, target set, run complete
[+] Report saved: ixf_report_20260601_153045.json
ixf cve CVE-2023-6448 show info
[*] Module loaded: CVE-2023-6448 Unitronics UniStream PLC
name : CVE-2023-6448 Unitronics UniStream PLC
cvss : 9.8
impact : CRITICAL
mitre : T0812, T0859
ixf assess risk/ics_risk_scorer
[*] Loading assessment/risk/ics_risk_scorer...
ICS Risk Score Methodology
Network exposure: 30% Internet-facing: CRITICAL
Authentication: 25% No Modbus auth: HIGH
...
Composite Score: 8.7/10 (CRITICAL)
ixf ttp-list --tactic evasion
TTP Index — Evasion (TA0103)
T0838 Modify Alarm Settings 2 modules
T0844 Program Organization 1 module
T0849 Masquerading 1 module
T0851 Rootkit 2 modules
T0856 Spoof Reporting 2 modules
T0858 Change Credential 4 modules
T0872 Indicator Removal 1 module
T0874 Hooking 1 module
ixf mitre-report layer
[+] ATT&CK Navigator layer saved: ixf_mitre_layer_20260601_154200.json
[i] Open at: https://mitre-attack.github.io/attack-navigator/
ixf nse install
[*] Installing IXF NSE scripts...
[+] ics-sweep.nse → installed
[+] ics-default-creds.nse → installed
...
[+] All 8 IXF NSE scripts installed.
ixf llm-status
LLM Provider Status
gemini configured gemini-2.5-flash
openai not configured
...
Active: gemini
ixf sast /opt/plc_projects/water_treatment/ --mode sast
[*] Analyzing water_treatment/ (5 files, 245 lines)...
[*] Provider: gemini | Sanitized: 2 credentials, 1 IP
[*] Sending to LLM...
SAST REPORT: 1 CRITICAL | 2 HIGH | 1 MEDIUM | 1 LOW
FINDING [CRITICAL]: Unvalidated Chlorine Dosing Setpoint (line 48)
...
[+] Report saved: .tmp/sast_results/water_treatment_20260601.md
ixf setg target 10.0.0.100 use scanners/ics/modbus_detect run use scanners/ics/s7_comm_scanner run
[*] Global: target => 10.0.0.100
[*] Module loaded: Modbus TCP Device Detect
[SIMULATE] Modbus scan on 10.0.0.100:502...
[*] Module loaded: Siemens S7 Scanner
[SIMULATE] S7comm scan on 10.0.0.100:102...
ixf ttp-check T0859 192.168.1.100
[*] T0859 (Valid Accounts) check-only on 192.168.1.100...
creds/siemens/ssh_default_creds → POTENTIAL (port 22 open)
creds/siemens/telnet_default_creds → NOT VULNERABLE (port 23 closed)
...
ixf mitre-all 192.168.1.100 report html mitre-report layer
[*] Full MITRE sweep on 192.168.1.100 (simulate)...
[+] MITRE sweep complete: 74 techniques
[+] Report saved: ixf_report_20260601_161200.html
[+] Navigator layer saved: ixf_mitre_layer_20260601_161215.json
Chain as many commands as needed. Each use loads a new module; set, check, and run operate on the currently loaded module:
# Three modules, all in sequence, single invocation ixf \ use scanners/ics/modbus_detect \ set target 192.168.1.100 \ run \ use scanners/ics/s7_comm_scanner \ set target 192.168.1.100 \ run \ use cve/siemens/cve_2021_22681_s7_1200_hardcoded_key \ set target 192.168.1.100 \ run \ report json
[*] Indexing 1193 modules...
[*] Module loaded: Modbus TCP Device Detect
[*] target => 192.168.1.100
[SIMULATE] Modbus scan...
[*] Module loaded: Siemens S7 Scanner
[*] target => 192.168.1.100
[SIMULATE] S7comm scan...
[*] Module loaded: CVE-2021-22681 Siemens S7-1200/1500 PLC
[*] target => 192.168.1.100
[SIMULATE] CVE-2021-22681 exploit chain...
[+] Report saved: ixf_report_20260601_154500.json
# Execute a single technique ixf ttp T0843 192.168.1.100 # Execute with rate limiting (500ms between modules) ixf ttp T0843 192.168.1.100 --rate-limit 500 # Stop on first confirmed hit ixf ttp T0843 192.168.1.100 --stop-on-first # Save output to file ixf ttp T0843 192.168.1.100 --output /tmp/t0843_results.json # Read-only check (no exploit) ixf ttp-check T0843 192.168.1.100 # Force simulate for a technique ixf ttp-simulate T0843 192.168.1.100 # List all TTPs ixf ttp-list # List TTPs by tactic ixf ttp-list --tactic discovery ixf ttp-list --tactic impact ixf ttp-list --tactic "initial-access" # MITRE scan — tactic sweep ixf mitre-scan discovery 192.168.1.0/24 ixf mitre-scan initial-access 192.168.1.100 ixf mitre-scan impact 192.168.1.100 ixf mitre-scan collection 192.168.1.100 # MITRE scan — single technique ixf mitre-scan T0843 192.168.1.100 ixf mitre-scan T0836 192.168.1.100 # MITRE scan — TA-ID ixf mitre-scan TA0102 192.168.1.0/24 # Full MITRE sweep ixf mitre-all 192.168.1.100 # Coverage ixf mitre-coverage ixf coverage # Reports ixf mitre-report layer ixf mitre-report json ixf mitre-report html # Specific technique module list ixf mitre T0843 ixf mitre T0836 ixf mitre T0819 # Technique list by tactic ixf mitre-list ixf mitre-list discovery ixf mitre-list evasion ixf mitre-list "impair-process-control"
IXF output can be piped to standard shell tools for filtering, parsing, and integration with other tooling.
ixf search siemens | grep CRITICAL│ use cve/siemens/cve_2021_22681_s7_1200_hardcoded_key CRITICAL CVE-2021-22681 │
│ use cve/siemens/cve_2022_38465_s7_global_key CRITICAL CVE-2022-38465 │
│ use cve/siemens/cve_2023_44317_simatic_pcs_rce CRITICAL CVE-2023-44317 │
ixf vendors siemens | grep -c "CVE\|N/A" # 27
ixf search modbus | grep "^ use " | awk '{print 2ドル}'
exploits/protocols/modbus/modbus_write_single_register
exploits/protocols/modbus/modbus_flood_dos
exploits/protocols/modbus/modbus_read_coils
...
ixf use scanners/ics/modbus_detect set target 192.168.1.100 run report json cat ixf_report_*.json | jq '.events[] | select(.result == "VULNERABLE")'
{
"module": "scanners/ics/modbus_detect",
"target": "192.168.1.100",
"result": "VULNERABLE",
"impact": "READ",
"timestamp": "2026年06月01日T20:15:43Z"
}ixf search CVE-2021 | grep "CVE-2021" | awk '{print $NF}'
ixf protocols | grep "502"
MODBUS TCP 502/TCP 18 modules
PROFIsafe 502/TCP 1 module
ixf mitre-coverage | grep "TOTAL" # TOTAL: 96/103 (93%)
ixf vendors | awk '/^ [A-Z]/{print 1,ドル 2ドル}'
ixf sast /opt/plc.st 2>&1 | grep "SEVERITY: CRITICAL" # FINDING [SEVERITY: CRITICAL]: Unvalidated Chlorine Dosing Setpoint
ixf mitre-report layer ls -la ixf_mitre_layer_*.json wc -l ixf_mitre_layer_*.json
ixf use scanners/ics/modbus_detect set target 192.168.1.0/24 run report json jq '.events | length' ixf_report_*.json # 14
ixf ttp-list | awk '3ドル >= 5 {print 0ドル}' # Shows techniques with 5+ modules
for subnet in 192.168.1.0/24 10.0.0.0/24 172.16.0.0/24; do echo "=== Scanning $subnet ===" ixf use scanners/ics/modbus_detect set target $subnet check 2>&1 done | grep -E "VULNERABLE|NOT VULNERABLE"
ixf ttp T0843 192.168.1.100 2>&1 | grep "Running module" | sed 's/.*: //'
cve/siemens/cve_2021_22681_s7_1200_hardcoded_key
cve/siemens/cve_2022_38465_s7_global_key
cve/rockwell/cve_2022_1161_controllogix_modified_fw
exploits/protocols/s7comm/s7_unauthorized_cpu_control
assessment/mitre_ics/t0843_program_upload
ixf vendors | grep -E "(Siemens|Rockwell|Schneider|Honeywell)" | awk '{print 1,ドル 2,ドル $NF}'
A complete ICS security assessment script using IXF in non-interactive mode:
#!/usr/bin/env bash # ixf_assessment.sh — Automated ICS Security Assessment # Usage: ./ixf_assessment.sh <target_ip> [output_dir] # Requires: ixf installed (pip install industrialxpl-forge) # Author: Andre Henrique (@mrhenrike) | União Geek set -euo pipefail TARGET="${1:?Usage: 0ドル <target_ip> [output_dir]}" OUTPUT_DIR="${2:-.tmp/assessment_$(date +%Y%m%d_%H%M%S)}" LOGFILE="$OUTPUT_DIR/assessment.log" # Setup mkdir -p "$OUTPUT_DIR" echo "=== IXF ICS Security Assessment ===" | tee "$LOGFILE" echo "Target: $TARGET" | tee -a "$LOGFILE" echo "Started: $(date -u +%Y-%m-%dT%H:%M:%SZ)" | tee -a "$LOGFILE" echo "" | tee -a "$LOGFILE" # Check IXF is available if ! command -v ixf &>/dev/null; then echo "ERROR: ixf not found. Install: pip install industrialxpl-forge" >&2 exit 1 fi echo "[Phase 1] Module Index" | tee -a "$LOGFILE" ixf stats 2>&1 | tee -a "$LOGFILE" echo "" | tee -a "$LOGFILE" echo "[Phase 2] Protocol Discovery (Modbus)" | tee -a "$LOGFILE" ixf use scanners/ics/modbus_detect \ set target "$TARGET" \ check 2>&1 | tee -a "$LOGFILE" echo "" | tee -a "$LOGFILE" echo "[Phase 3] Protocol Discovery (S7comm)" | tee -a "$LOGFILE" ixf use scanners/ics/s7_comm_scanner \ set target "$TARGET" \ check 2>&1 | tee -a "$LOGFILE" echo "" | tee -a "$LOGFILE" echo "[Phase 4] MITRE Discovery Sweep (Simulate)" | tee -a "$LOGFILE" ixf mitre-scan discovery "$TARGET" 2>&1 | tee -a "$LOGFILE" echo "" | tee -a "$LOGFILE" echo "[Phase 5] TTP T0846 — Remote System Discovery" | tee -a "$LOGFILE" ixf ttp T0846 "$TARGET" 2>&1 | tee -a "$LOGFILE" echo "" | tee -a "$LOGFILE" echo "[Phase 6] TTP T0812 — Default Credentials Check" | tee -a "$LOGFILE" ixf ttp-check T0812 "$TARGET" 2>&1 | tee -a "$LOGFILE" echo "" | tee -a "$LOGFILE" echo "[Phase 7] TTP T0819 — Exploit Public-Facing Application (Simulate)" | tee -a "$LOGFILE" ixf ttp T0819 "$TARGET" 2>&1 | tee -a "$LOGFILE" echo "" | tee -a "$LOGFILE" echo "[Phase 8] IEC 62443 Zone/Conduit Assessment" | tee -a "$LOGFILE" ixf assess iec62443/zone_conduit_audit 2>&1 | tee -a "$LOGFILE" echo "" | tee -a "$LOGFILE" echo "[Phase 9] NIST SP 800-82r3 Checklist" | tee -a "$LOGFILE" ixf assess nist_sp800_82/control_checklist 2>&1 | tee -a "$LOGFILE" echo "" | tee -a "$LOGFILE" echo "[Phase 10] MITRE Coverage" | tee -a "$LOGFILE" ixf mitre-coverage 2>&1 | tee -a "$LOGFILE" echo "" | tee -a "$LOGFILE" echo "[Phase 11] Generate Reports" | tee -a "$LOGFILE" ixf report json 2>&1 | tee -a "$LOGFILE" ixf report html 2>&1 | tee -a "$LOGFILE" ixf mitre-report layer 2>&1 | tee -a "$LOGFILE" # Move generated reports to output dir mv ixf_report_*.json "$OUTPUT_DIR/" 2>/dev/null || true mv ixf_report_*.html "$OUTPUT_DIR/" 2>/dev/null || true mv ixf_mitre_*.json "$OUTPUT_DIR/" 2>/dev/null || true echo "" | tee -a "$LOGFILE" echo "=== Assessment Complete ===" | tee -a "$LOGFILE" echo "Finished: $(date -u +%Y-%m-%dT%H:%M:%SZ)" | tee -a "$LOGFILE" echo "Output directory: $OUTPUT_DIR" | tee -a "$LOGFILE" echo "Files:" | tee -a "$LOGFILE" ls -la "$OUTPUT_DIR/" | tee -a "$LOGFILE" # Summary: count findings VULNERABLE_COUNT=$(grep -c "VULNERABLE\|POTENTIAL\|CRITICAL\|HIGH" "$LOGFILE" || true) echo "" | tee -a "$LOGFILE" echo "Findings requiring attention: $VULNERABLE_COUNT" | tee -a "$LOGFILE"
Usage:
chmod +x ixf_assessment.sh ./ixf_assessment.sh 192.168.1.100 ./ixf_assessment.sh 192.168.1.100 /reports/q2-2026/
import subprocess import sys def run_ixf(*args: str) -> str: """Run IXF with given arguments and return output.""" result = subprocess.run( ["ixf"] + list(args), capture_output=True, text=True, timeout=120, ) return result.stdout + result.stderr # Simulate a module output = run_ixf( "use", "scanners/ics/modbus_detect", "set", "target", "192.168.1.100", "run", ) print(output)
import re output = run_ixf("mitre-coverage") total_match = re.search(r"TOTAL\s+(\d+)/(\d+)\s+\((\d+)%\)", output) if total_match: covered, total, pct = total_match.groups() print(f"MITRE coverage: {covered}/{total} ({pct}%)")
def search_modules(keyword: str) -> list[str]: output = run_ixf("search", keyword) paths = [] for line in output.splitlines(): if "use " in line: match = re.search(r"use\s+(\S+)", line) if match: paths.append(match.group(1)) return paths siemens_modules = search_modules("siemens") print(f"Found {len(siemens_modules)} Siemens modules")
def run_ttp(tid: str, target: str) -> dict: output = run_ixf("ttp", tid, target) return { "technique": tid, "target": target, "output": output, "simulated": "[SIMULATE" in output, "modules_run": output.count("Running module"), } result = run_ttp("T0843", "192.168.1.100") print(f"Ran {result['modules_run']} modules for {result['technique']}")
import json import glob # Run assessment run_ixf( "use", "scanners/ics/modbus_detect", "set", "target", "192.168.1.0/24", "run", "report", "json", ) # Parse most recent report reports = sorted(glob.glob("ixf_report_*.json")) if reports: with open(reports[-1]) as f: report = json.load(f) print(f"Session events: {len(report.get('events', []))}")
targets = ["192.168.1.100", "192.168.1.101", "10.0.0.50"] for target in targets: output = run_ixf( "use", "scanners/ics/modbus_detect", "set", "target", target, "check", ) status = "VULNERABLE" if "VULNERABLE" in output else "NOT VULNERABLE" print(f"{target}: {status}")
import os # Configure API key via environment (never hardcode) env = os.environ.copy() env["GOOGLE_AI_STUDIO_API_KEY"] = os.environ["GEMINI_KEY"] result = subprocess.run( ["ixf", "sast", "/opt/plc_projects/", "--mode", "sast"], capture_output=True, text=True, env=env, timeout=300, ) print(result.stdout)
import schedule import time def daily_scan(): """Run daily ICS reconnaissance scan.""" output = run_ixf("mitre-scan", "discovery", "192.168.1.0/24") # Parse and alert on new findings if "VULNERABLE" in output or "POTENTIAL" in output: alert_security_team(output) # Generate report run_ixf("report", "json") schedule.every().day.at("02:00").do(daily_scan) while True: schedule.run_pending() time.sleep(60)
def get_module_options(module_path: str) -> dict: output = run_ixf("use", module_path, "show", "options") options = {} for line in output.splitlines(): # Parse table rows: | option | value | required | description | parts = [p.strip() for p in line.split("|") if p.strip()] if len(parts) >= 3 and parts[0] not in ("Option", "-"): options[parts[0]] = { "value": parts[1], "required": parts[2].lower() == "yes", } return options opts = get_module_options("scanners/ics/modbus_detect") print(opts) # {"target": {"value": "", "required": True}, "port": {"value": "502", ...}}
def get_vendor_list() -> list[str]: output = run_ixf("vendors") vendors = [] for line in output.splitlines(): # Lines starting with vendor names match = re.match(r"\s{2}(\w[\w\s\/]+?)\s{2,}", line) if match: vendors.append(match.group(1).strip()) return vendors vendors = get_vendor_list() print(f"Total vendors: {len(vendors)}")
def has_modbus(target: str) -> bool: output = run_ixf( "use", "scanners/ics/modbus_detect", "set", "target", target, "check", ) return "[+] VULNERABLE" in output or "Modbus device detected" in output if has_modbus("192.168.1.100"): print("Target has Modbus — checking for CVEs...") print(run_ixf("search", "modbus"))
def get_ttp_coverage() -> dict: output = run_ixf("mitre-coverage") tactics = {} pattern = re.compile(r"(\w[\w\s]+\(TA\d+\))\s+:\s+(\d+)/(\d+)\s+\((\d+)%\)") for match in pattern.finditer(output): name, covered, total, pct = match.groups() tactics[name] = {"covered": int(covered), "total": int(total), "pct": int(pct)} return tactics coverage = get_ttp_coverage() for tactic, data in coverage.items(): if data["pct"] < 80: print(f"LOW COVERAGE: {tactic} ({data['pct']}%)")
cve_list = [ "CVE-2021-22681", "CVE-2022-38465", "CVE-2023-6448", "CVE-2022-29965", ] for cve in cve_list: output = run_ixf("search", cve) found = cve in output print(f"{cve}: {'COVERED' if found else 'NOT COVERED'}")
assessments = [ "iec62443/zone_conduit_audit", "nist_sp800_82/control_checklist", "risk/ics_risk_scorer", "threat_intel/ics_kill_chain", ] for assess_module in assessments: print(f"\n=== {assess_module} ===") output = run_ixf("assess", assess_module) print(output)
from pathlib import Path from datetime import datetime def full_assessment(target: str, output_dir: str = ".tmp/assessment") -> Path: """Run full ICS assessment and save results.""" out = Path(output_dir) out.mkdir(parents=True, exist_ok=True) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") phases = [ # (phase_name, ixf_args) ("discovery_modbus", ["use", "scanners/ics/modbus_detect", "set", "target", target, "check"]), ("discovery_s7", ["use", "scanners/ics/s7_comm_scanner", "set", "target", target, "check"]), ("mitre_discovery", ["mitre-scan", "discovery", target]), ("ttp_t0846", ["ttp", "T0846", target]), ("ttp_t0812", ["ttp-check", "T0812", target]), ("assess_iec62443", ["assess", "iec62443/zone_conduit_audit"]), ("assess_nist", ["assess", "nist_sp800_82/control_checklist"]), ("coverage", ["mitre-coverage"]), ] all_output = [] for phase_name, args in phases: print(f"[*] Running: {phase_name}") out_text = run_ixf(*args) all_output.append(f"=== {phase_name} ===\n{out_text}\n") # Write full log log_path = out / f"assessment_{target.replace('.', '_')}_{timestamp}.log" log_path.write_text("\n".join(all_output)) # Generate reports run_ixf("report", "json") run_ixf("mitre-report", "layer") print(f"[+] Assessment complete. Output: {log_path}") return log_path full_assessment("192.168.1.100", "/reports/assessment_q2/")
# .github/workflows/ics_security_scan.yml name: ICS Security Assessment on: schedule: - cron: "0 2 * * 1" # Every Monday at 02:00 UTC workflow_dispatch: inputs: target: description: "Target IP or CIDR" required: true default: "192.168.1.100" jobs: ics-scan: name: ICS Security Scan runs-on: ubuntu-latest timeout-minutes: 30 steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.13" - name: Install IXF run: pip install industrialxpl-forge - name: Verify IXF Installation run: | ixf stats ixf mitre-coverage - name: Run MITRE Coverage Check run: | ixf mitre-coverage > coverage_report.txt cat coverage_report.txt - name: Run Discovery Sweep (Simulate) env: TARGET: ${{ github.event.inputs.target || '192.168.1.100' }} run: | ixf use scanners/ics/modbus_detect set target "$TARGET" run > modbus_scan.txt || true ixf mitre-scan discovery "$TARGET" > discovery_scan.txt || true - name: Run Assessment Modules run: | ixf assess iec62443/zone_conduit_audit > iec62443_report.txt ixf assess nist_sp800_82/control_checklist > nist_report.txt ixf assess risk/ics_risk_scorer > risk_report.txt - name: Generate Reports env: TARGET: ${{ github.event.inputs.target || '192.168.1.100' }} run: | ixf use scanners/ics/modbus_detect set target "$TARGET" run report json ixf mitre-report layer ixf mitre-report html - name: Upload Assessment Artifacts uses: actions/upload-artifact@v4 with: name: ics-assessment-${{ github.run_number }} path: | ixf_report_*.json ixf_report_*.html ixf_mitre_*.json coverage_report.txt *_report.txt retention-days: 30 - name: Check for Critical Findings run: | if grep -q "CRITICAL\|CATASTROPHIC" modbus_scan.txt discovery_scan.txt 2>/dev/null; then echo "::warning::Critical findings detected in ICS scan" fi echo "ICS assessment complete."
// Jenkinsfile — ICS Security Assessment Pipeline pipeline { agent { docker { image 'python:3.13-slim' args '-u root' } } parameters { string(name: 'TARGET', defaultValue: '192.168.1.100', description: 'Target IP or CIDR') choice(name: 'SCAN_DEPTH', choices: ['simulate', 'check', 'full'], description: 'Scan depth') } environment { GOOGLE_AI_STUDIO_API_KEY = credentials('gemini-api-key') } stages { stage('Install IXF') { steps { sh 'pip install industrialxpl-forge' sh 'ixf stats' } } stage('Protocol Discovery') { steps { sh """ ixf use scanners/ics/modbus_detect \ set target ${params.TARGET} \ check > modbus_check.txt || true cat modbus_check.txt """ } } stage('MITRE Sweep') { steps { sh "ixf mitre-scan discovery ${params.TARGET} > mitre_discovery.txt" sh "ixf mitre-coverage" } } stage('TTP Analysis') { parallel { stage('T0843 - Program Download') { steps { sh "ixf ttp T0843 ${params.TARGET}" } } stage('T0812 - Default Credentials') { steps { sh "ixf ttp-check T0812 ${params.TARGET}" } } stage('T0846 - Remote Discovery') { steps { sh "ixf ttp T0846 ${params.TARGET}" } } } } stage('Compliance Assessment') { steps { sh 'ixf assess iec62443/zone_conduit_audit' sh 'ixf assess nist_sp800_82/control_checklist' sh 'ixf assess risk/ics_risk_scorer' } } stage('Generate Reports') { steps { sh 'ixf report json' sh 'ixf report html' sh 'ixf mitre-report layer' } } } post { always { archiveArtifacts artifacts: 'ixf_report_*.json, ixf_report_*.html, ixf_mitre_*.json, *.txt' } failure { echo 'ICS assessment pipeline failed' } success { echo 'ICS assessment complete' } } }
# .gitlab-ci.yml — ICS Security Assessment stages: - setup - scan - assess - report variables: TARGET: "192.168.1.100" PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip" cache: key: ixf-pip paths: - .cache/pip install-ixf: stage: setup image: python:3.13-slim script: - pip install industrialxpl-forge - ixf stats - ixf mitre-coverage artifacts: paths: - coverage_output.txt protocol-scan: stage: scan image: python:3.13-slim script: - pip install industrialxpl-forge - ixf use scanners/ics/modbus_detect set target $TARGET check || true - ixf use scanners/ics/s7_comm_scanner set target $TARGET check || true - ixf mitre-scan discovery $TARGET > discovery_report.txt artifacts: paths: - discovery_report.txt expire_in: 1 week ttp-analysis: stage: scan image: python:3.13-slim parallel: matrix: - TTP_ID: T0843 - TTP_ID: T0812 - TTP_ID: T0846 - TTP_ID: T0819 script: - pip install industrialxpl-forge - ixf ttp $TTP_ID $TARGET > ttp_${TTP_ID}_results.txt artifacts: paths: - ttp_*.txt compliance-assess: stage: assess image: python:3.13-slim script: - pip install industrialxpl-forge - ixf assess iec62443/zone_conduit_audit > iec62443.txt - ixf assess nist_sp800_82/control_checklist > nist_report.txt - ixf assess risk/ics_risk_scorer > risk_score.txt artifacts: paths: - iec62443.txt - nist_report.txt - risk_score.txt generate-reports: stage: report image: python:3.13-slim script: - pip install industrialxpl-forge - ixf report json - ixf report html - ixf mitre-report layer - ixf mitre-report html artifacts: paths: - ixf_report_*.json - ixf_report_*.html - ixf_mitre_*.json expire_in: 4 weeks
# Run IXF in Docker docker run --rm python:3.13-slim bash -c \ "pip install industrialxpl-forge -q && ixf stats" # With target network access docker run --rm --network host python:3.13-slim bash -c \ "pip install industrialxpl-forge -q && \ ixf use scanners/ics/modbus_detect set target 192.168.1.100 run" # Dockerfile for custom IXF image cat > Dockerfile << 'EOF' FROM python:3.13-slim RUN pip install industrialxpl-forge WORKDIR /assessment ENTRYPOINT ["ixf"] EOF docker build -t ixf:latest . # Run assessment docker run --rm -v $(pwd)/reports:/assessment ixf:latest \ use scanners/ics/modbus_detect \ set target 192.168.1.100 \ run \ report json # Interactive shell docker run -it --rm ixf:latest
| Exit Code | Meaning | When |
|---|---|---|
0 |
Success | All commands executed without error |
1 |
General error | Unhandled exception, invalid command |
2 |
Module not found |
use or cve command with unknown module |
3 |
Validation error | Required option not set, invalid value |
4 |
Connection error | check/run failed due to network unreachable |
5 |
LLM error | SAST command with no configured provider |
6 |
Permission error | NSE install without sudo/admin |
10 |
Aborted | DestructiveGate confirmation rejected |
Check exit code in bash:
ixf use scanners/ics/modbus_detect set target 192.168.1.100 check EXIT_CODE=$? case $EXIT_CODE in 0) echo "Success" ;; 2) echo "Module not found" ;; 4) echo "Connection failed — target unreachable" ;; *) echo "Error: $EXIT_CODE" ;; esac
import json import glob # Find latest JSON report reports = sorted(glob.glob("ixf_report_*.json")) if not reports: print("No reports found. Run: ixf report json") exit(1) with open(reports[-1]) as f: report = json.load(f) # Top-level structure print(f"Session ID: {report.get('session_id')}") print(f"Started: {report.get('started_at')}") print(f"Target: {report.get('target')}") print(f"Events: {len(report.get('events', []))}") # Filter events by type vulnerable = [e for e in report.get("events", []) if e.get("result") == "VULNERABLE"] simulated = [e for e in report.get("events", []) if e.get("simulated", True)] print(f"Vulnerable: {len(vulnerable)}") print(f"Simulated: {len(simulated)}") # Most severe findings for event in sorted(report.get("events", []), key=lambda e: e.get("cvss", 0), reverse=True)[:5]: print(f"{event.get('module')} — CVSS: {event.get('cvss', 'N/A')} — {event.get('result')}")
Previous: Module Development | Next: PolyExploit Runner