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
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
CrackMapExec handler.
|
|
4
|
+
|
|
5
|
+
Consolidates parsing and display logic for CrackMapExec Windows/AD enumeration jobs.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
from typing import Any, Dict, Optional
|
|
12
|
+
|
|
13
|
+
import click
|
|
14
|
+
|
|
15
|
+
from souleyez.engine.job_status import (
|
|
16
|
+
STATUS_DONE,
|
|
17
|
+
STATUS_ERROR,
|
|
18
|
+
STATUS_NO_RESULTS,
|
|
19
|
+
STATUS_WARNING,
|
|
20
|
+
)
|
|
21
|
+
from souleyez.handlers.base import BaseToolHandler
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class CrackMapExecHandler(BaseToolHandler):
|
|
27
|
+
"""Handler for CrackMapExec Windows/AD enumeration jobs."""
|
|
28
|
+
|
|
29
|
+
tool_name = "crackmapexec"
|
|
30
|
+
display_name = "CrackMapExec"
|
|
31
|
+
|
|
32
|
+
# All handlers enabled
|
|
33
|
+
has_error_handler = True
|
|
34
|
+
has_warning_handler = True
|
|
35
|
+
has_no_results_handler = True
|
|
36
|
+
has_done_handler = True
|
|
37
|
+
|
|
38
|
+
def parse_job(
|
|
39
|
+
self,
|
|
40
|
+
engagement_id: int,
|
|
41
|
+
log_path: str,
|
|
42
|
+
job: Dict[str, Any],
|
|
43
|
+
host_manager: Optional[Any] = None,
|
|
44
|
+
findings_manager: Optional[Any] = None,
|
|
45
|
+
credentials_manager: Optional[Any] = None,
|
|
46
|
+
) -> Dict[str, Any]:
|
|
47
|
+
"""
|
|
48
|
+
Parse CrackMapExec job results.
|
|
49
|
+
|
|
50
|
+
Extracts hosts, credentials, and SMB information.
|
|
51
|
+
"""
|
|
52
|
+
try:
|
|
53
|
+
from souleyez.parsers.crackmapexec_parser import parse_crackmapexec
|
|
54
|
+
|
|
55
|
+
# Import managers if not provided
|
|
56
|
+
if host_manager is None:
|
|
57
|
+
from souleyez.storage.hosts import HostManager
|
|
58
|
+
|
|
59
|
+
host_manager = HostManager()
|
|
60
|
+
if credentials_manager is None:
|
|
61
|
+
from souleyez.storage.credentials import CredentialsManager
|
|
62
|
+
|
|
63
|
+
credentials_manager = CredentialsManager()
|
|
64
|
+
|
|
65
|
+
target = job.get("target", "")
|
|
66
|
+
parsed = parse_crackmapexec(log_path, target)
|
|
67
|
+
|
|
68
|
+
if "error" in parsed:
|
|
69
|
+
return parsed
|
|
70
|
+
|
|
71
|
+
# Store hosts
|
|
72
|
+
for host in parsed.get("findings", {}).get("hosts", []):
|
|
73
|
+
host_manager.add_or_update_host(
|
|
74
|
+
engagement_id,
|
|
75
|
+
{
|
|
76
|
+
"ip": host["ip"],
|
|
77
|
+
"hostname": host.get("hostname"),
|
|
78
|
+
"domain": host.get("domain"),
|
|
79
|
+
"os": host.get("os"),
|
|
80
|
+
"status": "up",
|
|
81
|
+
},
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Store credentials
|
|
85
|
+
creds_added = 0
|
|
86
|
+
for cred in parsed.get("findings", {}).get("credentials", []):
|
|
87
|
+
host = host_manager.get_host_by_ip(engagement_id, target)
|
|
88
|
+
if host:
|
|
89
|
+
credentials_manager.add_credential(
|
|
90
|
+
engagement_id=engagement_id,
|
|
91
|
+
host_id=host["id"],
|
|
92
|
+
username=cred["username"],
|
|
93
|
+
password=cred["password"],
|
|
94
|
+
service="smb",
|
|
95
|
+
credential_type="password",
|
|
96
|
+
tool="crackmapexec",
|
|
97
|
+
status="valid",
|
|
98
|
+
)
|
|
99
|
+
creds_added += 1
|
|
100
|
+
|
|
101
|
+
# Determine status - check all finding types
|
|
102
|
+
hosts_found = len(parsed.get("findings", {}).get("hosts", []))
|
|
103
|
+
shares_found = len(parsed.get("findings", {}).get("shares", []))
|
|
104
|
+
users_found = len(parsed.get("findings", {}).get("users", []))
|
|
105
|
+
vulns_found = len(parsed.get("findings", {}).get("vulnerabilities", []))
|
|
106
|
+
pw_must_change = parsed.get("password_must_change", [])
|
|
107
|
+
|
|
108
|
+
# Check for transient errors that should trigger retry
|
|
109
|
+
transient_errors = [
|
|
110
|
+
"NETBIOS connection with the remote host timed out",
|
|
111
|
+
"Connection reset by peer",
|
|
112
|
+
"Connection timed out",
|
|
113
|
+
"Error enumerating shares",
|
|
114
|
+
]
|
|
115
|
+
log_content = ""
|
|
116
|
+
try:
|
|
117
|
+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
118
|
+
log_content = f.read()
|
|
119
|
+
except Exception:
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
has_transient_error = any(
|
|
123
|
+
err.lower() in log_content.lower() for err in transient_errors
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if (
|
|
127
|
+
hosts_found > 0
|
|
128
|
+
or shares_found > 0
|
|
129
|
+
or users_found > 0
|
|
130
|
+
or creds_added > 0
|
|
131
|
+
or vulns_found > 0
|
|
132
|
+
or len(pw_must_change) > 0
|
|
133
|
+
):
|
|
134
|
+
status = STATUS_DONE
|
|
135
|
+
elif has_transient_error:
|
|
136
|
+
status = STATUS_WARNING # Transient error - may be retried
|
|
137
|
+
else:
|
|
138
|
+
status = STATUS_NO_RESULTS
|
|
139
|
+
|
|
140
|
+
# Extract readable shares for chaining (AD attack paths)
|
|
141
|
+
all_shares = parsed.get("findings", {}).get("shares", [])
|
|
142
|
+
readable_shares = [
|
|
143
|
+
s
|
|
144
|
+
for s in all_shares
|
|
145
|
+
if s.get("permissions") and "READ" in s.get("permissions", "").upper()
|
|
146
|
+
]
|
|
147
|
+
|
|
148
|
+
# Extract admin credentials for post-exploitation chaining
|
|
149
|
+
admin_creds = parsed.get("valid_admin_credentials", [])
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
"tool": "crackmapexec",
|
|
153
|
+
"status": status,
|
|
154
|
+
"target": target,
|
|
155
|
+
"hosts_found": parsed.get("hosts_found", 0),
|
|
156
|
+
"shares_found": parsed.get("shares_found", 0),
|
|
157
|
+
"users_found": parsed.get("users_found", 0),
|
|
158
|
+
"credentials_added": creds_added,
|
|
159
|
+
"vulnerabilities_found": parsed.get("vulnerabilities_found", 0),
|
|
160
|
+
"domains": parsed.get("domains", []),
|
|
161
|
+
# For chaining - AD attack paths
|
|
162
|
+
"readable_shares": readable_shares,
|
|
163
|
+
"shares": all_shares,
|
|
164
|
+
# For post-exploitation chaining
|
|
165
|
+
"valid_admin_credentials": admin_creds,
|
|
166
|
+
"has_admin_access": len(admin_creds) > 0,
|
|
167
|
+
# For password change chaining (STATUS_PASSWORD_MUST_CHANGE)
|
|
168
|
+
"password_must_change": pw_must_change,
|
|
169
|
+
"has_password_must_change": len(pw_must_change) > 0,
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
except Exception as e:
|
|
173
|
+
logger.error(f"Error parsing crackmapexec job: {e}")
|
|
174
|
+
return {"error": str(e)}
|
|
175
|
+
|
|
176
|
+
def display_done(
|
|
177
|
+
self,
|
|
178
|
+
job: Dict[str, Any],
|
|
179
|
+
log_path: str,
|
|
180
|
+
show_all: bool = False,
|
|
181
|
+
show_passwords: bool = False,
|
|
182
|
+
) -> None:
|
|
183
|
+
"""Display successful CrackMapExec results."""
|
|
184
|
+
try:
|
|
185
|
+
from souleyez.parsers.crackmapexec_parser import parse_crackmapexec_output
|
|
186
|
+
|
|
187
|
+
if not log_path or not os.path.exists(log_path):
|
|
188
|
+
return
|
|
189
|
+
|
|
190
|
+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
191
|
+
log_content = f.read()
|
|
192
|
+
parsed = parse_crackmapexec_output(log_content, job.get("target", ""))
|
|
193
|
+
|
|
194
|
+
findings = parsed.get("findings", {})
|
|
195
|
+
hosts = findings.get("hosts", [])
|
|
196
|
+
shares = findings.get("shares", [])
|
|
197
|
+
users = findings.get("users", [])
|
|
198
|
+
vulns = findings.get("vulnerabilities", [])
|
|
199
|
+
creds = findings.get("credentials", [])
|
|
200
|
+
auth_info = findings.get("auth_info", {})
|
|
201
|
+
pw_must_change = findings.get("password_must_change", [])
|
|
202
|
+
|
|
203
|
+
# Only show summary if there's meaningful data
|
|
204
|
+
has_results = (
|
|
205
|
+
hosts
|
|
206
|
+
or shares
|
|
207
|
+
or users
|
|
208
|
+
or vulns
|
|
209
|
+
or creds
|
|
210
|
+
or auth_info
|
|
211
|
+
or pw_must_change
|
|
212
|
+
)
|
|
213
|
+
if not has_results:
|
|
214
|
+
self.display_no_results(job, log_path)
|
|
215
|
+
return
|
|
216
|
+
|
|
217
|
+
# Header
|
|
218
|
+
click.echo(click.style("=" * 70, fg="cyan"))
|
|
219
|
+
click.echo(click.style("SMB ENUMERATION RESULTS", bold=True, fg="cyan"))
|
|
220
|
+
click.echo(click.style("=" * 70, fg="cyan"))
|
|
221
|
+
click.echo()
|
|
222
|
+
|
|
223
|
+
# Host information
|
|
224
|
+
if hosts:
|
|
225
|
+
for host in hosts:
|
|
226
|
+
click.echo(
|
|
227
|
+
click.style(
|
|
228
|
+
f"Target: {host.get('hostname', 'Unknown')} ({host['ip']}:{host.get('port', 445)})",
|
|
229
|
+
bold=True,
|
|
230
|
+
fg="green",
|
|
231
|
+
)
|
|
232
|
+
)
|
|
233
|
+
if host.get("os"):
|
|
234
|
+
click.echo(click.style(f" OS: {host['os']}", fg="white"))
|
|
235
|
+
if host.get("domain"):
|
|
236
|
+
click.echo(
|
|
237
|
+
click.style(f" Domain: {host['domain']}", fg="white")
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
# Security info
|
|
241
|
+
if host.get("signing") or host.get("smbv1"):
|
|
242
|
+
click.echo()
|
|
243
|
+
click.echo(click.style(" Security Status:", bold=True))
|
|
244
|
+
if host.get("signing"):
|
|
245
|
+
signing_color = (
|
|
246
|
+
"red" if host["signing"].lower() == "false" else "green"
|
|
247
|
+
)
|
|
248
|
+
click.echo(
|
|
249
|
+
click.style(
|
|
250
|
+
f" SMB Signing: {host['signing']}",
|
|
251
|
+
fg=signing_color,
|
|
252
|
+
)
|
|
253
|
+
)
|
|
254
|
+
if host.get("smbv1"):
|
|
255
|
+
smbv1_color = (
|
|
256
|
+
"red" if host["smbv1"].lower() == "true" else "green"
|
|
257
|
+
)
|
|
258
|
+
smbv1_status = (
|
|
259
|
+
"Enabled (VULNERABLE)"
|
|
260
|
+
if host["smbv1"].lower() == "true"
|
|
261
|
+
else "Disabled"
|
|
262
|
+
)
|
|
263
|
+
click.echo(
|
|
264
|
+
click.style(
|
|
265
|
+
f" SMBv1: {smbv1_status}", fg=smbv1_color
|
|
266
|
+
)
|
|
267
|
+
)
|
|
268
|
+
click.echo()
|
|
269
|
+
|
|
270
|
+
# Shares
|
|
271
|
+
if shares:
|
|
272
|
+
click.echo(
|
|
273
|
+
click.style(
|
|
274
|
+
f"Shares Found ({len(shares)}):", bold=True, fg="yellow"
|
|
275
|
+
)
|
|
276
|
+
)
|
|
277
|
+
for share in shares:
|
|
278
|
+
name = share.get("name", "Unknown")
|
|
279
|
+
perms = share.get("permissions", "")
|
|
280
|
+
click.echo(f" - {name} ({perms})")
|
|
281
|
+
click.echo()
|
|
282
|
+
|
|
283
|
+
# Users
|
|
284
|
+
if users:
|
|
285
|
+
click.echo(
|
|
286
|
+
click.style(f"Users Found ({len(users)}):", bold=True, fg="green")
|
|
287
|
+
)
|
|
288
|
+
max_users = None if show_all else 15
|
|
289
|
+
display_users = users if max_users is None else users[:max_users]
|
|
290
|
+
for user in display_users:
|
|
291
|
+
click.echo(f" - {user}")
|
|
292
|
+
if max_users and len(users) > max_users:
|
|
293
|
+
click.echo(f" ... and {len(users) - max_users} more")
|
|
294
|
+
click.echo()
|
|
295
|
+
|
|
296
|
+
# Credentials
|
|
297
|
+
if creds:
|
|
298
|
+
click.echo(
|
|
299
|
+
click.style(
|
|
300
|
+
f"Credentials Found ({len(creds)}):", bold=True, fg="red"
|
|
301
|
+
)
|
|
302
|
+
)
|
|
303
|
+
for cred in creds:
|
|
304
|
+
username = cred.get("username", "Unknown")
|
|
305
|
+
if show_passwords and cred.get("password"):
|
|
306
|
+
click.echo(f" - {username}:{cred['password']}")
|
|
307
|
+
else:
|
|
308
|
+
click.echo(f" - {username}:***")
|
|
309
|
+
click.echo()
|
|
310
|
+
|
|
311
|
+
# Password Must Change - HIGH VALUE FINDING
|
|
312
|
+
if pw_must_change:
|
|
313
|
+
click.echo(click.style("=" * 70, fg="red", bold=True))
|
|
314
|
+
click.echo(
|
|
315
|
+
click.style(
|
|
316
|
+
f"PASSWORD MUST CHANGE ({len(pw_must_change)}):",
|
|
317
|
+
bold=True,
|
|
318
|
+
fg="red",
|
|
319
|
+
)
|
|
320
|
+
)
|
|
321
|
+
click.echo(
|
|
322
|
+
click.style(
|
|
323
|
+
" These users can authenticate but MUST change their password!",
|
|
324
|
+
fg="yellow",
|
|
325
|
+
)
|
|
326
|
+
)
|
|
327
|
+
click.echo()
|
|
328
|
+
for entry in pw_must_change:
|
|
329
|
+
domain = entry.get("domain", "")
|
|
330
|
+
username = entry.get("username", "")
|
|
331
|
+
password = entry.get("password", "")
|
|
332
|
+
if show_passwords:
|
|
333
|
+
click.echo(
|
|
334
|
+
click.style(
|
|
335
|
+
f" {domain}\\{username}:{password}",
|
|
336
|
+
fg="red",
|
|
337
|
+
bold=True,
|
|
338
|
+
)
|
|
339
|
+
)
|
|
340
|
+
else:
|
|
341
|
+
click.echo(
|
|
342
|
+
click.style(
|
|
343
|
+
f" {domain}\\{username}:***",
|
|
344
|
+
fg="red",
|
|
345
|
+
bold=True,
|
|
346
|
+
)
|
|
347
|
+
)
|
|
348
|
+
click.echo()
|
|
349
|
+
click.echo(
|
|
350
|
+
click.style(
|
|
351
|
+
" NEXT STEP: Use smbpasswd to change password, then login",
|
|
352
|
+
fg="green",
|
|
353
|
+
bold=True,
|
|
354
|
+
)
|
|
355
|
+
)
|
|
356
|
+
click.echo(
|
|
357
|
+
click.style(
|
|
358
|
+
f" Example: smbpasswd -r <target> -U {pw_must_change[0].get('username', 'USER')}",
|
|
359
|
+
fg="cyan",
|
|
360
|
+
)
|
|
361
|
+
)
|
|
362
|
+
click.echo(click.style("=" * 70, fg="red", bold=True))
|
|
363
|
+
click.echo()
|
|
364
|
+
|
|
365
|
+
# Vulnerabilities
|
|
366
|
+
if vulns:
|
|
367
|
+
click.echo(
|
|
368
|
+
click.style(f"Vulnerabilities ({len(vulns)}):", bold=True, fg="red")
|
|
369
|
+
)
|
|
370
|
+
for vuln in vulns:
|
|
371
|
+
click.echo(f" - {vuln}")
|
|
372
|
+
click.echo()
|
|
373
|
+
|
|
374
|
+
click.echo(click.style("=" * 70, fg="cyan"))
|
|
375
|
+
click.echo()
|
|
376
|
+
|
|
377
|
+
except Exception as e:
|
|
378
|
+
logger.debug(f"Error in display_done: {e}")
|
|
379
|
+
|
|
380
|
+
def display_warning(
|
|
381
|
+
self,
|
|
382
|
+
job: Dict[str, Any],
|
|
383
|
+
log_path: str,
|
|
384
|
+
log_content: Optional[str] = None,
|
|
385
|
+
) -> None:
|
|
386
|
+
"""Display warning status for CrackMapExec."""
|
|
387
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
388
|
+
click.echo(click.style("[WARNING] CRACKMAPEXEC", bold=True, fg="yellow"))
|
|
389
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
390
|
+
click.echo()
|
|
391
|
+
click.echo(" Scan completed with warnings. Check raw logs for details.")
|
|
392
|
+
click.echo(" Press [r] to view raw logs.")
|
|
393
|
+
click.echo()
|
|
394
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
395
|
+
click.echo()
|
|
396
|
+
|
|
397
|
+
def display_error(
|
|
398
|
+
self,
|
|
399
|
+
job: Dict[str, Any],
|
|
400
|
+
log_path: str,
|
|
401
|
+
log_content: Optional[str] = None,
|
|
402
|
+
) -> None:
|
|
403
|
+
"""Display error status for CrackMapExec."""
|
|
404
|
+
# Read log if not provided
|
|
405
|
+
if log_content is None and log_path and os.path.exists(log_path):
|
|
406
|
+
try:
|
|
407
|
+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
408
|
+
log_content = f.read()
|
|
409
|
+
except Exception:
|
|
410
|
+
log_content = ""
|
|
411
|
+
|
|
412
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
413
|
+
click.echo(click.style("[ERROR] CRACKMAPEXEC FAILED", bold=True, fg="red"))
|
|
414
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
415
|
+
click.echo()
|
|
416
|
+
|
|
417
|
+
# Check for common CME errors
|
|
418
|
+
error_msg = None
|
|
419
|
+
if log_content:
|
|
420
|
+
if "Connection refused" in log_content or "Connection reset" in log_content:
|
|
421
|
+
error_msg = "Connection refused - SMB service may be down"
|
|
422
|
+
elif "timed out" in log_content.lower() or "timeout" in log_content.lower():
|
|
423
|
+
error_msg = "Connection timed out - target may be slow or filtering"
|
|
424
|
+
elif "STATUS_LOGON_FAILURE" in log_content:
|
|
425
|
+
error_msg = "Authentication failed - invalid credentials"
|
|
426
|
+
elif "STATUS_ACCESS_DENIED" in log_content:
|
|
427
|
+
error_msg = "Access denied - insufficient privileges"
|
|
428
|
+
elif "Errno 113" in log_content or "No route to host" in log_content:
|
|
429
|
+
error_msg = "No route to host - network unreachable"
|
|
430
|
+
elif "[-]" in log_content:
|
|
431
|
+
match = re.search(r"\[-\]\s*(.+?)(?:\n|$)", log_content)
|
|
432
|
+
if match:
|
|
433
|
+
error_msg = match.group(1).strip()[:100]
|
|
434
|
+
|
|
435
|
+
if error_msg:
|
|
436
|
+
click.echo(f" {error_msg}")
|
|
437
|
+
else:
|
|
438
|
+
click.echo(" Scan failed - see raw logs for details (press 'r')")
|
|
439
|
+
|
|
440
|
+
click.echo()
|
|
441
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
442
|
+
click.echo()
|
|
443
|
+
|
|
444
|
+
def display_no_results(
|
|
445
|
+
self,
|
|
446
|
+
job: Dict[str, Any],
|
|
447
|
+
log_path: str,
|
|
448
|
+
) -> None:
|
|
449
|
+
"""Display no_results status for CrackMapExec."""
|
|
450
|
+
click.echo(click.style("=" * 70, fg="cyan"))
|
|
451
|
+
click.echo(click.style("SMB ENUMERATION RESULTS", bold=True, fg="cyan"))
|
|
452
|
+
click.echo(click.style("=" * 70, fg="cyan"))
|
|
453
|
+
click.echo()
|
|
454
|
+
|
|
455
|
+
if job.get("target"):
|
|
456
|
+
click.echo(click.style(f"Target: {job.get('target')}", bold=True))
|
|
457
|
+
click.echo()
|
|
458
|
+
|
|
459
|
+
# Show summary counts (all zeros for no_results)
|
|
460
|
+
click.echo(click.style("Summary:", bold=True))
|
|
461
|
+
click.echo(f" Hosts: 0")
|
|
462
|
+
click.echo(f" Shares: 0")
|
|
463
|
+
click.echo(f" Users: 0")
|
|
464
|
+
click.echo(f" Credentials: 0")
|
|
465
|
+
click.echo(f" Vulnerabilities: 0")
|
|
466
|
+
click.echo()
|
|
467
|
+
|
|
468
|
+
click.echo(
|
|
469
|
+
click.style("Result: No SMB information discovered", fg="yellow", bold=True)
|
|
470
|
+
)
|
|
471
|
+
click.echo()
|
|
472
|
+
click.echo(" The scan did not find any hosts, shares, or users.")
|
|
473
|
+
click.echo()
|
|
474
|
+
click.echo(click.style("Possible reasons:", dim=True))
|
|
475
|
+
click.echo(" - Target does not have SMB enabled (port 445)")
|
|
476
|
+
click.echo(" - Firewall blocking SMB traffic")
|
|
477
|
+
click.echo(" - Host is offline or unreachable")
|
|
478
|
+
click.echo(" - Authentication required (try: -u user -p password)")
|
|
479
|
+
click.echo()
|
|
480
|
+
click.echo(click.style("Tips:", dim=True))
|
|
481
|
+
click.echo(" - Verify connectivity: nmap -p 445 <target>")
|
|
482
|
+
click.echo(" - Try null session: --shares")
|
|
483
|
+
click.echo(" - Try different protocols: smb, winrm, ldap, ssh")
|
|
484
|
+
click.echo()
|
|
485
|
+
click.echo(click.style("=" * 70, fg="cyan"))
|
|
486
|
+
click.echo()
|