souleyez 2.43.29__py3-none-any.whl → 3.0.0__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 +9564 -2881
- 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 +564 -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 +409 -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 +417 -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 +913 -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 +219 -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 +237 -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 +23034 -10679
- 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-3.0.0.dist-info}/METADATA +2 -2
- souleyez-3.0.0.dist-info/RECORD +443 -0
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/WHEEL +1 -1
- souleyez-2.43.29.dist-info/RECORD +0 -379
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/entry_points.txt +0 -0
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/top_level.txt +0 -0
|
@@ -17,13 +17,16 @@ from souleyez.ui.design_system import DesignSystem
|
|
|
17
17
|
def get_terminal_width() -> int:
|
|
18
18
|
"""Get terminal width, defaulting to 120 if not available."""
|
|
19
19
|
import shutil
|
|
20
|
+
|
|
20
21
|
try:
|
|
21
22
|
return shutil.get_terminal_size().columns
|
|
22
23
|
except Exception:
|
|
23
24
|
return 120
|
|
24
25
|
|
|
25
26
|
|
|
26
|
-
def get_target_services(
|
|
27
|
+
def get_target_services(
|
|
28
|
+
target_ip: str, engagement_id: int
|
|
29
|
+
) -> Tuple[List[Dict], Set[str], Set[int]]:
|
|
27
30
|
"""
|
|
28
31
|
Get services running on target host.
|
|
29
32
|
|
|
@@ -38,14 +41,14 @@ def get_target_services(target_ip: str, engagement_id: int) -> Tuple[List[Dict],
|
|
|
38
41
|
if not host:
|
|
39
42
|
return [], set(), set()
|
|
40
43
|
|
|
41
|
-
services = hm.get_host_services(host[
|
|
44
|
+
services = hm.get_host_services(host["id"])
|
|
42
45
|
|
|
43
46
|
service_names = set()
|
|
44
47
|
ports = set()
|
|
45
48
|
|
|
46
49
|
for svc in services:
|
|
47
|
-
svc_name = (svc.get(
|
|
48
|
-
port = svc.get(
|
|
50
|
+
svc_name = (svc.get("service_name") or "").lower()
|
|
51
|
+
port = svc.get("port")
|
|
49
52
|
|
|
50
53
|
if svc_name:
|
|
51
54
|
service_names.add(svc_name)
|
|
@@ -68,44 +71,46 @@ def get_msf_job_status(target_ip: str, engagement_id: int) -> Dict[str, Dict]:
|
|
|
68
71
|
|
|
69
72
|
# Filter to MSF jobs for this target and engagement
|
|
70
73
|
msf_jobs = [
|
|
71
|
-
j
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
and
|
|
74
|
+
j
|
|
75
|
+
for j in all_jobs
|
|
76
|
+
if j.get("tool") == "msf_auxiliary"
|
|
77
|
+
and j.get("engagement_id") == engagement_id
|
|
78
|
+
and target_ip in (j.get("target") or "")
|
|
75
79
|
]
|
|
76
80
|
|
|
77
81
|
status_map = {}
|
|
78
82
|
|
|
79
83
|
for job in msf_jobs:
|
|
80
|
-
args = job.get(
|
|
84
|
+
args = job.get("args", [])
|
|
81
85
|
if args:
|
|
82
86
|
module_path = args[0]
|
|
83
87
|
current_status = status_map.get(module_path, {})
|
|
84
|
-
job_status = job.get(
|
|
88
|
+
job_status = job.get("status", "unknown")
|
|
85
89
|
|
|
86
90
|
# Keep most recent / highest priority status
|
|
87
|
-
if job_status ==
|
|
88
|
-
status_map[module_path] = {
|
|
89
|
-
'status': 'running',
|
|
90
|
-
'job_id': job.get('id'),
|
|
91
|
-
'started_at': job.get('queued_at')
|
|
92
|
-
}
|
|
93
|
-
elif job_status == 'done' and current_status.get('status') != 'running':
|
|
91
|
+
if job_status == "running":
|
|
94
92
|
status_map[module_path] = {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
93
|
+
"status": "running",
|
|
94
|
+
"job_id": job.get("id"),
|
|
95
|
+
"started_at": job.get("queued_at"),
|
|
98
96
|
}
|
|
99
|
-
elif job_status
|
|
97
|
+
elif job_status == "done" and current_status.get("status") != "running":
|
|
100
98
|
status_map[module_path] = {
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
"status": "done",
|
|
100
|
+
"job_id": job.get("id"),
|
|
101
|
+
"completed_at": job.get("completed_at"),
|
|
103
102
|
}
|
|
103
|
+
elif job_status in ("pending", "queued") and current_status.get(
|
|
104
|
+
"status"
|
|
105
|
+
) not in ("running", "done"):
|
|
106
|
+
status_map[module_path] = {"status": "pending", "job_id": job.get("id")}
|
|
104
107
|
|
|
105
108
|
return status_map
|
|
106
109
|
|
|
107
110
|
|
|
108
|
-
def get_recommendations(
|
|
111
|
+
def get_recommendations(
|
|
112
|
+
target_ip: str, engagement_id: int, services: List[Dict]
|
|
113
|
+
) -> List[Dict]:
|
|
109
114
|
"""
|
|
110
115
|
Generate smart recommendations based on discovered services and findings.
|
|
111
116
|
|
|
@@ -121,65 +126,75 @@ def get_recommendations(target_ip: str, engagement_id: int, services: List[Dict]
|
|
|
121
126
|
service_names = set()
|
|
122
127
|
ports = set()
|
|
123
128
|
for svc in services:
|
|
124
|
-
svc_name = (svc.get(
|
|
129
|
+
svc_name = (svc.get("service_name") or "").lower()
|
|
125
130
|
if svc_name:
|
|
126
131
|
service_names.add(svc_name)
|
|
127
|
-
port = svc.get(
|
|
132
|
+
port = svc.get("port")
|
|
128
133
|
if port:
|
|
129
134
|
ports.add(port)
|
|
130
135
|
|
|
131
136
|
# Check for SMBv1 or SMB - recommend MS17-010 check
|
|
132
|
-
if
|
|
133
|
-
recommendations.append(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
if "smb" in service_names or "microsoft-ds" in service_names or 445 in ports:
|
|
138
|
+
recommendations.append(
|
|
139
|
+
{
|
|
140
|
+
"module": "auxiliary/scanner/smb/smb_ms17_010",
|
|
141
|
+
"name": "MS17-010 Check",
|
|
142
|
+
"reason": "SMB detected - check for EternalBlue",
|
|
143
|
+
"priority": "high",
|
|
144
|
+
}
|
|
145
|
+
)
|
|
139
146
|
|
|
140
147
|
# Check for anonymous FTP
|
|
141
|
-
if
|
|
142
|
-
recommendations.append(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
+
if "ftp" in service_names or 21 in ports:
|
|
149
|
+
recommendations.append(
|
|
150
|
+
{
|
|
151
|
+
"module": "auxiliary/scanner/ftp/anonymous",
|
|
152
|
+
"name": "FTP Anonymous Check",
|
|
153
|
+
"reason": "FTP detected - check anonymous access",
|
|
154
|
+
"priority": "high",
|
|
155
|
+
}
|
|
156
|
+
)
|
|
148
157
|
|
|
149
158
|
# Check for VNC without auth
|
|
150
|
-
if
|
|
151
|
-
recommendations.append(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
159
|
+
if "vnc" in service_names or 5900 in ports or 5901 in ports:
|
|
160
|
+
recommendations.append(
|
|
161
|
+
{
|
|
162
|
+
"module": "auxiliary/scanner/vnc/vnc_none_auth",
|
|
163
|
+
"name": "VNC None Auth",
|
|
164
|
+
"reason": "VNC detected - check for no-auth",
|
|
165
|
+
"priority": "high",
|
|
166
|
+
}
|
|
167
|
+
)
|
|
157
168
|
|
|
158
169
|
# Check for RDP NLA
|
|
159
|
-
if
|
|
160
|
-
recommendations.append(
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
170
|
+
if "rdp" in service_names or "ms-wbt-server" in service_names or 3389 in ports:
|
|
171
|
+
recommendations.append(
|
|
172
|
+
{
|
|
173
|
+
"module": "auxiliary/scanner/rdp/rdp_scanner",
|
|
174
|
+
"name": "RDP Scanner",
|
|
175
|
+
"reason": "RDP detected - check NLA settings",
|
|
176
|
+
"priority": "medium",
|
|
177
|
+
}
|
|
178
|
+
)
|
|
166
179
|
|
|
167
180
|
# HTTP services - recommend directory scan
|
|
168
|
-
if any(s in service_names for s in [
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
181
|
+
if any(s in service_names for s in ["http", "https"]) or any(
|
|
182
|
+
p in ports for p in [80, 443, 8080]
|
|
183
|
+
):
|
|
184
|
+
recommendations.append(
|
|
185
|
+
{
|
|
186
|
+
"module": "auxiliary/scanner/http/dir_scanner",
|
|
187
|
+
"name": "HTTP Dir Scanner",
|
|
188
|
+
"reason": "Web server detected - discover directories",
|
|
189
|
+
"priority": "medium",
|
|
190
|
+
}
|
|
191
|
+
)
|
|
175
192
|
|
|
176
193
|
return recommendations
|
|
177
194
|
|
|
178
195
|
|
|
179
196
|
def filter_presets_by_services(
|
|
180
|
-
preset_categories: Dict[str, List[Dict]],
|
|
181
|
-
service_names: Set[str],
|
|
182
|
-
ports: Set[int]
|
|
197
|
+
preset_categories: Dict[str, List[Dict]], service_names: Set[str], ports: Set[int]
|
|
183
198
|
) -> Tuple[Dict[str, List[Dict]], int]:
|
|
184
199
|
"""
|
|
185
200
|
Filter presets to only show those relevant to target's services.
|
|
@@ -197,8 +212,8 @@ def filter_presets_by_services(
|
|
|
197
212
|
for category, presets in preset_categories.items():
|
|
198
213
|
relevant_presets = []
|
|
199
214
|
for preset in presets:
|
|
200
|
-
preset_services = set(s.lower() for s in preset.get(
|
|
201
|
-
preset_ports = set(preset.get(
|
|
215
|
+
preset_services = set(s.lower() for s in preset.get("services", []))
|
|
216
|
+
preset_ports = set(preset.get("ports", []))
|
|
202
217
|
|
|
203
218
|
# Check if any of target's services/ports match
|
|
204
219
|
service_match = bool(preset_services & service_names)
|
|
@@ -219,7 +234,7 @@ def render_msf_auxiliary_menu(
|
|
|
219
234
|
target: str,
|
|
220
235
|
preset_categories: Dict[str, List[Dict]],
|
|
221
236
|
engagement_id: int,
|
|
222
|
-
show_all: bool = False
|
|
237
|
+
show_all: bool = False,
|
|
223
238
|
) -> Optional[Dict]:
|
|
224
239
|
"""
|
|
225
240
|
Render enhanced MSF Auxiliary preset menu.
|
|
@@ -262,25 +277,36 @@ def render_msf_auxiliary_menu(
|
|
|
262
277
|
|
|
263
278
|
# Header
|
|
264
279
|
click.echo()
|
|
265
|
-
click.echo(click.style("=" * width, fg=
|
|
266
|
-
click.echo(
|
|
267
|
-
|
|
280
|
+
click.echo(click.style("=" * width, fg="cyan"))
|
|
281
|
+
click.echo(
|
|
282
|
+
click.style(
|
|
283
|
+
" MSF Auxiliary (Metasploit)".center(width), fg="cyan", bold=True
|
|
284
|
+
)
|
|
285
|
+
)
|
|
286
|
+
click.echo(click.style("=" * width, fg="cyan"))
|
|
268
287
|
click.echo()
|
|
269
288
|
|
|
270
289
|
# Target info with detected services
|
|
271
|
-
click.echo(click.style(f" Target: {target}", fg=
|
|
290
|
+
click.echo(click.style(f" Target: {target}", fg="green", bold=True))
|
|
272
291
|
if services:
|
|
273
292
|
svc_summary = ", ".join(sorted(service_names)[:8])
|
|
274
293
|
if len(service_names) > 8:
|
|
275
294
|
svc_summary += f" (+{len(service_names) - 8} more)"
|
|
276
|
-
click.echo(click.style(f" Services: {svc_summary}", fg=
|
|
277
|
-
click.echo(
|
|
295
|
+
click.echo(click.style(f" Services: {svc_summary}", fg="cyan"))
|
|
296
|
+
click.echo(
|
|
297
|
+
click.style(
|
|
298
|
+
f" Ports: {', '.join(str(p) for p in sorted(ports)[:12])}",
|
|
299
|
+
fg="cyan",
|
|
300
|
+
)
|
|
301
|
+
)
|
|
278
302
|
else:
|
|
279
|
-
click.echo(
|
|
303
|
+
click.echo(
|
|
304
|
+
click.style(" Services: No service data (run nmap first)", fg="yellow")
|
|
305
|
+
)
|
|
280
306
|
click.echo()
|
|
281
307
|
|
|
282
308
|
# Quick actions
|
|
283
|
-
click.echo(click.style(" QUICK ACTIONS", bold=True, fg=
|
|
309
|
+
click.echo(click.style(" QUICK ACTIONS", bold=True, fg="yellow"))
|
|
284
310
|
click.echo(" [e] Run ALL enumeration modules for detected services")
|
|
285
311
|
click.echo(" [v] Run ALL vulnerability scans for detected services")
|
|
286
312
|
click.echo(" [/] Search modules by keyword")
|
|
@@ -290,11 +316,11 @@ def render_msf_auxiliary_menu(
|
|
|
290
316
|
|
|
291
317
|
# Recommendations (if any)
|
|
292
318
|
if recommendations and not show_all:
|
|
293
|
-
click.echo(click.style(" RECOMMENDED", bold=True, fg=
|
|
319
|
+
click.echo(click.style(" RECOMMENDED", bold=True, fg="red"))
|
|
294
320
|
for i, rec in enumerate(recommendations[:3], 1):
|
|
295
|
-
priority_color =
|
|
296
|
-
status = job_status_map.get(rec[
|
|
297
|
-
status_icon = _get_status_icon(status.get(
|
|
321
|
+
priority_color = "red" if rec["priority"] == "high" else "yellow"
|
|
322
|
+
status = job_status_map.get(rec["module"], {})
|
|
323
|
+
status_icon = _get_status_icon(status.get("status"))
|
|
298
324
|
click.echo(f" [r{i}] {status_icon} {rec['name']} - {rec['reason']}")
|
|
299
325
|
click.echo()
|
|
300
326
|
|
|
@@ -303,24 +329,28 @@ def render_msf_auxiliary_menu(
|
|
|
303
329
|
preset_map = {} # num -> preset
|
|
304
330
|
|
|
305
331
|
for category_name, category_presets in filtered_categories.items():
|
|
306
|
-
display_name = category_name.replace(
|
|
307
|
-
click.echo(click.style(f" {display_name}:", bold=True, fg=
|
|
332
|
+
display_name = category_name.replace("_", " ").title()
|
|
333
|
+
click.echo(click.style(f" {display_name}:", bold=True, fg="cyan"))
|
|
308
334
|
|
|
309
335
|
for preset in category_presets:
|
|
310
336
|
# Get job status for this module
|
|
311
|
-
module_path = preset[
|
|
337
|
+
module_path = preset["args"][0] if preset.get("args") else ""
|
|
312
338
|
status = job_status_map.get(module_path, {})
|
|
313
|
-
status_icon = _get_status_icon(status.get(
|
|
339
|
+
status_icon = _get_status_icon(status.get("status"))
|
|
314
340
|
status_info = _get_status_info(status)
|
|
315
341
|
|
|
316
342
|
# Format name with status
|
|
317
|
-
name = preset[
|
|
318
|
-
desc = preset[
|
|
343
|
+
name = preset["name"]
|
|
344
|
+
desc = preset["desc"]
|
|
319
345
|
|
|
320
346
|
if status_info:
|
|
321
|
-
click.echo(
|
|
347
|
+
click.echo(
|
|
348
|
+
f" {preset_num:2d}. {status_icon} {name:<24} - {desc} {status_info}"
|
|
349
|
+
)
|
|
322
350
|
else:
|
|
323
|
-
click.echo(
|
|
351
|
+
click.echo(
|
|
352
|
+
f" {preset_num:2d}. {status_icon} {name:<24} - {desc}"
|
|
353
|
+
)
|
|
324
354
|
|
|
325
355
|
preset_map[preset_num] = preset
|
|
326
356
|
preset_num += 1
|
|
@@ -334,41 +364,60 @@ def render_msf_auxiliary_menu(
|
|
|
334
364
|
|
|
335
365
|
# Hidden modules notice
|
|
336
366
|
if hidden_count > 0 and not show_all:
|
|
337
|
-
click.echo(
|
|
367
|
+
click.echo(
|
|
368
|
+
click.style(
|
|
369
|
+
f" ({hidden_count} modules hidden - no matching services)",
|
|
370
|
+
fg="yellow",
|
|
371
|
+
dim=True,
|
|
372
|
+
)
|
|
373
|
+
)
|
|
338
374
|
click.echo()
|
|
339
375
|
|
|
340
376
|
try:
|
|
341
|
-
choice =
|
|
342
|
-
click.
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
377
|
+
choice = (
|
|
378
|
+
click.prompt(
|
|
379
|
+
click.style(" Select option", fg="green", bold=True),
|
|
380
|
+
type=str,
|
|
381
|
+
default="1",
|
|
382
|
+
show_default=False,
|
|
383
|
+
)
|
|
384
|
+
.strip()
|
|
385
|
+
.lower()
|
|
386
|
+
)
|
|
347
387
|
|
|
348
388
|
# Handle special commands
|
|
349
|
-
if choice ==
|
|
389
|
+
if choice == "q":
|
|
350
390
|
return None
|
|
351
391
|
|
|
352
|
-
elif choice ==
|
|
392
|
+
elif choice == "e":
|
|
353
393
|
# Batch enumeration
|
|
354
|
-
return _batch_select(
|
|
394
|
+
return _batch_select(
|
|
395
|
+
filtered_categories.get("enumeration", []), "enumeration"
|
|
396
|
+
)
|
|
355
397
|
|
|
356
|
-
elif choice ==
|
|
398
|
+
elif choice == "v":
|
|
357
399
|
# Batch vulnerability scan
|
|
358
|
-
return _batch_select(
|
|
400
|
+
return _batch_select(
|
|
401
|
+
filtered_categories.get("vulnerability_scanning", []),
|
|
402
|
+
"vulnerability",
|
|
403
|
+
)
|
|
359
404
|
|
|
360
|
-
elif choice ==
|
|
405
|
+
elif choice == "/":
|
|
361
406
|
# Search
|
|
362
|
-
search_term =
|
|
407
|
+
search_term = (
|
|
408
|
+
click.prompt(" Search", type=str, default="").strip().lower()
|
|
409
|
+
)
|
|
363
410
|
if search_term:
|
|
364
411
|
return _search_presets(preset_categories, search_term)
|
|
365
412
|
continue
|
|
366
413
|
|
|
367
|
-
elif choice ==
|
|
414
|
+
elif choice == "*":
|
|
368
415
|
# Show all - recursive call with show_all=True
|
|
369
|
-
return render_msf_auxiliary_menu(
|
|
416
|
+
return render_msf_auxiliary_menu(
|
|
417
|
+
target, preset_categories, engagement_id, show_all=True
|
|
418
|
+
)
|
|
370
419
|
|
|
371
|
-
elif choice.startswith(
|
|
420
|
+
elif choice.startswith("r") and len(choice) == 2:
|
|
372
421
|
# Recommendation selection
|
|
373
422
|
try:
|
|
374
423
|
rec_num = int(choice[1])
|
|
@@ -377,7 +426,7 @@ def render_msf_auxiliary_menu(
|
|
|
377
426
|
# Find matching preset
|
|
378
427
|
for presets in preset_categories.values():
|
|
379
428
|
for preset in presets:
|
|
380
|
-
if preset.get(
|
|
429
|
+
if preset.get("args", [""])[0] == rec["module"]:
|
|
381
430
|
return preset
|
|
382
431
|
except ValueError:
|
|
383
432
|
pass
|
|
@@ -390,9 +439,9 @@ def render_msf_auxiliary_menu(
|
|
|
390
439
|
return preset_map[num]
|
|
391
440
|
elif num == preset_num:
|
|
392
441
|
# Custom args
|
|
393
|
-
return {
|
|
442
|
+
return {"custom": True}
|
|
394
443
|
except ValueError:
|
|
395
|
-
click.echo(click.style(" Invalid selection", fg=
|
|
444
|
+
click.echo(click.style(" Invalid selection", fg="red"))
|
|
396
445
|
click.pause()
|
|
397
446
|
|
|
398
447
|
except (KeyboardInterrupt, click.Abort):
|
|
@@ -401,14 +450,14 @@ def render_msf_auxiliary_menu(
|
|
|
401
450
|
|
|
402
451
|
def _get_status_icon(status: Optional[str]) -> str:
|
|
403
452
|
"""Get status icon for a module."""
|
|
404
|
-
if status ==
|
|
405
|
-
return click.style("", fg=
|
|
406
|
-
elif status ==
|
|
407
|
-
return click.style("", fg=
|
|
408
|
-
elif status ==
|
|
409
|
-
return click.style("", fg=
|
|
453
|
+
if status == "done":
|
|
454
|
+
return click.style("", fg="green")
|
|
455
|
+
elif status == "running":
|
|
456
|
+
return click.style("", fg="yellow")
|
|
457
|
+
elif status == "pending":
|
|
458
|
+
return click.style("", fg="blue")
|
|
410
459
|
else:
|
|
411
|
-
return click.style("", fg=
|
|
460
|
+
return click.style("", fg="white", dim=True)
|
|
412
461
|
|
|
413
462
|
|
|
414
463
|
def _get_status_info(status: Dict) -> str:
|
|
@@ -416,28 +465,28 @@ def _get_status_info(status: Dict) -> str:
|
|
|
416
465
|
if not status:
|
|
417
466
|
return ""
|
|
418
467
|
|
|
419
|
-
s = status.get(
|
|
420
|
-
if s ==
|
|
421
|
-
return click.style(f"(Job #{status.get('job_id')})", fg=
|
|
422
|
-
elif s ==
|
|
423
|
-
return click.style(f"(Running #{status.get('job_id')})", fg=
|
|
424
|
-
elif s ==
|
|
425
|
-
return click.style(f"(Queued #{status.get('job_id')})", fg=
|
|
468
|
+
s = status.get("status")
|
|
469
|
+
if s == "done":
|
|
470
|
+
return click.style(f"(Job #{status.get('job_id')})", fg="green", dim=True)
|
|
471
|
+
elif s == "running":
|
|
472
|
+
return click.style(f"(Running #{status.get('job_id')})", fg="yellow")
|
|
473
|
+
elif s == "pending":
|
|
474
|
+
return click.style(f"(Queued #{status.get('job_id')})", fg="blue", dim=True)
|
|
426
475
|
return ""
|
|
427
476
|
|
|
428
477
|
|
|
429
478
|
def _batch_select(presets: List[Dict], batch_type: str) -> Optional[Dict]:
|
|
430
479
|
"""Handle batch selection of multiple presets."""
|
|
431
480
|
if not presets:
|
|
432
|
-
click.echo(
|
|
481
|
+
click.echo(
|
|
482
|
+
click.style(
|
|
483
|
+
f" No {batch_type} modules available for this target", fg="yellow"
|
|
484
|
+
)
|
|
485
|
+
)
|
|
433
486
|
click.pause()
|
|
434
487
|
return None
|
|
435
488
|
|
|
436
|
-
return {
|
|
437
|
-
'batch': True,
|
|
438
|
-
'batch_type': batch_type,
|
|
439
|
-
'presets': presets
|
|
440
|
-
}
|
|
489
|
+
return {"batch": True, "batch_type": batch_type, "presets": presets}
|
|
441
490
|
|
|
442
491
|
|
|
443
492
|
def _search_presets(preset_categories: Dict, search_term: str) -> Optional[Dict]:
|
|
@@ -446,15 +495,15 @@ def _search_presets(preset_categories: Dict, search_term: str) -> Optional[Dict]
|
|
|
446
495
|
|
|
447
496
|
for category, presets in preset_categories.items():
|
|
448
497
|
for preset in presets:
|
|
449
|
-
name = preset.get(
|
|
450
|
-
desc = preset.get(
|
|
451
|
-
module = preset.get(
|
|
498
|
+
name = preset.get("name", "").lower()
|
|
499
|
+
desc = preset.get("desc", "").lower()
|
|
500
|
+
module = preset.get("args", [""])[0].lower()
|
|
452
501
|
|
|
453
502
|
if search_term in name or search_term in desc or search_term in module:
|
|
454
503
|
matches.append(preset)
|
|
455
504
|
|
|
456
505
|
if not matches:
|
|
457
|
-
click.echo(click.style(f" No modules matching '{search_term}'", fg=
|
|
506
|
+
click.echo(click.style(f" No modules matching '{search_term}'", fg="yellow"))
|
|
458
507
|
click.pause()
|
|
459
508
|
return None
|
|
460
509
|
|
|
@@ -463,13 +512,15 @@ def _search_presets(preset_categories: Dict, search_term: str) -> Optional[Dict]
|
|
|
463
512
|
|
|
464
513
|
# Show matches
|
|
465
514
|
click.echo()
|
|
466
|
-
click.echo(click.style(f" Found {len(matches)} matching modules:", fg=
|
|
515
|
+
click.echo(click.style(f" Found {len(matches)} matching modules:", fg="cyan"))
|
|
467
516
|
for i, preset in enumerate(matches, 1):
|
|
468
517
|
click.echo(f" {i}. {preset['name']} - {preset['desc']}")
|
|
469
518
|
click.echo()
|
|
470
519
|
|
|
471
520
|
try:
|
|
472
|
-
choice = click.prompt(
|
|
521
|
+
choice = click.prompt(
|
|
522
|
+
" Select option", type=int, default=1, show_default=False
|
|
523
|
+
)
|
|
473
524
|
if 1 <= choice <= len(matches):
|
|
474
525
|
return matches[choice - 1]
|
|
475
526
|
except (ValueError, KeyboardInterrupt, click.Abort):
|