Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions Backend/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,56 @@
- In Docker, data persists under `api/data/` (mounted to `/app/data`).
- Production: keep `DISABLE_AUTH=false`, use strong keys, configure CORS appropriately.

## Scenario Generation Playbook (Agent Instructions)
Use this workflow whenever creating a new attack scenario under `Backend/scenarios/`.

1) Implement the scenario script
- Create `Backend/scenarios/<scenario_id>.py` in `snake_case` (example: `identity_theft_ransomware_scenario.py`).
- Include profiles (`VICTIM_PROFILE`, `ATTACKER_PROFILE`) and phase/step generators.
- Return a top-level scenario object with:
- `scenario_name`, `description`, `generated_at`, `total_events`, `events`
- Each event formatted as: `{"timestamp", "source", "phase", "event"}`
- Save JSON output to `Backend/scenarios/configs/<scenario_id>.json` in `__main__`.

2) Reuse existing generators first
- Prefer functions from `event_generators/` over custom raw payloads.
- Common modules used by scenarios:
- Endpoint: `event_generators/endpoint_security/sentinelone_endpoint.py`
- Identity: `event_generators/identity_access/okta_authentication.py`
- Network: `event_generators/network_security/paloalto_firewall.py`
- Windows logs: `event_generators/endpoint_security/microsoft_windows_eventlog.py`
- Add new generator logic only if required fields cannot be represented with existing overrides.

3) Correlation-ready scenarios
- If scenario supports SIEM time anchoring, add `CORRELATION_CONFIG` in the scenario module.
- Include:
- `scenario_id`, `name`, `description`, `default_query`
- `time_anchors` and `phase_mapping`
- `fallback_behavior` (typically `offset_from_now`)

4) Register in backend APIs
- Add scenario metadata to:
- `api/app/services/scenario_service.py` (`self.scenario_templates`)
- `api/app/routers/scenarios.py` (`/templates` payload)
- If correlation-enabled, also register import/append logic in:
- `api/app/routers/scenarios.py` under `/correlation`

5) Register in frontend scenario dropdown
- Add scenario entry to `Frontend/log_generator_ui.py` route `GET /scenarios`.
- Ensure `id` exactly matches the scenario filename/module id.
- If the UI should auto-send generated JSON to HEC, include the scenario id in the auto-replay allowlist in `Frontend/log_generator_ui.py`.

6) Verify execution and HEC replay
- Validate import/compile:
- `python3 -c "from <scenario_module> import <entry_fn>; print('Import OK')"`
- Generate scenario:
- `python3 Backend/scenarios/<scenario_id>.py`
- Confirm JSON exists at `Backend/scenarios/configs/<scenario_id>.json`.
- Run replay manually if needed:
- `python3 Backend/scenarios/scenario_hec_sender.py --scenario Backend/scenarios/configs/<scenario_id>.json --auto --preserve-timestamps`

7) Expected operator log signals
- During generation: phase banners + `Total Events` + `Scenario saved to ...`.
- During replay: sender analysis + progress + transmission summary.
- If generation completes but replay does not start, check frontend auto-replay allowlist for missing scenario id.

28 changes: 25 additions & 3 deletions Backend/api/app/routers/scenarios.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class CorrelationRunRequest(BaseModel):
workers: int = 10
overwrite_parser: bool = False
suppress_alerts: bool = False
strip_helios_prefix: bool = False
include_helios_prefix: bool = False

# Initialize scenario service
scenario_service = ScenarioService()
Expand Down Expand Up @@ -146,6 +146,15 @@ async def get_scenario_templates(
"generators": ["proofpoint", "microsoft_365_collaboration", "sentinelone_endpoint", "paloalto_firewall"],
"severity": "high",
"mitre_tactics": ["T1566.002", "T1204.002", "T1059.001", "T1053.005", "T1071.001"]
},
{
"id": "identity_theft_ransomware",
"name": "Cross-Platform Identity Theft & Ransomware",
"description": "Advanced identity-led attack: esentutl.exe LOLBin credential theft, stolen OAuth refresh-token abuse through Okta, C2 via ScreenConnect/ngrok/AnyDesk, AD recon (SharpHound, ADRecon), LSASS dump + Okta privilege escalation, and ALPHV/BlackCat ransomware execution with VSS deletion.",
"duration_minutes": 55,
"generators": ["sentinelone_endpoint", "okta_authentication", "paloalto_firewall", "microsoft_windows_eventlog"],
"severity": "critical",
"mitre_tactics": ["T1539", "T1550.001", "T1078", "T1071.001", "T1059.001", "T1003.001", "T1486", "T1490"]
}
]

Expand Down Expand Up @@ -193,7 +202,20 @@ async def list_correlation_scenarios(
except ImportError as e:
logger.warning(f"Failed to import apollo_ransomware_scenario: {e}")

# Add more correlation scenarios here as they are created
# Identity Theft & Ransomware Scenario
try:
from identity_theft_ransomware_scenario import CORRELATION_CONFIG as SS_CORRELATION_CONFIG
correlation_scenarios.append({
"id": SS_CORRELATION_CONFIG["scenario_id"],
"name": SS_CORRELATION_CONFIG["name"],
"description": SS_CORRELATION_CONFIG["description"],
"default_query": SS_CORRELATION_CONFIG["default_query"],
"time_anchors": SS_CORRELATION_CONFIG["time_anchors"],
"phase_mapping": SS_CORRELATION_CONFIG["phase_mapping"],
"fallback_behavior": SS_CORRELATION_CONFIG.get("fallback_behavior", "offset_from_now")
})
except ImportError as e:
logger.warning(f"Failed to import identity_theft_ransomware_scenario: {e}")

return BaseResponse(
success=True,
Expand Down Expand Up @@ -324,7 +346,7 @@ async def run_correlation_scenario(
tag_trace=request.tag_trace,
overwrite_parser=request.overwrite_parser,
suppress_alerts=request.suppress_alerts,
strip_helios_prefix=request.strip_helios_prefix,
include_helios_prefix=request.include_helios_prefix,
background_tasks=background_tasks
)

Expand Down
43 changes: 35 additions & 8 deletions Backend/api/app/services/scenario_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,19 @@ def __init__(self):
{"name": "Command & Control", "generators": ["sentinelone_endpoint", "paloalto_firewall"], "duration": 1},
{"name": "Detection & Response", "generators": ["proofpoint", "sentinelone_endpoint"], "duration": 1}
]
},
"identity_theft_ransomware": {
"id": "identity_theft_ransomware",
"name": "Cross-Platform Identity Theft & Ransomware",
"description": "Advanced identity-led attack: esentutl.exe LOLBin credential theft, stolen OAuth refresh-token abuse through Okta, C2 via ScreenConnect/ngrok/AnyDesk, AD recon (SharpHound, ADRecon), LSASS dump + Okta privilege escalation, and ALPHV/BlackCat ransomware execution with VSS deletion.",
"phases": [
{"name": "Initial Access / Credential Theft", "generators": ["sentinelone_endpoint", "microsoft_windows_eventlog", "okta_authentication"], "duration": 10},
{"name": "Command & Control", "generators": ["sentinelone_endpoint", "paloalto_firewall"], "duration": 5},
{"name": "Endpoint Discovery & Staging", "generators": ["sentinelone_endpoint"], "duration": 10},
{"name": "Credential & Privilege Abuse", "generators": ["sentinelone_endpoint", "microsoft_windows_eventlog", "okta_authentication"], "duration": 10},
{"name": "Ransomware Preparation", "generators": ["sentinelone_endpoint"], "duration": 10},
{"name": "Ransomware Execution / Impact", "generators": ["sentinelone_endpoint", "microsoft_windows_eventlog"], "duration": 10}
]
}
}
self._load_json_scenarios()
Expand Down Expand Up @@ -249,6 +262,12 @@ async def list_scenarios(

# Add metadata
for scenario in scenarios:
data_sources = sorted({
generator
for phase in scenario.get("phases", [])
for generator in phase.get("generators", [])
})
scenario["data_sources"] = data_sources
scenario["phase_count"] = len(scenario.get("phases", []))
scenario["estimated_duration_minutes"] = sum(
phase.get("duration", 0) for phase in scenario.get("phases", [])
Expand Down Expand Up @@ -302,7 +321,7 @@ async def start_correlation_scenario(
dry_run: bool = False,
overwrite_parser: bool = False,
suppress_alerts: bool = False,
strip_helios_prefix: bool = False,
include_helios_prefix: bool = False,
background_tasks=None
) -> str:
"""Start correlation scenario execution with SIEM context and trace ID support"""
Expand All @@ -321,7 +340,7 @@ async def start_correlation_scenario(
"tag_trace": tag_trace,
"overwrite_parser": overwrite_parser,
"suppress_alerts": suppress_alerts,
"strip_helios_prefix": strip_helios_prefix,
"include_helios_prefix": include_helios_prefix,
"progress": 0
}

Expand All @@ -335,7 +354,7 @@ async def start_correlation_scenario(
tag_phase,
tag_trace,
suppress_alerts,
strip_helios_prefix
include_helios_prefix
)

return execution_id
Expand All @@ -349,7 +368,7 @@ async def _execute_correlation_scenario(
tag_phase: bool = True,
tag_trace: bool = True,
suppress_alerts: bool = False,
strip_helios_prefix: bool = False
include_helios_prefix: bool = False
):
"""Execute correlation scenario with SIEM context and trace ID support"""
import sys
Expand All @@ -371,13 +390,19 @@ async def _execute_correlation_scenario(
os.environ['S1_TRACE_ID'] = trace_id
os.environ['S1_TAG_PHASE'] = '1' if tag_phase else '0'
os.environ['S1_TAG_TRACE'] = '1' if tag_trace else '0'
os.environ['SCENARIO_INCLUDE_HELIOS_PREFIX'] = 'true' if include_helios_prefix else 'false'

# Import and run the scenario
module = __import__(scenario_id)
scenario_result = module.generate_apollo_ransomware_scenario(
scenario_module_map = {
'apollo_ransomware_scenario': ('apollo_ransomware_scenario', 'generate_apollo_ransomware_scenario'),
'identity_theft_ransomware': ('identity_theft_ransomware_scenario', 'generate_identity_theft_ransomware_scenario'),
}
module_name, function_name = scenario_module_map.get(scenario_id, (scenario_id, f'generate_{scenario_id}'))
module = __import__(module_name)
scenario_func = getattr(module, function_name)
scenario_result = scenario_func(
siem_context=siem_context,
suppress_alerts=suppress_alerts,
strip_helios_prefix=strip_helios_prefix,
include_helios_prefix=include_helios_prefix,
)

# Update execution status
Expand All @@ -400,6 +425,8 @@ async def _execute_correlation_scenario(
os.environ.pop('S1_TRACE_ID', None)
os.environ.pop('S1_TAG_PHASE', None)
os.environ.pop('S1_TAG_TRACE', None)
os.environ.pop('SCENARIO_INCLUDE_HELIOS_PREFIX', None)


async def _execute_scenario(self, execution_id: str, scenario: Dict[str, Any]):
"""Execute scenario in background"""
Expand Down
Loading
Loading