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
|
@@ -22,7 +22,11 @@ from souleyez.storage.hosts import HostManager
|
|
|
22
22
|
from souleyez.storage.findings import FindingsManager
|
|
23
23
|
from souleyez.storage.credentials import CredentialsManager
|
|
24
24
|
from souleyez.storage.exploit_attempts import record_attempt
|
|
25
|
-
from souleyez.storage.msf_sessions import
|
|
25
|
+
from souleyez.storage.msf_sessions import (
|
|
26
|
+
add_msf_session,
|
|
27
|
+
get_msf_sessions,
|
|
28
|
+
close_msf_session,
|
|
29
|
+
)
|
|
26
30
|
|
|
27
31
|
logger = logging.getLogger(__name__)
|
|
28
32
|
|
|
@@ -38,7 +42,7 @@ def get_msf_database_config() -> Optional[Dict[str, Any]]:
|
|
|
38
42
|
"""
|
|
39
43
|
# Check user config first, then system-wide config (Kali uses system-wide)
|
|
40
44
|
user_db_path = Path.home() / ".msf4" / "database.yml"
|
|
41
|
-
system_db_path = Path(
|
|
45
|
+
system_db_path = Path("/usr/share/metasploit-framework/config/database.yml")
|
|
42
46
|
|
|
43
47
|
db_yml_path = None
|
|
44
48
|
if user_db_path.exists():
|
|
@@ -53,13 +57,13 @@ def get_msf_database_config() -> Optional[Dict[str, Any]]:
|
|
|
53
57
|
try:
|
|
54
58
|
import yaml
|
|
55
59
|
|
|
56
|
-
with open(db_yml_path,
|
|
60
|
+
with open(db_yml_path, "r") as f:
|
|
57
61
|
config = yaml.safe_load(f)
|
|
58
62
|
|
|
59
63
|
# MSF database.yml has environment sections (development, production, etc.)
|
|
60
64
|
# Look for 'production' first, then 'development', then any section
|
|
61
65
|
env_config = None
|
|
62
|
-
for env in [
|
|
66
|
+
for env in ["production", "development"]:
|
|
63
67
|
if env in config:
|
|
64
68
|
env_config = config[env]
|
|
65
69
|
break
|
|
@@ -67,7 +71,7 @@ def get_msf_database_config() -> Optional[Dict[str, Any]]:
|
|
|
67
71
|
if not env_config:
|
|
68
72
|
# Take the first available section
|
|
69
73
|
for key, val in config.items():
|
|
70
|
-
if isinstance(val, dict) and
|
|
74
|
+
if isinstance(val, dict) and "database" in val:
|
|
71
75
|
env_config = val
|
|
72
76
|
break
|
|
73
77
|
|
|
@@ -76,12 +80,12 @@ def get_msf_database_config() -> Optional[Dict[str, Any]]:
|
|
|
76
80
|
return None
|
|
77
81
|
|
|
78
82
|
return {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
"host": env_config.get("host", "localhost"),
|
|
84
|
+
"port": env_config.get("port", 5432),
|
|
85
|
+
"database": env_config.get("database", "msf"),
|
|
86
|
+
"username": env_config.get("username", "msf"),
|
|
87
|
+
"password": env_config.get("password", ""),
|
|
88
|
+
"workspace": "default",
|
|
85
89
|
}
|
|
86
90
|
except ImportError:
|
|
87
91
|
logger.debug("PyYAML not available, cannot parse database.yml")
|
|
@@ -106,6 +110,7 @@ def get_msf_active_sessions_count() -> Optional[int]:
|
|
|
106
110
|
# Method 1: Try MSF RPC first (most reliable when msfrpcd is running)
|
|
107
111
|
try:
|
|
108
112
|
from souleyez.core.msf_rpc_client import MSFRPCClient, MSGPACK_AVAILABLE
|
|
113
|
+
|
|
109
114
|
if MSGPACK_AVAILABLE:
|
|
110
115
|
# Try connecting without password first (some setups don't require it)
|
|
111
116
|
client = MSFRPCClient()
|
|
@@ -156,22 +161,22 @@ def _map_msf_state_to_status(msf_state: Optional[str]) -> str:
|
|
|
156
161
|
SoulEyez status value ('up' or 'down')
|
|
157
162
|
"""
|
|
158
163
|
if not msf_state:
|
|
159
|
-
return
|
|
164
|
+
return "up" # Default for null/empty
|
|
160
165
|
|
|
161
166
|
state_lower = msf_state.lower()
|
|
162
167
|
|
|
163
168
|
# Map MSF state to SoulEyez status
|
|
164
|
-
if state_lower ==
|
|
165
|
-
return
|
|
166
|
-
elif state_lower ==
|
|
167
|
-
return
|
|
168
|
-
elif state_lower in (
|
|
169
|
+
if state_lower == "alive":
|
|
170
|
+
return "up"
|
|
171
|
+
elif state_lower == "dead":
|
|
172
|
+
return "down"
|
|
173
|
+
elif state_lower in ("up", "down"):
|
|
169
174
|
# Already in correct format
|
|
170
175
|
return state_lower
|
|
171
176
|
else:
|
|
172
177
|
# Unknown state - log warning and default to up
|
|
173
178
|
logger.warning(f"Unknown MSF host state '{msf_state}', defaulting to 'up'")
|
|
174
|
-
return
|
|
179
|
+
return "up"
|
|
175
180
|
|
|
176
181
|
|
|
177
182
|
class MSFSyncManager:
|
|
@@ -182,7 +187,7 @@ class MSFSyncManager:
|
|
|
182
187
|
db_path: str,
|
|
183
188
|
engagement_id: int,
|
|
184
189
|
msf_db_config: Optional[Dict[str, Any]] = None,
|
|
185
|
-
msf_rpc_config: Optional[Dict[str, Any]] = None
|
|
190
|
+
msf_rpc_config: Optional[Dict[str, Any]] = None,
|
|
186
191
|
):
|
|
187
192
|
"""
|
|
188
193
|
Initialize MSF sync manager
|
|
@@ -196,19 +201,19 @@ class MSFSyncManager:
|
|
|
196
201
|
self.db_path = db_path
|
|
197
202
|
self.engagement_id = engagement_id
|
|
198
203
|
self.msf_db_config = msf_db_config or {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
204
|
+
"host": "localhost",
|
|
205
|
+
"port": 5432,
|
|
206
|
+
"database": "msf",
|
|
207
|
+
"username": "msf",
|
|
208
|
+
"password": "",
|
|
209
|
+
"workspace": "default",
|
|
205
210
|
}
|
|
206
211
|
self.msf_rpc_config = msf_rpc_config or {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
+
"host": "127.0.0.1",
|
|
213
|
+
"port": 55553,
|
|
214
|
+
"username": "msf",
|
|
215
|
+
"password": "",
|
|
216
|
+
"ssl": False,
|
|
212
217
|
}
|
|
213
218
|
|
|
214
219
|
def import_msf_data(
|
|
@@ -217,7 +222,7 @@ class MSFSyncManager:
|
|
|
217
222
|
import_services: bool = True,
|
|
218
223
|
import_vulns: bool = True,
|
|
219
224
|
import_creds: bool = True,
|
|
220
|
-
import_sessions: bool = True
|
|
225
|
+
import_sessions: bool = True,
|
|
221
226
|
) -> Dict[str, int]:
|
|
222
227
|
"""
|
|
223
228
|
Import data from MSF database into SoulEyez
|
|
@@ -233,12 +238,12 @@ class MSFSyncManager:
|
|
|
233
238
|
Dictionary with counts of imported items
|
|
234
239
|
"""
|
|
235
240
|
stats = {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
241
|
+
"hosts": 0,
|
|
242
|
+
"services": 0,
|
|
243
|
+
"vulns": 0,
|
|
244
|
+
"creds": 0,
|
|
245
|
+
"sessions": 0,
|
|
246
|
+
"errors": 0,
|
|
242
247
|
}
|
|
243
248
|
|
|
244
249
|
try:
|
|
@@ -247,58 +252,62 @@ class MSFSyncManager:
|
|
|
247
252
|
if import_hosts:
|
|
248
253
|
try:
|
|
249
254
|
hosts_imported = self._import_hosts(msf_db)
|
|
250
|
-
stats[
|
|
255
|
+
stats["hosts"] = hosts_imported
|
|
251
256
|
except (KeyError, AttributeError) as e:
|
|
252
257
|
logger.error(f"Schema error importing hosts: {e}")
|
|
253
|
-
logger.error(
|
|
254
|
-
|
|
258
|
+
logger.error(
|
|
259
|
+
"MSF database schema may have changed. Consider using XML export."
|
|
260
|
+
)
|
|
261
|
+
stats["errors"] += 1
|
|
255
262
|
|
|
256
263
|
# Import services
|
|
257
264
|
if import_services:
|
|
258
265
|
try:
|
|
259
266
|
services_imported = self._import_services(msf_db)
|
|
260
|
-
stats[
|
|
267
|
+
stats["services"] = services_imported
|
|
261
268
|
except (KeyError, AttributeError) as e:
|
|
262
269
|
logger.error(f"Schema error importing services: {e}")
|
|
263
|
-
stats[
|
|
270
|
+
stats["errors"] += 1
|
|
264
271
|
|
|
265
272
|
# Import vulnerabilities
|
|
266
273
|
if import_vulns:
|
|
267
274
|
try:
|
|
268
275
|
vulns_imported = self._import_vulns(msf_db)
|
|
269
|
-
stats[
|
|
276
|
+
stats["vulns"] = vulns_imported
|
|
270
277
|
except (KeyError, AttributeError) as e:
|
|
271
278
|
logger.error(f"Schema error importing vulnerabilities: {e}")
|
|
272
|
-
stats[
|
|
279
|
+
stats["errors"] += 1
|
|
273
280
|
|
|
274
281
|
# Import credentials
|
|
275
282
|
if import_creds:
|
|
276
283
|
try:
|
|
277
284
|
creds_imported = self._import_creds(msf_db)
|
|
278
|
-
stats[
|
|
285
|
+
stats["creds"] = creds_imported
|
|
279
286
|
except (KeyError, AttributeError) as e:
|
|
280
287
|
logger.error(f"Schema error importing credentials: {e}")
|
|
281
|
-
stats[
|
|
288
|
+
stats["errors"] += 1
|
|
282
289
|
|
|
283
290
|
# Import sessions
|
|
284
291
|
if import_sessions:
|
|
285
292
|
try:
|
|
286
293
|
sessions_imported = self._import_sessions(msf_db)
|
|
287
|
-
stats[
|
|
294
|
+
stats["sessions"] = sessions_imported
|
|
288
295
|
except (KeyError, AttributeError) as e:
|
|
289
296
|
logger.error(f"Schema error importing sessions: {e}")
|
|
290
|
-
stats[
|
|
297
|
+
stats["errors"] += 1
|
|
291
298
|
|
|
292
299
|
except MSFDatabaseSchemaError as e:
|
|
293
300
|
logger.error(f"MSF database schema is incompatible: {e}")
|
|
294
301
|
logger.error("RECOMMENDATION: Use MSF XML export instead:")
|
|
295
302
|
logger.error(" In msfconsole: db_export -f xml /tmp/msf_export.xml")
|
|
296
303
|
logger.error(" Then import the XML file into SoulEyez")
|
|
297
|
-
stats[
|
|
304
|
+
stats["errors"] += 1
|
|
298
305
|
except Exception as e:
|
|
299
306
|
logger.error(f"Failed to import MSF data: {e}")
|
|
300
|
-
logger.error(
|
|
301
|
-
|
|
307
|
+
logger.error(
|
|
308
|
+
"If this error persists, try using MSF XML export as an alternative"
|
|
309
|
+
)
|
|
310
|
+
stats["errors"] += 1
|
|
302
311
|
|
|
303
312
|
return stats
|
|
304
313
|
|
|
@@ -312,24 +321,28 @@ class MSFSyncManager:
|
|
|
312
321
|
for msf_host in msf_hosts:
|
|
313
322
|
try:
|
|
314
323
|
# Check if host already exists
|
|
315
|
-
existing = host_mgr.get_host_by_ip(
|
|
324
|
+
existing = host_mgr.get_host_by_ip(
|
|
325
|
+
self.engagement_id, msf_host["address"]
|
|
326
|
+
)
|
|
316
327
|
|
|
317
328
|
if not existing:
|
|
318
329
|
# Add new host
|
|
319
330
|
# Map MSF state ('alive'/'dead') to SoulEyez status ('up'/'down')
|
|
320
|
-
msf_state = msf_host.get(
|
|
331
|
+
msf_state = msf_host.get("state")
|
|
321
332
|
souleyez_status = _map_msf_state_to_status(msf_state)
|
|
322
333
|
|
|
323
334
|
host_data = {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
335
|
+
"ip": msf_host["address"],
|
|
336
|
+
"hostname": msf_host.get("name") or None,
|
|
337
|
+
"mac_address": msf_host.get("mac") or None,
|
|
338
|
+
"os": msf_host.get("os_name") or None,
|
|
339
|
+
"status": souleyez_status,
|
|
329
340
|
}
|
|
330
341
|
host_mgr.add_or_update_host(self.engagement_id, host_data)
|
|
331
342
|
count += 1
|
|
332
|
-
logger.debug(
|
|
343
|
+
logger.debug(
|
|
344
|
+
f"Imported host {msf_host['address']} with status '{souleyez_status}' (MSF state: '{msf_state}')"
|
|
345
|
+
)
|
|
333
346
|
except Exception as e:
|
|
334
347
|
logger.error(f"Failed to import host {msf_host['address']}: {e}")
|
|
335
348
|
|
|
@@ -346,27 +359,33 @@ class MSFSyncManager:
|
|
|
346
359
|
for msf_svc in msf_services:
|
|
347
360
|
try:
|
|
348
361
|
# Get or create host
|
|
349
|
-
host = host_mgr.get_host_by_ip(
|
|
362
|
+
host = host_mgr.get_host_by_ip(
|
|
363
|
+
self.engagement_id, msf_svc["host_address"]
|
|
364
|
+
)
|
|
350
365
|
if not host:
|
|
351
366
|
# Create host if it doesn't exist
|
|
352
|
-
logger.debug(
|
|
353
|
-
|
|
367
|
+
logger.debug(
|
|
368
|
+
f"Creating host for service: {msf_svc['host_address']}"
|
|
369
|
+
)
|
|
370
|
+
host_data = {"ip": msf_svc["host_address"]}
|
|
354
371
|
host_id = host_mgr.add_or_update_host(self.engagement_id, host_data)
|
|
355
372
|
else:
|
|
356
|
-
host_id = host[
|
|
373
|
+
host_id = host["id"]
|
|
357
374
|
|
|
358
375
|
# Add or update service (add_service handles duplicates)
|
|
359
376
|
service_data = {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
377
|
+
"port": msf_svc["port"],
|
|
378
|
+
"protocol": msf_svc.get("proto", "tcp"),
|
|
379
|
+
"state": msf_svc.get("state", "open"),
|
|
380
|
+
"service": msf_svc.get("name") or "unknown",
|
|
381
|
+
"product": msf_svc.get("info") or None,
|
|
365
382
|
}
|
|
366
383
|
host_mgr.add_service(host_id, service_data)
|
|
367
384
|
count += 1
|
|
368
385
|
except Exception as e:
|
|
369
|
-
logger.error(
|
|
386
|
+
logger.error(
|
|
387
|
+
f"Failed to import service on {msf_svc.get('host_address')}:{msf_svc.get('port')}: {e}"
|
|
388
|
+
)
|
|
370
389
|
skipped += 1
|
|
371
390
|
|
|
372
391
|
if skipped > 0:
|
|
@@ -386,46 +405,56 @@ class MSFSyncManager:
|
|
|
386
405
|
for msf_vuln in msf_vulns:
|
|
387
406
|
try:
|
|
388
407
|
# Get host
|
|
389
|
-
host = host_mgr.get_host_by_ip(
|
|
408
|
+
host = host_mgr.get_host_by_ip(
|
|
409
|
+
self.engagement_id, msf_vuln["host_address"]
|
|
410
|
+
)
|
|
390
411
|
if not host:
|
|
391
|
-
logger.debug(
|
|
412
|
+
logger.debug(
|
|
413
|
+
f"Skipping vuln - host not found: {msf_vuln['host_address']}"
|
|
414
|
+
)
|
|
392
415
|
skipped += 1
|
|
393
416
|
continue
|
|
394
417
|
|
|
395
418
|
# Get service if available
|
|
396
|
-
port = msf_vuln.get(
|
|
419
|
+
port = msf_vuln.get("service_port")
|
|
397
420
|
if port:
|
|
398
|
-
services = host_mgr.get_host_services(host[
|
|
421
|
+
services = host_mgr.get_host_services(host["id"])
|
|
399
422
|
matching_services = [
|
|
400
|
-
s
|
|
401
|
-
|
|
423
|
+
s
|
|
424
|
+
for s in services
|
|
425
|
+
if s["port"] == port
|
|
426
|
+
and s["protocol"] == msf_vuln.get("service_proto", "tcp")
|
|
402
427
|
]
|
|
403
428
|
else:
|
|
404
429
|
matching_services = []
|
|
405
430
|
|
|
406
431
|
# Determine severity from vuln name/info
|
|
407
|
-
severity = self._determine_severity(msf_vuln.get(
|
|
432
|
+
severity = self._determine_severity(msf_vuln.get("name", ""))
|
|
408
433
|
|
|
409
434
|
# Add finding
|
|
410
435
|
findings_mgr.add_finding(
|
|
411
436
|
engagement_id=self.engagement_id,
|
|
412
|
-
title=msf_vuln.get(
|
|
413
|
-
finding_type=
|
|
437
|
+
title=msf_vuln.get("name", "Unknown Vulnerability"),
|
|
438
|
+
finding_type="vulnerability",
|
|
414
439
|
severity=severity,
|
|
415
|
-
description=msf_vuln.get(
|
|
416
|
-
host_id=host[
|
|
417
|
-
tool=
|
|
418
|
-
refs=msf_vuln.get(
|
|
440
|
+
description=msf_vuln.get("info") or "",
|
|
441
|
+
host_id=host["id"],
|
|
442
|
+
tool="metasploit",
|
|
443
|
+
refs=msf_vuln.get("refs") or "",
|
|
419
444
|
port=port,
|
|
420
|
-
evidence=f"MSF Vuln ID: {msf_vuln['id']}"
|
|
445
|
+
evidence=f"MSF Vuln ID: {msf_vuln['id']}",
|
|
421
446
|
)
|
|
422
447
|
count += 1
|
|
423
448
|
except Exception as e:
|
|
424
|
-
logger.error(
|
|
449
|
+
logger.error(
|
|
450
|
+
f"Failed to import vulnerability '{msf_vuln.get('name')}': {e}"
|
|
451
|
+
)
|
|
425
452
|
skipped += 1
|
|
426
453
|
|
|
427
454
|
if skipped > 0:
|
|
428
|
-
logger.warning(
|
|
455
|
+
logger.warning(
|
|
456
|
+
f"Skipped {skipped} vulnerabilities (host not found or errors)"
|
|
457
|
+
)
|
|
429
458
|
|
|
430
459
|
return count
|
|
431
460
|
|
|
@@ -441,41 +470,55 @@ class MSFSyncManager:
|
|
|
441
470
|
for msf_cred in msf_creds:
|
|
442
471
|
try:
|
|
443
472
|
# Skip if no host address
|
|
444
|
-
if not msf_cred.get(
|
|
445
|
-
logger.debug(
|
|
473
|
+
if not msf_cred.get("host_address"):
|
|
474
|
+
logger.debug(
|
|
475
|
+
f"Skipping credential - no host address (service-less credential)"
|
|
476
|
+
)
|
|
446
477
|
skipped += 1
|
|
447
478
|
continue
|
|
448
479
|
|
|
449
480
|
# Get host
|
|
450
|
-
host = host_mgr.get_host_by_ip(
|
|
481
|
+
host = host_mgr.get_host_by_ip(
|
|
482
|
+
self.engagement_id, msf_cred["host_address"]
|
|
483
|
+
)
|
|
451
484
|
if not host:
|
|
452
|
-
logger.debug(
|
|
485
|
+
logger.debug(
|
|
486
|
+
f"Skipping credential - host not found: {msf_cred['host_address']}"
|
|
487
|
+
)
|
|
453
488
|
skipped += 1
|
|
454
489
|
continue
|
|
455
490
|
|
|
456
491
|
# Create credential finding
|
|
457
|
-
username = msf_cred.get(
|
|
458
|
-
private_data = msf_cred.get(
|
|
459
|
-
private_type = msf_cred.get(
|
|
492
|
+
username = msf_cred.get("username", "N/A")
|
|
493
|
+
private_data = msf_cred.get("private_data", "N/A")
|
|
494
|
+
private_type = msf_cred.get("private_type", "password")
|
|
460
495
|
|
|
461
496
|
findings_mgr.add_finding(
|
|
462
497
|
engagement_id=self.engagement_id,
|
|
463
498
|
title=f"Credential Found: {username}",
|
|
464
|
-
finding_type=
|
|
465
|
-
severity=
|
|
499
|
+
finding_type="credential",
|
|
500
|
+
severity="high",
|
|
466
501
|
description=f"Username: {username}\nType: {private_type}\nStatus: {msf_cred.get('status', 'unknown')}",
|
|
467
|
-
host_id=host[
|
|
468
|
-
tool=
|
|
469
|
-
port=msf_cred.get(
|
|
470
|
-
evidence=
|
|
502
|
+
host_id=host["id"],
|
|
503
|
+
tool="metasploit",
|
|
504
|
+
port=msf_cred.get("service_port"),
|
|
505
|
+
evidence=(
|
|
506
|
+
f"Private: {private_data[:20]}..."
|
|
507
|
+
if len(private_data) > 20
|
|
508
|
+
else private_data
|
|
509
|
+
),
|
|
471
510
|
)
|
|
472
511
|
count += 1
|
|
473
512
|
except Exception as e:
|
|
474
|
-
logger.error(
|
|
513
|
+
logger.error(
|
|
514
|
+
f"Failed to import credential for {msf_cred.get('username')}: {e}"
|
|
515
|
+
)
|
|
475
516
|
skipped += 1
|
|
476
517
|
|
|
477
518
|
if skipped > 0:
|
|
478
|
-
logger.warning(
|
|
519
|
+
logger.warning(
|
|
520
|
+
f"Skipped {skipped} credentials (no host address or host not found)"
|
|
521
|
+
)
|
|
479
522
|
|
|
480
523
|
return count
|
|
481
524
|
|
|
@@ -489,37 +532,42 @@ class MSFSyncManager:
|
|
|
489
532
|
|
|
490
533
|
# Get database connection for msf_sessions functions
|
|
491
534
|
from souleyez.storage.database import get_db
|
|
535
|
+
|
|
492
536
|
db = get_db()
|
|
493
537
|
conn = db.get_connection()
|
|
494
538
|
|
|
495
539
|
for msf_session in msf_sessions:
|
|
496
540
|
try:
|
|
497
541
|
# Get host
|
|
498
|
-
host = host_mgr.get_host_by_ip(
|
|
542
|
+
host = host_mgr.get_host_by_ip(
|
|
543
|
+
self.engagement_id, msf_session["host_address"]
|
|
544
|
+
)
|
|
499
545
|
if not host:
|
|
500
|
-
session_num = msf_session.get(
|
|
501
|
-
logger.debug(
|
|
546
|
+
session_num = msf_session.get("local_id") or msf_session["id"]
|
|
547
|
+
logger.debug(
|
|
548
|
+
f"Skipping session {session_num} - host not found: {msf_session['host_address']}"
|
|
549
|
+
)
|
|
502
550
|
skipped += 1
|
|
503
551
|
continue
|
|
504
552
|
|
|
505
553
|
# Add session to msf_sessions table
|
|
506
|
-
is_active = msf_session.get(
|
|
554
|
+
is_active = msf_session.get("closed_at") is None
|
|
507
555
|
|
|
508
556
|
session_id = add_msf_session(
|
|
509
557
|
conn,
|
|
510
558
|
engagement_id=self.engagement_id,
|
|
511
|
-
host_id=host[
|
|
512
|
-
msf_session_id=msf_session.get(
|
|
513
|
-
session_type=msf_session.get(
|
|
514
|
-
via_exploit=msf_session.get(
|
|
515
|
-
via_payload=msf_session.get(
|
|
516
|
-
platform=msf_session.get(
|
|
559
|
+
host_id=host["id"],
|
|
560
|
+
msf_session_id=msf_session.get("local_id") or msf_session["id"],
|
|
561
|
+
session_type=msf_session.get("stype"),
|
|
562
|
+
via_exploit=msf_session.get("via_exploit"),
|
|
563
|
+
via_payload=msf_session.get("via_payload"),
|
|
564
|
+
platform=msf_session.get("platform"),
|
|
517
565
|
arch=None, # Not available in DB schema
|
|
518
566
|
username=None, # Not available in DB schema
|
|
519
|
-
port=msf_session.get(
|
|
567
|
+
port=msf_session.get("port"),
|
|
520
568
|
tunnel_peer=None, # Not available in DB schema
|
|
521
|
-
opened_at=msf_session.get(
|
|
522
|
-
notes=msf_session.get(
|
|
569
|
+
opened_at=msf_session.get("opened_at"),
|
|
570
|
+
notes=msf_session.get("desc"),
|
|
523
571
|
)
|
|
524
572
|
|
|
525
573
|
# Mark as closed if applicable
|
|
@@ -527,24 +575,24 @@ class MSFSyncManager:
|
|
|
527
575
|
close_msf_session(
|
|
528
576
|
conn,
|
|
529
577
|
self.engagement_id,
|
|
530
|
-
msf_session.get(
|
|
531
|
-
msf_session.get(
|
|
578
|
+
msf_session.get("local_id") or msf_session["id"],
|
|
579
|
+
msf_session.get("close_reason"),
|
|
532
580
|
)
|
|
533
581
|
|
|
534
582
|
count += 1
|
|
535
583
|
|
|
536
584
|
# If session was created via exploit, mark that exploit as successful
|
|
537
|
-
via_exploit = msf_session.get(
|
|
538
|
-
if via_exploit and via_exploit not in [
|
|
539
|
-
session_num = msf_session.get(
|
|
585
|
+
via_exploit = msf_session.get("via_exploit")
|
|
586
|
+
if via_exploit and via_exploit not in ["unknown", ""]:
|
|
587
|
+
session_num = msf_session.get("local_id") or msf_session["id"]
|
|
540
588
|
self._mark_exploit_success(
|
|
541
|
-
host[
|
|
542
|
-
via_exploit,
|
|
543
|
-
f"Session {session_num} created"
|
|
589
|
+
host["id"], via_exploit, f"Session {session_num} created"
|
|
544
590
|
)
|
|
545
591
|
|
|
546
592
|
except Exception as e:
|
|
547
|
-
session_num = msf_session.get(
|
|
593
|
+
session_num = msf_session.get("local_id") or msf_session.get(
|
|
594
|
+
"id", "unknown"
|
|
595
|
+
)
|
|
548
596
|
logger.error(f"Failed to import session {session_num}: {e}")
|
|
549
597
|
skipped += 1
|
|
550
598
|
|
|
@@ -568,11 +616,7 @@ class MSFSyncManager:
|
|
|
568
616
|
Returns:
|
|
569
617
|
Dictionary with counts of updated exploits
|
|
570
618
|
"""
|
|
571
|
-
stats = {
|
|
572
|
-
'success': 0,
|
|
573
|
-
'failed': 0,
|
|
574
|
-
'errors': 0
|
|
575
|
-
}
|
|
619
|
+
stats = {"success": 0, "failed": 0, "errors": 0}
|
|
576
620
|
|
|
577
621
|
try:
|
|
578
622
|
with MSFDatabase(**self.msf_db_config) as msf_db:
|
|
@@ -584,42 +628,45 @@ class MSFSyncManager:
|
|
|
584
628
|
for attempt in exploit_attempts:
|
|
585
629
|
try:
|
|
586
630
|
# Get host
|
|
587
|
-
host = host_mgr.get_host_by_ip(
|
|
631
|
+
host = host_mgr.get_host_by_ip(
|
|
632
|
+
self.engagement_id, attempt["host_address"]
|
|
633
|
+
)
|
|
588
634
|
if not host:
|
|
589
635
|
continue
|
|
590
636
|
|
|
591
637
|
# Get service if available
|
|
592
638
|
service_id = None
|
|
593
|
-
if attempt.get(
|
|
594
|
-
services = host_mgr.get_host_services(host[
|
|
639
|
+
if attempt.get("service_port"):
|
|
640
|
+
services = host_mgr.get_host_services(host["id"])
|
|
595
641
|
matching_services = [
|
|
596
|
-
s
|
|
597
|
-
|
|
598
|
-
|
|
642
|
+
s
|
|
643
|
+
for s in services
|
|
644
|
+
if s["port"] == attempt["service_port"]
|
|
645
|
+
and s["protocol"] == attempt.get("service_proto", "tcp")
|
|
599
646
|
]
|
|
600
647
|
if matching_services:
|
|
601
|
-
service_id = matching_services[0][
|
|
648
|
+
service_id = matching_services[0]["id"]
|
|
602
649
|
|
|
603
650
|
# Record exploit attempt with success status
|
|
604
|
-
exploit_name = attempt.get(
|
|
651
|
+
exploit_name = attempt.get("vuln_name", "unknown")
|
|
605
652
|
record_attempt(
|
|
606
653
|
engagement_id=self.engagement_id,
|
|
607
|
-
host_id=host[
|
|
654
|
+
host_id=host["id"],
|
|
608
655
|
exploit_identifier=f"msf:{exploit_name}",
|
|
609
656
|
exploit_title=exploit_name,
|
|
610
|
-
status=
|
|
657
|
+
status="success",
|
|
611
658
|
service_id=service_id,
|
|
612
|
-
notes=f"Exploited at: {attempt['exploited_at']}"
|
|
659
|
+
notes=f"Exploited at: {attempt['exploited_at']}",
|
|
613
660
|
)
|
|
614
|
-
stats[
|
|
661
|
+
stats["success"] += 1
|
|
615
662
|
|
|
616
663
|
except Exception as e:
|
|
617
664
|
logger.error(f"Failed to sync exploit result: {e}")
|
|
618
|
-
stats[
|
|
665
|
+
stats["errors"] += 1
|
|
619
666
|
|
|
620
667
|
except Exception as e:
|
|
621
668
|
logger.error(f"Failed to sync exploit results: {e}")
|
|
622
|
-
stats[
|
|
669
|
+
stats["errors"] += 1
|
|
623
670
|
|
|
624
671
|
return stats
|
|
625
672
|
|
|
@@ -638,50 +685,55 @@ class MSFSyncManager:
|
|
|
638
685
|
with MSFRPCClient(**self.msf_rpc_config) as rpc:
|
|
639
686
|
rpc_sessions = rpc.list_sessions()
|
|
640
687
|
for session_id, session_info in rpc_sessions.items():
|
|
641
|
-
sessions.append(
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
688
|
+
sessions.append(
|
|
689
|
+
{
|
|
690
|
+
"id": session_id,
|
|
691
|
+
"type": session_info.get("type", "unknown"),
|
|
692
|
+
"tunnel": session_info.get("tunnel_peer", "unknown"),
|
|
693
|
+
"via_exploit": session_info.get(
|
|
694
|
+
"via_exploit", "unknown"
|
|
695
|
+
),
|
|
696
|
+
"via_payload": session_info.get(
|
|
697
|
+
"via_payload", "unknown"
|
|
698
|
+
),
|
|
699
|
+
"info": session_info.get("info", ""),
|
|
700
|
+
"username": session_info.get("username", ""),
|
|
701
|
+
"platform": session_info.get("platform", ""),
|
|
702
|
+
"arch": session_info.get("arch", ""),
|
|
703
|
+
"source": "rpc",
|
|
704
|
+
}
|
|
705
|
+
)
|
|
653
706
|
return sessions
|
|
654
707
|
except Exception as rpc_error:
|
|
655
|
-
logger.debug(
|
|
708
|
+
logger.debug(
|
|
709
|
+
f"RPC not available, falling back to database: {rpc_error}"
|
|
710
|
+
)
|
|
656
711
|
|
|
657
712
|
# Fallback to database
|
|
658
713
|
with MSFDatabase(**self.msf_db_config) as msf_db:
|
|
659
714
|
msf_sessions = msf_db.get_sessions(active_only=True)
|
|
660
715
|
for session in msf_sessions:
|
|
661
|
-
sessions.append(
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
716
|
+
sessions.append(
|
|
717
|
+
{
|
|
718
|
+
"id": session["id"],
|
|
719
|
+
"type": session.get("stype", "unknown"),
|
|
720
|
+
"host": session.get("host_address", "unknown"),
|
|
721
|
+
"port": session.get("port", 0),
|
|
722
|
+
"via_exploit": session.get("via_exploit", "unknown"),
|
|
723
|
+
"via_payload": session.get("via_payload", "unknown"),
|
|
724
|
+
"platform": session.get("platform", ""),
|
|
725
|
+
"opened_at": session.get("opened_at"),
|
|
726
|
+
"last_seen": session.get("last_seen"),
|
|
727
|
+
"source": "database",
|
|
728
|
+
}
|
|
729
|
+
)
|
|
673
730
|
|
|
674
731
|
except Exception as e:
|
|
675
732
|
logger.error(f"Failed to get active sessions: {e}")
|
|
676
733
|
|
|
677
734
|
return sessions
|
|
678
735
|
|
|
679
|
-
def _mark_exploit_success(
|
|
680
|
-
self,
|
|
681
|
-
host_id: int,
|
|
682
|
-
exploit_name: str,
|
|
683
|
-
notes: str = ""
|
|
684
|
-
):
|
|
736
|
+
def _mark_exploit_success(self, host_id: int, exploit_name: str, notes: str = ""):
|
|
685
737
|
"""Mark an exploit as successful"""
|
|
686
738
|
try:
|
|
687
739
|
# Record successful exploit attempt
|
|
@@ -690,9 +742,9 @@ class MSFSyncManager:
|
|
|
690
742
|
host_id=host_id,
|
|
691
743
|
exploit_identifier=f"msf:{exploit_name}",
|
|
692
744
|
exploit_title=exploit_name,
|
|
693
|
-
status=
|
|
745
|
+
status="success",
|
|
694
746
|
service_id=None,
|
|
695
|
-
notes=notes
|
|
747
|
+
notes=notes,
|
|
696
748
|
)
|
|
697
749
|
except Exception as e:
|
|
698
750
|
logger.debug(f"Failed to mark exploit success: {e}")
|
|
@@ -701,14 +753,22 @@ class MSFSyncManager:
|
|
|
701
753
|
"""Determine severity from vulnerability name"""
|
|
702
754
|
vuln_lower = vuln_name.lower()
|
|
703
755
|
|
|
704
|
-
if any(
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
756
|
+
if any(
|
|
757
|
+
x in vuln_lower
|
|
758
|
+
for x in ["rce", "remote code", "command execution", "backdoor"]
|
|
759
|
+
):
|
|
760
|
+
return "critical"
|
|
761
|
+
elif any(
|
|
762
|
+
x in vuln_lower
|
|
763
|
+
for x in ["exploit", "overflow", "injection", "authentication bypass"]
|
|
764
|
+
):
|
|
765
|
+
return "high"
|
|
766
|
+
elif any(
|
|
767
|
+
x in vuln_lower for x in ["disclosure", "exposure", "misconfiguration"]
|
|
768
|
+
):
|
|
769
|
+
return "medium"
|
|
710
770
|
else:
|
|
711
|
-
return
|
|
771
|
+
return "low"
|
|
712
772
|
|
|
713
773
|
def get_msf_stats(self) -> Dict[str, Any]:
|
|
714
774
|
"""
|