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.
- 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
souleyez/core/msf_database.py
CHANGED
|
@@ -13,6 +13,7 @@ from datetime import datetime
|
|
|
13
13
|
try:
|
|
14
14
|
import psycopg2
|
|
15
15
|
from psycopg2.extras import RealDictCursor
|
|
16
|
+
|
|
16
17
|
PSYCOPG2_AVAILABLE = True
|
|
17
18
|
except ImportError:
|
|
18
19
|
PSYCOPG2_AVAILABLE = False
|
|
@@ -20,12 +21,13 @@ except ImportError:
|
|
|
20
21
|
logger = logging.getLogger(__name__)
|
|
21
22
|
|
|
22
23
|
# Tested MSF versions (major.minor)
|
|
23
|
-
TESTED_MSF_VERSIONS = [
|
|
24
|
+
TESTED_MSF_VERSIONS = ["6.2", "6.3", "6.4"]
|
|
24
25
|
KNOWN_SCHEMA_VERSION = 20230313 # MSF schema version we're compatible with
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
class MSFDatabaseSchemaError(Exception):
|
|
28
29
|
"""Raised when MSF database schema is incompatible"""
|
|
30
|
+
|
|
29
31
|
pass
|
|
30
32
|
|
|
31
33
|
|
|
@@ -39,7 +41,7 @@ class MSFDatabase:
|
|
|
39
41
|
database: str = "msf",
|
|
40
42
|
username: str = "msf",
|
|
41
43
|
password: str = "",
|
|
42
|
-
workspace: str = "default"
|
|
44
|
+
workspace: str = "default",
|
|
43
45
|
):
|
|
44
46
|
"""
|
|
45
47
|
Initialize MSF database connection
|
|
@@ -81,7 +83,7 @@ class MSFDatabase:
|
|
|
81
83
|
database=self.database,
|
|
82
84
|
user=self.username,
|
|
83
85
|
password=self.password,
|
|
84
|
-
connect_timeout=10 # Prevent hanging on unreachable database
|
|
86
|
+
connect_timeout=10, # Prevent hanging on unreachable database
|
|
85
87
|
)
|
|
86
88
|
|
|
87
89
|
# Get workspace ID
|
|
@@ -109,8 +111,7 @@ class MSFDatabase:
|
|
|
109
111
|
try:
|
|
110
112
|
with self.conn.cursor() as cur:
|
|
111
113
|
cur.execute(
|
|
112
|
-
"SELECT id FROM workspaces WHERE name = %s",
|
|
113
|
-
(workspace_name,)
|
|
114
|
+
"SELECT id FROM workspaces WHERE name = %s", (workspace_name,)
|
|
114
115
|
)
|
|
115
116
|
result = cur.fetchone()
|
|
116
117
|
return result[0] if result else None
|
|
@@ -125,10 +126,14 @@ class MSFDatabase:
|
|
|
125
126
|
version_info = self._get_msf_version_info()
|
|
126
127
|
|
|
127
128
|
if version_info:
|
|
128
|
-
version_str = version_info.get(
|
|
129
|
+
version_str = version_info.get("version", "unknown")
|
|
129
130
|
# Check if version is tested
|
|
130
|
-
major_minor =
|
|
131
|
-
|
|
131
|
+
major_minor = (
|
|
132
|
+
".".join(version_str.split(".")[:2])
|
|
133
|
+
if "." in version_str
|
|
134
|
+
else version_str
|
|
135
|
+
)
|
|
136
|
+
if major_minor not in TESTED_MSF_VERSIONS and version_str != "unknown":
|
|
132
137
|
logger.warning(
|
|
133
138
|
f"MSF version {version_str} has not been tested with this integration. "
|
|
134
139
|
f"Tested versions: {', '.join(TESTED_MSF_VERSIONS)}. "
|
|
@@ -152,18 +157,20 @@ class MSFDatabase:
|
|
|
152
157
|
# or check for schema_migrations to infer version
|
|
153
158
|
with self.conn.cursor(cursor_factory=RealDictCursor) as cur:
|
|
154
159
|
# Check latest schema migration
|
|
155
|
-
cur.execute(
|
|
160
|
+
cur.execute(
|
|
161
|
+
"""
|
|
156
162
|
SELECT version
|
|
157
163
|
FROM schema_migrations
|
|
158
164
|
ORDER BY version DESC
|
|
159
165
|
LIMIT 1
|
|
160
|
-
"""
|
|
166
|
+
"""
|
|
167
|
+
)
|
|
161
168
|
result = cur.fetchone()
|
|
162
169
|
|
|
163
170
|
if result:
|
|
164
171
|
return {
|
|
165
|
-
|
|
166
|
-
|
|
172
|
+
"schema_version": str(result["version"]),
|
|
173
|
+
"version": "unknown", # Can't reliably determine from DB
|
|
167
174
|
}
|
|
168
175
|
except Exception as e:
|
|
169
176
|
logger.debug(f"Could not determine MSF version: {e}")
|
|
@@ -175,33 +182,48 @@ class MSFDatabase:
|
|
|
175
182
|
try:
|
|
176
183
|
with self.conn.cursor() as cur:
|
|
177
184
|
# Check critical tables exist
|
|
178
|
-
critical_tables = [
|
|
185
|
+
critical_tables = [
|
|
186
|
+
"hosts",
|
|
187
|
+
"services",
|
|
188
|
+
"vulns",
|
|
189
|
+
"sessions",
|
|
190
|
+
"workspaces",
|
|
191
|
+
]
|
|
179
192
|
|
|
180
193
|
for table in critical_tables:
|
|
181
|
-
cur.execute(
|
|
194
|
+
cur.execute(
|
|
195
|
+
"""
|
|
182
196
|
SELECT EXISTS (
|
|
183
197
|
SELECT FROM information_schema.tables
|
|
184
198
|
WHERE table_name = %s
|
|
185
199
|
)
|
|
186
|
-
""",
|
|
200
|
+
""",
|
|
201
|
+
(table,),
|
|
202
|
+
)
|
|
187
203
|
|
|
188
204
|
exists = cur.fetchone()[0]
|
|
189
205
|
if not exists:
|
|
190
|
-
logger.error(
|
|
206
|
+
logger.error(
|
|
207
|
+
f"Critical table '{table}' not found in MSF database"
|
|
208
|
+
)
|
|
191
209
|
return False
|
|
192
210
|
|
|
193
211
|
# Validate key columns exist in hosts table
|
|
194
|
-
cur.execute(
|
|
212
|
+
cur.execute(
|
|
213
|
+
"""
|
|
195
214
|
SELECT column_name
|
|
196
215
|
FROM information_schema.columns
|
|
197
216
|
WHERE table_name = 'hosts'
|
|
198
|
-
"""
|
|
217
|
+
"""
|
|
218
|
+
)
|
|
199
219
|
columns = {row[0] for row in cur.fetchall()}
|
|
200
220
|
|
|
201
|
-
required_columns = {
|
|
221
|
+
required_columns = {"id", "address", "workspace_id"}
|
|
202
222
|
if not required_columns.issubset(columns):
|
|
203
223
|
missing = required_columns - columns
|
|
204
|
-
logger.error(
|
|
224
|
+
logger.error(
|
|
225
|
+
f"Required columns missing from 'hosts' table: {missing}"
|
|
226
|
+
)
|
|
205
227
|
return False
|
|
206
228
|
|
|
207
229
|
return True
|
|
@@ -219,7 +241,8 @@ class MSFDatabase:
|
|
|
219
241
|
"""
|
|
220
242
|
try:
|
|
221
243
|
with self.conn.cursor(cursor_factory=RealDictCursor) as cur:
|
|
222
|
-
cur.execute(
|
|
244
|
+
cur.execute(
|
|
245
|
+
"""
|
|
223
246
|
SELECT id, name, created_at, updated_at,
|
|
224
247
|
boundary,
|
|
225
248
|
description,
|
|
@@ -227,7 +250,8 @@ class MSFDatabase:
|
|
|
227
250
|
limit_to_network
|
|
228
251
|
FROM workspaces
|
|
229
252
|
ORDER BY name
|
|
230
|
-
"""
|
|
253
|
+
"""
|
|
254
|
+
)
|
|
231
255
|
return [dict(row) for row in cur.fetchall()]
|
|
232
256
|
except Exception as e:
|
|
233
257
|
logger.error(f"Failed to list workspaces: {e}")
|
|
@@ -250,7 +274,8 @@ class MSFDatabase:
|
|
|
250
274
|
|
|
251
275
|
try:
|
|
252
276
|
with self.conn.cursor(cursor_factory=RealDictCursor) as cur:
|
|
253
|
-
cur.execute(
|
|
277
|
+
cur.execute(
|
|
278
|
+
"""
|
|
254
279
|
SELECT
|
|
255
280
|
id,
|
|
256
281
|
address,
|
|
@@ -270,13 +295,17 @@ class MSFDatabase:
|
|
|
270
295
|
FROM hosts
|
|
271
296
|
WHERE workspace_id = %s
|
|
272
297
|
ORDER BY address
|
|
273
|
-
""",
|
|
298
|
+
""",
|
|
299
|
+
(ws_id,),
|
|
300
|
+
)
|
|
274
301
|
return [dict(row) for row in cur.fetchall()]
|
|
275
302
|
except Exception as e:
|
|
276
303
|
logger.error(f"Failed to get hosts: {e}")
|
|
277
304
|
return []
|
|
278
305
|
|
|
279
|
-
def get_services(
|
|
306
|
+
def get_services(
|
|
307
|
+
self, workspace_id: Optional[int] = None, host_id: Optional[int] = None
|
|
308
|
+
) -> List[Dict[str, Any]]:
|
|
280
309
|
"""
|
|
281
310
|
Get all services from MSF database
|
|
282
311
|
|
|
@@ -343,7 +372,8 @@ class MSFDatabase:
|
|
|
343
372
|
with self.conn.cursor(cursor_factory=RealDictCursor) as cur:
|
|
344
373
|
# Try full query first (without v.refs which doesn't exist in some MSF versions)
|
|
345
374
|
try:
|
|
346
|
-
cur.execute(
|
|
375
|
+
cur.execute(
|
|
376
|
+
"""
|
|
347
377
|
SELECT
|
|
348
378
|
v.id,
|
|
349
379
|
v.host_id,
|
|
@@ -361,16 +391,21 @@ class MSFDatabase:
|
|
|
361
391
|
LEFT JOIN services s ON v.service_id = s.id
|
|
362
392
|
WHERE h.workspace_id = %s
|
|
363
393
|
ORDER BY v.created_at DESC
|
|
364
|
-
""",
|
|
394
|
+
""",
|
|
395
|
+
(ws_id,),
|
|
396
|
+
)
|
|
365
397
|
results = cur.fetchall()
|
|
366
398
|
return [dict(row) for row in results]
|
|
367
399
|
except Exception as schema_error:
|
|
368
400
|
# Rollback the failed transaction
|
|
369
401
|
self.conn.rollback()
|
|
370
|
-
logger.warning(
|
|
402
|
+
logger.warning(
|
|
403
|
+
f"Full vulns query failed, trying minimal query: {schema_error}"
|
|
404
|
+
)
|
|
371
405
|
|
|
372
406
|
# Try minimal query with only essential columns
|
|
373
|
-
cur.execute(
|
|
407
|
+
cur.execute(
|
|
408
|
+
"""
|
|
374
409
|
SELECT
|
|
375
410
|
v.id,
|
|
376
411
|
v.host_id,
|
|
@@ -382,12 +417,15 @@ class MSFDatabase:
|
|
|
382
417
|
JOIN hosts h ON v.host_id = h.id
|
|
383
418
|
WHERE h.workspace_id = %s
|
|
384
419
|
ORDER BY v.id DESC
|
|
385
|
-
""",
|
|
420
|
+
""",
|
|
421
|
+
(ws_id,),
|
|
422
|
+
)
|
|
386
423
|
results = cur.fetchall()
|
|
387
424
|
return [dict(row) for row in results]
|
|
388
425
|
except Exception as e:
|
|
389
426
|
logger.error(f"Failed to get vulnerabilities: {e}")
|
|
390
427
|
import traceback
|
|
428
|
+
|
|
391
429
|
logger.error(f"Traceback: {traceback.format_exc()}")
|
|
392
430
|
# Rollback transaction on error
|
|
393
431
|
try:
|
|
@@ -415,7 +453,8 @@ class MSFDatabase:
|
|
|
415
453
|
with self.conn.cursor(cursor_factory=RealDictCursor) as cur:
|
|
416
454
|
# MSF uses a complex credential schema with cores, logins, and privates
|
|
417
455
|
# We'll join them to get usable credential data
|
|
418
|
-
cur.execute(
|
|
456
|
+
cur.execute(
|
|
457
|
+
"""
|
|
419
458
|
SELECT
|
|
420
459
|
l.id,
|
|
421
460
|
l.core_id,
|
|
@@ -439,7 +478,9 @@ class MSFDatabase:
|
|
|
439
478
|
LEFT JOIN hosts h ON s.host_id = h.id
|
|
440
479
|
WHERE c.workspace_id = %s
|
|
441
480
|
ORDER BY l.created_at DESC
|
|
442
|
-
""",
|
|
481
|
+
""",
|
|
482
|
+
(ws_id,),
|
|
483
|
+
)
|
|
443
484
|
return [dict(row) for row in cur.fetchall()]
|
|
444
485
|
except Exception as e:
|
|
445
486
|
logger.debug(f"Failed to get credentials: {e}")
|
|
@@ -450,7 +491,9 @@ class MSFDatabase:
|
|
|
450
491
|
pass
|
|
451
492
|
return []
|
|
452
493
|
|
|
453
|
-
def get_sessions(
|
|
494
|
+
def get_sessions(
|
|
495
|
+
self, workspace_id: Optional[int] = None, active_only: bool = True
|
|
496
|
+
) -> List[Dict[str, Any]]:
|
|
454
497
|
"""
|
|
455
498
|
Get all sessions from MSF database
|
|
456
499
|
|
|
@@ -503,7 +546,9 @@ class MSFDatabase:
|
|
|
503
546
|
except Exception as schema_error:
|
|
504
547
|
# Rollback the failed transaction
|
|
505
548
|
self.conn.rollback()
|
|
506
|
-
logger.warning(
|
|
549
|
+
logger.warning(
|
|
550
|
+
f"Full sessions query failed, trying minimal query: {schema_error}"
|
|
551
|
+
)
|
|
507
552
|
|
|
508
553
|
# Try minimal query with only essential columns
|
|
509
554
|
query = """
|
|
@@ -532,6 +577,7 @@ class MSFDatabase:
|
|
|
532
577
|
except Exception as e:
|
|
533
578
|
logger.error(f"Failed to get sessions: {e}")
|
|
534
579
|
import traceback
|
|
580
|
+
|
|
535
581
|
logger.error(f"Traceback: {traceback.format_exc()}")
|
|
536
582
|
# Rollback transaction on error
|
|
537
583
|
try:
|
|
@@ -552,7 +598,8 @@ class MSFDatabase:
|
|
|
552
598
|
"""
|
|
553
599
|
try:
|
|
554
600
|
with self.conn.cursor(cursor_factory=RealDictCursor) as cur:
|
|
555
|
-
cur.execute(
|
|
601
|
+
cur.execute(
|
|
602
|
+
"""
|
|
556
603
|
SELECT
|
|
557
604
|
id,
|
|
558
605
|
session_id,
|
|
@@ -565,13 +612,17 @@ class MSFDatabase:
|
|
|
565
612
|
FROM session_events
|
|
566
613
|
WHERE session_id = %s
|
|
567
614
|
ORDER BY created_at
|
|
568
|
-
""",
|
|
615
|
+
""",
|
|
616
|
+
(session_id,),
|
|
617
|
+
)
|
|
569
618
|
return [dict(row) for row in cur.fetchall()]
|
|
570
619
|
except Exception as e:
|
|
571
620
|
logger.error(f"Failed to get session events: {e}")
|
|
572
621
|
return []
|
|
573
622
|
|
|
574
|
-
def get_exploit_attempts(
|
|
623
|
+
def get_exploit_attempts(
|
|
624
|
+
self, workspace_id: Optional[int] = None
|
|
625
|
+
) -> List[Dict[str, Any]]:
|
|
575
626
|
"""
|
|
576
627
|
Get exploit attempt data from MSF database
|
|
577
628
|
|
|
@@ -591,7 +642,8 @@ class MSFDatabase:
|
|
|
591
642
|
|
|
592
643
|
try:
|
|
593
644
|
with self.conn.cursor(cursor_factory=RealDictCursor) as cur:
|
|
594
|
-
cur.execute(
|
|
645
|
+
cur.execute(
|
|
646
|
+
"""
|
|
595
647
|
SELECT
|
|
596
648
|
v.id,
|
|
597
649
|
v.host_id,
|
|
@@ -609,13 +661,17 @@ class MSFDatabase:
|
|
|
609
661
|
WHERE h.workspace_id = %s
|
|
610
662
|
AND v.exploited_at IS NOT NULL
|
|
611
663
|
ORDER BY v.exploited_at DESC
|
|
612
|
-
""",
|
|
664
|
+
""",
|
|
665
|
+
(ws_id,),
|
|
666
|
+
)
|
|
613
667
|
return [dict(row) for row in cur.fetchall()]
|
|
614
668
|
except Exception as e:
|
|
615
669
|
logger.error(f"Failed to get exploit attempts: {e}")
|
|
616
670
|
return []
|
|
617
671
|
|
|
618
|
-
def get_database_stats(
|
|
672
|
+
def get_database_stats(
|
|
673
|
+
self, workspace_id: Optional[int] = None, max_retries: int = 3
|
|
674
|
+
) -> Dict[str, int]:
|
|
619
675
|
"""
|
|
620
676
|
Get statistics about MSF database contents
|
|
621
677
|
|
|
@@ -632,16 +688,17 @@ class MSFDatabase:
|
|
|
632
688
|
return {}
|
|
633
689
|
|
|
634
690
|
stats = {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
691
|
+
"hosts": 0,
|
|
692
|
+
"services": 0,
|
|
693
|
+
"vulns": 0,
|
|
694
|
+
"creds": 0,
|
|
695
|
+
"active_sessions": 0,
|
|
696
|
+
"total_sessions": 0,
|
|
641
697
|
}
|
|
642
698
|
|
|
643
699
|
# Retry logic for database locks
|
|
644
700
|
import time
|
|
701
|
+
|
|
645
702
|
for attempt in range(max_retries):
|
|
646
703
|
try:
|
|
647
704
|
with self.conn.cursor() as cur:
|
|
@@ -649,35 +706,41 @@ class MSFDatabase:
|
|
|
649
706
|
try:
|
|
650
707
|
cur.execute(
|
|
651
708
|
"SELECT COUNT(*) FROM hosts WHERE workspace_id = %s",
|
|
652
|
-
(ws_id,)
|
|
709
|
+
(ws_id,),
|
|
653
710
|
)
|
|
654
|
-
stats[
|
|
711
|
+
stats["hosts"] = cur.fetchone()[0]
|
|
655
712
|
except Exception as e:
|
|
656
713
|
logger.warning(f"Failed to count hosts: {e}")
|
|
657
714
|
self.conn.rollback() # Clear aborted transaction state
|
|
658
715
|
|
|
659
716
|
# Count services
|
|
660
717
|
try:
|
|
661
|
-
cur.execute(
|
|
718
|
+
cur.execute(
|
|
719
|
+
"""
|
|
662
720
|
SELECT COUNT(*)
|
|
663
721
|
FROM services s
|
|
664
722
|
JOIN hosts h ON s.host_id = h.id
|
|
665
723
|
WHERE h.workspace_id = %s
|
|
666
|
-
""",
|
|
667
|
-
|
|
724
|
+
""",
|
|
725
|
+
(ws_id,),
|
|
726
|
+
)
|
|
727
|
+
stats["services"] = cur.fetchone()[0]
|
|
668
728
|
except Exception as e:
|
|
669
729
|
logger.warning(f"Failed to count services: {e}")
|
|
670
730
|
self.conn.rollback() # Clear aborted transaction state
|
|
671
731
|
|
|
672
732
|
# Count vulns
|
|
673
733
|
try:
|
|
674
|
-
cur.execute(
|
|
734
|
+
cur.execute(
|
|
735
|
+
"""
|
|
675
736
|
SELECT COUNT(*)
|
|
676
737
|
FROM vulns v
|
|
677
738
|
JOIN hosts h ON v.host_id = h.id
|
|
678
739
|
WHERE h.workspace_id = %s
|
|
679
|
-
""",
|
|
680
|
-
|
|
740
|
+
""",
|
|
741
|
+
(ws_id,),
|
|
742
|
+
)
|
|
743
|
+
stats["vulns"] = cur.fetchone()[0]
|
|
681
744
|
except Exception as e:
|
|
682
745
|
logger.warning(f"Failed to count vulns: {e}")
|
|
683
746
|
self.conn.rollback() # Clear aborted transaction state
|
|
@@ -685,39 +748,50 @@ class MSFDatabase:
|
|
|
685
748
|
# Count creds - credential schema varies by MSF version
|
|
686
749
|
# Some versions don't have credential tables at all
|
|
687
750
|
try:
|
|
688
|
-
cur.execute(
|
|
751
|
+
cur.execute(
|
|
752
|
+
"""
|
|
689
753
|
SELECT COUNT(*)
|
|
690
754
|
FROM cores
|
|
691
755
|
WHERE workspace_id = %s
|
|
692
|
-
""",
|
|
693
|
-
|
|
756
|
+
""",
|
|
757
|
+
(ws_id,),
|
|
758
|
+
)
|
|
759
|
+
stats["creds"] = cur.fetchone()[0]
|
|
694
760
|
except Exception as e:
|
|
695
|
-
logger.debug(
|
|
761
|
+
logger.debug(
|
|
762
|
+
f"Failed to count credentials (table may not exist in this MSF version): {e}"
|
|
763
|
+
)
|
|
696
764
|
self.conn.rollback() # Clear aborted transaction state
|
|
697
765
|
|
|
698
766
|
# Count active sessions
|
|
699
767
|
try:
|
|
700
|
-
cur.execute(
|
|
768
|
+
cur.execute(
|
|
769
|
+
"""
|
|
701
770
|
SELECT COUNT(*)
|
|
702
771
|
FROM sessions s
|
|
703
772
|
JOIN hosts h ON s.host_id = h.id
|
|
704
773
|
WHERE h.workspace_id = %s
|
|
705
774
|
AND s.closed_at IS NULL
|
|
706
|
-
""",
|
|
707
|
-
|
|
775
|
+
""",
|
|
776
|
+
(ws_id,),
|
|
777
|
+
)
|
|
778
|
+
stats["active_sessions"] = cur.fetchone()[0]
|
|
708
779
|
except Exception as e:
|
|
709
780
|
logger.warning(f"Failed to count active sessions: {e}")
|
|
710
781
|
self.conn.rollback() # Clear aborted transaction state
|
|
711
782
|
|
|
712
783
|
# Count total sessions
|
|
713
784
|
try:
|
|
714
|
-
cur.execute(
|
|
785
|
+
cur.execute(
|
|
786
|
+
"""
|
|
715
787
|
SELECT COUNT(*)
|
|
716
788
|
FROM sessions s
|
|
717
789
|
JOIN hosts h ON s.host_id = h.id
|
|
718
790
|
WHERE h.workspace_id = %s
|
|
719
|
-
""",
|
|
720
|
-
|
|
791
|
+
""",
|
|
792
|
+
(ws_id,),
|
|
793
|
+
)
|
|
794
|
+
stats["total_sessions"] = cur.fetchone()[0]
|
|
721
795
|
except Exception as e:
|
|
722
796
|
logger.warning(f"Failed to count total sessions: {e}")
|
|
723
797
|
self.conn.rollback() # Clear aborted transaction state
|
|
@@ -728,10 +802,14 @@ class MSFDatabase:
|
|
|
728
802
|
except Exception as e:
|
|
729
803
|
# Check if it's a database lock error
|
|
730
804
|
error_msg = str(e).lower()
|
|
731
|
-
if
|
|
805
|
+
if "lock" in error_msg or "locked" in error_msg:
|
|
732
806
|
if attempt < max_retries - 1:
|
|
733
|
-
wait_time = 0.5 * (
|
|
734
|
-
|
|
807
|
+
wait_time = 0.5 * (
|
|
808
|
+
attempt + 1
|
|
809
|
+
) # Incremental backoff: 0.5s, 1s, 1.5s
|
|
810
|
+
logger.warning(
|
|
811
|
+
f"MSF database is locked (attempt {attempt + 1}/{max_retries}), retrying in {wait_time}s..."
|
|
812
|
+
)
|
|
735
813
|
time.sleep(wait_time)
|
|
736
814
|
# Rollback the transaction before retrying
|
|
737
815
|
try:
|
|
@@ -739,11 +817,16 @@ class MSFDatabase:
|
|
|
739
817
|
except:
|
|
740
818
|
pass
|
|
741
819
|
else:
|
|
742
|
-
logger.error(
|
|
743
|
-
|
|
820
|
+
logger.error(
|
|
821
|
+
f"MSF database is locked after {max_retries} attempts: {e}"
|
|
822
|
+
)
|
|
823
|
+
logger.error(
|
|
824
|
+
"Recommendation: Wait for MSF operations to complete, then try again"
|
|
825
|
+
)
|
|
744
826
|
else:
|
|
745
827
|
logger.error(f"Failed to get database stats: {e}")
|
|
746
828
|
import traceback
|
|
829
|
+
|
|
747
830
|
logger.debug(f"Traceback: {traceback.format_exc()}")
|
|
748
831
|
break # Don't retry for non-lock errors
|
|
749
832
|
|
|
@@ -764,7 +847,7 @@ def test_msf_database_connection(
|
|
|
764
847
|
port: int = 5432,
|
|
765
848
|
database: str = "msf",
|
|
766
849
|
username: str = "msf",
|
|
767
|
-
password: str = ""
|
|
850
|
+
password: str = "",
|
|
768
851
|
) -> bool:
|
|
769
852
|
"""
|
|
770
853
|
Test MSF database connection
|