souleyez 2.43.26__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 +23434 -10286
- 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.26.dist-info → souleyez-2.43.34.dist-info}/METADATA +1 -1
- souleyez-2.43.34.dist-info/RECORD +443 -0
- {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/WHEEL +1 -1
- souleyez-2.43.26.dist-info/RECORD +0 -379
- {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/entry_points.txt +0 -0
- {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Impacket GetUserSPNs handler.
|
|
4
|
+
|
|
5
|
+
Handles parsing and display for Kerberoasting results.
|
|
6
|
+
"""
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
import re
|
|
10
|
+
from typing import Any, Dict, Optional
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
from souleyez.engine.job_status import STATUS_DONE, STATUS_ERROR, STATUS_NO_RESULTS
|
|
15
|
+
from souleyez.handlers.base import BaseToolHandler
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ImpacketGetUserSPNsHandler(BaseToolHandler):
|
|
21
|
+
"""Handler for Impacket GetUserSPNs Kerberoasting jobs."""
|
|
22
|
+
|
|
23
|
+
tool_name = "impacket-GetUserSPNs"
|
|
24
|
+
display_name = "GetUserSPNs (Kerberoast)"
|
|
25
|
+
|
|
26
|
+
has_error_handler = True
|
|
27
|
+
has_warning_handler = True
|
|
28
|
+
has_no_results_handler = True
|
|
29
|
+
has_done_handler = True
|
|
30
|
+
|
|
31
|
+
def parse_job(
|
|
32
|
+
self,
|
|
33
|
+
engagement_id: int,
|
|
34
|
+
log_path: str,
|
|
35
|
+
job: Dict[str, Any],
|
|
36
|
+
host_manager: Optional[Any] = None,
|
|
37
|
+
findings_manager: Optional[Any] = None,
|
|
38
|
+
credentials_manager: Optional[Any] = None,
|
|
39
|
+
) -> Dict[str, Any]:
|
|
40
|
+
"""Parse GetUserSPNs Kerberoasting results."""
|
|
41
|
+
try:
|
|
42
|
+
# Import managers if not provided
|
|
43
|
+
if host_manager is None:
|
|
44
|
+
from souleyez.storage.hosts import HostManager
|
|
45
|
+
|
|
46
|
+
host_manager = HostManager()
|
|
47
|
+
if findings_manager is None:
|
|
48
|
+
from souleyez.storage.findings import FindingsManager
|
|
49
|
+
|
|
50
|
+
findings_manager = FindingsManager()
|
|
51
|
+
if credentials_manager is None:
|
|
52
|
+
from souleyez.storage.credentials import CredentialsManager
|
|
53
|
+
|
|
54
|
+
credentials_manager = CredentialsManager()
|
|
55
|
+
|
|
56
|
+
target = job.get("target", "")
|
|
57
|
+
|
|
58
|
+
# Read log file
|
|
59
|
+
if not log_path or not os.path.exists(log_path):
|
|
60
|
+
return {
|
|
61
|
+
"tool": "impacket-GetUserSPNs",
|
|
62
|
+
"status": STATUS_ERROR,
|
|
63
|
+
"error": "Log file not found",
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
67
|
+
output = f.read()
|
|
68
|
+
|
|
69
|
+
# Parse for SPNs and hashes
|
|
70
|
+
spns = []
|
|
71
|
+
kerberos_hashes = []
|
|
72
|
+
|
|
73
|
+
# Extract SPN entries (table format)
|
|
74
|
+
# ServicePrincipalName Name MemberOf PasswordLastSet LastLogon Delegation
|
|
75
|
+
spn_pattern = r"^([^\s]+)\s+([^\s]+)\s+([^\s]*)\s+(\d{4}-\d{2}-\d{2}[^\s]*)\s+(\d{4}-\d{2}-\d{2}[^\s]*)"
|
|
76
|
+
for line in output.split("\n"):
|
|
77
|
+
# Skip header lines
|
|
78
|
+
if "ServicePrincipalName" in line or "----" in line:
|
|
79
|
+
continue
|
|
80
|
+
match = re.match(spn_pattern, line.strip())
|
|
81
|
+
if match:
|
|
82
|
+
spns.append(
|
|
83
|
+
{
|
|
84
|
+
"spn": match.group(1),
|
|
85
|
+
"name": match.group(2),
|
|
86
|
+
"member_of": match.group(3) if match.group(3) else None,
|
|
87
|
+
"password_last_set": match.group(4),
|
|
88
|
+
"last_logon": match.group(5),
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Extract Kerberos TGS hashes ($krb5tgs$...)
|
|
93
|
+
hash_pattern = r"(\$krb5tgs\$\d+\$\*[^\s]+)"
|
|
94
|
+
for match in re.finditer(hash_pattern, output):
|
|
95
|
+
kerberos_hashes.append(match.group(1))
|
|
96
|
+
|
|
97
|
+
# Get or create host
|
|
98
|
+
host_id = None
|
|
99
|
+
ip_match = re.search(r"(\d+\.\d+\.\d+\.\d+)", target)
|
|
100
|
+
if ip_match:
|
|
101
|
+
host_ip = ip_match.group(1)
|
|
102
|
+
host = host_manager.get_host_by_ip(engagement_id, host_ip)
|
|
103
|
+
if host:
|
|
104
|
+
host_id = host["id"]
|
|
105
|
+
else:
|
|
106
|
+
host_id = host_manager.add_or_update_host(
|
|
107
|
+
engagement_id, {"ip": host_ip, "status": "up"}
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Store hashes as credentials
|
|
111
|
+
hashes_stored = 0
|
|
112
|
+
for hash_val in kerberos_hashes:
|
|
113
|
+
# Extract username from hash ($krb5tgs$23$*USERNAME$REALM$...)
|
|
114
|
+
user_match = re.search(
|
|
115
|
+
r"\$krb5tgs\$\d+\$\*([^$\*]+)\$([^$\*]+)", hash_val
|
|
116
|
+
)
|
|
117
|
+
if user_match and host_id:
|
|
118
|
+
username = user_match.group(1)
|
|
119
|
+
domain = user_match.group(2)
|
|
120
|
+
try:
|
|
121
|
+
credentials_manager.add_credential(
|
|
122
|
+
engagement_id=engagement_id,
|
|
123
|
+
host_id=host_id,
|
|
124
|
+
username=username,
|
|
125
|
+
password=hash_val,
|
|
126
|
+
credential_type="kerberos_tgs",
|
|
127
|
+
source="kerberoast",
|
|
128
|
+
tool="impacket-GetUserSPNs",
|
|
129
|
+
notes=f"Kerberos TGS hash for {username}@{domain} - crack with hashcat -m 13100",
|
|
130
|
+
)
|
|
131
|
+
hashes_stored += 1
|
|
132
|
+
except Exception as e:
|
|
133
|
+
logger.warning(f"Could not store Kerberos hash: {e}")
|
|
134
|
+
|
|
135
|
+
# Create finding if SPNs found
|
|
136
|
+
if spns and host_id:
|
|
137
|
+
spn_list = ", ".join([s["name"] for s in spns])
|
|
138
|
+
findings_manager.add_finding(
|
|
139
|
+
engagement_id=engagement_id,
|
|
140
|
+
host_id=host_id,
|
|
141
|
+
finding_type="kerberoastable_accounts",
|
|
142
|
+
severity="high",
|
|
143
|
+
title="Kerberoastable Service Accounts Found",
|
|
144
|
+
description=(
|
|
145
|
+
f"Found {len(spns)} account(s) with Service Principal Names (SPNs) that can be "
|
|
146
|
+
f"Kerberoasted: {spn_list}. TGS tickets have been requested and can be cracked "
|
|
147
|
+
f"offline to recover plaintext passwords."
|
|
148
|
+
),
|
|
149
|
+
evidence=f"Accounts: {spn_list}\nHashes extracted: {len(kerberos_hashes)}",
|
|
150
|
+
tool="impacket-GetUserSPNs",
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
# Determine status
|
|
154
|
+
if kerberos_hashes:
|
|
155
|
+
status = STATUS_DONE
|
|
156
|
+
elif spns:
|
|
157
|
+
status = STATUS_DONE
|
|
158
|
+
elif "error" in output.lower() or "exception" in output.lower():
|
|
159
|
+
status = STATUS_ERROR
|
|
160
|
+
else:
|
|
161
|
+
status = STATUS_NO_RESULTS
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
"tool": "impacket-GetUserSPNs",
|
|
165
|
+
"status": status,
|
|
166
|
+
"target": target,
|
|
167
|
+
"spns_found": len(spns),
|
|
168
|
+
"spns": spns,
|
|
169
|
+
"hashes_found": len(kerberos_hashes),
|
|
170
|
+
"hashes": kerberos_hashes,
|
|
171
|
+
"hashes_stored": hashes_stored,
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
except Exception as e:
|
|
175
|
+
logger.error(f"Error parsing GetUserSPNs job: {e}")
|
|
176
|
+
return {
|
|
177
|
+
"tool": "impacket-GetUserSPNs",
|
|
178
|
+
"status": STATUS_ERROR,
|
|
179
|
+
"error": str(e),
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
def display_done(
|
|
183
|
+
self,
|
|
184
|
+
job: Dict[str, Any],
|
|
185
|
+
log_path: str,
|
|
186
|
+
show_all: bool = False,
|
|
187
|
+
show_passwords: bool = False,
|
|
188
|
+
) -> None:
|
|
189
|
+
"""Display successful Kerberoasting results."""
|
|
190
|
+
click.echo()
|
|
191
|
+
click.echo(click.style("=" * 70, fg="green"))
|
|
192
|
+
click.echo(click.style("KERBEROASTING SUCCESSFUL!", fg="green", bold=True))
|
|
193
|
+
click.echo(click.style("=" * 70, fg="green"))
|
|
194
|
+
click.echo()
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
198
|
+
output = f.read()
|
|
199
|
+
|
|
200
|
+
# Count SPNs and hashes
|
|
201
|
+
spn_count = 0
|
|
202
|
+
hash_count = 0
|
|
203
|
+
accounts = []
|
|
204
|
+
|
|
205
|
+
# Parse SPN table
|
|
206
|
+
in_table = False
|
|
207
|
+
for line in output.split("\n"):
|
|
208
|
+
if "ServicePrincipalName" in line:
|
|
209
|
+
in_table = True
|
|
210
|
+
continue
|
|
211
|
+
if in_table and "----" in line:
|
|
212
|
+
continue
|
|
213
|
+
if in_table and line.strip():
|
|
214
|
+
parts = line.split()
|
|
215
|
+
if len(parts) >= 2 and "/" in parts[0]:
|
|
216
|
+
spn_count += 1
|
|
217
|
+
accounts.append(parts[1]) # Account name
|
|
218
|
+
elif not parts[0].startswith("$"):
|
|
219
|
+
in_table = False
|
|
220
|
+
|
|
221
|
+
# Count hashes
|
|
222
|
+
hash_count = len(re.findall(r"\$krb5tgs\$", output))
|
|
223
|
+
|
|
224
|
+
if accounts:
|
|
225
|
+
click.echo(
|
|
226
|
+
click.style(
|
|
227
|
+
f" Kerberoastable Accounts: {len(accounts)}", bold=True
|
|
228
|
+
)
|
|
229
|
+
)
|
|
230
|
+
for acc in accounts:
|
|
231
|
+
click.echo(f" - {click.style(acc, fg='cyan', bold=True)}")
|
|
232
|
+
click.echo()
|
|
233
|
+
|
|
234
|
+
if hash_count:
|
|
235
|
+
click.echo(
|
|
236
|
+
click.style(
|
|
237
|
+
f" TGS Hashes Captured: {hash_count}", fg="yellow", bold=True
|
|
238
|
+
)
|
|
239
|
+
)
|
|
240
|
+
click.echo()
|
|
241
|
+
click.echo(click.style(" Crack with:", bold=True))
|
|
242
|
+
click.echo(" hashcat -m 13100 hashes.txt /path/to/wordlist")
|
|
243
|
+
click.echo()
|
|
244
|
+
|
|
245
|
+
click.echo(click.style(" Next Steps:", bold=True))
|
|
246
|
+
click.echo(" - Crack hashes offline with hashcat/john")
|
|
247
|
+
click.echo(" - Use cracked passwords for lateral movement")
|
|
248
|
+
click.echo(" - Check if accounts have admin privileges")
|
|
249
|
+
|
|
250
|
+
except Exception as e:
|
|
251
|
+
click.echo(f" Error reading results: {e}")
|
|
252
|
+
|
|
253
|
+
click.echo()
|
|
254
|
+
click.echo(click.style("=" * 70, fg="green"))
|
|
255
|
+
click.echo()
|
|
256
|
+
|
|
257
|
+
def display_warning(
|
|
258
|
+
self,
|
|
259
|
+
job: Dict[str, Any],
|
|
260
|
+
log_path: str,
|
|
261
|
+
log_content: Optional[str] = None,
|
|
262
|
+
) -> None:
|
|
263
|
+
"""Display warning - but check if we actually got results."""
|
|
264
|
+
# First check if we actually got hashes (success case)
|
|
265
|
+
if log_content is None and log_path:
|
|
266
|
+
try:
|
|
267
|
+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
268
|
+
log_content = f.read()
|
|
269
|
+
except Exception:
|
|
270
|
+
log_content = ""
|
|
271
|
+
|
|
272
|
+
if log_content and "$krb5tgs$" in log_content:
|
|
273
|
+
# We got hashes! Display as success
|
|
274
|
+
self.display_done(job, log_path)
|
|
275
|
+
return
|
|
276
|
+
|
|
277
|
+
click.echo()
|
|
278
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
279
|
+
click.echo(click.style("KERBEROASTING - WARNING", fg="yellow", bold=True))
|
|
280
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
281
|
+
click.echo()
|
|
282
|
+
click.echo(" Scan completed with warnings.")
|
|
283
|
+
if log_content:
|
|
284
|
+
if "KDC_ERR" in log_content:
|
|
285
|
+
click.echo(" Kerberos error - credentials may be invalid")
|
|
286
|
+
elif "CCache file is not found" in log_content:
|
|
287
|
+
click.echo(" No cached credentials (this is normal)")
|
|
288
|
+
click.echo()
|
|
289
|
+
click.echo(" Press [r] to view raw logs for details.")
|
|
290
|
+
click.echo()
|
|
291
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
292
|
+
click.echo()
|
|
293
|
+
|
|
294
|
+
def display_error(
|
|
295
|
+
self,
|
|
296
|
+
job: Dict[str, Any],
|
|
297
|
+
log_path: str,
|
|
298
|
+
show_all: bool = False,
|
|
299
|
+
) -> None:
|
|
300
|
+
"""Display Kerberoasting error."""
|
|
301
|
+
click.echo()
|
|
302
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
303
|
+
click.echo(click.style("KERBEROASTING FAILED", fg="red", bold=True))
|
|
304
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
305
|
+
click.echo()
|
|
306
|
+
|
|
307
|
+
try:
|
|
308
|
+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
309
|
+
output = f.read()
|
|
310
|
+
|
|
311
|
+
if "KDC_ERR_PREAUTH_FAILED" in output:
|
|
312
|
+
click.echo(" Authentication failed - invalid credentials")
|
|
313
|
+
elif "KDC_ERR_C_PRINCIPAL_UNKNOWN" in output:
|
|
314
|
+
click.echo(" User not found in domain")
|
|
315
|
+
elif "Connection refused" in output:
|
|
316
|
+
click.echo(" Could not connect to domain controller")
|
|
317
|
+
else:
|
|
318
|
+
click.echo(" Kerberoasting failed - check raw logs for details")
|
|
319
|
+
|
|
320
|
+
except Exception:
|
|
321
|
+
click.echo(" Could not read error details")
|
|
322
|
+
|
|
323
|
+
click.echo()
|
|
324
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
325
|
+
click.echo()
|
|
326
|
+
|
|
327
|
+
def display_no_results(
|
|
328
|
+
self,
|
|
329
|
+
job: Dict[str, Any],
|
|
330
|
+
log_path: str,
|
|
331
|
+
show_all: bool = False,
|
|
332
|
+
) -> None:
|
|
333
|
+
"""Display no Kerberoastable accounts found."""
|
|
334
|
+
click.echo()
|
|
335
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
336
|
+
click.echo(click.style("NO KERBEROASTABLE ACCOUNTS", fg="yellow", bold=True))
|
|
337
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
338
|
+
click.echo()
|
|
339
|
+
click.echo(" No accounts with SPNs found in this domain.")
|
|
340
|
+
click.echo(" This means there are no service accounts to Kerberoast.")
|
|
341
|
+
click.echo()
|
|
342
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
343
|
+
click.echo()
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Impacket psexec handler.
|
|
4
|
+
|
|
5
|
+
Consolidates parsing and display logic for Impacket psexec remote execution jobs.
|
|
6
|
+
"""
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
import re
|
|
10
|
+
from typing import Any, Dict, Optional
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
from souleyez.engine.job_status import STATUS_DONE, STATUS_NO_RESULTS
|
|
15
|
+
from souleyez.handlers.base import BaseToolHandler
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ImpacketPsexecHandler(BaseToolHandler):
|
|
21
|
+
"""Handler for Impacket psexec remote execution jobs."""
|
|
22
|
+
|
|
23
|
+
tool_name = "impacket-psexec"
|
|
24
|
+
display_name = "PSExec"
|
|
25
|
+
|
|
26
|
+
# All handlers enabled
|
|
27
|
+
has_error_handler = True
|
|
28
|
+
has_warning_handler = True
|
|
29
|
+
has_no_results_handler = True
|
|
30
|
+
has_done_handler = True
|
|
31
|
+
|
|
32
|
+
def parse_job(
|
|
33
|
+
self,
|
|
34
|
+
engagement_id: int,
|
|
35
|
+
log_path: str,
|
|
36
|
+
job: Dict[str, Any],
|
|
37
|
+
host_manager: Optional[Any] = None,
|
|
38
|
+
findings_manager: Optional[Any] = None,
|
|
39
|
+
credentials_manager: Optional[Any] = None,
|
|
40
|
+
) -> Dict[str, Any]:
|
|
41
|
+
"""
|
|
42
|
+
Parse psexec job results.
|
|
43
|
+
|
|
44
|
+
Checks for successful remote shell establishment.
|
|
45
|
+
"""
|
|
46
|
+
try:
|
|
47
|
+
from souleyez.parsers.impacket_parser import parse_psexec
|
|
48
|
+
|
|
49
|
+
# Import managers if not provided
|
|
50
|
+
if host_manager is None:
|
|
51
|
+
from souleyez.storage.hosts import HostManager
|
|
52
|
+
|
|
53
|
+
host_manager = HostManager()
|
|
54
|
+
|
|
55
|
+
target = job.get("target", "")
|
|
56
|
+
parsed = parse_psexec(log_path, target)
|
|
57
|
+
|
|
58
|
+
if "error" in parsed:
|
|
59
|
+
return parsed
|
|
60
|
+
|
|
61
|
+
# Get or create host
|
|
62
|
+
host_id = None
|
|
63
|
+
ip_match = re.search(r"@?(\d+\.\d+\.\d+\.\d+)", target)
|
|
64
|
+
if ip_match:
|
|
65
|
+
host_ip = ip_match.group(1)
|
|
66
|
+
host_id = host_manager.add_or_update_host(
|
|
67
|
+
engagement_id, {"ip": host_ip, "status": "up"}
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
success = parsed.get("success", False)
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
"tool": "impacket-psexec",
|
|
74
|
+
"status": STATUS_DONE if success else STATUS_NO_RESULTS,
|
|
75
|
+
"target": target,
|
|
76
|
+
"success": success,
|
|
77
|
+
"output_lines": parsed.get("output_lines", 0),
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
except Exception as e:
|
|
81
|
+
logger.error(f"Error parsing psexec job: {e}")
|
|
82
|
+
return {"error": str(e)}
|
|
83
|
+
|
|
84
|
+
def display_done(
|
|
85
|
+
self,
|
|
86
|
+
job: Dict[str, Any],
|
|
87
|
+
log_path: str,
|
|
88
|
+
show_all: bool = False,
|
|
89
|
+
show_passwords: bool = False,
|
|
90
|
+
) -> None:
|
|
91
|
+
"""Display successful psexec results."""
|
|
92
|
+
try:
|
|
93
|
+
from souleyez.parsers.impacket_parser import parse_psexec
|
|
94
|
+
|
|
95
|
+
if not log_path or not os.path.exists(log_path):
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
target = job.get("target", "unknown")
|
|
99
|
+
parsed = parse_psexec(log_path, target)
|
|
100
|
+
|
|
101
|
+
success = parsed.get("success", False)
|
|
102
|
+
output_lines = parsed.get("output_lines", 0)
|
|
103
|
+
|
|
104
|
+
click.echo(click.style("=" * 70, fg="green"))
|
|
105
|
+
click.echo(click.style("PSEXEC RESULTS", bold=True, fg="green"))
|
|
106
|
+
click.echo(click.style("=" * 70, fg="green"))
|
|
107
|
+
click.echo()
|
|
108
|
+
|
|
109
|
+
if success:
|
|
110
|
+
click.echo(
|
|
111
|
+
click.style(" [SUCCESS] Remote shell established!", fg="green")
|
|
112
|
+
)
|
|
113
|
+
click.echo(f" Output lines captured: {output_lines}")
|
|
114
|
+
click.echo()
|
|
115
|
+
click.echo(" Press 'r' to view full command output.")
|
|
116
|
+
else:
|
|
117
|
+
click.echo(
|
|
118
|
+
click.style(
|
|
119
|
+
" Connection made but no shell prompt detected.", fg="yellow"
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
click.echo(" Press 'r' to view raw output for details.")
|
|
123
|
+
|
|
124
|
+
click.echo()
|
|
125
|
+
click.echo(click.style("=" * 70, fg="green"))
|
|
126
|
+
click.echo()
|
|
127
|
+
|
|
128
|
+
except Exception as e:
|
|
129
|
+
logger.debug(f"Error in display_done: {e}")
|
|
130
|
+
|
|
131
|
+
def display_warning(
|
|
132
|
+
self,
|
|
133
|
+
job: Dict[str, Any],
|
|
134
|
+
log_path: str,
|
|
135
|
+
log_content: Optional[str] = None,
|
|
136
|
+
) -> None:
|
|
137
|
+
"""Display warning status for psexec."""
|
|
138
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
139
|
+
click.echo(click.style("[WARNING] PSEXEC", bold=True, fg="yellow"))
|
|
140
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
141
|
+
click.echo()
|
|
142
|
+
click.echo(" Remote execution completed with warnings.")
|
|
143
|
+
click.echo(" Check raw logs for details (press 'r').")
|
|
144
|
+
click.echo()
|
|
145
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
146
|
+
click.echo()
|
|
147
|
+
|
|
148
|
+
def display_error(
|
|
149
|
+
self,
|
|
150
|
+
job: Dict[str, Any],
|
|
151
|
+
log_path: str,
|
|
152
|
+
log_content: Optional[str] = None,
|
|
153
|
+
) -> None:
|
|
154
|
+
"""Display error status for psexec."""
|
|
155
|
+
# Read log if not provided
|
|
156
|
+
if log_content is None and log_path and os.path.exists(log_path):
|
|
157
|
+
try:
|
|
158
|
+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
159
|
+
log_content = f.read()
|
|
160
|
+
except Exception:
|
|
161
|
+
log_content = ""
|
|
162
|
+
|
|
163
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
164
|
+
click.echo(click.style("[ERROR] PSEXEC FAILED", bold=True, fg="red"))
|
|
165
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
166
|
+
click.echo()
|
|
167
|
+
|
|
168
|
+
# Check for common psexec errors
|
|
169
|
+
error_msg = None
|
|
170
|
+
if log_content:
|
|
171
|
+
if "Connection refused" in log_content:
|
|
172
|
+
error_msg = "Connection refused - target SMB service may be down"
|
|
173
|
+
elif (
|
|
174
|
+
"Access denied" in log_content.lower()
|
|
175
|
+
or "STATUS_ACCESS_DENIED" in log_content
|
|
176
|
+
):
|
|
177
|
+
error_msg = "Access denied - need admin privileges on target"
|
|
178
|
+
elif "STATUS_LOGON_FAILURE" in log_content:
|
|
179
|
+
error_msg = "Logon failure - invalid credentials"
|
|
180
|
+
elif "timed out" in log_content.lower() or "timeout" in log_content.lower():
|
|
181
|
+
error_msg = "Connection timed out - target may be unreachable"
|
|
182
|
+
elif (
|
|
183
|
+
"Service installation" in log_content
|
|
184
|
+
and "failed" in log_content.lower()
|
|
185
|
+
):
|
|
186
|
+
error_msg = "Service installation failed - AV may be blocking"
|
|
187
|
+
elif "ERROR_SERVICE" in log_content or (
|
|
188
|
+
"service" in log_content.lower() and "error" in log_content.lower()
|
|
189
|
+
):
|
|
190
|
+
error_msg = "Service error - may be blocked by endpoint protection"
|
|
191
|
+
|
|
192
|
+
if error_msg:
|
|
193
|
+
click.echo(f" {error_msg}")
|
|
194
|
+
else:
|
|
195
|
+
click.echo(
|
|
196
|
+
" Remote execution failed - check raw logs for details (press 'r')"
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
click.echo()
|
|
200
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
201
|
+
click.echo()
|
|
202
|
+
|
|
203
|
+
def display_no_results(
|
|
204
|
+
self,
|
|
205
|
+
job: Dict[str, Any],
|
|
206
|
+
log_path: str,
|
|
207
|
+
) -> None:
|
|
208
|
+
"""Display no_results status for psexec."""
|
|
209
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
210
|
+
click.echo(click.style("PSEXEC RESULTS", bold=True, fg="yellow"))
|
|
211
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
212
|
+
click.echo()
|
|
213
|
+
click.echo(" No command output captured.")
|
|
214
|
+
click.echo()
|
|
215
|
+
click.echo(click.style("Possible reasons:", dim=True))
|
|
216
|
+
click.echo(" - Connection failed before shell was established")
|
|
217
|
+
click.echo(" - Insufficient privileges (need local admin)")
|
|
218
|
+
click.echo(" - AV/EDR blocked the service installation")
|
|
219
|
+
click.echo(" - Try smbexec or wmiexec as alternatives")
|
|
220
|
+
click.echo()
|
|
221
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
222
|
+
click.echo()
|