souleyez 2.43.29__py3-none-any.whl → 2.43.34__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of souleyez might be problematic. Click here for more details.
- souleyez/__init__.py +1 -2
- souleyez/ai/__init__.py +21 -15
- souleyez/ai/action_mapper.py +249 -150
- souleyez/ai/chain_advisor.py +116 -100
- souleyez/ai/claude_provider.py +29 -28
- souleyez/ai/context_builder.py +80 -62
- souleyez/ai/executor.py +158 -117
- souleyez/ai/feedback_handler.py +136 -121
- souleyez/ai/llm_factory.py +27 -20
- souleyez/ai/llm_provider.py +4 -2
- souleyez/ai/ollama_provider.py +6 -9
- souleyez/ai/ollama_service.py +44 -37
- souleyez/ai/path_scorer.py +91 -76
- souleyez/ai/recommender.py +176 -144
- souleyez/ai/report_context.py +74 -73
- souleyez/ai/report_service.py +84 -66
- souleyez/ai/result_parser.py +222 -229
- souleyez/ai/safety.py +67 -44
- souleyez/auth/__init__.py +23 -22
- souleyez/auth/audit.py +36 -26
- souleyez/auth/engagement_access.py +65 -48
- souleyez/auth/permissions.py +14 -3
- souleyez/auth/session_manager.py +54 -37
- souleyez/auth/user_manager.py +109 -64
- souleyez/commands/audit.py +40 -43
- souleyez/commands/auth.py +35 -15
- souleyez/commands/deliverables.py +55 -50
- souleyez/commands/engagement.py +47 -28
- souleyez/commands/license.py +32 -23
- souleyez/commands/screenshots.py +36 -32
- souleyez/commands/user.py +82 -36
- souleyez/config.py +52 -44
- souleyez/core/credential_tester.py +87 -81
- souleyez/core/cve_mappings.py +179 -192
- souleyez/core/cve_matcher.py +162 -148
- souleyez/core/msf_auto_mapper.py +100 -83
- souleyez/core/msf_chain_engine.py +294 -256
- souleyez/core/msf_database.py +153 -70
- souleyez/core/msf_integration.py +679 -673
- souleyez/core/msf_rpc_client.py +40 -42
- souleyez/core/msf_rpc_manager.py +77 -79
- souleyez/core/msf_sync_manager.py +241 -181
- souleyez/core/network_utils.py +22 -15
- souleyez/core/parser_handler.py +34 -25
- souleyez/core/pending_chains.py +114 -63
- souleyez/core/templates.py +158 -107
- souleyez/core/tool_chaining.py +9526 -2879
- souleyez/core/version_utils.py +79 -94
- souleyez/core/vuln_correlation.py +136 -89
- souleyez/core/web_utils.py +33 -32
- souleyez/data/wordlists/ad_users.txt +378 -0
- souleyez/data/wordlists/api_endpoints_large.txt +769 -0
- souleyez/data/wordlists/home_dir_sensitive.txt +39 -0
- souleyez/data/wordlists/lfi_payloads.txt +82 -0
- souleyez/data/wordlists/passwords_brute.txt +1548 -0
- souleyez/data/wordlists/passwords_crack.txt +2479 -0
- souleyez/data/wordlists/passwords_spray.txt +386 -0
- souleyez/data/wordlists/subdomains_large.txt +5057 -0
- souleyez/data/wordlists/usernames_common.txt +694 -0
- souleyez/data/wordlists/web_dirs_large.txt +4769 -0
- souleyez/detection/__init__.py +1 -1
- souleyez/detection/attack_signatures.py +12 -17
- souleyez/detection/mitre_mappings.py +61 -55
- souleyez/detection/validator.py +97 -86
- souleyez/devtools.py +23 -10
- souleyez/docs/README.md +4 -4
- souleyez/docs/api-reference/cli-commands.md +2 -2
- souleyez/docs/developer-guide/adding-new-tools.md +562 -0
- souleyez/docs/user-guide/auto-chaining.md +30 -8
- souleyez/docs/user-guide/getting-started.md +1 -1
- souleyez/docs/user-guide/installation.md +26 -3
- souleyez/docs/user-guide/metasploit-integration.md +2 -2
- souleyez/docs/user-guide/rbac.md +1 -1
- souleyez/docs/user-guide/scope-management.md +1 -1
- souleyez/docs/user-guide/siem-integration.md +1 -1
- souleyez/docs/user-guide/tools-reference.md +1 -8
- souleyez/docs/user-guide/worker-management.md +1 -1
- souleyez/engine/background.py +1239 -535
- souleyez/engine/base.py +4 -1
- souleyez/engine/job_status.py +17 -49
- souleyez/engine/log_sanitizer.py +103 -77
- souleyez/engine/manager.py +38 -7
- souleyez/engine/result_handler.py +2200 -1550
- souleyez/engine/worker_manager.py +50 -41
- souleyez/export/evidence_bundle.py +72 -62
- souleyez/feature_flags/features.py +16 -20
- souleyez/feature_flags.py +5 -9
- souleyez/handlers/__init__.py +11 -0
- souleyez/handlers/base.py +188 -0
- souleyez/handlers/bash_handler.py +277 -0
- souleyez/handlers/bloodhound_handler.py +243 -0
- souleyez/handlers/certipy_handler.py +311 -0
- souleyez/handlers/crackmapexec_handler.py +486 -0
- souleyez/handlers/dnsrecon_handler.py +344 -0
- souleyez/handlers/enum4linux_handler.py +400 -0
- souleyez/handlers/evil_winrm_handler.py +493 -0
- souleyez/handlers/ffuf_handler.py +815 -0
- souleyez/handlers/gobuster_handler.py +1114 -0
- souleyez/handlers/gpp_extract_handler.py +334 -0
- souleyez/handlers/hashcat_handler.py +444 -0
- souleyez/handlers/hydra_handler.py +563 -0
- souleyez/handlers/impacket_getuserspns_handler.py +343 -0
- souleyez/handlers/impacket_psexec_handler.py +222 -0
- souleyez/handlers/impacket_secretsdump_handler.py +426 -0
- souleyez/handlers/john_handler.py +286 -0
- souleyez/handlers/katana_handler.py +425 -0
- souleyez/handlers/kerbrute_handler.py +298 -0
- souleyez/handlers/ldapsearch_handler.py +636 -0
- souleyez/handlers/lfi_extract_handler.py +464 -0
- souleyez/handlers/msf_auxiliary_handler.py +408 -0
- souleyez/handlers/msf_exploit_handler.py +380 -0
- souleyez/handlers/nikto_handler.py +413 -0
- souleyez/handlers/nmap_handler.py +821 -0
- souleyez/handlers/nuclei_handler.py +359 -0
- souleyez/handlers/nxc_handler.py +371 -0
- souleyez/handlers/rdp_sec_check_handler.py +353 -0
- souleyez/handlers/registry.py +292 -0
- souleyez/handlers/responder_handler.py +232 -0
- souleyez/handlers/service_explorer_handler.py +434 -0
- souleyez/handlers/smbclient_handler.py +344 -0
- souleyez/handlers/smbmap_handler.py +510 -0
- souleyez/handlers/smbpasswd_handler.py +296 -0
- souleyez/handlers/sqlmap_handler.py +1116 -0
- souleyez/handlers/theharvester_handler.py +601 -0
- souleyez/handlers/web_login_test_handler.py +327 -0
- souleyez/handlers/whois_handler.py +277 -0
- souleyez/handlers/wpscan_handler.py +554 -0
- souleyez/history.py +32 -16
- souleyez/importers/msf_importer.py +106 -75
- souleyez/importers/smart_importer.py +208 -147
- souleyez/integrations/siem/__init__.py +10 -10
- souleyez/integrations/siem/base.py +17 -18
- souleyez/integrations/siem/elastic.py +108 -122
- souleyez/integrations/siem/factory.py +207 -80
- souleyez/integrations/siem/googlesecops.py +146 -154
- souleyez/integrations/siem/rule_mappings/__init__.py +1 -1
- souleyez/integrations/siem/rule_mappings/wazuh_rules.py +8 -5
- souleyez/integrations/siem/sentinel.py +107 -109
- souleyez/integrations/siem/splunk.py +246 -212
- souleyez/integrations/siem/wazuh.py +65 -71
- souleyez/integrations/wazuh/__init__.py +5 -5
- souleyez/integrations/wazuh/client.py +70 -93
- souleyez/integrations/wazuh/config.py +85 -57
- souleyez/integrations/wazuh/host_mapper.py +28 -36
- souleyez/integrations/wazuh/sync.py +78 -68
- souleyez/intelligence/__init__.py +4 -5
- souleyez/intelligence/correlation_analyzer.py +309 -295
- souleyez/intelligence/exploit_knowledge.py +661 -623
- souleyez/intelligence/exploit_suggestions.py +159 -139
- souleyez/intelligence/gap_analyzer.py +132 -97
- souleyez/intelligence/gap_detector.py +251 -214
- souleyez/intelligence/sensitive_tables.py +266 -129
- souleyez/intelligence/service_parser.py +137 -123
- souleyez/intelligence/surface_analyzer.py +407 -268
- souleyez/intelligence/target_parser.py +159 -162
- souleyez/licensing/__init__.py +6 -6
- souleyez/licensing/validator.py +17 -19
- souleyez/log_config.py +79 -54
- souleyez/main.py +1505 -687
- souleyez/migrations/fix_job_counter.py +16 -14
- souleyez/parsers/bloodhound_parser.py +41 -39
- souleyez/parsers/crackmapexec_parser.py +178 -111
- souleyez/parsers/dalfox_parser.py +72 -77
- souleyez/parsers/dnsrecon_parser.py +103 -91
- souleyez/parsers/enum4linux_parser.py +183 -153
- souleyez/parsers/ffuf_parser.py +29 -25
- souleyez/parsers/gobuster_parser.py +301 -41
- souleyez/parsers/hashcat_parser.py +324 -79
- souleyez/parsers/http_fingerprint_parser.py +350 -103
- souleyez/parsers/hydra_parser.py +131 -111
- souleyez/parsers/impacket_parser.py +231 -178
- souleyez/parsers/john_parser.py +98 -86
- souleyez/parsers/katana_parser.py +316 -0
- souleyez/parsers/msf_parser.py +943 -498
- souleyez/parsers/nikto_parser.py +346 -65
- souleyez/parsers/nmap_parser.py +262 -174
- souleyez/parsers/nuclei_parser.py +40 -44
- souleyez/parsers/responder_parser.py +26 -26
- souleyez/parsers/searchsploit_parser.py +74 -74
- souleyez/parsers/service_explorer_parser.py +279 -0
- souleyez/parsers/smbmap_parser.py +180 -124
- souleyez/parsers/sqlmap_parser.py +434 -308
- souleyez/parsers/theharvester_parser.py +75 -57
- souleyez/parsers/whois_parser.py +135 -94
- souleyez/parsers/wpscan_parser.py +278 -190
- souleyez/plugins/afp.py +44 -36
- souleyez/plugins/afp_brute.py +114 -46
- souleyez/plugins/ard.py +48 -37
- souleyez/plugins/bloodhound.py +95 -61
- souleyez/plugins/certipy.py +303 -0
- souleyez/plugins/crackmapexec.py +186 -85
- souleyez/plugins/dalfox.py +120 -59
- souleyez/plugins/dns_hijack.py +146 -41
- souleyez/plugins/dnsrecon.py +97 -61
- souleyez/plugins/enum4linux.py +91 -66
- souleyez/plugins/evil_winrm.py +291 -0
- souleyez/plugins/ffuf.py +166 -90
- souleyez/plugins/firmware_extract.py +133 -29
- souleyez/plugins/gobuster.py +387 -190
- souleyez/plugins/gpp_extract.py +393 -0
- souleyez/plugins/hashcat.py +100 -73
- souleyez/plugins/http_fingerprint.py +854 -267
- souleyez/plugins/hydra.py +566 -200
- souleyez/plugins/impacket_getnpusers.py +117 -69
- souleyez/plugins/impacket_psexec.py +84 -64
- souleyez/plugins/impacket_secretsdump.py +103 -69
- souleyez/plugins/impacket_smbclient.py +89 -75
- souleyez/plugins/john.py +86 -69
- souleyez/plugins/katana.py +313 -0
- souleyez/plugins/kerbrute.py +237 -0
- souleyez/plugins/lfi_extract.py +541 -0
- souleyez/plugins/macos_ssh.py +117 -48
- souleyez/plugins/mdns.py +35 -30
- souleyez/plugins/msf_auxiliary.py +253 -130
- souleyez/plugins/msf_exploit.py +239 -161
- souleyez/plugins/nikto.py +134 -78
- souleyez/plugins/nmap.py +275 -91
- souleyez/plugins/nuclei.py +180 -89
- souleyez/plugins/nxc.py +285 -0
- souleyez/plugins/plugin_base.py +35 -36
- souleyez/plugins/plugin_template.py +13 -5
- souleyez/plugins/rdp_sec_check.py +130 -0
- souleyez/plugins/responder.py +112 -71
- souleyez/plugins/router_http_brute.py +76 -65
- souleyez/plugins/router_ssh_brute.py +118 -41
- souleyez/plugins/router_telnet_brute.py +124 -42
- souleyez/plugins/routersploit.py +91 -59
- souleyez/plugins/routersploit_exploit.py +77 -55
- souleyez/plugins/searchsploit.py +91 -77
- souleyez/plugins/service_explorer.py +1160 -0
- souleyez/plugins/smbmap.py +122 -72
- souleyez/plugins/smbpasswd.py +215 -0
- souleyez/plugins/sqlmap.py +301 -113
- souleyez/plugins/theharvester.py +127 -75
- souleyez/plugins/tr069.py +79 -57
- souleyez/plugins/upnp.py +65 -47
- souleyez/plugins/upnp_abuse.py +73 -55
- souleyez/plugins/vnc_access.py +129 -42
- souleyez/plugins/vnc_brute.py +109 -38
- souleyez/plugins/web_login_test.py +417 -0
- souleyez/plugins/whois.py +77 -58
- souleyez/plugins/wpscan.py +173 -69
- souleyez/reporting/__init__.py +2 -1
- souleyez/reporting/attack_chain.py +411 -346
- souleyez/reporting/charts.py +436 -501
- souleyez/reporting/compliance_mappings.py +334 -201
- souleyez/reporting/detection_report.py +126 -125
- souleyez/reporting/formatters.py +828 -591
- souleyez/reporting/generator.py +386 -302
- souleyez/reporting/metrics.py +72 -75
- souleyez/scanner.py +35 -29
- souleyez/security/__init__.py +37 -11
- souleyez/security/scope_validator.py +175 -106
- souleyez/security/validation.py +223 -149
- souleyez/security.py +22 -6
- souleyez/storage/credentials.py +247 -186
- souleyez/storage/crypto.py +296 -129
- souleyez/storage/database.py +73 -50
- souleyez/storage/db.py +58 -36
- souleyez/storage/deliverable_evidence.py +177 -128
- souleyez/storage/deliverable_exporter.py +282 -246
- souleyez/storage/deliverable_templates.py +134 -116
- souleyez/storage/deliverables.py +135 -130
- souleyez/storage/engagements.py +109 -56
- souleyez/storage/evidence.py +181 -152
- souleyez/storage/execution_log.py +31 -17
- souleyez/storage/exploit_attempts.py +93 -57
- souleyez/storage/exploits.py +67 -36
- souleyez/storage/findings.py +48 -61
- souleyez/storage/hosts.py +176 -144
- souleyez/storage/migrate_to_engagements.py +43 -19
- souleyez/storage/migrations/_001_add_credential_enhancements.py +22 -12
- souleyez/storage/migrations/_002_add_status_tracking.py +10 -7
- souleyez/storage/migrations/_003_add_execution_log.py +14 -8
- souleyez/storage/migrations/_005_screenshots.py +13 -5
- souleyez/storage/migrations/_006_deliverables.py +13 -5
- souleyez/storage/migrations/_007_deliverable_templates.py +12 -7
- souleyez/storage/migrations/_008_add_nuclei_table.py +10 -4
- souleyez/storage/migrations/_010_evidence_linking.py +17 -10
- souleyez/storage/migrations/_011_timeline_tracking.py +20 -13
- souleyez/storage/migrations/_012_team_collaboration.py +34 -21
- souleyez/storage/migrations/_013_add_host_tags.py +12 -6
- souleyez/storage/migrations/_014_exploit_attempts.py +22 -10
- souleyez/storage/migrations/_015_add_mac_os_fields.py +15 -7
- souleyez/storage/migrations/_016_add_domain_field.py +10 -4
- souleyez/storage/migrations/_017_msf_sessions.py +16 -8
- souleyez/storage/migrations/_018_add_osint_target.py +10 -6
- souleyez/storage/migrations/_019_add_engagement_type.py +10 -6
- souleyez/storage/migrations/_020_add_rbac.py +36 -15
- souleyez/storage/migrations/_021_wazuh_integration.py +20 -8
- souleyez/storage/migrations/_022_wazuh_indexer_columns.py +6 -4
- souleyez/storage/migrations/_023_fix_detection_results_fk.py +16 -6
- souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +26 -10
- souleyez/storage/migrations/_025_multi_siem_support.py +3 -5
- souleyez/storage/migrations/_026_add_engagement_scope.py +31 -12
- souleyez/storage/migrations/_027_multi_siem_persistence.py +32 -15
- souleyez/storage/migrations/__init__.py +26 -26
- souleyez/storage/migrations/migration_manager.py +19 -19
- souleyez/storage/msf_sessions.py +100 -65
- souleyez/storage/osint.py +17 -24
- souleyez/storage/recommendation_engine.py +269 -235
- souleyez/storage/screenshots.py +33 -32
- souleyez/storage/smb_shares.py +136 -92
- souleyez/storage/sqlmap_data.py +183 -128
- souleyez/storage/team_collaboration.py +135 -141
- souleyez/storage/timeline_tracker.py +122 -94
- souleyez/storage/wazuh_vulns.py +64 -66
- souleyez/storage/web_paths.py +33 -37
- souleyez/testing/credential_tester.py +221 -205
- souleyez/ui/__init__.py +1 -1
- souleyez/ui/ai_quotes.py +12 -12
- souleyez/ui/attack_surface.py +2439 -1516
- souleyez/ui/chain_rules_view.py +914 -382
- souleyez/ui/correlation_view.py +312 -230
- souleyez/ui/dashboard.py +2382 -1130
- souleyez/ui/deliverables_view.py +148 -62
- souleyez/ui/design_system.py +13 -13
- souleyez/ui/errors.py +49 -49
- souleyez/ui/evidence_linking_view.py +284 -179
- souleyez/ui/evidence_vault.py +393 -285
- souleyez/ui/exploit_suggestions_view.py +555 -349
- souleyez/ui/export_view.py +100 -66
- souleyez/ui/gap_analysis_view.py +315 -171
- souleyez/ui/help_system.py +105 -97
- souleyez/ui/intelligence_view.py +436 -293
- souleyez/ui/interactive.py +22827 -10678
- souleyez/ui/interactive_selector.py +75 -68
- souleyez/ui/log_formatter.py +47 -39
- souleyez/ui/menu_components.py +22 -13
- souleyez/ui/msf_auxiliary_menu.py +184 -133
- souleyez/ui/pending_chains_view.py +336 -172
- souleyez/ui/progress_indicators.py +5 -3
- souleyez/ui/recommendations_view.py +195 -137
- souleyez/ui/rule_builder.py +343 -225
- souleyez/ui/setup_wizard.py +678 -284
- souleyez/ui/shortcuts.py +217 -165
- souleyez/ui/splunk_gap_analysis_view.py +452 -270
- souleyez/ui/splunk_vulns_view.py +139 -86
- souleyez/ui/team_dashboard.py +498 -335
- souleyez/ui/template_selector.py +196 -105
- souleyez/ui/terminal.py +6 -6
- souleyez/ui/timeline_view.py +198 -127
- souleyez/ui/tool_setup.py +264 -164
- souleyez/ui/tutorial.py +202 -72
- souleyez/ui/tutorial_state.py +40 -40
- souleyez/ui/wazuh_vulns_view.py +235 -141
- souleyez/ui/wordlist_browser.py +260 -107
- souleyez/ui.py +464 -312
- souleyez/utils/tool_checker.py +427 -367
- souleyez/utils.py +33 -29
- souleyez/wordlists.py +134 -167
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/METADATA +1 -1
- souleyez-2.43.34.dist-info/RECORD +443 -0
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/WHEEL +1 -1
- souleyez-2.43.29.dist-info/RECORD +0 -379
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/entry_points.txt +0 -0
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/top_level.txt +0 -0
|
@@ -30,15 +30,15 @@ def _generate_exploit_identifier(exploit: Dict) -> str:
|
|
|
30
30
|
Unique identifier string like "msf:exploit/unix/ftp/vsftpd_234_backdoor"
|
|
31
31
|
or "cve:CVE-2011-2523" or "edb:49757" or "title:<hash>"
|
|
32
32
|
"""
|
|
33
|
-
if exploit.get(
|
|
33
|
+
if exploit.get("msf_module"):
|
|
34
34
|
return f"msf:{exploit['msf_module']}"
|
|
35
|
-
elif exploit.get(
|
|
35
|
+
elif exploit.get("cve"):
|
|
36
36
|
return f"cve:{exploit['cve']}"
|
|
37
|
-
elif exploit.get(
|
|
37
|
+
elif exploit.get("edb_id"):
|
|
38
38
|
return f"edb:{exploit['edb_id']}"
|
|
39
39
|
else:
|
|
40
40
|
# Fallback: use title (should be rare)
|
|
41
|
-
title_hash = hash(exploit.get(
|
|
41
|
+
title_hash = hash(exploit.get("title", "unknown"))
|
|
42
42
|
return f"title:{title_hash}"
|
|
43
43
|
|
|
44
44
|
|
|
@@ -54,12 +54,12 @@ class ExploitSuggestionEngine:
|
|
|
54
54
|
"""
|
|
55
55
|
self.version_extractor = ServiceVersionExtractor()
|
|
56
56
|
self.knowledge_base = ExploitKnowledgeBase(use_searchsploit=use_searchsploit)
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
def generate_suggestions(self, engagement_id: int, host_id: int = None) -> Dict:
|
|
59
59
|
"""
|
|
60
60
|
Generate exploit suggestions for engagement or specific host.
|
|
61
61
|
Results cached for 30 seconds to improve dashboard performance.
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
Returns:
|
|
64
64
|
{
|
|
65
65
|
'hosts': [
|
|
@@ -100,7 +100,7 @@ class ExploitSuggestionEngine:
|
|
|
100
100
|
cached_result, cached_time = _SUGGESTION_CACHE[cache_key]
|
|
101
101
|
if time.time() - cached_time < _CACHE_TIMEOUT:
|
|
102
102
|
return cached_result
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
# Cache miss or expired - do the analysis
|
|
105
105
|
from souleyez.storage.hosts import HostManager
|
|
106
106
|
from souleyez.storage.findings import FindingsManager
|
|
@@ -118,42 +118,46 @@ class ExploitSuggestionEngine:
|
|
|
118
118
|
hosts = [hm.get_host(host_id)]
|
|
119
119
|
else:
|
|
120
120
|
hosts = hm.list_hosts(engagement_id)
|
|
121
|
-
|
|
121
|
+
|
|
122
|
+
# Fetch all findings ONCE (not per-host - N+1 query fix)
|
|
123
|
+
all_findings = fm.list_findings(engagement_id)
|
|
124
|
+
|
|
122
125
|
results = []
|
|
123
|
-
|
|
126
|
+
|
|
124
127
|
for host in hosts:
|
|
125
128
|
if not host:
|
|
126
129
|
continue
|
|
127
130
|
|
|
128
|
-
h_id = host.get(
|
|
129
|
-
host_ip = host.get(
|
|
131
|
+
h_id = host.get("id")
|
|
132
|
+
host_ip = host.get("ip") or host.get("ip_address")
|
|
130
133
|
|
|
131
|
-
#
|
|
132
|
-
|
|
133
|
-
host_findings = [
|
|
134
|
-
f for f in all_findings
|
|
135
|
-
if f.get('host') == host_ip
|
|
136
|
-
]
|
|
134
|
+
# Filter findings for this host
|
|
135
|
+
host_findings = [f for f in all_findings if f.get("host") == host_ip]
|
|
137
136
|
|
|
138
137
|
# Get Wazuh vulnerabilities for this host
|
|
139
138
|
host_wazuh_vulns = [
|
|
140
|
-
v
|
|
141
|
-
|
|
139
|
+
v
|
|
140
|
+
for v in wazuh_vulns
|
|
141
|
+
if v.get("host_id") == h_id
|
|
142
|
+
or v.get("agent_ip") == host_ip
|
|
143
|
+
or v.get("host_ip") == host_ip
|
|
142
144
|
]
|
|
143
145
|
|
|
144
146
|
# Build set of CVEs found by scans (from findings)
|
|
145
147
|
scan_cves = set()
|
|
146
148
|
for finding in host_findings:
|
|
147
149
|
# Extract CVE from finding title or refs
|
|
148
|
-
title = finding.get(
|
|
149
|
-
refs = finding.get(
|
|
150
|
+
title = finding.get("title", "")
|
|
151
|
+
refs = finding.get("refs", "")
|
|
150
152
|
for text in [title, refs]:
|
|
151
|
-
cve_match = re.search(r
|
|
153
|
+
cve_match = re.search(r"CVE-\d{4}-\d{4,}", text, re.IGNORECASE)
|
|
152
154
|
if cve_match:
|
|
153
155
|
scan_cves.add(cve_match.group(0).upper())
|
|
154
156
|
|
|
155
157
|
# Build set of CVEs from Wazuh
|
|
156
|
-
wazuh_cves = {
|
|
158
|
+
wazuh_cves = {
|
|
159
|
+
v.get("cve_id").upper() for v in host_wazuh_vulns if v.get("cve_id")
|
|
160
|
+
}
|
|
157
161
|
|
|
158
162
|
# Extract versions from all sources
|
|
159
163
|
services = self.version_extractor.combine_sources(h_id, host_findings)
|
|
@@ -164,52 +168,48 @@ class ExploitSuggestionEngine:
|
|
|
164
168
|
|
|
165
169
|
for svc in services:
|
|
166
170
|
exploits = self.knowledge_base.find_exploits(
|
|
167
|
-
svc[
|
|
168
|
-
svc['version_number'],
|
|
169
|
-
svc.get('product')
|
|
171
|
+
svc["service"], svc["version_number"], svc.get("product")
|
|
170
172
|
)
|
|
171
173
|
|
|
172
174
|
# Enrich exploits with attempt status and detection source
|
|
173
175
|
for exploit in exploits:
|
|
174
176
|
exploit_id = _generate_exploit_identifier(exploit)
|
|
175
|
-
exploit[
|
|
177
|
+
exploit["identifier"] = exploit_id
|
|
176
178
|
|
|
177
179
|
# Look up attempt status
|
|
178
180
|
status = exploit_attempts.get_attempt_status(
|
|
179
181
|
engagement_id=engagement_id,
|
|
180
182
|
host_id=h_id,
|
|
181
183
|
exploit_identifier=exploit_id,
|
|
182
|
-
service_id=svc.get(
|
|
184
|
+
service_id=svc.get("service_id"),
|
|
183
185
|
)
|
|
184
|
-
exploit[
|
|
186
|
+
exploit["attempt_status"] = status or "not_tried"
|
|
185
187
|
|
|
186
188
|
# Set detection source
|
|
187
|
-
cve = exploit.get(
|
|
189
|
+
cve = exploit.get("cve", "").upper() if exploit.get("cve") else None
|
|
188
190
|
if cve:
|
|
189
191
|
seen_cves_in_services.add(cve)
|
|
190
192
|
in_scan = cve in scan_cves
|
|
191
193
|
in_wazuh = cve in wazuh_cves
|
|
192
194
|
if in_scan and in_wazuh:
|
|
193
|
-
exploit[
|
|
195
|
+
exploit["detection_source"] = "both"
|
|
194
196
|
elif in_wazuh:
|
|
195
|
-
exploit[
|
|
197
|
+
exploit["detection_source"] = "wazuh"
|
|
196
198
|
else:
|
|
197
|
-
exploit[
|
|
199
|
+
exploit["detection_source"] = "scan"
|
|
198
200
|
else:
|
|
199
|
-
exploit[
|
|
201
|
+
exploit["detection_source"] = "scan"
|
|
200
202
|
|
|
201
|
-
techniques = self.knowledge_base.get_techniques(svc[
|
|
203
|
+
techniques = self.knowledge_base.get_techniques(svc["service"])
|
|
202
204
|
|
|
203
|
-
enriched_services.append(
|
|
204
|
-
**svc,
|
|
205
|
-
|
|
206
|
-
'techniques': techniques
|
|
207
|
-
})
|
|
205
|
+
enriched_services.append(
|
|
206
|
+
{**svc, "exploits": exploits, "techniques": techniques}
|
|
207
|
+
)
|
|
208
208
|
|
|
209
209
|
# Add Wazuh-only CVE exploits (not already covered by service exploits)
|
|
210
210
|
wazuh_only_exploits = []
|
|
211
211
|
for wazuh_vuln in host_wazuh_vulns:
|
|
212
|
-
cve_id = wazuh_vuln.get(
|
|
212
|
+
cve_id = wazuh_vuln.get("cve_id")
|
|
213
213
|
if not cve_id:
|
|
214
214
|
continue
|
|
215
215
|
cve_upper = cve_id.upper()
|
|
@@ -220,69 +220,73 @@ class ExploitSuggestionEngine:
|
|
|
220
220
|
cve_exploits = self.knowledge_base.find_exploits_by_cve(cve_id)
|
|
221
221
|
for exploit in cve_exploits:
|
|
222
222
|
exploit_id = _generate_exploit_identifier(exploit)
|
|
223
|
-
exploit[
|
|
223
|
+
exploit["identifier"] = exploit_id
|
|
224
224
|
|
|
225
225
|
# Look up attempt status
|
|
226
226
|
status = exploit_attempts.get_attempt_status(
|
|
227
227
|
engagement_id=engagement_id,
|
|
228
228
|
host_id=h_id,
|
|
229
|
-
exploit_identifier=exploit_id
|
|
229
|
+
exploit_identifier=exploit_id,
|
|
230
230
|
)
|
|
231
|
-
exploit[
|
|
231
|
+
exploit["attempt_status"] = status or "not_tried"
|
|
232
232
|
|
|
233
233
|
# Set detection source
|
|
234
234
|
in_scan = cve_upper in scan_cves
|
|
235
235
|
if in_scan:
|
|
236
|
-
exploit[
|
|
236
|
+
exploit["detection_source"] = "both"
|
|
237
237
|
else:
|
|
238
|
-
exploit[
|
|
238
|
+
exploit["detection_source"] = "wazuh"
|
|
239
239
|
|
|
240
240
|
# Add package context from Wazuh
|
|
241
|
-
exploit[
|
|
242
|
-
exploit[
|
|
241
|
+
exploit["wazuh_package"] = wazuh_vuln.get("package_name")
|
|
242
|
+
exploit["wazuh_version"] = wazuh_vuln.get("package_version")
|
|
243
243
|
|
|
244
244
|
wazuh_only_exploits.append(exploit)
|
|
245
245
|
seen_cves_in_services.add(cve_upper)
|
|
246
246
|
|
|
247
247
|
# Add Wazuh-only exploits as a pseudo-service if any exist
|
|
248
248
|
if wazuh_only_exploits:
|
|
249
|
-
enriched_services.append(
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
249
|
+
enriched_services.append(
|
|
250
|
+
{
|
|
251
|
+
"port": None,
|
|
252
|
+
"service": "wazuh_detected",
|
|
253
|
+
"version": None,
|
|
254
|
+
"product": None,
|
|
255
|
+
"version_number": None,
|
|
256
|
+
"exploits": wazuh_only_exploits,
|
|
257
|
+
"techniques": [],
|
|
258
|
+
"source": "wazuh",
|
|
259
|
+
}
|
|
260
|
+
)
|
|
259
261
|
|
|
260
262
|
# Sort all exploits to prioritize 'both' detection source
|
|
261
263
|
for svc in enriched_services:
|
|
262
|
-
if svc.get(
|
|
263
|
-
svc[
|
|
264
|
+
if svc.get("exploits"):
|
|
265
|
+
svc["exploits"] = self._prioritize_exploits(svc["exploits"])
|
|
264
266
|
|
|
265
267
|
# Only include hosts with exploits or techniques
|
|
266
|
-
if any(s.get(
|
|
267
|
-
results.append(
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
268
|
+
if any(s.get("exploits") or s.get("techniques") for s in enriched_services):
|
|
269
|
+
results.append(
|
|
270
|
+
{
|
|
271
|
+
"host_id": h_id,
|
|
272
|
+
"ip": host_ip,
|
|
273
|
+
"hostname": host.get("hostname"),
|
|
274
|
+
"services": enriched_services,
|
|
275
|
+
"wazuh_cve_count": len(wazuh_cves),
|
|
276
|
+
}
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
result = {"hosts": results}
|
|
280
|
+
|
|
277
281
|
# Cache the result
|
|
278
282
|
_SUGGESTION_CACHE[cache_key] = (result, time.time())
|
|
279
|
-
|
|
283
|
+
|
|
280
284
|
return result
|
|
281
|
-
|
|
285
|
+
|
|
282
286
|
def get_host_summary(self, engagement_id: int, host_id: int) -> Dict:
|
|
283
287
|
"""
|
|
284
288
|
Get exploit summary for a specific host.
|
|
285
|
-
|
|
289
|
+
|
|
286
290
|
Returns:
|
|
287
291
|
{
|
|
288
292
|
'total_exploits': 5,
|
|
@@ -294,56 +298,62 @@ class ExploitSuggestionEngine:
|
|
|
294
298
|
}
|
|
295
299
|
"""
|
|
296
300
|
suggestions = self.generate_suggestions(engagement_id, host_id)
|
|
297
|
-
|
|
298
|
-
if not suggestions[
|
|
301
|
+
|
|
302
|
+
if not suggestions["hosts"]:
|
|
299
303
|
return {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
304
|
+
"total_exploits": 0,
|
|
305
|
+
"critical": 0,
|
|
306
|
+
"high": 0,
|
|
307
|
+
"medium": 0,
|
|
308
|
+
"low": 0,
|
|
309
|
+
"services_with_exploits": 0,
|
|
310
|
+
"top_exploits": [],
|
|
307
311
|
}
|
|
308
|
-
|
|
309
|
-
host_data = suggestions[
|
|
312
|
+
|
|
313
|
+
host_data = suggestions["hosts"][0]
|
|
310
314
|
all_exploits = []
|
|
311
315
|
services_with_exploits = 0
|
|
312
|
-
|
|
313
|
-
for svc in host_data[
|
|
314
|
-
if svc.get(
|
|
316
|
+
|
|
317
|
+
for svc in host_data["services"]:
|
|
318
|
+
if svc.get("exploits"):
|
|
315
319
|
services_with_exploits += 1
|
|
316
|
-
all_exploits.extend(svc[
|
|
317
|
-
|
|
320
|
+
all_exploits.extend(svc["exploits"])
|
|
321
|
+
|
|
318
322
|
# Count by severity
|
|
319
323
|
severity_counts = {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
+
"critical": len(
|
|
325
|
+
[e for e in all_exploits if e.get("severity") == "critical"]
|
|
326
|
+
),
|
|
327
|
+
"high": len([e for e in all_exploits if e.get("severity") == "high"]),
|
|
328
|
+
"medium": len([e for e in all_exploits if e.get("severity") == "medium"]),
|
|
329
|
+
"low": len([e for e in all_exploits if e.get("severity") == "low"]),
|
|
324
330
|
}
|
|
325
|
-
|
|
331
|
+
|
|
326
332
|
# Get top 5 exploits
|
|
327
333
|
top_exploits = sorted(
|
|
328
334
|
all_exploits,
|
|
329
335
|
key=lambda x: (
|
|
330
|
-
{
|
|
331
|
-
|
|
336
|
+
{"critical": 4, "high": 3, "medium": 2, "low": 1}.get(
|
|
337
|
+
x.get("severity", "low"), 0
|
|
338
|
+
),
|
|
339
|
+
{"exact": 3, "range": 2, "generic": 1}.get(
|
|
340
|
+
x.get("match_type", "generic"), 0
|
|
341
|
+
),
|
|
332
342
|
),
|
|
333
|
-
reverse=True
|
|
343
|
+
reverse=True,
|
|
334
344
|
)[:5]
|
|
335
|
-
|
|
345
|
+
|
|
336
346
|
return {
|
|
337
|
-
|
|
347
|
+
"total_exploits": len(all_exploits),
|
|
338
348
|
**severity_counts,
|
|
339
|
-
|
|
340
|
-
|
|
349
|
+
"services_with_exploits": services_with_exploits,
|
|
350
|
+
"top_exploits": top_exploits,
|
|
341
351
|
}
|
|
342
|
-
|
|
352
|
+
|
|
343
353
|
def get_engagement_summary(self, engagement_id: int) -> Dict:
|
|
344
354
|
"""
|
|
345
355
|
Get exploit summary for entire engagement.
|
|
346
|
-
|
|
356
|
+
|
|
347
357
|
Returns:
|
|
348
358
|
{
|
|
349
359
|
'total_hosts': 5,
|
|
@@ -355,45 +365,55 @@ class ExploitSuggestionEngine:
|
|
|
355
365
|
}
|
|
356
366
|
"""
|
|
357
367
|
suggestions = self.generate_suggestions(engagement_id)
|
|
358
|
-
|
|
359
|
-
total_hosts = len(suggestions[
|
|
368
|
+
|
|
369
|
+
total_hosts = len(suggestions["hosts"])
|
|
360
370
|
all_exploits = []
|
|
361
|
-
|
|
371
|
+
|
|
362
372
|
host_exploit_counts = []
|
|
363
|
-
|
|
364
|
-
for host in suggestions[
|
|
373
|
+
|
|
374
|
+
for host in suggestions["hosts"]:
|
|
365
375
|
host_exploits = []
|
|
366
|
-
for svc in host[
|
|
367
|
-
if svc.get(
|
|
368
|
-
host_exploits.extend(svc[
|
|
369
|
-
|
|
376
|
+
for svc in host["services"]:
|
|
377
|
+
if svc.get("exploits"):
|
|
378
|
+
host_exploits.extend(svc["exploits"])
|
|
379
|
+
|
|
370
380
|
all_exploits.extend(host_exploits)
|
|
371
|
-
|
|
381
|
+
|
|
372
382
|
if host_exploits:
|
|
373
|
-
host_exploit_counts.append(
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
383
|
+
host_exploit_counts.append(
|
|
384
|
+
{
|
|
385
|
+
"host_id": host["host_id"],
|
|
386
|
+
"ip": host["ip"],
|
|
387
|
+
"hostname": host.get("hostname"),
|
|
388
|
+
"exploit_count": len(host_exploits),
|
|
389
|
+
"critical_count": len(
|
|
390
|
+
[
|
|
391
|
+
e
|
|
392
|
+
for e in host_exploits
|
|
393
|
+
if e.get("severity") == "critical"
|
|
394
|
+
]
|
|
395
|
+
),
|
|
396
|
+
}
|
|
397
|
+
)
|
|
398
|
+
|
|
381
399
|
# Sort hosts by exploit count
|
|
382
400
|
top_vulnerable = sorted(
|
|
383
401
|
host_exploit_counts,
|
|
384
|
-
key=lambda x: (x[
|
|
385
|
-
reverse=True
|
|
402
|
+
key=lambda x: (x["critical_count"], x["exploit_count"]),
|
|
403
|
+
reverse=True,
|
|
386
404
|
)[:10]
|
|
387
|
-
|
|
405
|
+
|
|
388
406
|
return {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
407
|
+
"total_hosts": total_hosts,
|
|
408
|
+
"hosts_with_exploits": len(host_exploit_counts),
|
|
409
|
+
"total_exploits": len(all_exploits),
|
|
410
|
+
"critical": len(
|
|
411
|
+
[e for e in all_exploits if e.get("severity") == "critical"]
|
|
412
|
+
),
|
|
413
|
+
"high": len([e for e in all_exploits if e.get("severity") == "high"]),
|
|
414
|
+
"medium": len([e for e in all_exploits if e.get("severity") == "medium"]),
|
|
415
|
+
"low": len([e for e in all_exploits if e.get("severity") == "low"]),
|
|
416
|
+
"top_vulnerable_hosts": top_vulnerable,
|
|
397
417
|
}
|
|
398
418
|
|
|
399
419
|
def _prioritize_exploits(self, exploits: List[Dict]) -> List[Dict]:
|
|
@@ -411,14 +431,14 @@ class ExploitSuggestionEngine:
|
|
|
411
431
|
Returns:
|
|
412
432
|
Sorted list of exploits
|
|
413
433
|
"""
|
|
414
|
-
source_order = {
|
|
415
|
-
severity_order = {
|
|
416
|
-
match_order = {
|
|
434
|
+
source_order = {"both": 3, "wazuh": 2, "scan": 1}
|
|
435
|
+
severity_order = {"critical": 5, "high": 4, "medium": 3, "low": 2, "info": 1}
|
|
436
|
+
match_order = {"exact": 3, "range": 2, "generic": 1}
|
|
417
437
|
|
|
418
438
|
def sort_key(exploit):
|
|
419
|
-
source_score = source_order.get(exploit.get(
|
|
420
|
-
severity_score = severity_order.get(exploit.get(
|
|
421
|
-
match_score = match_order.get(exploit.get(
|
|
439
|
+
source_score = source_order.get(exploit.get("detection_source", "scan"), 0)
|
|
440
|
+
severity_score = severity_order.get(exploit.get("severity", "low"), 0)
|
|
441
|
+
match_score = match_order.get(exploit.get("match_type", "generic"), 0)
|
|
422
442
|
return (source_score, severity_score, match_score)
|
|
423
443
|
|
|
424
444
|
return sorted(exploits, key=sort_key, reverse=True)
|