Skip to content

Commit a6f9bb6

Browse files
Merge pull request #75 from jmorascalyr/fix-pp-apollo
Fix pp apollo
2 parents 6e916f5 + 2992163 commit a6f9bb6

6 files changed

Lines changed: 78 additions & 18 deletions

File tree

Backend/api/app/alerts/templates/proofpoint_email_alert.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,5 @@
3636
"attack_surface_ids": [1],
3737
"severity_id": 4,
3838
"state_id": 1,
39-
"s1_classification_id": 1
39+
"s1_classification_id": 28
4040
}

Backend/api/app/routers/scenarios.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class CorrelationRunRequest(BaseModel):
4141
tag_trace: bool = True
4242
workers: int = 10
4343
overwrite_parser: bool = False
44+
suppress_alerts: bool = False
45+
strip_helios_prefix: bool = False
4446

4547
# Initialize scenario service
4648
scenario_service = ScenarioService()
@@ -321,6 +323,8 @@ async def run_correlation_scenario(
321323
tag_phase=request.tag_phase,
322324
tag_trace=request.tag_trace,
323325
overwrite_parser=request.overwrite_parser,
326+
suppress_alerts=request.suppress_alerts,
327+
strip_helios_prefix=request.strip_helios_prefix,
324328
background_tasks=background_tasks
325329
)
326330

Backend/api/app/services/scenario_service.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,8 @@ async def start_correlation_scenario(
292292
speed: str = "fast",
293293
dry_run: bool = False,
294294
overwrite_parser: bool = False,
295+
suppress_alerts: bool = False,
296+
strip_helios_prefix: bool = False,
295297
background_tasks=None
296298
) -> str:
297299
"""Start correlation scenario execution with SIEM context and trace ID support"""
@@ -309,6 +311,8 @@ async def start_correlation_scenario(
309311
"tag_phase": tag_phase,
310312
"tag_trace": tag_trace,
311313
"overwrite_parser": overwrite_parser,
314+
"suppress_alerts": suppress_alerts,
315+
"strip_helios_prefix": strip_helios_prefix,
312316
"progress": 0
313317
}
314318

@@ -320,7 +324,9 @@ async def start_correlation_scenario(
320324
siem_context,
321325
trace_id,
322326
tag_phase,
323-
tag_trace
327+
tag_trace,
328+
suppress_alerts,
329+
strip_helios_prefix
324330
)
325331

326332
return execution_id
@@ -332,7 +338,9 @@ async def _execute_correlation_scenario(
332338
siem_context: Dict[str, Any],
333339
trace_id: Optional[str] = None,
334340
tag_phase: bool = True,
335-
tag_trace: bool = True
341+
tag_trace: bool = True,
342+
suppress_alerts: bool = False,
343+
strip_helios_prefix: bool = False
336344
):
337345
"""Execute correlation scenario with SIEM context and trace ID support"""
338346
import sys
@@ -357,7 +365,11 @@ async def _execute_correlation_scenario(
357365

358366
# Import and run the scenario
359367
module = __import__(scenario_id)
360-
scenario_result = module.generate_apollo_ransomware_scenario(siem_context=siem_context)
368+
scenario_result = module.generate_apollo_ransomware_scenario(
369+
siem_context=siem_context,
370+
suppress_alerts=suppress_alerts,
371+
strip_helios_prefix=strip_helios_prefix,
372+
)
361373

362374
# Update execution status
363375
if execution_id in self.running_scenarios:

Backend/scenarios/apollo_ransomware_scenario.py

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,8 @@ def load_alert_template(template_id: str) -> Optional[Dict]:
306306
def send_phase_alert(
307307
phase_name: str,
308308
base_time: datetime,
309-
uam_config: dict
309+
uam_config: dict,
310+
strip_helios_prefix: bool = False
310311
) -> bool:
311312
"""Send alert for a specific phase with correct timing.
312313
@@ -341,16 +342,12 @@ def send_phase_alert(
341342
alert["metadata"]["logged_time"] = time_ms
342343
alert["metadata"]["modified_time"] = time_ms
343344

344-
# Set resource - use XDR Asset ID for endpoint alerts, shared GUID for email/user alerts
345+
# Set resource - use XDR Asset ID for endpoint alerts, fresh UUID for email/user alerts
345346
target_machine = mapping.get("target_machine", "bridge")
346347
if target_machine == "email":
347-
# Proofpoint/M365 alerts link to the user email with a consistent GUID
348-
email_asset_uid = uam_config.get('email_asset_uid')
349-
if not email_asset_uid:
350-
email_asset_uid = str(uuid.uuid5(uuid.NAMESPACE_DNS, VICTIM_PROFILE["email"]))
351-
uam_config['email_asset_uid'] = email_asset_uid
348+
# Fresh UUID each time — S1 UAM silently drops site-scoped alerts with static resource UIDs
352349
alert["resources"] = [{
353-
"uid": email_asset_uid,
350+
"uid": str(uuid.uuid4()),
354351
"name": VICTIM_PROFILE["email"]
355352
}]
356353
elif target_machine == "enterprise":
@@ -382,6 +379,12 @@ def send_phase_alert(
382379
else:
383380
alert[key] = value
384381

382+
# Strip "HELIOS - " prefix from alert title if requested
383+
if strip_helios_prefix:
384+
title = alert.get("finding_info", {}).get("title", "")
385+
if title.startswith("HELIOS - "):
386+
alert["finding_info"]["title"] = title[len("HELIOS - "):]
387+
385388
# Send alert via UAM ingest API
386389
try:
387390
ingest_url = uam_config['uam_ingest_url'].rstrip('/') + '/v1/alerts'
@@ -667,15 +670,26 @@ def generate_m365_sharepoint_exfil(base_time: datetime) -> List[Dict]:
667670
return events
668671

669672

670-
def generate_apollo_ransomware_scenario(siem_context: Optional[Dict] = None) -> Dict:
673+
def generate_apollo_ransomware_scenario(
674+
siem_context: Optional[Dict] = None,
675+
suppress_alerts: Optional[bool] = None,
676+
strip_helios_prefix: Optional[bool] = None,
677+
) -> Dict:
671678
"""Generate the complete Apollo ransomware scenario (Proofpoint + M365 only)
672679
673680
Args:
674681
siem_context: Optional dict with SIEM query results for timestamp correlation.
675682
Expected format: {"results": [...], "anchors": {...}}
676683
If provided, timestamps are calculated relative to existing EDR/WEL data.
677684
If None, falls back to offset from current time.
685+
suppress_alerts: If True, skip sending UAM alerts. Env fallback: SCENARIO_SUPPRESS_ALERTS.
686+
strip_helios_prefix: If True, remove "HELIOS - " prefix from alert titles. Env fallback: SCENARIO_STRIP_HELIOS_PREFIX.
678687
"""
688+
# Resolve options from args or env vars
689+
if suppress_alerts is None:
690+
suppress_alerts = os.getenv('SCENARIO_SUPPRESS_ALERTS', 'false').lower() == 'true'
691+
if strip_helios_prefix is None:
692+
strip_helios_prefix = os.getenv('SCENARIO_STRIP_HELIOS_PREFIX', 'false').lower() == 'true'
679693

680694
# Determine base time based on SIEM context or fallback
681695
use_correlation = False
@@ -718,7 +732,7 @@ def generate_apollo_ransomware_scenario(siem_context: Optional[Dict] = None) ->
718732
print("=" * 80 + "\n")
719733

720734
# Initialize alert detonation from env vars
721-
alerts_enabled = os.getenv('SCENARIO_ALERTS_ENABLED', 'false').lower() == 'true'
735+
alerts_enabled = (not suppress_alerts) and os.getenv('SCENARIO_ALERTS_ENABLED', 'false').lower() == 'true'
722736
uam_config = None
723737

724738
if alerts_enabled:
@@ -783,6 +797,8 @@ def generate_apollo_ransomware_scenario(siem_context: Optional[Dict] = None) ->
783797
print("\n🚨 ALERT DETONATION ENABLED")
784798
print(f" UAM Ingest: {uam_ingest_url}")
785799
print(f" Account ID: {uam_account_id}")
800+
if strip_helios_prefix:
801+
print(f" Strip HELIOS prefix: Yes")
786802
print("=" * 80)
787803
else:
788804
print("⚠️ SCENARIO_ALERTS_ENABLED=true but UAM credentials missing")
@@ -825,13 +841,13 @@ def generate_apollo_ransomware_scenario(siem_context: Optional[Dict] = None) ->
825841
# Send corresponding alert if enabled and phase has alert mapping
826842
if alerts_enabled and phase_name in ALERT_PHASE_MAPPING:
827843
print(f" 📤 Sending alert for {phase_name}...", end=" ")
828-
success = send_phase_alert(phase_name, phase_base_time, uam_config)
844+
success = send_phase_alert(phase_name, phase_base_time, uam_config, strip_helios_prefix=strip_helios_prefix)
829845
print(f"{'✓' if success else '✗'}")
830846

831847
# Send RDP alert after data exfiltration phase
832848
if alerts_enabled and phase_name == "📤 PHASE 4: Data Exfiltration":
833849
print(f" 📤 Sending RDP download alert...", end=" ")
834-
success = send_phase_alert("rdp_download", phase_base_time, uam_config)
850+
success = send_phase_alert("rdp_download", phase_base_time, uam_config, strip_helios_prefix=strip_helios_prefix)
835851
print(f"{'✓' if success else '✗'}")
836852

837853
# Send standalone WEL alerts (not tied to a specific event generation phase)
@@ -845,7 +861,7 @@ def generate_apollo_ransomware_scenario(siem_context: Optional[Dict] = None) ->
845861
]
846862
for alert_key, alert_desc in wel_alerts:
847863
print(f" 📤 {alert_desc}...", end=" ")
848-
success = send_phase_alert(alert_key, base_time, uam_config)
864+
success = send_phase_alert(alert_key, base_time, uam_config, strip_helios_prefix=strip_helios_prefix)
849865
print(f"{'✓' if success else '✗'}")
850866

851867
all_events.sort(key=lambda x: x["timestamp"])

Frontend/log_generator_ui.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,8 @@ def run_correlation_scenario():
612612
trace_id = (data.get('trace_id') or '').strip()
613613
local_token = data.get('hec_token')
614614
overwrite_parser = data.get('overwrite_parser', False)
615+
suppress_alerts = data.get('suppress_alerts', False)
616+
strip_helios_prefix = data.get('strip_helios_prefix', False)
615617

616618
if not scenario_id:
617619
return jsonify({'error': 'scenario_id is required'}), 400
@@ -815,7 +817,14 @@ def generate_and_stream():
815817
yield "INFO: ⚠️ S1 asset linking disabled (no S1 API token on destination)\n"
816818
except Exception as s1e:
817819
logger.warning(f"Could not resolve S1 API token: {s1e}")
818-
yield "INFO: 🚨 Alert detonation enabled (UAM credentials found)\n"
820+
if suppress_alerts:
821+
env['SCENARIO_SUPPRESS_ALERTS'] = 'true'
822+
yield "INFO: 🔇 Alert detonation suppressed by user\n"
823+
else:
824+
yield "INFO: 🚨 Alert detonation enabled (UAM credentials found)\n"
825+
if strip_helios_prefix:
826+
env['SCENARIO_STRIP_HELIOS_PREFIX'] = 'true'
827+
yield "INFO: ✂️ HELIOS prefix will be stripped from alert titles\n"
819828
else:
820829
yield "INFO: ⚠️ Alert detonation disabled (no UAM credentials on destination)\n"
821830

Frontend/templates/log_generator.html

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,21 @@ <h4 class="text-sm font-semibold text-[#c8b3e8] mb-3">Discovered Time Anchors</h
795795
<p class="text-xs text-[#b8a0d2]">If checked, will overwrite existing parsers in the destination SIEM instead of skipping them. Use with caution.</p>
796796
</div>
797797
</details>
798+
<details>
799+
<summary class="cursor-pointer text-sm font-semibold text-[#c8b3e8]">Alert Options</summary>
800+
<div class="mt-3 space-y-2 text-sm">
801+
<label class="flex items-center gap-2">
802+
<input type="checkbox" id="correlation-suppress-alerts">
803+
<span class="text-[#f3e8ff]">Suppress alerts</span>
804+
</label>
805+
<p class="text-xs text-[#b8a0d2]">Skip sending UAM alerts even if credentials are configured on the destination.</p>
806+
<label class="flex items-center gap-2">
807+
<input type="checkbox" id="correlation-strip-helios-prefix">
808+
<span class="text-[#f3e8ff]">Strip <code>HELIOS -</code> prefix from alert titles</span>
809+
</label>
810+
<p class="text-xs text-[#b8a0d2]">Remove the "HELIOS - " prefix from alert titles before sending to S1.</p>
811+
</div>
812+
</details>
798813

799814
<div class="flex gap-3">
800815
<button type="button" id="run-correlation-scenario-btn" class="btn flex-1" disabled>
@@ -3487,6 +3502,8 @@ <h3 class="text-xl font-bold text-[#f3e8ff]">Select Parser Version</h3>
34873502
const tagPhase = document.getElementById('correlation-tag-phase').checked;
34883503
const tagTrace = document.getElementById('correlation-tag-trace').checked;
34893504
const overwriteParser = document.getElementById('correlation-overwrite-parser').checked;
3505+
const suppressAlerts = document.getElementById('correlation-suppress-alerts').checked;
3506+
const stripHeliosPrefix = document.getElementById('correlation-strip-helios-prefix').checked;
34903507
let traceId = correlationTraceIdInput.value.trim();
34913508

34923509
if (tagTrace && !traceId) {
@@ -3519,6 +3536,8 @@ <h3 class="text-xl font-bold text-[#f3e8ff]">Select Parser Version</h3>
35193536
tag_trace: tagTrace,
35203537
trace_id: traceId,
35213538
overwrite_parser: overwriteParser,
3539+
suppress_alerts: suppressAlerts,
3540+
strip_helios_prefix: stripHeliosPrefix,
35223541
hec_token: localToken
35233542
})
35243543
});

0 commit comments

Comments
 (0)