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.
Potentially problematic release.
This version of souleyez might be problematic. Click here for more details.
- 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,510 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
SMBMap handler.
|
|
4
|
+
|
|
5
|
+
Consolidates parsing and display logic for SMBMap SMB share enumeration 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_ERROR, STATUS_NO_RESULTS
|
|
15
|
+
from souleyez.handlers.base import BaseToolHandler
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SMBMapHandler(BaseToolHandler):
|
|
21
|
+
"""Handler for SMBMap SMB share enumeration jobs."""
|
|
22
|
+
|
|
23
|
+
tool_name = "smbmap"
|
|
24
|
+
display_name = "SMBMap"
|
|
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 SMBMap job results.
|
|
43
|
+
|
|
44
|
+
Extracts SMB shares and stores them along with findings.
|
|
45
|
+
"""
|
|
46
|
+
try:
|
|
47
|
+
from souleyez.parsers.smbmap_parser import (
|
|
48
|
+
parse_smbmap_output,
|
|
49
|
+
extract_findings,
|
|
50
|
+
)
|
|
51
|
+
from souleyez.storage.smb_shares import SMBSharesManager
|
|
52
|
+
from souleyez.engine.result_handler import detect_tool_error
|
|
53
|
+
|
|
54
|
+
# Import managers if not provided
|
|
55
|
+
if host_manager is None:
|
|
56
|
+
from souleyez.storage.hosts import HostManager
|
|
57
|
+
|
|
58
|
+
host_manager = HostManager()
|
|
59
|
+
if findings_manager is None:
|
|
60
|
+
from souleyez.storage.findings import FindingsManager
|
|
61
|
+
|
|
62
|
+
findings_manager = FindingsManager()
|
|
63
|
+
|
|
64
|
+
target = job.get("target", "")
|
|
65
|
+
|
|
66
|
+
# Read log file
|
|
67
|
+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
68
|
+
output = f.read()
|
|
69
|
+
|
|
70
|
+
parsed = parse_smbmap_output(output, target)
|
|
71
|
+
|
|
72
|
+
# Get or create host from target
|
|
73
|
+
host_id = None
|
|
74
|
+
if parsed.get("target"):
|
|
75
|
+
is_ip = re.match(
|
|
76
|
+
r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", parsed["target"]
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
if is_ip:
|
|
80
|
+
host = host_manager.get_host_by_ip(engagement_id, parsed["target"])
|
|
81
|
+
if host:
|
|
82
|
+
host_id = host["id"]
|
|
83
|
+
else:
|
|
84
|
+
host_id = host_manager.add_or_update_host(
|
|
85
|
+
engagement_id, {"ip": parsed["target"], "status": "up"}
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if not host_id:
|
|
89
|
+
return {"error": "Could not determine target host"}
|
|
90
|
+
|
|
91
|
+
# Store SMB shares
|
|
92
|
+
smm = SMBSharesManager()
|
|
93
|
+
shares_added = 0
|
|
94
|
+
files_added = 0
|
|
95
|
+
|
|
96
|
+
for share in parsed.get("shares", []):
|
|
97
|
+
share_id = smm.add_share(host_id, share)
|
|
98
|
+
shares_added += 1
|
|
99
|
+
|
|
100
|
+
# Add files if any
|
|
101
|
+
share_files = [
|
|
102
|
+
f
|
|
103
|
+
for f in parsed.get("files", [])
|
|
104
|
+
if f.get("share") == share["name"]
|
|
105
|
+
]
|
|
106
|
+
for file_data in share_files:
|
|
107
|
+
smm.add_file(share_id, file_data)
|
|
108
|
+
files_added += 1
|
|
109
|
+
|
|
110
|
+
# Extract and store findings
|
|
111
|
+
findings_added = 0
|
|
112
|
+
findings = extract_findings(parsed)
|
|
113
|
+
for finding in findings:
|
|
114
|
+
findings_manager.add_finding(
|
|
115
|
+
engagement_id=engagement_id,
|
|
116
|
+
host_id=host_id,
|
|
117
|
+
finding_type="smb_share",
|
|
118
|
+
severity=finding.get("severity"),
|
|
119
|
+
title=finding.get("title"),
|
|
120
|
+
description=finding.get("description"),
|
|
121
|
+
evidence=finding.get("evidence"),
|
|
122
|
+
tool="smbmap",
|
|
123
|
+
)
|
|
124
|
+
findings_added += 1
|
|
125
|
+
|
|
126
|
+
# Check for smbmap errors
|
|
127
|
+
smbmap_error = detect_tool_error(output, "smbmap")
|
|
128
|
+
|
|
129
|
+
# Detect GPP (Group Policy Preferences) files/directories containing credentials
|
|
130
|
+
gpp_files = []
|
|
131
|
+
|
|
132
|
+
# GPP XML files that contain credentials
|
|
133
|
+
gpp_xml_patterns = [
|
|
134
|
+
r"Groups\.xml",
|
|
135
|
+
r"ScheduledTasks\.xml",
|
|
136
|
+
r"Services\.xml",
|
|
137
|
+
r"Drives\.xml",
|
|
138
|
+
r"DataSources\.xml",
|
|
139
|
+
r"Printers\.xml",
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
# GPP directory names -> corresponding XML file
|
|
143
|
+
# If we see these directories under Preferences, the XML file is inside
|
|
144
|
+
gpp_dir_to_file = {
|
|
145
|
+
"groups": "Groups.xml",
|
|
146
|
+
"scheduledtasks": "ScheduledTasks.xml",
|
|
147
|
+
"services": "Services.xml",
|
|
148
|
+
"drives": "Drives.xml",
|
|
149
|
+
"datasources": "DataSources.xml",
|
|
150
|
+
"printers": "Printers.xml",
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
# Check raw output for GPP file paths
|
|
154
|
+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
155
|
+
raw_output = f.read()
|
|
156
|
+
|
|
157
|
+
# Track the current path context from smbmap output
|
|
158
|
+
current_path = None
|
|
159
|
+
current_share = None
|
|
160
|
+
|
|
161
|
+
for line in raw_output.split("\n"):
|
|
162
|
+
# Detect path headers like: ./Replication//active.htb/Policies/{GUID}/MACHINE/Preferences
|
|
163
|
+
path_header = re.match(r"\./([^/]+)//(.+)", line.strip())
|
|
164
|
+
if path_header:
|
|
165
|
+
current_share = path_header.group(1)
|
|
166
|
+
current_path = path_header.group(2)
|
|
167
|
+
continue
|
|
168
|
+
|
|
169
|
+
# Method 1: Direct XML file detection (if depth is sufficient)
|
|
170
|
+
for pattern in gpp_xml_patterns:
|
|
171
|
+
if re.search(pattern, line, re.IGNORECASE):
|
|
172
|
+
# Found actual XML file in listing
|
|
173
|
+
file_match = re.search(
|
|
174
|
+
r"(Groups|ScheduledTasks|Services|Drives|DataSources|Printers)\.xml",
|
|
175
|
+
line,
|
|
176
|
+
re.IGNORECASE,
|
|
177
|
+
)
|
|
178
|
+
if file_match and current_path:
|
|
179
|
+
gpp_files.append(
|
|
180
|
+
{
|
|
181
|
+
"share": current_share,
|
|
182
|
+
"path": current_path + "/" + file_match.group(0),
|
|
183
|
+
"file": file_match.group(0),
|
|
184
|
+
"detected_by": "direct",
|
|
185
|
+
}
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
# Method 2: Detect GPP directories under Preferences (infer XML file inside)
|
|
189
|
+
# This handles shallow depth scans where we see Groups directory but not Groups.xml
|
|
190
|
+
if current_path and "/Preferences" in current_path:
|
|
191
|
+
for dir_name, xml_file in gpp_dir_to_file.items():
|
|
192
|
+
# Match directory listing ending with the GPP dir name
|
|
193
|
+
# Format: dr--r--r-- 0 Sat Jul 21 00:37:44 2018 Groups
|
|
194
|
+
if re.search(r"\s" + dir_name + r"\s*$", line, re.IGNORECASE):
|
|
195
|
+
# Found GPP directory - infer the XML file path
|
|
196
|
+
inferred_path = (
|
|
197
|
+
current_path
|
|
198
|
+
+ "/"
|
|
199
|
+
+ dir_name.capitalize()
|
|
200
|
+
+ "/"
|
|
201
|
+
+ xml_file
|
|
202
|
+
)
|
|
203
|
+
gpp_files.append(
|
|
204
|
+
{
|
|
205
|
+
"share": current_share,
|
|
206
|
+
"path": inferred_path,
|
|
207
|
+
"file": xml_file,
|
|
208
|
+
"detected_by": "directory_inference",
|
|
209
|
+
}
|
|
210
|
+
)
|
|
211
|
+
break
|
|
212
|
+
|
|
213
|
+
# Deduplicate GPP files
|
|
214
|
+
seen_paths = set()
|
|
215
|
+
unique_gpp = []
|
|
216
|
+
for gpp in gpp_files:
|
|
217
|
+
path_key = gpp.get("path", "")
|
|
218
|
+
if path_key and path_key not in seen_paths:
|
|
219
|
+
seen_paths.add(path_key)
|
|
220
|
+
unique_gpp.append(gpp)
|
|
221
|
+
gpp_files = unique_gpp
|
|
222
|
+
|
|
223
|
+
if gpp_files:
|
|
224
|
+
logger.info(
|
|
225
|
+
f"smbmap found {len(gpp_files)} GPP file(s) - potential credentials!"
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Determine status
|
|
229
|
+
if smbmap_error:
|
|
230
|
+
status = STATUS_ERROR
|
|
231
|
+
elif shares_added > 0 or findings_added > 0:
|
|
232
|
+
status = STATUS_DONE
|
|
233
|
+
else:
|
|
234
|
+
status = STATUS_NO_RESULTS
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
"tool": "smbmap",
|
|
238
|
+
"status": status,
|
|
239
|
+
"host": parsed.get("target"),
|
|
240
|
+
"connection_status": parsed.get("status", "Unknown"),
|
|
241
|
+
"shares_added": shares_added,
|
|
242
|
+
"files_added": files_added,
|
|
243
|
+
"findings_added": findings_added,
|
|
244
|
+
"gpp_files": gpp_files,
|
|
245
|
+
"has_gpp_files": len(gpp_files) > 0,
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
except Exception as e:
|
|
249
|
+
logger.error(f"Error parsing smbmap job: {e}")
|
|
250
|
+
return {"error": str(e)}
|
|
251
|
+
|
|
252
|
+
def display_done(
|
|
253
|
+
self,
|
|
254
|
+
job: Dict[str, Any],
|
|
255
|
+
log_path: str,
|
|
256
|
+
show_all: bool = False,
|
|
257
|
+
show_passwords: bool = False,
|
|
258
|
+
) -> None:
|
|
259
|
+
"""Display successful SMBMap results."""
|
|
260
|
+
try:
|
|
261
|
+
from souleyez.parsers.smbmap_parser import parse_smbmap_output
|
|
262
|
+
|
|
263
|
+
if not log_path or not os.path.exists(log_path):
|
|
264
|
+
return
|
|
265
|
+
|
|
266
|
+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
267
|
+
log_content = f.read()
|
|
268
|
+
parsed = parse_smbmap_output(log_content, job.get("target", ""))
|
|
269
|
+
|
|
270
|
+
shares = parsed.get("shares", [])
|
|
271
|
+
|
|
272
|
+
# Header
|
|
273
|
+
click.echo(click.style("=" * 70, fg="cyan"))
|
|
274
|
+
click.echo(click.style("SMB SHARE ENUMERATION", bold=True, fg="cyan"))
|
|
275
|
+
click.echo(click.style("=" * 70, fg="cyan"))
|
|
276
|
+
click.echo()
|
|
277
|
+
|
|
278
|
+
if parsed.get("target"):
|
|
279
|
+
click.echo(click.style(f"Target: {parsed['target']}", bold=True))
|
|
280
|
+
elif job.get("target"):
|
|
281
|
+
click.echo(click.style(f"Target: {job.get('target')}", bold=True))
|
|
282
|
+
if parsed.get("status"):
|
|
283
|
+
auth_color = (
|
|
284
|
+
"green" if parsed["status"] == "Authenticated" else "yellow"
|
|
285
|
+
)
|
|
286
|
+
click.echo(
|
|
287
|
+
f"Authentication: {click.style(parsed['status'], fg=auth_color)}"
|
|
288
|
+
)
|
|
289
|
+
click.echo()
|
|
290
|
+
|
|
291
|
+
if not shares:
|
|
292
|
+
self.display_no_results(job, log_path)
|
|
293
|
+
return
|
|
294
|
+
|
|
295
|
+
# Group shares by permissions
|
|
296
|
+
writable = [s for s in shares if s.get("writable")]
|
|
297
|
+
readable = [
|
|
298
|
+
s for s in shares if s.get("readable") and not s.get("writable")
|
|
299
|
+
]
|
|
300
|
+
no_access = [
|
|
301
|
+
s for s in shares if not s.get("readable") and not s.get("writable")
|
|
302
|
+
]
|
|
303
|
+
|
|
304
|
+
# Writable shares (HIGH RISK)
|
|
305
|
+
if writable:
|
|
306
|
+
click.echo(
|
|
307
|
+
click.style(
|
|
308
|
+
f"Writable Shares ({len(writable)}):", bold=True, fg="red"
|
|
309
|
+
)
|
|
310
|
+
)
|
|
311
|
+
for share in writable:
|
|
312
|
+
comment = f" - {share['comment']}" if share.get("comment") else ""
|
|
313
|
+
click.echo(f" - {share['name']} ({share['permissions']}){comment}")
|
|
314
|
+
click.echo()
|
|
315
|
+
|
|
316
|
+
# Readable shares
|
|
317
|
+
if readable:
|
|
318
|
+
click.echo(
|
|
319
|
+
click.style(
|
|
320
|
+
f"Readable Shares ({len(readable)}):", bold=True, fg="yellow"
|
|
321
|
+
)
|
|
322
|
+
)
|
|
323
|
+
for share in readable:
|
|
324
|
+
comment = f" - {share['comment']}" if share.get("comment") else ""
|
|
325
|
+
click.echo(f" - {share['name']} ({share['permissions']}){comment}")
|
|
326
|
+
click.echo()
|
|
327
|
+
|
|
328
|
+
# No access shares
|
|
329
|
+
if no_access:
|
|
330
|
+
click.echo(
|
|
331
|
+
click.style(
|
|
332
|
+
f"Restricted Shares ({len(no_access)}):", bold=True, dim=True
|
|
333
|
+
)
|
|
334
|
+
)
|
|
335
|
+
max_no_access = None if show_all else 5
|
|
336
|
+
display_no_access = (
|
|
337
|
+
no_access if max_no_access is None else no_access[:max_no_access]
|
|
338
|
+
)
|
|
339
|
+
for share in display_no_access:
|
|
340
|
+
comment = f" - {share['comment']}" if share.get("comment") else ""
|
|
341
|
+
click.echo(f" - {share['name']}{comment}")
|
|
342
|
+
if max_no_access and len(no_access) > max_no_access:
|
|
343
|
+
click.echo(f" ... and {len(no_access) - max_no_access} more")
|
|
344
|
+
click.echo()
|
|
345
|
+
|
|
346
|
+
# File enumeration results
|
|
347
|
+
files = parsed.get("files", [])
|
|
348
|
+
if files:
|
|
349
|
+
click.echo(click.style(f"Files Enumerated: {len(files)}", bold=True))
|
|
350
|
+
if show_all:
|
|
351
|
+
for f in files[:50]:
|
|
352
|
+
click.echo(
|
|
353
|
+
f" - {f.get('share', '?')}/{f.get('name', 'unknown')}"
|
|
354
|
+
)
|
|
355
|
+
if len(files) > 50:
|
|
356
|
+
click.echo(f" ... and {len(files) - 50} more files")
|
|
357
|
+
click.echo()
|
|
358
|
+
|
|
359
|
+
# Extract and display directory tree from raw output
|
|
360
|
+
# Look for path headers like ./Share//path
|
|
361
|
+
tree_lines = []
|
|
362
|
+
gpp_paths = []
|
|
363
|
+
for line in log_content.split("\n"):
|
|
364
|
+
# Match path headers: ./Replication//active.htb/Policies/...
|
|
365
|
+
path_match = re.match(r"\./([^/]+)//(.+)", line.strip())
|
|
366
|
+
if path_match:
|
|
367
|
+
share = path_match.group(1)
|
|
368
|
+
path = path_match.group(2)
|
|
369
|
+
full_path = f"{share}/{path}"
|
|
370
|
+
tree_lines.append(full_path)
|
|
371
|
+
# Check for GPP-related paths
|
|
372
|
+
if "/Preferences/" in path or "/Preferences" in path:
|
|
373
|
+
gpp_paths.append(full_path)
|
|
374
|
+
|
|
375
|
+
if tree_lines:
|
|
376
|
+
click.echo(click.style("Directory Tree:", bold=True))
|
|
377
|
+
# Show unique directory paths (limit to avoid spam)
|
|
378
|
+
shown = 0
|
|
379
|
+
max_show = 15 if not show_all else 50
|
|
380
|
+
for path in tree_lines:
|
|
381
|
+
if shown >= max_show:
|
|
382
|
+
click.echo(f" ... and {len(tree_lines) - shown} more paths")
|
|
383
|
+
break
|
|
384
|
+
# Highlight GPP paths
|
|
385
|
+
if "/Preferences" in path:
|
|
386
|
+
click.echo(
|
|
387
|
+
f" {click.style('→', fg='yellow')} {click.style(path, fg='yellow', bold=True)}"
|
|
388
|
+
)
|
|
389
|
+
else:
|
|
390
|
+
click.echo(f" {path}")
|
|
391
|
+
shown += 1
|
|
392
|
+
click.echo()
|
|
393
|
+
|
|
394
|
+
# Highlight GPP findings
|
|
395
|
+
if gpp_paths:
|
|
396
|
+
click.echo(
|
|
397
|
+
click.style(
|
|
398
|
+
"⚠️ GPP (Group Policy Preferences) Paths Found!",
|
|
399
|
+
bold=True,
|
|
400
|
+
fg="yellow",
|
|
401
|
+
)
|
|
402
|
+
)
|
|
403
|
+
click.echo(
|
|
404
|
+
click.style(
|
|
405
|
+
" These may contain encrypted credentials (MS14-025):",
|
|
406
|
+
fg="yellow",
|
|
407
|
+
)
|
|
408
|
+
)
|
|
409
|
+
for gpp_path in gpp_paths[:5]:
|
|
410
|
+
click.echo(f" • {gpp_path}")
|
|
411
|
+
click.echo()
|
|
412
|
+
|
|
413
|
+
click.echo(click.style("=" * 70, fg="cyan"))
|
|
414
|
+
click.echo()
|
|
415
|
+
|
|
416
|
+
except Exception as e:
|
|
417
|
+
logger.debug(f"Error in display_done: {e}")
|
|
418
|
+
|
|
419
|
+
def display_warning(
|
|
420
|
+
self,
|
|
421
|
+
job: Dict[str, Any],
|
|
422
|
+
log_path: str,
|
|
423
|
+
log_content: Optional[str] = None,
|
|
424
|
+
) -> None:
|
|
425
|
+
"""Display warning status for SMBMap."""
|
|
426
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
427
|
+
click.echo(click.style("[WARNING] SMBMAP", bold=True, fg="yellow"))
|
|
428
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
429
|
+
click.echo()
|
|
430
|
+
click.echo(" Scan completed with warnings. Check raw logs for details.")
|
|
431
|
+
click.echo(" Press [r] to view raw logs.")
|
|
432
|
+
click.echo()
|
|
433
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
434
|
+
click.echo()
|
|
435
|
+
|
|
436
|
+
def display_error(
|
|
437
|
+
self,
|
|
438
|
+
job: Dict[str, Any],
|
|
439
|
+
log_path: str,
|
|
440
|
+
log_content: Optional[str] = None,
|
|
441
|
+
) -> None:
|
|
442
|
+
"""Display error status for SMBMap."""
|
|
443
|
+
# Read log if not provided
|
|
444
|
+
if log_content is None and log_path and os.path.exists(log_path):
|
|
445
|
+
try:
|
|
446
|
+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
447
|
+
log_content = f.read()
|
|
448
|
+
except Exception:
|
|
449
|
+
log_content = ""
|
|
450
|
+
|
|
451
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
452
|
+
click.echo(click.style("[ERROR] SMBMAP FAILED", bold=True, fg="red"))
|
|
453
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
454
|
+
click.echo()
|
|
455
|
+
|
|
456
|
+
# Check for common smbmap errors
|
|
457
|
+
error_msg = None
|
|
458
|
+
if log_content:
|
|
459
|
+
if "Connection refused" in log_content or "Connection reset" in log_content:
|
|
460
|
+
error_msg = "Connection refused - SMB service may be down"
|
|
461
|
+
elif "timed out" in log_content.lower() or "timeout" in log_content.lower():
|
|
462
|
+
error_msg = "Connection timed out - target may be slow or filtering"
|
|
463
|
+
elif (
|
|
464
|
+
"Authentication error" in log_content or "LOGON_FAILURE" in log_content
|
|
465
|
+
):
|
|
466
|
+
error_msg = "Authentication failed - invalid credentials"
|
|
467
|
+
elif "access denied" in log_content.lower():
|
|
468
|
+
error_msg = "Access denied - insufficient privileges"
|
|
469
|
+
elif "Errno 113" in log_content or "No route to host" in log_content:
|
|
470
|
+
error_msg = "No route to host - network unreachable"
|
|
471
|
+
elif "[-]" in log_content:
|
|
472
|
+
match = re.search(r"\[-\]\s*(.+?)(?:\n|$)", log_content)
|
|
473
|
+
if match:
|
|
474
|
+
error_msg = match.group(1).strip()[:100]
|
|
475
|
+
|
|
476
|
+
if error_msg:
|
|
477
|
+
click.echo(f" {error_msg}")
|
|
478
|
+
else:
|
|
479
|
+
click.echo(" Scan failed - see raw logs for details (press 'r')")
|
|
480
|
+
|
|
481
|
+
click.echo()
|
|
482
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
483
|
+
click.echo()
|
|
484
|
+
|
|
485
|
+
def display_no_results(
|
|
486
|
+
self,
|
|
487
|
+
job: Dict[str, Any],
|
|
488
|
+
log_path: str,
|
|
489
|
+
) -> None:
|
|
490
|
+
"""Display no_results status for SMBMap."""
|
|
491
|
+
click.echo(click.style("=" * 70, fg="cyan"))
|
|
492
|
+
click.echo(click.style("SMB SHARE ENUMERATION", bold=True, fg="cyan"))
|
|
493
|
+
click.echo(click.style("=" * 70, fg="cyan"))
|
|
494
|
+
click.echo()
|
|
495
|
+
|
|
496
|
+
if job.get("target"):
|
|
497
|
+
click.echo(click.style(f"Target: {job.get('target')}", bold=True))
|
|
498
|
+
click.echo()
|
|
499
|
+
|
|
500
|
+
click.echo(click.style("Result: No SMB shares found", fg="yellow", bold=True))
|
|
501
|
+
click.echo()
|
|
502
|
+
click.echo(" The scan did not find any accessible SMB shares.")
|
|
503
|
+
click.echo()
|
|
504
|
+
click.echo(click.style("Tips:", dim=True))
|
|
505
|
+
click.echo(" - Try with credentials: -u user -p password")
|
|
506
|
+
click.echo(" - Check if SMB is enabled on the target")
|
|
507
|
+
click.echo(" - Verify the target IP is correct")
|
|
508
|
+
click.echo()
|
|
509
|
+
click.echo(click.style("=" * 70, fg="cyan"))
|
|
510
|
+
click.echo()
|