73 行
3.1 KiB
Python
73 行
3.1 KiB
Python
from __future__ import annotations
|
|
|
|
import subprocess
|
|
from pathlib import Path
|
|
from typing import Any, Dict, List
|
|
|
|
from lab.runners.dispatcher import run_attack as run_runner_attack
|
|
from lab.utils import write_json
|
|
|
|
|
|
TOOL_COMMANDS = {
|
|
"xss-fuzzer": ["python3", "/Users/x/websafe/02-xss/tools/xss-fuzzer.py"],
|
|
"xss-scanner": ["go", "run", "/Users/x/websafe/02-xss/tools/xss-scanner.go"],
|
|
"sqli-scanner": ["python3", "/Users/x/websafe/01-sql-injection/tools/sqli-scanner.py"],
|
|
"blind-sqli": ["python3", "/Users/x/websafe/01-sql-injection/tools/blind-sqli.py"],
|
|
"session-lab": ["python3", "/Users/x/websafe/03-authentication/session/tools/session-lab.py"],
|
|
"misconfig-lab": ["python3", "/Users/x/websafe/04-server-security/misconfiguration/tools/misconfig-lab.py"],
|
|
"tls-scanner": ["python3", "/Users/x/websafe/04-server-security/tls/tools/tls-scanner.py"],
|
|
"site-scope-mapper": ["python3", "/Users/x/websafe/04-server-security/infrastructure/tools/site-scope-mapper.py"],
|
|
}
|
|
|
|
|
|
def _render_args(step: Dict[str, Any], profile: Dict[str, Any], advisory: Dict[str, Any], run_dir: Path) -> List[str]:
|
|
target = (profile.get("baseline_urls") or [""])[0]
|
|
mapping = {
|
|
"{target_url}": target,
|
|
"{run_id}": run_dir.name,
|
|
"{case_id}": advisory["canonical_id"],
|
|
"{evidence_dir}": str(run_dir / "logs"),
|
|
}
|
|
args: List[str] = []
|
|
for item in step.get("args", []):
|
|
rendered = item
|
|
for key, value in mapping.items():
|
|
rendered = rendered.replace(key, value)
|
|
args.append(rendered)
|
|
return args
|
|
|
|
|
|
def run_attack(profile: Dict[str, Any], advisory: Dict[str, Any], run_dir: Path, dry_run: bool = False) -> Dict[str, Any]:
|
|
if profile.get("runner_id") and not dry_run:
|
|
return run_runner_attack(profile, advisory, run_dir)
|
|
|
|
steps: List[Dict[str, Any]] = []
|
|
for step in profile.get("attack_actions", []):
|
|
tool_name = step.get("tool")
|
|
args = _render_args(step, profile, advisory, run_dir)
|
|
record = {
|
|
"kind": step.get("kind", "tool"),
|
|
"tool": tool_name,
|
|
"args": args,
|
|
"status": "planned" if dry_run else "skipped",
|
|
}
|
|
if step.get("kind") == "tool" and tool_name in TOOL_COMMANDS and not dry_run:
|
|
output_path = run_dir / "logs" / f"{tool_name}.json"
|
|
cmd = TOOL_COMMANDS[tool_name] + args + ["--ack-authorized", "--format", "json", "--output", str(output_path)]
|
|
completed = subprocess.run(cmd, text=True, capture_output=True)
|
|
record.update(
|
|
{
|
|
"status": "completed" if completed.returncode == 0 else "failed",
|
|
"returncode": completed.returncode,
|
|
"stdout_excerpt": completed.stdout[-400:],
|
|
"stderr_excerpt": completed.stderr[-400:],
|
|
"result_path": str(output_path),
|
|
}
|
|
)
|
|
elif step.get("kind") == "note" and not dry_run:
|
|
record["status"] = "completed"
|
|
steps.append(record)
|
|
payload = {"steps": steps}
|
|
write_json(run_dir / "logs" / "attack.json", payload)
|
|
return payload
|