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_integration.py
CHANGED
|
@@ -11,108 +11,110 @@ from typing import List, Dict, Optional, Tuple
|
|
|
11
11
|
|
|
12
12
|
# CVE Database mapping CVEs to MSF modules
|
|
13
13
|
CVE_DATABASE = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
"CVE-2017-0143": {
|
|
15
|
+
"modules": ["exploit/windows/smb/ms17_010_eternalblue"],
|
|
16
|
+
"affected_versions": {
|
|
17
|
+
"smb": ["SMBv1"],
|
|
18
|
+
"os": [
|
|
19
|
+
"Windows 7",
|
|
20
|
+
"Windows Server 2008",
|
|
21
|
+
"Windows Server 2008 R2",
|
|
22
|
+
"Windows 8.1",
|
|
23
|
+
"Windows 10",
|
|
24
|
+
],
|
|
19
25
|
},
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
"cvss": 8.1,
|
|
27
|
+
"reliability": "excellent",
|
|
28
|
+
"requires": [],
|
|
29
|
+
"impact": "Remote Code Execution",
|
|
24
30
|
},
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
"CVE-2019-0708": {
|
|
32
|
+
"modules": ["exploit/windows/rdp/cve_2019_0708_bluekeep_rce"],
|
|
33
|
+
"affected_versions": {
|
|
34
|
+
"rdp": ["7.0", "7.1", "8.0", "8.1"],
|
|
35
|
+
"os": ["Windows 7", "Windows Server 2008", "Windows Server 2008 R2"],
|
|
30
36
|
},
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
"cvss": 9.8,
|
|
38
|
+
"reliability": "normal",
|
|
39
|
+
"requires": [],
|
|
40
|
+
"impact": "Remote Code Execution",
|
|
35
41
|
},
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
'requires': [],
|
|
44
|
-
'impact': 'Remote Code Execution'
|
|
42
|
+
"CVE-2011-2523": {
|
|
43
|
+
"modules": ["exploit/unix/ftp/vsftpd_234_backdoor"],
|
|
44
|
+
"affected_versions": {"ftp": ["vsftpd 2.3.4"]},
|
|
45
|
+
"cvss": 10.0,
|
|
46
|
+
"reliability": "excellent",
|
|
47
|
+
"requires": [],
|
|
48
|
+
"impact": "Remote Code Execution",
|
|
45
49
|
},
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
'reliability': 'excellent',
|
|
54
|
-
'requires': [],
|
|
55
|
-
'impact': 'Remote Code Execution'
|
|
50
|
+
"CVE-2014-6271": {
|
|
51
|
+
"modules": ["exploit/multi/http/apache_mod_cgi_bash_env_exec"],
|
|
52
|
+
"affected_versions": {"bash": ["<4.3"], "cgi": ["*"]},
|
|
53
|
+
"cvss": 9.8,
|
|
54
|
+
"reliability": "excellent",
|
|
55
|
+
"requires": [],
|
|
56
|
+
"impact": "Remote Code Execution",
|
|
56
57
|
},
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
"CVE-2007-2447": {
|
|
59
|
+
"modules": ["exploit/multi/samba/usermap_script"],
|
|
60
|
+
"affected_versions": {
|
|
61
|
+
"smb": [
|
|
62
|
+
"Samba 3.0.20",
|
|
63
|
+
"Samba 3.0.21",
|
|
64
|
+
"Samba 3.0.22",
|
|
65
|
+
"Samba 3.0.23",
|
|
66
|
+
"Samba 3.0.24",
|
|
67
|
+
"Samba 3.0.25",
|
|
68
|
+
]
|
|
61
69
|
},
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
70
|
+
"cvss": 10.0,
|
|
71
|
+
"reliability": "excellent",
|
|
72
|
+
"requires": [],
|
|
73
|
+
"impact": "Remote Code Execution",
|
|
66
74
|
},
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
75
|
+
"CVE-2008-4250": {
|
|
76
|
+
"modules": ["exploit/windows/smb/ms08_067_netapi"],
|
|
77
|
+
"affected_versions": {
|
|
78
|
+
"os": ["Windows 2000", "Windows XP", "Windows Server 2003"]
|
|
71
79
|
},
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
80
|
+
"cvss": 10.0,
|
|
81
|
+
"reliability": "excellent",
|
|
82
|
+
"requires": [],
|
|
83
|
+
"impact": "Remote Code Execution",
|
|
76
84
|
},
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
'requires': [],
|
|
85
|
-
'impact': 'Remote Code Execution'
|
|
85
|
+
"CVE-2012-1823": {
|
|
86
|
+
"modules": ["exploit/multi/http/php_cgi_arg_injection"],
|
|
87
|
+
"affected_versions": {"php": ["5.3.0-5.3.12", "5.4.0-5.4.2"]},
|
|
88
|
+
"cvss": 7.5,
|
|
89
|
+
"reliability": "good",
|
|
90
|
+
"requires": [],
|
|
91
|
+
"impact": "Remote Code Execution",
|
|
86
92
|
},
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
'requires': ['local_access'],
|
|
95
|
-
'impact': 'Privilege Escalation'
|
|
93
|
+
"CVE-2021-4034": {
|
|
94
|
+
"modules": ["exploit/linux/local/cve_2021_4034_pwnkit_lpe_pkexec"],
|
|
95
|
+
"affected_versions": {"polkit": ["<=0.120"]},
|
|
96
|
+
"cvss": 7.8,
|
|
97
|
+
"reliability": "excellent",
|
|
98
|
+
"requires": ["local_access"],
|
|
99
|
+
"impact": "Privilege Escalation",
|
|
96
100
|
},
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
'requires': ['local_access'],
|
|
105
|
-
'impact': 'Privilege Escalation'
|
|
101
|
+
"CVE-2021-3156": {
|
|
102
|
+
"modules": ["exploit/linux/local/sudo_baron_samedit"],
|
|
103
|
+
"affected_versions": {"sudo": ["1.8.2-1.8.31p2", "1.9.0-1.9.5p1"]},
|
|
104
|
+
"cvss": 7.8,
|
|
105
|
+
"reliability": "excellent",
|
|
106
|
+
"requires": ["local_access"],
|
|
107
|
+
"impact": "Privilege Escalation",
|
|
106
108
|
},
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
"CVE-2017-7494": {
|
|
110
|
+
"modules": ["exploit/linux/samba/is_known_pipename"],
|
|
111
|
+
"affected_versions": {
|
|
112
|
+
"smb": ["Samba 3.5.0-4.6.4", "Samba 4.5.0-4.5.9", "Samba 4.4.0-4.4.13"]
|
|
111
113
|
},
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
"cvss": 7.5,
|
|
115
|
+
"reliability": "good",
|
|
116
|
+
"requires": [],
|
|
117
|
+
"impact": "Remote Code Execution",
|
|
116
118
|
},
|
|
117
119
|
}
|
|
118
120
|
|
|
@@ -124,79 +126,79 @@ class VersionMatcher:
|
|
|
124
126
|
def parse_version(version_string: str) -> Optional[Tuple]:
|
|
125
127
|
"""
|
|
126
128
|
Parse version string to comparable tuple (major, minor, patch).
|
|
127
|
-
|
|
129
|
+
|
|
128
130
|
Args:
|
|
129
131
|
version_string: Version string like "2.4.49" or "OpenSSH 7.4"
|
|
130
|
-
|
|
132
|
+
|
|
131
133
|
Returns:
|
|
132
134
|
Tuple of (major, minor, patch) or None if unparseable
|
|
133
135
|
"""
|
|
134
136
|
# Extract version numbers from string
|
|
135
|
-
version_match = re.search(r
|
|
137
|
+
version_match = re.search(r"(\d+)\.(\d+)(?:\.(\d+))?", version_string)
|
|
136
138
|
if not version_match:
|
|
137
139
|
return None
|
|
138
|
-
|
|
140
|
+
|
|
139
141
|
major = int(version_match.group(1))
|
|
140
142
|
minor = int(version_match.group(2))
|
|
141
143
|
patch = int(version_match.group(3)) if version_match.group(3) else 0
|
|
142
|
-
|
|
144
|
+
|
|
143
145
|
return (major, minor, patch)
|
|
144
146
|
|
|
145
147
|
@staticmethod
|
|
146
148
|
def is_in_range(version: Tuple, range_str: str) -> bool:
|
|
147
149
|
"""
|
|
148
150
|
Check if version is within a range string.
|
|
149
|
-
|
|
151
|
+
|
|
150
152
|
Args:
|
|
151
153
|
version: Parsed version tuple (major, minor, patch)
|
|
152
154
|
range_str: Range like "7.0-7.6", "<=0.120", "<4.3", or exact "2.3.4"
|
|
153
|
-
|
|
155
|
+
|
|
154
156
|
Returns:
|
|
155
157
|
True if version is in range
|
|
156
158
|
"""
|
|
157
159
|
# Handle <= comparisons
|
|
158
|
-
if range_str.startswith(
|
|
160
|
+
if range_str.startswith("<="):
|
|
159
161
|
max_ver_str = range_str[2:].strip()
|
|
160
162
|
max_ver = VersionMatcher.parse_version(max_ver_str)
|
|
161
163
|
if max_ver:
|
|
162
164
|
return version <= max_ver
|
|
163
|
-
|
|
165
|
+
|
|
164
166
|
# Handle < comparisons
|
|
165
|
-
if range_str.startswith(
|
|
167
|
+
if range_str.startswith("<"):
|
|
166
168
|
max_ver_str = range_str[1:].strip()
|
|
167
169
|
max_ver = VersionMatcher.parse_version(max_ver_str)
|
|
168
170
|
if max_ver:
|
|
169
171
|
return version < max_ver
|
|
170
|
-
|
|
172
|
+
|
|
171
173
|
# Handle range (e.g., "1.8.2-1.8.31p2")
|
|
172
|
-
if
|
|
173
|
-
parts = range_str.split(
|
|
174
|
+
if "-" in range_str:
|
|
175
|
+
parts = range_str.split("-")
|
|
174
176
|
if len(parts) == 2:
|
|
175
177
|
min_ver = VersionMatcher.parse_version(parts[0].strip())
|
|
176
178
|
max_ver = VersionMatcher.parse_version(parts[1].strip())
|
|
177
179
|
if min_ver and max_ver:
|
|
178
180
|
return min_ver <= version <= max_ver
|
|
179
|
-
|
|
181
|
+
|
|
180
182
|
# Handle exact match or wildcard
|
|
181
|
-
if range_str ==
|
|
183
|
+
if range_str == "*":
|
|
182
184
|
return True
|
|
183
|
-
|
|
185
|
+
|
|
184
186
|
# Exact version match
|
|
185
187
|
exact_ver = VersionMatcher.parse_version(range_str)
|
|
186
188
|
if exact_ver:
|
|
187
189
|
return version == exact_ver
|
|
188
|
-
|
|
190
|
+
|
|
189
191
|
return False
|
|
190
192
|
|
|
191
193
|
@staticmethod
|
|
192
194
|
def is_vulnerable(service_version: str, vulnerable_ranges: List[str]) -> bool:
|
|
193
195
|
"""
|
|
194
196
|
Check if service version falls within any vulnerable range.
|
|
195
|
-
|
|
197
|
+
|
|
196
198
|
Args:
|
|
197
199
|
service_version: Service version string (e.g., "OpenSSH 7.4", "2.4.49")
|
|
198
200
|
vulnerable_ranges: List of version ranges
|
|
199
|
-
|
|
201
|
+
|
|
200
202
|
Returns:
|
|
201
203
|
True if version is vulnerable
|
|
202
204
|
"""
|
|
@@ -207,37 +209,40 @@ class VersionMatcher:
|
|
|
207
209
|
if range_str.lower() in service_version.lower():
|
|
208
210
|
return True
|
|
209
211
|
return False
|
|
210
|
-
|
|
212
|
+
|
|
211
213
|
for range_str in vulnerable_ranges:
|
|
212
214
|
if VersionMatcher.is_in_range(parsed_version, range_str):
|
|
213
215
|
return True
|
|
214
|
-
|
|
216
|
+
|
|
215
217
|
return False
|
|
216
218
|
|
|
217
219
|
@staticmethod
|
|
218
220
|
def get_cves_for_version(service: str, version: str) -> List[str]:
|
|
219
221
|
"""
|
|
220
222
|
Return CVEs affecting this specific service version.
|
|
221
|
-
|
|
223
|
+
|
|
222
224
|
Args:
|
|
223
225
|
service: Service name (e.g., 'ssh', 'smb', 'http')
|
|
224
226
|
version: Version string
|
|
225
|
-
|
|
227
|
+
|
|
226
228
|
Returns:
|
|
227
229
|
List of CVE IDs
|
|
228
230
|
"""
|
|
229
231
|
matching_cves = []
|
|
230
|
-
|
|
232
|
+
|
|
231
233
|
for cve_id, cve_data in CVE_DATABASE.items():
|
|
232
|
-
affected_versions = cve_data.get(
|
|
233
|
-
|
|
234
|
+
affected_versions = cve_data.get("affected_versions", {})
|
|
235
|
+
|
|
234
236
|
# Check if this CVE affects this service type
|
|
235
237
|
for service_type, version_ranges in affected_versions.items():
|
|
236
|
-
if
|
|
238
|
+
if (
|
|
239
|
+
service_type.lower() in service.lower()
|
|
240
|
+
or service.lower() in service_type.lower()
|
|
241
|
+
):
|
|
237
242
|
if VersionMatcher.is_vulnerable(version, version_ranges):
|
|
238
243
|
matching_cves.append(cve_id)
|
|
239
244
|
break
|
|
240
|
-
|
|
245
|
+
|
|
241
246
|
return matching_cves
|
|
242
247
|
|
|
243
248
|
|
|
@@ -246,12 +251,13 @@ class MSFResourceGenerator:
|
|
|
246
251
|
|
|
247
252
|
def __init__(self, output_dir: str = None):
|
|
248
253
|
"""Initialize generator with output directory."""
|
|
249
|
-
self.output_dir = output_dir or os.path.join(os.getcwd(),
|
|
254
|
+
self.output_dir = output_dir or os.path.join(os.getcwd(), "msf_resources")
|
|
250
255
|
os.makedirs(self.output_dir, exist_ok=True)
|
|
251
256
|
|
|
252
257
|
def generate_header(self) -> str:
|
|
253
258
|
"""Generate resource script header with metadata."""
|
|
254
259
|
from datetime import datetime
|
|
260
|
+
|
|
255
261
|
return f"""# Metasploit Resource Script
|
|
256
262
|
# Generated by souleyez on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
|
257
263
|
#
|
|
@@ -260,7 +266,9 @@ class MSFResourceGenerator:
|
|
|
260
266
|
|
|
261
267
|
"""
|
|
262
268
|
|
|
263
|
-
def generate_smb_psexec_script(
|
|
269
|
+
def generate_smb_psexec_script(
|
|
270
|
+
self, writable_shares: List[Dict], credentials: List[Dict]
|
|
271
|
+
) -> str:
|
|
264
272
|
"""
|
|
265
273
|
Generate psexec attack script for writable SMB shares.
|
|
266
274
|
|
|
@@ -278,13 +286,17 @@ class MSFResourceGenerator:
|
|
|
278
286
|
# Group shares by host
|
|
279
287
|
shares_by_host = {}
|
|
280
288
|
for share in writable_shares:
|
|
281
|
-
host_ip = share.get(
|
|
289
|
+
host_ip = share.get("ip_address")
|
|
282
290
|
if host_ip not in shares_by_host:
|
|
283
291
|
shares_by_host[host_ip] = []
|
|
284
292
|
shares_by_host[host_ip].append(share)
|
|
285
293
|
|
|
286
294
|
# Find SMB credentials
|
|
287
|
-
smb_creds = [
|
|
295
|
+
smb_creds = [
|
|
296
|
+
c
|
|
297
|
+
for c in credentials
|
|
298
|
+
if c.get("service", "").lower() in ["smb", "smb2", "445", "cifs"]
|
|
299
|
+
]
|
|
288
300
|
|
|
289
301
|
if not smb_creds:
|
|
290
302
|
script += "# WARNING: No SMB credentials found!\n"
|
|
@@ -292,7 +304,7 @@ class MSFResourceGenerator:
|
|
|
292
304
|
|
|
293
305
|
for host_ip, shares in shares_by_host.items():
|
|
294
306
|
script += f"\n# Target: {host_ip}\n"
|
|
295
|
-
script += f"# Writable shares: {', '.join([s
|
|
307
|
+
script += f"# Writable shares: {', '.join([s.get('share_name', 'unknown') for s in shares])}\n"
|
|
296
308
|
script += "use exploit/windows/smb/psexec\n"
|
|
297
309
|
script += f"set RHOSTS {host_ip}\n"
|
|
298
310
|
|
|
@@ -310,7 +322,12 @@ class MSFResourceGenerator:
|
|
|
310
322
|
|
|
311
323
|
return script
|
|
312
324
|
|
|
313
|
-
def generate_ssh_bruteforce_script(
|
|
325
|
+
def generate_ssh_bruteforce_script(
|
|
326
|
+
self,
|
|
327
|
+
ssh_hosts: List[Dict],
|
|
328
|
+
username_file: str = None,
|
|
329
|
+
password_file: str = None,
|
|
330
|
+
) -> str:
|
|
314
331
|
"""
|
|
315
332
|
Generate SSH brute force script.
|
|
316
333
|
|
|
@@ -327,48 +344,19 @@ class MSFResourceGenerator:
|
|
|
327
344
|
script += "# WARNING: This is noisy and may trigger IDS/IPS alerts!\n"
|
|
328
345
|
script += "# Ensure you have authorization before running.\n\n"
|
|
329
346
|
|
|
330
|
-
# Default wordlists
|
|
347
|
+
# Default wordlists - use project's self-contained wordlists
|
|
348
|
+
from souleyez.wordlists import resolve_wordlist_path
|
|
349
|
+
|
|
331
350
|
if not username_file:
|
|
332
|
-
|
|
333
|
-
user_locations = [
|
|
334
|
-
"/usr/share/metasploit-framework/data/wordlists/unix_users.txt",
|
|
335
|
-
"/usr/share/wordlists/metasploit/unix_users.txt",
|
|
336
|
-
"/usr/share/seclists/Usernames/top-usernames-shortlist.txt",
|
|
337
|
-
]
|
|
338
|
-
for loc in user_locations:
|
|
339
|
-
if os.path.exists(loc):
|
|
340
|
-
username_file = loc
|
|
341
|
-
break
|
|
342
|
-
if not username_file:
|
|
343
|
-
username_file = "/usr/share/metasploit-framework/data/wordlists/unix_users.txt"
|
|
351
|
+
username_file = resolve_wordlist_path("data/wordlists/usernames_common.txt")
|
|
344
352
|
|
|
345
353
|
if not password_file:
|
|
346
|
-
|
|
347
|
-
pass_locations = [
|
|
348
|
-
"/usr/share/wordlists/rockyou.txt", # Uncompressed
|
|
349
|
-
"/usr/share/wordlists/fasttrack.txt",
|
|
350
|
-
"/usr/share/metasploit-framework/data/wordlists/unix_passwords.txt",
|
|
351
|
-
"/usr/share/seclists/Passwords/Common-Credentials/10-million-password-list-top-1000.txt",
|
|
352
|
-
]
|
|
353
|
-
for loc in pass_locations:
|
|
354
|
-
if os.path.exists(loc):
|
|
355
|
-
password_file = loc
|
|
356
|
-
break
|
|
357
|
-
|
|
358
|
-
# If rockyou.txt.gz exists but not rockyou.txt, add a note
|
|
359
|
-
if not password_file and os.path.exists("/usr/share/wordlists/rockyou.txt.gz"):
|
|
360
|
-
script += "# NOTE: rockyou.txt.gz needs to be extracted first:\n"
|
|
361
|
-
script += "# sudo gunzip /usr/share/wordlists/rockyou.txt.gz\n"
|
|
362
|
-
script += "# Using smaller wordlist instead:\n\n"
|
|
363
|
-
password_file = "/usr/share/metasploit-framework/data/wordlists/unix_passwords.txt"
|
|
364
|
-
elif not password_file:
|
|
365
|
-
# Fallback to MSF's smaller wordlist
|
|
366
|
-
password_file = "/usr/share/metasploit-framework/data/wordlists/unix_passwords.txt"
|
|
354
|
+
password_file = resolve_wordlist_path("data/wordlists/passwords_brute.txt")
|
|
367
355
|
|
|
368
356
|
script += "use auxiliary/scanner/ssh/ssh_login\n"
|
|
369
357
|
|
|
370
358
|
# Build RHOSTS list
|
|
371
|
-
rhosts =
|
|
359
|
+
rhosts = " ".join([h.get("ip_address", "") for h in ssh_hosts])
|
|
372
360
|
script += f"set RHOSTS {rhosts}\n"
|
|
373
361
|
script += f"set USER_FILE {username_file}\n"
|
|
374
362
|
script += f"set PASS_FILE {password_file}\n"
|
|
@@ -386,7 +374,9 @@ class MSFResourceGenerator:
|
|
|
386
374
|
|
|
387
375
|
return script
|
|
388
376
|
|
|
389
|
-
def generate_credential_spray_script(
|
|
377
|
+
def generate_credential_spray_script(
|
|
378
|
+
self, credentials: List[Dict], targets: List[Dict]
|
|
379
|
+
) -> str:
|
|
390
380
|
"""
|
|
391
381
|
Generate credential spraying script across multiple services.
|
|
392
382
|
|
|
@@ -403,24 +393,24 @@ class MSFResourceGenerator:
|
|
|
403
393
|
# Group targets by service
|
|
404
394
|
services_map = {}
|
|
405
395
|
for target in targets:
|
|
406
|
-
service = target.get(
|
|
407
|
-
port = target.get(
|
|
408
|
-
ip = target.get(
|
|
396
|
+
service = target.get("service_name", "unknown").lower()
|
|
397
|
+
port = target.get("port", 0)
|
|
398
|
+
ip = target.get("ip_address", "")
|
|
409
399
|
|
|
410
400
|
if service not in services_map:
|
|
411
401
|
services_map[service] = []
|
|
412
402
|
services_map[service].append((ip, port))
|
|
413
403
|
|
|
414
404
|
# SSH credential spray
|
|
415
|
-
if
|
|
405
|
+
if "ssh" in services_map:
|
|
416
406
|
script += "\n# SSH Credential Spray\n"
|
|
417
407
|
script += "use auxiliary/scanner/ssh/ssh_login\n"
|
|
418
|
-
rhosts =
|
|
408
|
+
rhosts = " ".join([ip for ip, _ in services_map["ssh"]])
|
|
419
409
|
script += f"set RHOSTS {rhosts}\n"
|
|
420
410
|
|
|
421
411
|
for cred in credentials:
|
|
422
|
-
username = cred.get(
|
|
423
|
-
password = cred.get(
|
|
412
|
+
username = cred.get("username", "")
|
|
413
|
+
password = cred.get("password", "")
|
|
424
414
|
if username and password:
|
|
425
415
|
script += f"set USERNAME {username}\n"
|
|
426
416
|
script += f"set PASSWORD {password}\n"
|
|
@@ -428,22 +418,22 @@ class MSFResourceGenerator:
|
|
|
428
418
|
script += "\n"
|
|
429
419
|
|
|
430
420
|
# SMB credential spray
|
|
431
|
-
if any(s in services_map for s in [
|
|
421
|
+
if any(s in services_map for s in ["smb", "microsoft-ds", "netbios-ssn"]):
|
|
432
422
|
script += "\n# SMB Credential Spray\n"
|
|
433
423
|
script += "use auxiliary/scanner/smb/smb_login\n"
|
|
434
424
|
|
|
435
425
|
# Get all SMB-related hosts
|
|
436
426
|
smb_hosts = []
|
|
437
|
-
for key in [
|
|
427
|
+
for key in ["smb", "microsoft-ds", "netbios-ssn"]:
|
|
438
428
|
if key in services_map:
|
|
439
429
|
smb_hosts.extend([ip for ip, _ in services_map[key]])
|
|
440
430
|
|
|
441
|
-
rhosts =
|
|
431
|
+
rhosts = " ".join(set(smb_hosts))
|
|
442
432
|
script += f"set RHOSTS {rhosts}\n"
|
|
443
433
|
|
|
444
434
|
for cred in credentials:
|
|
445
|
-
username = cred.get(
|
|
446
|
-
password = cred.get(
|
|
435
|
+
username = cred.get("username", "")
|
|
436
|
+
password = cred.get("password", "")
|
|
447
437
|
if username and password:
|
|
448
438
|
script += f"set SMBUser {username}\n"
|
|
449
439
|
script += f"set SMBPass {password}\n"
|
|
@@ -468,9 +458,9 @@ class MSFResourceGenerator:
|
|
|
468
458
|
script += "# Some exploits may crash services or cause system instability\n\n"
|
|
469
459
|
|
|
470
460
|
for vuln in vulnerabilities:
|
|
471
|
-
title = vuln.get(
|
|
472
|
-
host_ip = vuln.get(
|
|
473
|
-
port = vuln.get(
|
|
461
|
+
title = vuln.get("title", "Unknown")
|
|
462
|
+
host_ip = vuln.get("ip_address", "N/A")
|
|
463
|
+
port = vuln.get("port", 0)
|
|
474
464
|
|
|
475
465
|
script += f"\n# {title} on {host_ip}:{port}\n"
|
|
476
466
|
|
|
@@ -507,9 +497,9 @@ class MSFResourceGenerator:
|
|
|
507
497
|
"""Check if module requires payload configuration."""
|
|
508
498
|
# Modules that don't need payload configuration
|
|
509
499
|
no_payload_modules = [
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
500
|
+
"vsftpd_234_backdoor", # Has built-in backdoor
|
|
501
|
+
"usermap_script", # Uses command execution
|
|
502
|
+
"distcc_exec", # Direct command execution
|
|
513
503
|
]
|
|
514
504
|
|
|
515
505
|
for no_payload in no_payload_modules:
|
|
@@ -524,31 +514,31 @@ class MSFResourceGenerator:
|
|
|
524
514
|
module_lower = module.lower()
|
|
525
515
|
|
|
526
516
|
# Windows exploits
|
|
527
|
-
if
|
|
528
|
-
return
|
|
517
|
+
if "windows" in module_lower:
|
|
518
|
+
return "windows/shell_reverse_tcp"
|
|
529
519
|
|
|
530
|
-
# Linux exploits
|
|
531
|
-
if
|
|
532
|
-
return
|
|
520
|
+
# Linux/Unix exploits
|
|
521
|
+
if "linux" in module_lower or "unix" in module_lower:
|
|
522
|
+
return "cmd/unix/reverse_netcat"
|
|
533
523
|
|
|
534
|
-
# Multi-platform
|
|
535
|
-
return
|
|
524
|
+
# Multi-platform - default to unix shell (works on most targets)
|
|
525
|
+
return "cmd/unix/reverse_netcat"
|
|
536
526
|
|
|
537
527
|
def _get_exploit_module(self, vuln: Dict) -> Optional[str]:
|
|
538
528
|
"""Map vulnerability to MSF exploit module."""
|
|
539
|
-
title = vuln.get(
|
|
540
|
-
description = vuln.get(
|
|
529
|
+
title = vuln.get("title", "").lower()
|
|
530
|
+
description = vuln.get("description", "").lower()
|
|
541
531
|
|
|
542
532
|
# Simple keyword matching (can be expanded)
|
|
543
533
|
module_map = {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
534
|
+
"eternalblue": "exploit/windows/smb/ms17_010_eternalblue",
|
|
535
|
+
"ms17-010": "exploit/windows/smb/ms17_010_eternalblue",
|
|
536
|
+
"bluekeep": "exploit/windows/rdp/cve_2019_0708_bluekeep_rce",
|
|
537
|
+
"cve-2019-0708": "exploit/windows/rdp/cve_2019_0708_bluekeep_rce",
|
|
538
|
+
"shellshock": "exploit/multi/http/apache_mod_cgi_bash_env_exec",
|
|
539
|
+
"vsftpd 2.3.4": "exploit/unix/ftp/vsftpd_234_backdoor",
|
|
540
|
+
"distcc": "exploit/unix/misc/distcc_exec",
|
|
541
|
+
"samba 3.0.20": "exploit/multi/samba/usermap_script",
|
|
552
542
|
}
|
|
553
543
|
|
|
554
544
|
for keyword, module in module_map.items():
|
|
@@ -558,31 +548,33 @@ class MSFResourceGenerator:
|
|
|
558
548
|
return None
|
|
559
549
|
|
|
560
550
|
def generate_web_exploitation_script(
|
|
561
|
-
self,
|
|
562
|
-
web_services: List[Dict],
|
|
563
|
-
vulnerabilities: List[Dict] = None
|
|
551
|
+
self, web_services: List[Dict], vulnerabilities: List[Dict] = None
|
|
564
552
|
) -> str:
|
|
565
553
|
"""
|
|
566
554
|
Generate script for web application exploitation.
|
|
567
|
-
|
|
555
|
+
|
|
568
556
|
Args:
|
|
569
557
|
web_services: List of web services (HTTP/HTTPS)
|
|
570
558
|
vulnerabilities: Optional list of web vulnerabilities
|
|
571
|
-
|
|
559
|
+
|
|
572
560
|
Returns:
|
|
573
561
|
Resource script content
|
|
574
562
|
"""
|
|
575
563
|
script = self.generate_header()
|
|
576
564
|
script += "# Web Application Exploitation\n"
|
|
577
565
|
script += "# Tests for common web vulnerabilities\n\n"
|
|
578
|
-
|
|
566
|
+
|
|
579
567
|
for service in web_services:
|
|
580
|
-
host_ip = service.get(
|
|
581
|
-
port = service.get(
|
|
582
|
-
proto =
|
|
583
|
-
|
|
568
|
+
host_ip = service.get("ip_address", "")
|
|
569
|
+
port = service.get("port", 80)
|
|
570
|
+
proto = (
|
|
571
|
+
"https"
|
|
572
|
+
if service.get("service_name", "").lower() == "https"
|
|
573
|
+
else "http"
|
|
574
|
+
)
|
|
575
|
+
|
|
584
576
|
script += f"\n# Target: {proto}://{host_ip}:{port}\n"
|
|
585
|
-
|
|
577
|
+
|
|
586
578
|
# Shellshock test
|
|
587
579
|
script += "\n## Test for Shellshock (CVE-2014-6271)\n"
|
|
588
580
|
script += "use auxiliary/scanner/http/apache_mod_cgi_bash_env_exec\n"
|
|
@@ -590,197 +582,193 @@ class MSFResourceGenerator:
|
|
|
590
582
|
script += f"set RPORT {port}\n"
|
|
591
583
|
script += f"set SSL {'true' if proto == 'https' else 'false'}\n"
|
|
592
584
|
script += "run\n\n"
|
|
593
|
-
|
|
585
|
+
|
|
594
586
|
# Directory traversal
|
|
595
587
|
script += "## Directory Traversal Scanner\n"
|
|
596
588
|
script += "use auxiliary/scanner/http/dir_traversal\n"
|
|
597
589
|
script += f"set RHOSTS {host_ip}\n"
|
|
598
590
|
script += f"set RPORT {port}\n"
|
|
599
591
|
script += "run\n\n"
|
|
600
|
-
|
|
592
|
+
|
|
601
593
|
# File upload scanner
|
|
602
594
|
script += "## File Upload Scanner\n"
|
|
603
595
|
script += "use auxiliary/scanner/http/http_put\n"
|
|
604
596
|
script += f"set RHOSTS {host_ip}\n"
|
|
605
597
|
script += f"set RPORT {port}\n"
|
|
606
598
|
script += "run\n\n"
|
|
607
|
-
|
|
599
|
+
|
|
608
600
|
return script
|
|
609
601
|
|
|
610
602
|
def generate_post_exploitation_script(
|
|
611
|
-
self,
|
|
612
|
-
compromised_hosts: List[Dict],
|
|
613
|
-
objectives: List[str] = None
|
|
603
|
+
self, compromised_hosts: List[Dict], objectives: List[str] = None
|
|
614
604
|
) -> str:
|
|
615
605
|
"""
|
|
616
606
|
Generate post-exploitation script.
|
|
617
|
-
|
|
607
|
+
|
|
618
608
|
Args:
|
|
619
609
|
compromised_hosts: List of compromised hosts with session IDs
|
|
620
610
|
objectives: List of objectives ['escalate', 'pivot', 'dump_creds', 'persist', 'exfil']
|
|
621
|
-
|
|
611
|
+
|
|
622
612
|
Returns:
|
|
623
613
|
Resource script content
|
|
624
614
|
"""
|
|
625
615
|
if objectives is None:
|
|
626
|
-
objectives = [
|
|
627
|
-
|
|
616
|
+
objectives = ["escalate", "dump_creds"]
|
|
617
|
+
|
|
628
618
|
script = self.generate_header()
|
|
629
619
|
script += "# Post-Exploitation Activities\n"
|
|
630
620
|
script += "# WARNING: Only run on authorized systems!\n\n"
|
|
631
|
-
|
|
621
|
+
|
|
632
622
|
for host in compromised_hosts:
|
|
633
|
-
session_id = host.get(
|
|
634
|
-
host_ip = host.get(
|
|
635
|
-
os_type = host.get(
|
|
636
|
-
|
|
623
|
+
session_id = host.get("session_id", 1)
|
|
624
|
+
host_ip = host.get("ip_address", "N/A")
|
|
625
|
+
os_type = host.get("os", "unknown").lower()
|
|
626
|
+
|
|
637
627
|
script += f"\n# Host: {host_ip} (Session {session_id})\n"
|
|
638
|
-
|
|
639
|
-
if
|
|
628
|
+
|
|
629
|
+
if "escalate" in objectives:
|
|
640
630
|
script += "\n## Privilege Escalation\n"
|
|
641
|
-
if
|
|
631
|
+
if "windows" in os_type:
|
|
642
632
|
script += f"use post/multi/recon/local_exploit_suggester\n"
|
|
643
633
|
script += f"set SESSION {session_id}\n"
|
|
644
634
|
script += "run\n\n"
|
|
645
635
|
script += f"use post/windows/gather/enum_patches\n"
|
|
646
636
|
script += f"set SESSION {session_id}\n"
|
|
647
637
|
script += "run\n\n"
|
|
648
|
-
elif
|
|
638
|
+
elif "linux" in os_type:
|
|
649
639
|
script += f"use post/multi/recon/local_exploit_suggester\n"
|
|
650
640
|
script += f"set SESSION {session_id}\n"
|
|
651
641
|
script += "run\n\n"
|
|
652
|
-
|
|
653
|
-
if
|
|
642
|
+
|
|
643
|
+
if "dump_creds" in objectives:
|
|
654
644
|
script += "\n## Credential Dumping\n"
|
|
655
|
-
if
|
|
645
|
+
if "windows" in os_type:
|
|
656
646
|
script += f"use post/windows/gather/hashdump\n"
|
|
657
647
|
script += f"set SESSION {session_id}\n"
|
|
658
648
|
script += "run\n\n"
|
|
659
|
-
script +=
|
|
649
|
+
script += (
|
|
650
|
+
f"use post/windows/gather/credentials/credential_collector\n"
|
|
651
|
+
)
|
|
660
652
|
script += f"set SESSION {session_id}\n"
|
|
661
653
|
script += "run\n\n"
|
|
662
|
-
elif
|
|
654
|
+
elif "linux" in os_type:
|
|
663
655
|
script += f"use post/linux/gather/hashdump\n"
|
|
664
656
|
script += f"set SESSION {session_id}\n"
|
|
665
657
|
script += "run\n\n"
|
|
666
|
-
|
|
667
|
-
if
|
|
658
|
+
|
|
659
|
+
if "persist" in objectives:
|
|
668
660
|
script += "\n## Persistence\n"
|
|
669
|
-
if
|
|
661
|
+
if "windows" in os_type:
|
|
670
662
|
script += f"use exploit/windows/local/persistence_service\n"
|
|
671
663
|
script += f"set SESSION {session_id}\n"
|
|
672
664
|
script += "# exploit # Uncomment to create persistence\n\n"
|
|
673
|
-
elif
|
|
665
|
+
elif "linux" in os_type:
|
|
674
666
|
script += f"use post/linux/manage/sshkey_persistence\n"
|
|
675
667
|
script += f"set SESSION {session_id}\n"
|
|
676
668
|
script += "# run # Uncomment to create SSH key persistence\n\n"
|
|
677
|
-
|
|
678
|
-
if
|
|
669
|
+
|
|
670
|
+
if "pivot" in objectives:
|
|
679
671
|
script += "\n## Network Pivoting\n"
|
|
680
672
|
script += f"use post/multi/manage/autoroute\n"
|
|
681
673
|
script += f"set SESSION {session_id}\n"
|
|
682
674
|
script += "run\n\n"
|
|
683
675
|
script += "# Now you can scan internal network via this session\n\n"
|
|
684
|
-
|
|
676
|
+
|
|
685
677
|
return script
|
|
686
678
|
|
|
687
679
|
def generate_enumeration_script(
|
|
688
|
-
self,
|
|
689
|
-
services: List[Dict],
|
|
690
|
-
enumeration_depth: str = 'standard'
|
|
680
|
+
self, services: List[Dict], enumeration_depth: str = "standard"
|
|
691
681
|
) -> str:
|
|
692
682
|
"""
|
|
693
683
|
Generate comprehensive enumeration script.
|
|
694
|
-
|
|
684
|
+
|
|
695
685
|
Args:
|
|
696
686
|
services: List of services to enumerate
|
|
697
687
|
enumeration_depth: 'light', 'standard', or 'aggressive'
|
|
698
|
-
|
|
688
|
+
|
|
699
689
|
Returns:
|
|
700
690
|
Resource script content
|
|
701
691
|
"""
|
|
702
692
|
script = self.generate_header()
|
|
703
693
|
script += f"# Service Enumeration - {enumeration_depth.upper()} mode\n\n"
|
|
704
|
-
|
|
694
|
+
|
|
705
695
|
# Group by service type
|
|
706
696
|
service_groups = {}
|
|
707
697
|
for service in services:
|
|
708
|
-
service_name = service.get(
|
|
698
|
+
service_name = service.get("service_name", "unknown").lower()
|
|
709
699
|
if service_name not in service_groups:
|
|
710
700
|
service_groups[service_name] = []
|
|
711
701
|
service_groups[service_name].append(service)
|
|
712
|
-
|
|
702
|
+
|
|
713
703
|
for service_name, service_list in service_groups.items():
|
|
714
704
|
script += f"\n# {service_name.upper()} Enumeration\n"
|
|
715
|
-
|
|
716
|
-
rhosts =
|
|
717
|
-
port = service_list[0].get(
|
|
718
|
-
|
|
719
|
-
if service_name ==
|
|
705
|
+
|
|
706
|
+
rhosts = " ".join([s.get("ip_address", "") for s in service_list])
|
|
707
|
+
port = service_list[0].get("port", 0)
|
|
708
|
+
|
|
709
|
+
if service_name == "smb":
|
|
720
710
|
script += "use auxiliary/scanner/smb/smb_version\n"
|
|
721
711
|
script += f"set RHOSTS {rhosts}\n"
|
|
722
712
|
script += "run\n\n"
|
|
723
|
-
|
|
713
|
+
|
|
724
714
|
script += "use auxiliary/scanner/smb/smb_enumshares\n"
|
|
725
715
|
script += f"set RHOSTS {rhosts}\n"
|
|
726
716
|
script += "run\n\n"
|
|
727
|
-
|
|
728
|
-
if enumeration_depth in [
|
|
717
|
+
|
|
718
|
+
if enumeration_depth in ["standard", "aggressive"]:
|
|
729
719
|
script += "use auxiliary/scanner/smb/smb_enumusers\n"
|
|
730
720
|
script += f"set RHOSTS {rhosts}\n"
|
|
731
721
|
script += "run\n\n"
|
|
732
|
-
|
|
733
|
-
elif service_name ==
|
|
722
|
+
|
|
723
|
+
elif service_name == "ssh":
|
|
734
724
|
script += "use auxiliary/scanner/ssh/ssh_version\n"
|
|
735
725
|
script += f"set RHOSTS {rhosts}\n"
|
|
736
726
|
script += "run\n\n"
|
|
737
|
-
|
|
738
|
-
if enumeration_depth ==
|
|
727
|
+
|
|
728
|
+
if enumeration_depth == "aggressive":
|
|
739
729
|
script += "use auxiliary/scanner/ssh/ssh_enumusers\n"
|
|
740
730
|
script += f"set RHOSTS {rhosts}\n"
|
|
741
731
|
script += "run\n\n"
|
|
742
|
-
|
|
743
|
-
elif service_name in [
|
|
732
|
+
|
|
733
|
+
elif service_name in ["http", "https"]:
|
|
744
734
|
script += "use auxiliary/scanner/http/http_version\n"
|
|
745
735
|
script += f"set RHOSTS {rhosts}\n"
|
|
746
736
|
script += f"set RPORT {port}\n"
|
|
747
737
|
script += f"set SSL {'true' if service_name == 'https' else 'false'}\n"
|
|
748
738
|
script += "run\n\n"
|
|
749
|
-
|
|
739
|
+
|
|
750
740
|
script += "use auxiliary/scanner/http/robots_txt\n"
|
|
751
741
|
script += f"set RHOSTS {rhosts}\n"
|
|
752
742
|
script += f"set RPORT {port}\n"
|
|
753
743
|
script += "run\n\n"
|
|
754
|
-
|
|
744
|
+
|
|
755
745
|
return script
|
|
756
746
|
|
|
757
747
|
def generate_database_attack_script(
|
|
758
|
-
self,
|
|
759
|
-
db_services: List[Dict],
|
|
760
|
-
attack_type: str = 'auth'
|
|
748
|
+
self, db_services: List[Dict], attack_type: str = "auth"
|
|
761
749
|
) -> str:
|
|
762
750
|
"""
|
|
763
751
|
Generate database-specific attack script.
|
|
764
|
-
|
|
752
|
+
|
|
765
753
|
Args:
|
|
766
754
|
db_services: List of database services
|
|
767
755
|
attack_type: 'auth', 'extract', 'exploit'
|
|
768
|
-
|
|
756
|
+
|
|
769
757
|
Returns:
|
|
770
758
|
Resource script content
|
|
771
759
|
"""
|
|
772
760
|
script = self.generate_header()
|
|
773
761
|
script += f"# Database Attack - {attack_type.upper()} mode\n\n"
|
|
774
|
-
|
|
762
|
+
|
|
775
763
|
for db in db_services:
|
|
776
|
-
host_ip = db.get(
|
|
777
|
-
port = db.get(
|
|
778
|
-
service_name = db.get(
|
|
779
|
-
|
|
764
|
+
host_ip = db.get("ip_address", "")
|
|
765
|
+
port = db.get("port", 0)
|
|
766
|
+
service_name = db.get("service_name", "").lower()
|
|
767
|
+
|
|
780
768
|
script += f"\n# Target: {service_name} on {host_ip}:{port}\n"
|
|
781
|
-
|
|
782
|
-
if service_name in [
|
|
783
|
-
if attack_type ==
|
|
769
|
+
|
|
770
|
+
if service_name in ["mysql", "mariadb"]:
|
|
771
|
+
if attack_type == "auth":
|
|
784
772
|
script += "use auxiliary/scanner/mysql/mysql_login\n"
|
|
785
773
|
script += f"set RHOSTS {host_ip}\n"
|
|
786
774
|
script += f"set RPORT {port}\n"
|
|
@@ -788,34 +776,34 @@ class MSFResourceGenerator:
|
|
|
788
776
|
script += "# set USER_FILE /path/to/users.txt\n"
|
|
789
777
|
script += "# set PASS_FILE /path/to/passwords.txt\n"
|
|
790
778
|
script += "run\n\n"
|
|
791
|
-
elif attack_type ==
|
|
779
|
+
elif attack_type == "extract":
|
|
792
780
|
script += "use auxiliary/admin/mysql/mysql_enum\n"
|
|
793
781
|
script += f"set RHOSTS {host_ip}\n"
|
|
794
782
|
script += f"set RPORT {port}\n"
|
|
795
783
|
script += "# set USERNAME root\n"
|
|
796
784
|
script += "# set PASSWORD password\n"
|
|
797
785
|
script += "run\n\n"
|
|
798
|
-
|
|
799
|
-
elif service_name in [
|
|
800
|
-
if attack_type ==
|
|
786
|
+
|
|
787
|
+
elif service_name in ["postgresql", "postgres"]:
|
|
788
|
+
if attack_type == "auth":
|
|
801
789
|
script += "use auxiliary/scanner/postgres/postgres_login\n"
|
|
802
790
|
script += f"set RHOSTS {host_ip}\n"
|
|
803
791
|
script += f"set RPORT {port}\n"
|
|
804
792
|
script += "run\n\n"
|
|
805
|
-
|
|
806
|
-
elif service_name in [
|
|
807
|
-
if attack_type ==
|
|
793
|
+
|
|
794
|
+
elif service_name in ["mssql", "ms-sql-s"]:
|
|
795
|
+
if attack_type == "auth":
|
|
808
796
|
script += "use auxiliary/scanner/mssql/mssql_login\n"
|
|
809
797
|
script += f"set RHOSTS {host_ip}\n"
|
|
810
798
|
script += f"set RPORT {port}\n"
|
|
811
799
|
script += "run\n\n"
|
|
812
|
-
elif attack_type ==
|
|
800
|
+
elif attack_type == "extract":
|
|
813
801
|
script += "use auxiliary/admin/mssql/mssql_enum\n"
|
|
814
802
|
script += f"set RHOSTS {host_ip}\n"
|
|
815
803
|
script += "# set USERNAME sa\n"
|
|
816
804
|
script += "# set PASSWORD password\n"
|
|
817
805
|
script += "run\n\n"
|
|
818
|
-
|
|
806
|
+
|
|
819
807
|
return script
|
|
820
808
|
|
|
821
809
|
def save_script(self, content: str, filename: str) -> str:
|
|
@@ -831,7 +819,7 @@ class MSFResourceGenerator:
|
|
|
831
819
|
"""
|
|
832
820
|
filepath = os.path.join(self.output_dir, filename)
|
|
833
821
|
|
|
834
|
-
with open(filepath,
|
|
822
|
+
with open(filepath, "w") as f:
|
|
835
823
|
f.write(content)
|
|
836
824
|
|
|
837
825
|
# Make executable
|
|
@@ -845,358 +833,360 @@ class MSFModuleSelector:
|
|
|
845
833
|
|
|
846
834
|
# Module database with metadata
|
|
847
835
|
MODULES = {
|
|
848
|
-
|
|
849
|
-
|
|
836
|
+
"ssh": {
|
|
837
|
+
"scanner": [
|
|
850
838
|
{
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
839
|
+
"path": "auxiliary/scanner/ssh/ssh_version",
|
|
840
|
+
"name": "SSH Version Scanner",
|
|
841
|
+
"description": "Detect SSH server version",
|
|
842
|
+
"risk": "safe",
|
|
855
843
|
},
|
|
856
844
|
{
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
845
|
+
"path": "auxiliary/scanner/ssh/ssh_enumusers",
|
|
846
|
+
"name": "SSH User Enumeration",
|
|
847
|
+
"description": "Enumerate valid SSH usernames",
|
|
848
|
+
"risk": "safe",
|
|
861
849
|
},
|
|
862
850
|
{
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
851
|
+
"path": "auxiliary/scanner/ssh/ssh_login",
|
|
852
|
+
"name": "SSH Login Scanner",
|
|
853
|
+
"description": "Brute force SSH authentication",
|
|
854
|
+
"risk": "noisy",
|
|
867
855
|
},
|
|
868
856
|
],
|
|
869
|
-
|
|
857
|
+
"exploit": [],
|
|
870
858
|
},
|
|
871
|
-
|
|
872
|
-
|
|
859
|
+
"smb": {
|
|
860
|
+
"scanner": [
|
|
873
861
|
{
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
862
|
+
"path": "auxiliary/scanner/smb/smb_version",
|
|
863
|
+
"name": "SMB Version Detection",
|
|
864
|
+
"description": "Detect SMB version and OS",
|
|
865
|
+
"risk": "safe",
|
|
878
866
|
},
|
|
879
867
|
{
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
868
|
+
"path": "auxiliary/scanner/smb/smb_enumshares",
|
|
869
|
+
"name": "SMB Share Enumeration",
|
|
870
|
+
"description": "List available SMB shares",
|
|
871
|
+
"risk": "safe",
|
|
884
872
|
},
|
|
885
873
|
{
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
874
|
+
"path": "auxiliary/scanner/smb/smb_enumusers",
|
|
875
|
+
"name": "SMB User Enumeration",
|
|
876
|
+
"description": "Enumerate SMB users via RID cycling",
|
|
877
|
+
"risk": "safe",
|
|
890
878
|
},
|
|
891
879
|
{
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
880
|
+
"path": "auxiliary/scanner/smb/smb_login",
|
|
881
|
+
"name": "SMB Login Scanner",
|
|
882
|
+
"description": "Brute force SMB authentication",
|
|
883
|
+
"risk": "noisy",
|
|
896
884
|
},
|
|
897
885
|
],
|
|
898
|
-
|
|
886
|
+
"exploit": [
|
|
899
887
|
{
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
888
|
+
"path": "exploit/windows/smb/ms17_010_eternalblue",
|
|
889
|
+
"name": "EternalBlue SMB RCE",
|
|
890
|
+
"description": "Exploit MS17-010 (EternalBlue)",
|
|
891
|
+
"risk": "dangerous",
|
|
892
|
+
"cve": ["CVE-2017-0143", "CVE-2017-0144", "CVE-2017-0145"],
|
|
905
893
|
},
|
|
906
894
|
{
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
895
|
+
"path": "exploit/multi/samba/usermap_script",
|
|
896
|
+
"name": "Samba Usermap Script",
|
|
897
|
+
"description": "Samba 3.0.20-3.0.25 username map script command execution",
|
|
898
|
+
"risk": "dangerous",
|
|
899
|
+
"cve": ["CVE-2007-2447"],
|
|
912
900
|
},
|
|
913
901
|
{
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
902
|
+
"path": "exploit/windows/smb/psexec",
|
|
903
|
+
"name": "PsExec",
|
|
904
|
+
"description": "Execute commands via SMB (requires creds)",
|
|
905
|
+
"risk": "moderate",
|
|
906
|
+
"requires": "credentials",
|
|
919
907
|
},
|
|
920
|
-
]
|
|
908
|
+
],
|
|
921
909
|
},
|
|
922
|
-
|
|
923
|
-
|
|
910
|
+
"http": {
|
|
911
|
+
"scanner": [
|
|
924
912
|
{
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
913
|
+
"path": "auxiliary/scanner/http/dir_scanner",
|
|
914
|
+
"name": "Directory Scanner",
|
|
915
|
+
"description": "Brute force web directories",
|
|
916
|
+
"risk": "noisy",
|
|
929
917
|
},
|
|
930
918
|
{
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
919
|
+
"path": "auxiliary/scanner/http/http_version",
|
|
920
|
+
"name": "HTTP Version Detection",
|
|
921
|
+
"description": "Detect web server version",
|
|
922
|
+
"risk": "safe",
|
|
935
923
|
},
|
|
936
924
|
{
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
925
|
+
"path": "auxiliary/scanner/http/robots_txt",
|
|
926
|
+
"name": "Robots.txt Scanner",
|
|
927
|
+
"description": "Check robots.txt for paths",
|
|
928
|
+
"risk": "safe",
|
|
941
929
|
},
|
|
942
930
|
],
|
|
943
|
-
|
|
931
|
+
"exploit": [],
|
|
944
932
|
},
|
|
945
|
-
|
|
946
|
-
|
|
933
|
+
"https": {
|
|
934
|
+
"scanner": [
|
|
947
935
|
{
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
936
|
+
"path": "auxiliary/scanner/http/dir_scanner",
|
|
937
|
+
"name": "Directory Scanner",
|
|
938
|
+
"description": "Brute force web directories",
|
|
939
|
+
"risk": "noisy",
|
|
952
940
|
},
|
|
953
941
|
{
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
942
|
+
"path": "auxiliary/scanner/http/http_version",
|
|
943
|
+
"name": "HTTP Version Detection",
|
|
944
|
+
"description": "Detect web server version",
|
|
945
|
+
"risk": "safe",
|
|
958
946
|
},
|
|
959
947
|
{
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
948
|
+
"path": "auxiliary/scanner/ssl/ssl_version",
|
|
949
|
+
"name": "SSL/TLS Version Scanner",
|
|
950
|
+
"description": "Detect SSL/TLS version and ciphers",
|
|
951
|
+
"risk": "safe",
|
|
964
952
|
},
|
|
965
953
|
],
|
|
966
|
-
|
|
954
|
+
"exploit": [],
|
|
967
955
|
},
|
|
968
|
-
|
|
969
|
-
|
|
956
|
+
"ftp": {
|
|
957
|
+
"scanner": [
|
|
970
958
|
{
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
959
|
+
"path": "auxiliary/scanner/ftp/ftp_version",
|
|
960
|
+
"name": "FTP Version Scanner",
|
|
961
|
+
"description": "Detect FTP server version",
|
|
962
|
+
"risk": "safe",
|
|
975
963
|
},
|
|
976
964
|
{
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
965
|
+
"path": "auxiliary/scanner/ftp/anonymous",
|
|
966
|
+
"name": "FTP Anonymous Login",
|
|
967
|
+
"description": "Check for anonymous FTP access",
|
|
968
|
+
"risk": "safe",
|
|
981
969
|
},
|
|
982
970
|
{
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
971
|
+
"path": "auxiliary/scanner/ftp/ftp_login",
|
|
972
|
+
"name": "FTP Login Scanner",
|
|
973
|
+
"description": "Brute force FTP authentication",
|
|
974
|
+
"risk": "noisy",
|
|
987
975
|
},
|
|
988
976
|
],
|
|
989
|
-
|
|
977
|
+
"exploit": [
|
|
990
978
|
{
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
979
|
+
"path": "exploit/unix/ftp/vsftpd_234_backdoor",
|
|
980
|
+
"name": "VSFTPD 2.3.4 Backdoor",
|
|
981
|
+
"description": "Exploit VSFTPD 2.3.4 backdoor",
|
|
982
|
+
"risk": "dangerous",
|
|
983
|
+
"cve": ["CVE-2011-2523"],
|
|
996
984
|
},
|
|
997
985
|
{
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
986
|
+
"path": "exploit/linux/ftp/proftp_sreplace",
|
|
987
|
+
"name": "ProFTPD sreplace Buffer Overflow",
|
|
988
|
+
"description": "Exploit ProFTPD sreplace vulnerability",
|
|
989
|
+
"risk": "dangerous",
|
|
1002
990
|
},
|
|
1003
|
-
]
|
|
991
|
+
],
|
|
1004
992
|
},
|
|
1005
|
-
|
|
1006
|
-
|
|
993
|
+
"mysql": {
|
|
994
|
+
"scanner": [
|
|
1007
995
|
{
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
996
|
+
"path": "auxiliary/scanner/mysql/mysql_version",
|
|
997
|
+
"name": "MySQL Version Scanner",
|
|
998
|
+
"description": "Detect MySQL server version",
|
|
999
|
+
"risk": "safe",
|
|
1012
1000
|
},
|
|
1013
1001
|
{
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1002
|
+
"path": "auxiliary/scanner/mysql/mysql_login",
|
|
1003
|
+
"name": "MySQL Login Scanner",
|
|
1004
|
+
"description": "Brute force MySQL authentication",
|
|
1005
|
+
"risk": "noisy",
|
|
1018
1006
|
},
|
|
1019
1007
|
{
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1008
|
+
"path": "auxiliary/admin/mysql/mysql_enum",
|
|
1009
|
+
"name": "MySQL Enumeration",
|
|
1010
|
+
"description": "Enumerate MySQL databases and users",
|
|
1011
|
+
"risk": "safe",
|
|
1024
1012
|
},
|
|
1025
1013
|
{
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1014
|
+
"path": "auxiliary/admin/mysql/mysql_sql",
|
|
1015
|
+
"name": "MySQL SQL Query",
|
|
1016
|
+
"description": "Execute SQL queries on MySQL (requires creds)",
|
|
1017
|
+
"risk": "moderate",
|
|
1018
|
+
"requires": "credentials",
|
|
1031
1019
|
},
|
|
1032
1020
|
],
|
|
1033
|
-
|
|
1021
|
+
"exploit": [],
|
|
1034
1022
|
},
|
|
1035
|
-
|
|
1036
|
-
|
|
1023
|
+
"postgresql": {
|
|
1024
|
+
"scanner": [
|
|
1037
1025
|
{
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1026
|
+
"path": "auxiliary/scanner/postgres/postgres_version",
|
|
1027
|
+
"name": "PostgreSQL Version Scanner",
|
|
1028
|
+
"description": "Detect PostgreSQL version",
|
|
1029
|
+
"risk": "safe",
|
|
1042
1030
|
},
|
|
1043
1031
|
{
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1032
|
+
"path": "auxiliary/scanner/postgres/postgres_login",
|
|
1033
|
+
"name": "PostgreSQL Login Scanner",
|
|
1034
|
+
"description": "Brute force PostgreSQL authentication",
|
|
1035
|
+
"risk": "noisy",
|
|
1048
1036
|
},
|
|
1049
1037
|
],
|
|
1050
|
-
|
|
1038
|
+
"exploit": [],
|
|
1051
1039
|
},
|
|
1052
|
-
|
|
1053
|
-
|
|
1040
|
+
"mssql": {
|
|
1041
|
+
"scanner": [
|
|
1054
1042
|
{
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1043
|
+
"path": "auxiliary/scanner/mssql/mssql_ping",
|
|
1044
|
+
"name": "MSSQL Ping Scanner",
|
|
1045
|
+
"description": "Discover MSSQL instances",
|
|
1046
|
+
"risk": "safe",
|
|
1059
1047
|
},
|
|
1060
1048
|
{
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1049
|
+
"path": "auxiliary/scanner/mssql/mssql_login",
|
|
1050
|
+
"name": "MSSQL Login Scanner",
|
|
1051
|
+
"description": "Brute force MSSQL authentication",
|
|
1052
|
+
"risk": "noisy",
|
|
1065
1053
|
},
|
|
1066
1054
|
{
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1055
|
+
"path": "auxiliary/admin/mssql/mssql_enum",
|
|
1056
|
+
"name": "MSSQL Enumeration",
|
|
1057
|
+
"description": "Enumerate MSSQL databases and configuration",
|
|
1058
|
+
"risk": "safe",
|
|
1071
1059
|
},
|
|
1072
1060
|
],
|
|
1073
|
-
|
|
1061
|
+
"exploit": [],
|
|
1074
1062
|
},
|
|
1075
|
-
|
|
1076
|
-
|
|
1063
|
+
"rdp": {
|
|
1064
|
+
"scanner": [
|
|
1077
1065
|
{
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1066
|
+
"path": "auxiliary/scanner/rdp/rdp_scanner",
|
|
1067
|
+
"name": "RDP Scanner",
|
|
1068
|
+
"description": "Detect RDP service",
|
|
1069
|
+
"risk": "safe",
|
|
1082
1070
|
},
|
|
1083
1071
|
{
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1072
|
+
"path": "auxiliary/scanner/rdp/cve_2019_0708_bluekeep",
|
|
1073
|
+
"name": "BlueKeep Scanner",
|
|
1074
|
+
"description": "Check for CVE-2019-0708 vulnerability",
|
|
1075
|
+
"risk": "safe",
|
|
1088
1076
|
},
|
|
1089
1077
|
],
|
|
1090
|
-
|
|
1078
|
+
"exploit": [
|
|
1091
1079
|
{
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1080
|
+
"path": "exploit/windows/rdp/cve_2019_0708_bluekeep_rce",
|
|
1081
|
+
"name": "BlueKeep RDP RCE",
|
|
1082
|
+
"description": "Exploit CVE-2019-0708 (BlueKeep)",
|
|
1083
|
+
"risk": "dangerous",
|
|
1084
|
+
"cve": ["CVE-2019-0708"],
|
|
1097
1085
|
},
|
|
1098
|
-
]
|
|
1086
|
+
],
|
|
1099
1087
|
},
|
|
1100
|
-
|
|
1101
|
-
|
|
1088
|
+
"telnet": {
|
|
1089
|
+
"scanner": [
|
|
1102
1090
|
{
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1091
|
+
"path": "auxiliary/scanner/telnet/telnet_version",
|
|
1092
|
+
"name": "Telnet Version Scanner",
|
|
1093
|
+
"description": "Detect Telnet service version",
|
|
1094
|
+
"risk": "safe",
|
|
1107
1095
|
},
|
|
1108
1096
|
{
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1097
|
+
"path": "auxiliary/scanner/telnet/telnet_login",
|
|
1098
|
+
"name": "Telnet Login Scanner",
|
|
1099
|
+
"description": "Brute force Telnet authentication",
|
|
1100
|
+
"risk": "noisy",
|
|
1113
1101
|
},
|
|
1114
1102
|
],
|
|
1115
|
-
|
|
1103
|
+
"exploit": [],
|
|
1116
1104
|
},
|
|
1117
|
-
|
|
1118
|
-
|
|
1105
|
+
"smtp": {
|
|
1106
|
+
"scanner": [
|
|
1119
1107
|
{
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1108
|
+
"path": "auxiliary/scanner/smtp/smtp_version",
|
|
1109
|
+
"name": "SMTP Version Scanner",
|
|
1110
|
+
"description": "Detect SMTP server version",
|
|
1111
|
+
"risk": "safe",
|
|
1124
1112
|
},
|
|
1125
1113
|
{
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1114
|
+
"path": "auxiliary/scanner/smtp/smtp_enum",
|
|
1115
|
+
"name": "SMTP User Enumeration",
|
|
1116
|
+
"description": "Enumerate SMTP users via VRFY/EXPN",
|
|
1117
|
+
"risk": "safe",
|
|
1130
1118
|
},
|
|
1131
1119
|
],
|
|
1132
|
-
|
|
1120
|
+
"exploit": [],
|
|
1133
1121
|
},
|
|
1134
|
-
|
|
1135
|
-
|
|
1122
|
+
"vnc": {
|
|
1123
|
+
"scanner": [
|
|
1136
1124
|
{
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1125
|
+
"path": "auxiliary/scanner/vnc/vnc_none_auth",
|
|
1126
|
+
"name": "VNC No Authentication Scanner",
|
|
1127
|
+
"description": "Check for VNC without authentication",
|
|
1128
|
+
"risk": "safe",
|
|
1141
1129
|
},
|
|
1142
1130
|
{
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1131
|
+
"path": "auxiliary/scanner/vnc/vnc_login",
|
|
1132
|
+
"name": "VNC Login Scanner",
|
|
1133
|
+
"description": "Brute force VNC authentication",
|
|
1134
|
+
"risk": "noisy",
|
|
1147
1135
|
},
|
|
1148
1136
|
],
|
|
1149
|
-
|
|
1137
|
+
"exploit": [],
|
|
1150
1138
|
},
|
|
1151
|
-
|
|
1152
|
-
|
|
1139
|
+
"snmp": {
|
|
1140
|
+
"scanner": [
|
|
1153
1141
|
{
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1142
|
+
"path": "auxiliary/scanner/snmp/snmp_enum",
|
|
1143
|
+
"name": "SNMP Enumeration",
|
|
1144
|
+
"description": "Enumerate SNMP information",
|
|
1145
|
+
"risk": "safe",
|
|
1158
1146
|
},
|
|
1159
1147
|
{
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1148
|
+
"path": "auxiliary/scanner/snmp/snmp_login",
|
|
1149
|
+
"name": "SNMP Community String Scanner",
|
|
1150
|
+
"description": "Brute force SNMP community strings",
|
|
1151
|
+
"risk": "noisy",
|
|
1164
1152
|
},
|
|
1165
1153
|
],
|
|
1166
|
-
|
|
1154
|
+
"exploit": [],
|
|
1167
1155
|
},
|
|
1168
|
-
|
|
1169
|
-
|
|
1156
|
+
"nfs": {
|
|
1157
|
+
"scanner": [
|
|
1170
1158
|
{
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1159
|
+
"path": "auxiliary/scanner/nfs/nfsmount",
|
|
1160
|
+
"name": "NFS Mount Scanner",
|
|
1161
|
+
"description": "Enumerate NFS mounts",
|
|
1162
|
+
"risk": "safe",
|
|
1175
1163
|
},
|
|
1176
1164
|
],
|
|
1177
|
-
|
|
1165
|
+
"exploit": [],
|
|
1178
1166
|
},
|
|
1179
|
-
|
|
1180
|
-
|
|
1167
|
+
"redis": {
|
|
1168
|
+
"scanner": [
|
|
1181
1169
|
{
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1170
|
+
"path": "auxiliary/scanner/redis/redis_server",
|
|
1171
|
+
"name": "Redis Scanner",
|
|
1172
|
+
"description": "Detect Redis service",
|
|
1173
|
+
"risk": "safe",
|
|
1186
1174
|
},
|
|
1187
1175
|
],
|
|
1188
|
-
|
|
1176
|
+
"exploit": [
|
|
1189
1177
|
{
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1178
|
+
"path": "exploit/linux/redis/redis_replication_cmd_exec",
|
|
1179
|
+
"name": "Redis Replication Code Execution",
|
|
1180
|
+
"description": "Exploit Redis via replication",
|
|
1181
|
+
"risk": "dangerous",
|
|
1194
1182
|
},
|
|
1195
|
-
]
|
|
1183
|
+
],
|
|
1196
1184
|
},
|
|
1197
1185
|
}
|
|
1198
1186
|
|
|
1199
|
-
def get_recommendations(
|
|
1187
|
+
def get_recommendations(
|
|
1188
|
+
self, service: str, version: str = None, include_risk: List[str] = None
|
|
1189
|
+
) -> List[Dict]:
|
|
1200
1190
|
"""
|
|
1201
1191
|
Get module recommendations for a service.
|
|
1202
1192
|
|
|
@@ -1209,7 +1199,7 @@ class MSFModuleSelector:
|
|
|
1209
1199
|
List of recommended modules
|
|
1210
1200
|
"""
|
|
1211
1201
|
if include_risk is None:
|
|
1212
|
-
include_risk = [
|
|
1202
|
+
include_risk = ["safe", "noisy", "moderate"]
|
|
1213
1203
|
|
|
1214
1204
|
service_lower = service.lower()
|
|
1215
1205
|
recommendations = []
|
|
@@ -1218,15 +1208,17 @@ class MSFModuleSelector:
|
|
|
1218
1208
|
if service_lower in self.MODULES:
|
|
1219
1209
|
modules = self.MODULES[service_lower]
|
|
1220
1210
|
|
|
1221
|
-
for category in [
|
|
1211
|
+
for category in ["scanner", "exploit"]:
|
|
1222
1212
|
for module in modules.get(category, []):
|
|
1223
|
-
if module[
|
|
1224
|
-
module[
|
|
1213
|
+
if module["risk"] in include_risk:
|
|
1214
|
+
module["category"] = category
|
|
1225
1215
|
recommendations.append(module)
|
|
1226
1216
|
|
|
1227
1217
|
return recommendations
|
|
1228
1218
|
|
|
1229
|
-
def match_vulnerability_to_exploit(
|
|
1219
|
+
def match_vulnerability_to_exploit(
|
|
1220
|
+
self, vuln_title: str, vuln_desc: str = "", cves: List[str] = None
|
|
1221
|
+
) -> List[Dict]:
|
|
1230
1222
|
"""
|
|
1231
1223
|
Match a vulnerability to potential exploit modules.
|
|
1232
1224
|
|
|
@@ -1241,16 +1233,18 @@ class MSFModuleSelector:
|
|
|
1241
1233
|
matches = []
|
|
1242
1234
|
|
|
1243
1235
|
for service, modules in self.MODULES.items():
|
|
1244
|
-
for exploit in modules.get(
|
|
1236
|
+
for exploit in modules.get("exploit", []):
|
|
1245
1237
|
# Check CVE match
|
|
1246
|
-
if cves and
|
|
1247
|
-
if any(cve in exploit[
|
|
1238
|
+
if cves and "cve" in exploit:
|
|
1239
|
+
if any(cve in exploit["cve"] for cve in cves):
|
|
1248
1240
|
matches.append(exploit)
|
|
1249
1241
|
continue
|
|
1250
1242
|
|
|
1251
1243
|
# Check keyword match
|
|
1252
|
-
keywords = vuln_title.lower() +
|
|
1253
|
-
module_keywords =
|
|
1244
|
+
keywords = vuln_title.lower() + " " + vuln_desc.lower()
|
|
1245
|
+
module_keywords = (
|
|
1246
|
+
exploit["name"].lower() + " " + exploit["description"].lower()
|
|
1247
|
+
)
|
|
1254
1248
|
|
|
1255
1249
|
if any(word in keywords for word in module_keywords.split()):
|
|
1256
1250
|
matches.append(exploit)
|
|
@@ -1263,74 +1257,76 @@ class MSFModuleSelector:
|
|
|
1263
1257
|
version: str = None,
|
|
1264
1258
|
engagement_id: int = None,
|
|
1265
1259
|
risk_levels: List[str] = None,
|
|
1266
|
-
include_cve_matches: bool = True
|
|
1260
|
+
include_cve_matches: bool = True,
|
|
1267
1261
|
) -> List[Dict]:
|
|
1268
1262
|
"""
|
|
1269
1263
|
Get intelligent recommendations based on service and version.
|
|
1270
|
-
|
|
1264
|
+
|
|
1271
1265
|
Args:
|
|
1272
1266
|
service: Service name (ssh, smb, http, etc.)
|
|
1273
1267
|
version: Service version string
|
|
1274
1268
|
engagement_id: Engagement ID for context
|
|
1275
1269
|
risk_levels: Risk levels to include
|
|
1276
1270
|
include_cve_matches: Include CVE-matched modules
|
|
1277
|
-
|
|
1271
|
+
|
|
1278
1272
|
Returns:
|
|
1279
1273
|
Prioritized list of recommendations with scores
|
|
1280
1274
|
"""
|
|
1281
1275
|
if risk_levels is None:
|
|
1282
|
-
risk_levels = [
|
|
1283
|
-
|
|
1276
|
+
risk_levels = ["safe", "noisy", "moderate"]
|
|
1277
|
+
|
|
1284
1278
|
recommendations = []
|
|
1285
|
-
|
|
1279
|
+
|
|
1286
1280
|
# Get basic module recommendations
|
|
1287
1281
|
basic_recs = self.get_recommendations(service, version, risk_levels)
|
|
1288
|
-
|
|
1282
|
+
|
|
1289
1283
|
# Add CVE matching if version provided
|
|
1290
1284
|
if version and include_cve_matches:
|
|
1291
1285
|
matching_cves = VersionMatcher.get_cves_for_version(service, version)
|
|
1292
|
-
|
|
1286
|
+
|
|
1293
1287
|
for cve_id in matching_cves:
|
|
1294
1288
|
cve_data = CVE_DATABASE.get(cve_id, {})
|
|
1295
|
-
for module_path in cve_data.get(
|
|
1289
|
+
for module_path in cve_data.get("modules", []):
|
|
1296
1290
|
# Find if this module exists in our database
|
|
1297
1291
|
module_info = self._find_module_by_path(module_path)
|
|
1298
1292
|
if module_info:
|
|
1299
1293
|
# Enhance with CVE data
|
|
1300
1294
|
enhanced_module = module_info.copy()
|
|
1301
|
-
enhanced_module[
|
|
1302
|
-
enhanced_module[
|
|
1303
|
-
enhanced_module[
|
|
1304
|
-
enhanced_module[
|
|
1305
|
-
enhanced_module[
|
|
1295
|
+
enhanced_module["cve_match"] = cve_id
|
|
1296
|
+
enhanced_module["cvss"] = cve_data.get("cvss")
|
|
1297
|
+
enhanced_module["reliability"] = cve_data.get("reliability")
|
|
1298
|
+
enhanced_module["impact"] = cve_data.get("impact")
|
|
1299
|
+
enhanced_module["score"] = self._score_module(
|
|
1300
|
+
enhanced_module, engagement_id
|
|
1301
|
+
)
|
|
1306
1302
|
recommendations.append(enhanced_module)
|
|
1307
|
-
|
|
1303
|
+
|
|
1308
1304
|
# Add basic recommendations with scores
|
|
1309
1305
|
for module in basic_recs:
|
|
1310
|
-
if not any(r.get(
|
|
1311
|
-
module[
|
|
1306
|
+
if not any(r.get("path") == module.get("path") for r in recommendations):
|
|
1307
|
+
module["score"] = self._score_module(module, engagement_id)
|
|
1312
1308
|
recommendations.append(module)
|
|
1313
|
-
|
|
1309
|
+
|
|
1314
1310
|
# Sort by score descending
|
|
1315
|
-
recommendations.sort(key=lambda x: x.get(
|
|
1316
|
-
|
|
1311
|
+
recommendations.sort(key=lambda x: x.get("score", 0), reverse=True)
|
|
1312
|
+
|
|
1317
1313
|
return recommendations
|
|
1318
1314
|
|
|
1319
1315
|
def _find_module_by_path(self, module_path: str) -> Optional[Dict]:
|
|
1320
1316
|
"""Find module info by path in module database."""
|
|
1321
1317
|
for service, modules in self.MODULES.items():
|
|
1322
|
-
for category in [
|
|
1318
|
+
for category in ["scanner", "exploit"]:
|
|
1323
1319
|
for module in modules.get(category, []):
|
|
1324
|
-
if module.get(
|
|
1320
|
+
if module.get("path") == module_path:
|
|
1325
1321
|
result = module.copy()
|
|
1326
|
-
result[
|
|
1322
|
+
result["category"] = category
|
|
1327
1323
|
return result
|
|
1328
1324
|
return None
|
|
1329
1325
|
|
|
1330
1326
|
def _score_module(self, module: Dict, engagement_id: int = None) -> float:
|
|
1331
1327
|
"""
|
|
1332
1328
|
Score module (0-100) based on various factors.
|
|
1333
|
-
|
|
1329
|
+
|
|
1334
1330
|
Scoring breakdown:
|
|
1335
1331
|
- CVE match (30 points)
|
|
1336
1332
|
- CVSS score (25 points)
|
|
@@ -1339,33 +1335,34 @@ class MSFModuleSelector:
|
|
|
1339
1335
|
- Risk level (10 points)
|
|
1340
1336
|
"""
|
|
1341
1337
|
score = 0.0
|
|
1342
|
-
|
|
1338
|
+
|
|
1343
1339
|
# CVE match bonus
|
|
1344
|
-
if module.get(
|
|
1340
|
+
if module.get("cve_match"):
|
|
1345
1341
|
score += 30.0
|
|
1346
|
-
|
|
1342
|
+
|
|
1347
1343
|
# CVSS score (normalize to 25 points)
|
|
1348
|
-
cvss = module.get(
|
|
1344
|
+
cvss = module.get("cvss", 0)
|
|
1349
1345
|
if cvss:
|
|
1350
1346
|
score += (cvss / 10.0) * 25.0
|
|
1351
|
-
|
|
1347
|
+
|
|
1352
1348
|
# Reliability rating
|
|
1353
1349
|
reliability_scores = {
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1350
|
+
"excellent": 20.0,
|
|
1351
|
+
"good": 15.0,
|
|
1352
|
+
"normal": 10.0,
|
|
1353
|
+
"average": 5.0,
|
|
1354
|
+
"low": 2.0,
|
|
1359
1355
|
}
|
|
1360
|
-
reliability = module.get(
|
|
1356
|
+
reliability = module.get("reliability", "normal")
|
|
1361
1357
|
score += reliability_scores.get(reliability, 10.0)
|
|
1362
|
-
|
|
1358
|
+
|
|
1363
1359
|
# Prerequisites (check if we have creds available)
|
|
1364
|
-
if module.get(
|
|
1360
|
+
if module.get("requires") == "credentials":
|
|
1365
1361
|
if engagement_id:
|
|
1366
1362
|
# Check if we have credentials
|
|
1367
1363
|
try:
|
|
1368
1364
|
from souleyez.storage.credentials import CredentialsManager
|
|
1365
|
+
|
|
1369
1366
|
cm = CredentialsManager()
|
|
1370
1367
|
creds = cm.list_credentials(engagement_id)
|
|
1371
1368
|
if creds:
|
|
@@ -1376,33 +1373,28 @@ class MSFModuleSelector:
|
|
|
1376
1373
|
score += 5.0
|
|
1377
1374
|
else:
|
|
1378
1375
|
score += 15.0 # No prerequisites needed
|
|
1379
|
-
|
|
1376
|
+
|
|
1380
1377
|
# Risk level (lower risk = higher score for safety)
|
|
1381
|
-
risk_scores = {
|
|
1382
|
-
|
|
1383
|
-
'noisy': 7.0,
|
|
1384
|
-
'moderate': 5.0,
|
|
1385
|
-
'dangerous': 2.0
|
|
1386
|
-
}
|
|
1387
|
-
risk = module.get('risk', 'moderate')
|
|
1378
|
+
risk_scores = {"safe": 10.0, "noisy": 7.0, "moderate": 5.0, "dangerous": 2.0}
|
|
1379
|
+
risk = module.get("risk", "moderate")
|
|
1388
1380
|
score += risk_scores.get(risk, 5.0)
|
|
1389
|
-
|
|
1381
|
+
|
|
1390
1382
|
return score
|
|
1391
1383
|
|
|
1392
1384
|
def query_live_msf_modules(self, search_term: str) -> List[Dict]:
|
|
1393
1385
|
"""
|
|
1394
1386
|
Query actual MSF installation for modules.
|
|
1395
|
-
|
|
1387
|
+
|
|
1396
1388
|
Args:
|
|
1397
1389
|
search_term: Search term for msfconsole
|
|
1398
|
-
|
|
1390
|
+
|
|
1399
1391
|
Returns:
|
|
1400
1392
|
List of modules with metadata
|
|
1401
1393
|
"""
|
|
1402
1394
|
console = MSFConsoleManager()
|
|
1403
1395
|
if not console.is_available():
|
|
1404
1396
|
return []
|
|
1405
|
-
|
|
1397
|
+
|
|
1406
1398
|
try:
|
|
1407
1399
|
output = console.execute_command(f"search {search_term}")
|
|
1408
1400
|
modules = self._parse_msf_search_output(output)
|
|
@@ -1413,172 +1405,170 @@ class MSFModuleSelector:
|
|
|
1413
1405
|
def _parse_msf_search_output(self, output: str) -> List[Dict]:
|
|
1414
1406
|
"""Parse msfconsole search output."""
|
|
1415
1407
|
modules = []
|
|
1416
|
-
lines = output.split(
|
|
1417
|
-
|
|
1408
|
+
lines = output.split("\n")
|
|
1409
|
+
|
|
1418
1410
|
for line in lines:
|
|
1419
1411
|
# Skip headers and empty lines
|
|
1420
|
-
if not line.strip() or
|
|
1412
|
+
if not line.strip() or "Matching Modules" in line or "=====" in line:
|
|
1421
1413
|
continue
|
|
1422
|
-
|
|
1414
|
+
|
|
1423
1415
|
# Parse module line
|
|
1424
1416
|
parts = line.strip().split(None, 3)
|
|
1425
1417
|
if len(parts) >= 3:
|
|
1426
|
-
modules.append(
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1418
|
+
modules.append(
|
|
1419
|
+
{
|
|
1420
|
+
"path": parts[0],
|
|
1421
|
+
"disclosure_date": parts[1] if len(parts) > 1 else None,
|
|
1422
|
+
"rank": parts[2] if len(parts) > 2 else None,
|
|
1423
|
+
"name": parts[3] if len(parts) > 3 else parts[0],
|
|
1424
|
+
}
|
|
1425
|
+
)
|
|
1426
|
+
|
|
1433
1427
|
return modules
|
|
1434
1428
|
|
|
1435
1429
|
def get_credential_powered_modules(
|
|
1436
|
-
self,
|
|
1437
|
-
engagement_id: int,
|
|
1438
|
-
service_type: str = None
|
|
1430
|
+
self, engagement_id: int, service_type: str = None
|
|
1439
1431
|
) -> List[Dict]:
|
|
1440
1432
|
"""
|
|
1441
1433
|
Get modules that can leverage discovered credentials.
|
|
1442
|
-
|
|
1434
|
+
|
|
1443
1435
|
Args:
|
|
1444
1436
|
engagement_id: Engagement ID
|
|
1445
1437
|
service_type: Optional service type filter
|
|
1446
|
-
|
|
1438
|
+
|
|
1447
1439
|
Returns:
|
|
1448
1440
|
List of modules that can use available credentials
|
|
1449
1441
|
"""
|
|
1450
1442
|
try:
|
|
1451
1443
|
from souleyez.storage.credentials import CredentialsManager
|
|
1444
|
+
|
|
1452
1445
|
cm = CredentialsManager()
|
|
1453
1446
|
creds = cm.list_credentials(engagement_id)
|
|
1454
1447
|
except:
|
|
1455
1448
|
return []
|
|
1456
|
-
|
|
1449
|
+
|
|
1457
1450
|
if not creds:
|
|
1458
1451
|
return []
|
|
1459
|
-
|
|
1452
|
+
|
|
1460
1453
|
# Group creds by service
|
|
1461
1454
|
cred_map = {}
|
|
1462
1455
|
for cred in creds:
|
|
1463
|
-
service = cred.get(
|
|
1456
|
+
service = cred.get("service", "unknown").lower()
|
|
1464
1457
|
if service not in cred_map:
|
|
1465
1458
|
cred_map[service] = []
|
|
1466
1459
|
cred_map[service].append(cred)
|
|
1467
|
-
|
|
1460
|
+
|
|
1468
1461
|
powered_modules = []
|
|
1469
|
-
|
|
1462
|
+
|
|
1470
1463
|
for service, service_creds in cred_map.items():
|
|
1471
1464
|
if service_type and service_type.lower() not in service:
|
|
1472
1465
|
continue
|
|
1473
|
-
|
|
1466
|
+
|
|
1474
1467
|
# Get modules requiring credentials
|
|
1475
1468
|
modules = self.get_recommendations(
|
|
1476
|
-
service=service,
|
|
1477
|
-
include_risk=['safe', 'noisy', 'moderate']
|
|
1469
|
+
service=service, include_risk=["safe", "noisy", "moderate"]
|
|
1478
1470
|
)
|
|
1479
|
-
|
|
1471
|
+
|
|
1480
1472
|
for module in modules:
|
|
1481
|
-
if module.get(
|
|
1482
|
-
module[
|
|
1483
|
-
module[
|
|
1484
|
-
module[
|
|
1473
|
+
if module.get("requires") == "credentials":
|
|
1474
|
+
module["available_credentials"] = len(service_creds)
|
|
1475
|
+
module["ready_to_run"] = True
|
|
1476
|
+
module["score"] = 95.0 # High score since we have creds
|
|
1485
1477
|
powered_modules.append(module)
|
|
1486
|
-
|
|
1478
|
+
|
|
1487
1479
|
return powered_modules
|
|
1488
1480
|
|
|
1489
1481
|
|
|
1490
1482
|
class ModuleRecommendationEngine:
|
|
1491
1483
|
"""Advanced recommendation engine with context-aware scoring."""
|
|
1492
|
-
|
|
1484
|
+
|
|
1493
1485
|
def __init__(self):
|
|
1494
1486
|
self.module_selector = MSFModuleSelector()
|
|
1495
|
-
|
|
1487
|
+
|
|
1496
1488
|
def get_ranked_recommendations(
|
|
1497
|
-
self,
|
|
1498
|
-
host_id: int,
|
|
1499
|
-
service_id: int,
|
|
1500
|
-
engagement_id: int
|
|
1489
|
+
self, host_id: int, service_id: int, engagement_id: int
|
|
1501
1490
|
) -> List[Dict]:
|
|
1502
1491
|
"""
|
|
1503
1492
|
Return ranked list of modules with scores and rationale.
|
|
1504
|
-
|
|
1493
|
+
|
|
1505
1494
|
Args:
|
|
1506
1495
|
host_id: Host ID
|
|
1507
1496
|
service_id: Service ID
|
|
1508
1497
|
engagement_id: Engagement ID
|
|
1509
|
-
|
|
1498
|
+
|
|
1510
1499
|
Returns:
|
|
1511
1500
|
List of ranked recommendations with metadata
|
|
1512
1501
|
"""
|
|
1513
1502
|
try:
|
|
1514
1503
|
from souleyez.storage.hosts import HostManager
|
|
1504
|
+
|
|
1515
1505
|
hm = HostManager()
|
|
1516
1506
|
service = hm.get_service(service_id)
|
|
1517
|
-
|
|
1507
|
+
|
|
1518
1508
|
if not service:
|
|
1519
1509
|
return []
|
|
1520
|
-
|
|
1521
|
-
service_name = service.get(
|
|
1522
|
-
service_version = service.get(
|
|
1523
|
-
|
|
1510
|
+
|
|
1511
|
+
service_name = service.get("service_name", "")
|
|
1512
|
+
service_version = service.get("service_version", "")
|
|
1513
|
+
|
|
1524
1514
|
recommendations = self.module_selector.get_recommendations_for_service(
|
|
1525
1515
|
service=service_name,
|
|
1526
1516
|
version=service_version,
|
|
1527
1517
|
engagement_id=engagement_id,
|
|
1528
|
-
include_cve_matches=True
|
|
1518
|
+
include_cve_matches=True,
|
|
1529
1519
|
)
|
|
1530
|
-
|
|
1520
|
+
|
|
1531
1521
|
# Enhance with rationale
|
|
1532
1522
|
for rec in recommendations:
|
|
1533
|
-
rec[
|
|
1534
|
-
rec[
|
|
1535
|
-
rec[
|
|
1536
|
-
|
|
1523
|
+
rec["rationale"] = self._generate_rationale(rec)
|
|
1524
|
+
rec["prerequisites_met"] = self._check_prerequisites(rec, engagement_id)
|
|
1525
|
+
rec["estimated_success"] = self._estimate_success(rec)
|
|
1526
|
+
|
|
1537
1527
|
return recommendations
|
|
1538
1528
|
except:
|
|
1539
1529
|
return []
|
|
1540
|
-
|
|
1530
|
+
|
|
1541
1531
|
def _generate_rationale(self, module: Dict) -> str:
|
|
1542
1532
|
"""Generate human-readable rationale for recommendation."""
|
|
1543
1533
|
reasons = []
|
|
1544
|
-
|
|
1545
|
-
if module.get(
|
|
1534
|
+
|
|
1535
|
+
if module.get("cve_match"):
|
|
1546
1536
|
reasons.append(f"CVE {module['cve_match']} match")
|
|
1547
|
-
|
|
1548
|
-
if module.get(
|
|
1537
|
+
|
|
1538
|
+
if module.get("cvss", 0) >= 9.0:
|
|
1549
1539
|
reasons.append("Critical CVSS score")
|
|
1550
|
-
elif module.get(
|
|
1540
|
+
elif module.get("cvss", 0) >= 7.0:
|
|
1551
1541
|
reasons.append("High CVSS score")
|
|
1552
|
-
|
|
1553
|
-
if module.get(
|
|
1542
|
+
|
|
1543
|
+
if module.get("reliability") == "excellent":
|
|
1554
1544
|
reasons.append("Excellent reliability")
|
|
1555
|
-
|
|
1556
|
-
if module.get(
|
|
1545
|
+
|
|
1546
|
+
if module.get("ready_to_run"):
|
|
1557
1547
|
reasons.append("Credentials available")
|
|
1558
|
-
|
|
1548
|
+
|
|
1559
1549
|
if not reasons:
|
|
1560
1550
|
reasons.append("Standard recommendation")
|
|
1561
|
-
|
|
1551
|
+
|
|
1562
1552
|
return " + ".join(reasons)
|
|
1563
|
-
|
|
1553
|
+
|
|
1564
1554
|
def _check_prerequisites(self, module: Dict, engagement_id: int) -> bool:
|
|
1565
1555
|
"""Check if module prerequisites are met."""
|
|
1566
|
-
if module.get(
|
|
1567
|
-
return module.get(
|
|
1556
|
+
if module.get("requires") == "credentials":
|
|
1557
|
+
return module.get("available_credentials", 0) > 0
|
|
1568
1558
|
return True
|
|
1569
|
-
|
|
1559
|
+
|
|
1570
1560
|
def _estimate_success(self, module: Dict) -> str:
|
|
1571
1561
|
"""Estimate success probability."""
|
|
1572
|
-
score = module.get(
|
|
1573
|
-
|
|
1562
|
+
score = module.get("score", 0)
|
|
1563
|
+
|
|
1574
1564
|
if score >= 80:
|
|
1575
|
-
return
|
|
1565
|
+
return "high"
|
|
1576
1566
|
elif score >= 60:
|
|
1577
|
-
return
|
|
1567
|
+
return "medium"
|
|
1578
1568
|
elif score >= 40:
|
|
1579
|
-
return
|
|
1569
|
+
return "low"
|
|
1580
1570
|
else:
|
|
1581
|
-
return
|
|
1571
|
+
return "very low"
|
|
1582
1572
|
|
|
1583
1573
|
|
|
1584
1574
|
class MSFConsoleManager:
|
|
@@ -1592,14 +1582,15 @@ class MSFConsoleManager:
|
|
|
1592
1582
|
def _check_needs_no_readline(self) -> bool:
|
|
1593
1583
|
"""Check if --no-readline flag is needed (ARM64 Ubuntu has reline bug)."""
|
|
1594
1584
|
import platform
|
|
1585
|
+
|
|
1595
1586
|
# Only needed on ARM64 (aarch64)
|
|
1596
|
-
if platform.machine() !=
|
|
1587
|
+
if platform.machine() != "aarch64":
|
|
1597
1588
|
return False
|
|
1598
1589
|
# Check if we're on Ubuntu (not Kali which works fine)
|
|
1599
1590
|
try:
|
|
1600
|
-
with open(
|
|
1591
|
+
with open("/etc/os-release", "r") as f:
|
|
1601
1592
|
content = f.read().lower()
|
|
1602
|
-
if
|
|
1593
|
+
if "ubuntu" in content and "kali" not in content:
|
|
1603
1594
|
return True
|
|
1604
1595
|
except:
|
|
1605
1596
|
pass
|
|
@@ -1610,15 +1601,15 @@ class MSFConsoleManager:
|
|
|
1610
1601
|
import shutil
|
|
1611
1602
|
|
|
1612
1603
|
# First try shutil.which (fastest)
|
|
1613
|
-
path = shutil.which(
|
|
1604
|
+
path = shutil.which("msfconsole")
|
|
1614
1605
|
if path:
|
|
1615
1606
|
return path
|
|
1616
1607
|
|
|
1617
1608
|
# Try common locations
|
|
1618
1609
|
locations = [
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1610
|
+
"/usr/bin/msfconsole",
|
|
1611
|
+
"/opt/metasploit-framework/bin/msfconsole",
|
|
1612
|
+
"/usr/local/bin/msfconsole",
|
|
1622
1613
|
]
|
|
1623
1614
|
|
|
1624
1615
|
for loc in locations:
|
|
@@ -1631,7 +1622,9 @@ class MSFConsoleManager:
|
|
|
1631
1622
|
"""Check if msfconsole is available."""
|
|
1632
1623
|
return self.msf_path is not None
|
|
1633
1624
|
|
|
1634
|
-
def launch_with_resource(
|
|
1625
|
+
def launch_with_resource(
|
|
1626
|
+
self, resource_file: str, background: bool = False, use_sudo: bool = True
|
|
1627
|
+
) -> subprocess.Popen:
|
|
1635
1628
|
"""
|
|
1636
1629
|
Launch msfconsole with a resource script.
|
|
1637
1630
|
|
|
@@ -1648,24 +1641,31 @@ class MSFConsoleManager:
|
|
|
1648
1641
|
|
|
1649
1642
|
cmd = []
|
|
1650
1643
|
if use_sudo:
|
|
1651
|
-
cmd.append(
|
|
1644
|
+
cmd.append("sudo")
|
|
1652
1645
|
cmd.append(self.msf_path)
|
|
1653
|
-
cmd.append(
|
|
1646
|
+
cmd.append("-q")
|
|
1654
1647
|
if self._needs_no_readline:
|
|
1655
|
-
cmd.append(
|
|
1656
|
-
cmd.extend([
|
|
1648
|
+
cmd.append("--no-readline")
|
|
1649
|
+
cmd.extend(["-r", resource_file])
|
|
1657
1650
|
|
|
1658
1651
|
if background:
|
|
1659
1652
|
return subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
1660
1653
|
else:
|
|
1661
1654
|
# Interactive mode - use os.system for proper TTY handling
|
|
1662
1655
|
# subprocess.run doesn't properly inherit terminal attributes
|
|
1663
|
-
os.system(
|
|
1656
|
+
os.system(
|
|
1657
|
+
shlex.join(cmd)
|
|
1658
|
+
) # nosec B605 - intentional shell for TTY, args escaped with shlex
|
|
1664
1659
|
# Reset terminal after msfconsole (ARM64 Ruby readline corrupts it)
|
|
1665
|
-
os.system(
|
|
1660
|
+
os.system("stty sane 2>/dev/null") # nosec B605 - static command
|
|
1666
1661
|
return None
|
|
1667
1662
|
|
|
1668
|
-
def launch_interactive(
|
|
1663
|
+
def launch_interactive(
|
|
1664
|
+
self,
|
|
1665
|
+
pre_commands: List[str] = None,
|
|
1666
|
+
workspace: str = None,
|
|
1667
|
+
use_sudo: bool = True,
|
|
1668
|
+
) -> None:
|
|
1669
1669
|
"""
|
|
1670
1670
|
Launch interactive msfconsole with optional pre-commands.
|
|
1671
1671
|
|
|
@@ -1680,16 +1680,17 @@ class MSFConsoleManager:
|
|
|
1680
1680
|
# Build command
|
|
1681
1681
|
cmd = []
|
|
1682
1682
|
if use_sudo:
|
|
1683
|
-
cmd.append(
|
|
1683
|
+
cmd.append("sudo")
|
|
1684
1684
|
cmd.append(self.msf_path)
|
|
1685
|
-
cmd.append(
|
|
1685
|
+
cmd.append("-q")
|
|
1686
1686
|
if self._needs_no_readline:
|
|
1687
|
-
cmd.append(
|
|
1687
|
+
cmd.append("--no-readline")
|
|
1688
1688
|
|
|
1689
1689
|
# Create temporary resource script if we have pre-commands
|
|
1690
1690
|
if pre_commands or workspace:
|
|
1691
1691
|
import tempfile
|
|
1692
|
-
|
|
1692
|
+
|
|
1693
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".rc", delete=False) as f:
|
|
1693
1694
|
if workspace:
|
|
1694
1695
|
f.write(f"workspace -a {workspace}\n")
|
|
1695
1696
|
f.write(f"workspace {workspace}\n")
|
|
@@ -1701,18 +1702,22 @@ class MSFConsoleManager:
|
|
|
1701
1702
|
rc_file = f.name
|
|
1702
1703
|
|
|
1703
1704
|
try:
|
|
1704
|
-
cmd.extend([
|
|
1705
|
+
cmd.extend(["-r", rc_file])
|
|
1705
1706
|
# Use os.system for proper TTY handling
|
|
1706
|
-
os.system(
|
|
1707
|
+
os.system(
|
|
1708
|
+
shlex.join(cmd)
|
|
1709
|
+
) # nosec B605 - intentional shell for TTY, args escaped with shlex
|
|
1707
1710
|
# Reset terminal after msfconsole (ARM64 Ruby readline corrupts it)
|
|
1708
|
-
os.system(
|
|
1711
|
+
os.system("stty sane 2>/dev/null") # nosec B605 - static command
|
|
1709
1712
|
finally:
|
|
1710
1713
|
os.unlink(rc_file)
|
|
1711
1714
|
else:
|
|
1712
1715
|
# Just launch msfconsole - use os.system for proper TTY handling
|
|
1713
|
-
os.system(
|
|
1716
|
+
os.system(
|
|
1717
|
+
shlex.join(cmd)
|
|
1718
|
+
) # nosec B605 - intentional shell for TTY, args escaped with shlex
|
|
1714
1719
|
# Reset terminal after msfconsole (ARM64 Ruby readline corrupts it)
|
|
1715
|
-
os.system(
|
|
1720
|
+
os.system("stty sane 2>/dev/null") # nosec B605 - static command
|
|
1716
1721
|
|
|
1717
1722
|
def execute_command(self, command: str) -> str:
|
|
1718
1723
|
"""
|
|
@@ -1729,17 +1734,18 @@ class MSFConsoleManager:
|
|
|
1729
1734
|
|
|
1730
1735
|
# Create temporary rc file
|
|
1731
1736
|
import tempfile
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
f.write(
|
|
1737
|
+
|
|
1738
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".rc", delete=False) as f:
|
|
1739
|
+
f.write(command + "\n")
|
|
1740
|
+
f.write("exit\n")
|
|
1735
1741
|
rc_file = f.name
|
|
1736
1742
|
|
|
1737
1743
|
try:
|
|
1738
1744
|
result = subprocess.run(
|
|
1739
|
-
[self.msf_path,
|
|
1745
|
+
[self.msf_path, "-q", "-r", rc_file],
|
|
1740
1746
|
capture_output=True,
|
|
1741
1747
|
text=True,
|
|
1742
|
-
timeout=30
|
|
1748
|
+
timeout=30,
|
|
1743
1749
|
)
|
|
1744
1750
|
return result.stdout
|
|
1745
1751
|
finally:
|