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
|
@@ -15,7 +15,7 @@ from typing import Dict, Any, List
|
|
|
15
15
|
def parse_secretsdump(log_path: str, target: str) -> Dict[str, Any]:
|
|
16
16
|
"""
|
|
17
17
|
Parse secretsdump output for credentials and hashes.
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
Output format:
|
|
20
20
|
[*] Dumping local SAM hashes
|
|
21
21
|
Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
|
|
@@ -25,11 +25,13 @@ def parse_secretsdump(log_path: str, target: str) -> Dict[str, Any]:
|
|
|
25
25
|
credentials = []
|
|
26
26
|
hashes = []
|
|
27
27
|
tickets = []
|
|
28
|
-
|
|
28
|
+
lsa_secrets = []
|
|
29
|
+
kerberos_keys = []
|
|
30
|
+
|
|
29
31
|
try:
|
|
30
|
-
with open(log_path,
|
|
32
|
+
with open(log_path, "r", encoding="utf-8") as f:
|
|
31
33
|
content = f.read()
|
|
32
|
-
|
|
34
|
+
|
|
33
35
|
# Parse NTLM hashes with multiple format support
|
|
34
36
|
# Format 1: username:RID:LM:NT::: (standard secretsdump)
|
|
35
37
|
# Format 2: username:RID:LM:NT:: (without trailing colon)
|
|
@@ -38,10 +40,11 @@ def parse_secretsdump(log_path: str, target: str) -> Dict[str, Any]:
|
|
|
38
40
|
|
|
39
41
|
# Standard format with 32-char hashes and trailing colons
|
|
40
42
|
hash_patterns = [
|
|
41
|
-
r
|
|
42
|
-
r
|
|
43
|
+
r"([^:\s\\]+):(\d+):([0-9a-fA-F]{32}):([0-9a-fA-F]{32}):::?", # Standard
|
|
44
|
+
r"([^:\s]+)\\([^:\s]+):(\d+):([0-9a-fA-F]{32}):([0-9a-fA-F]{32}):::?", # Domain\user
|
|
43
45
|
]
|
|
44
46
|
|
|
47
|
+
seen_hashes = set() # Deduplicate hashes
|
|
45
48
|
for pattern in hash_patterns:
|
|
46
49
|
for match in re.finditer(pattern, content):
|
|
47
50
|
groups = match.groups()
|
|
@@ -54,18 +57,60 @@ def parse_secretsdump(log_path: str, target: str) -> Dict[str, Any]:
|
|
|
54
57
|
else:
|
|
55
58
|
continue
|
|
56
59
|
|
|
57
|
-
# Skip empty hashes (blank password indicator)
|
|
58
|
-
if
|
|
60
|
+
# Skip empty hashes (blank password indicator) - but keep Guest for completeness
|
|
61
|
+
if (
|
|
62
|
+
nt_hash.lower() == "31d6cfe0d16ae931b73c59d7e0c089c0"
|
|
63
|
+
and username.lower() != "guest"
|
|
64
|
+
):
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
# Deduplicate by username:nt_hash
|
|
68
|
+
hash_key = f"{username}:{nt_hash}"
|
|
69
|
+
if hash_key in seen_hashes:
|
|
59
70
|
continue
|
|
71
|
+
seen_hashes.add(hash_key)
|
|
72
|
+
|
|
73
|
+
hashes.append(
|
|
74
|
+
{
|
|
75
|
+
"username": username,
|
|
76
|
+
"rid": rid,
|
|
77
|
+
"lm_hash": lm_hash,
|
|
78
|
+
"nt_hash": nt_hash,
|
|
79
|
+
"hash_type": "NTLM",
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Parse LSA secrets (DefaultPassword, etc.)
|
|
84
|
+
# Format: [*] DefaultPassword
|
|
85
|
+
# (Unknown User):ROOT#123
|
|
86
|
+
lsa_pattern = r"\[\*\]\s*DefaultPassword\s*\n\s*\([^)]*\):([^\n]+)"
|
|
87
|
+
for match in re.finditer(lsa_pattern, content):
|
|
88
|
+
password = match.group(1).strip()
|
|
89
|
+
if password and not password.startswith("(null)"):
|
|
90
|
+
lsa_secrets.append(
|
|
91
|
+
{"secret_type": "DefaultPassword", "value": password}
|
|
92
|
+
)
|
|
93
|
+
# Also add as credential
|
|
94
|
+
credentials.append(
|
|
95
|
+
{
|
|
96
|
+
"domain": "",
|
|
97
|
+
"username": "(DefaultPassword)",
|
|
98
|
+
"password": password,
|
|
99
|
+
"credential_type": "lsa_secret",
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Parse Kerberos keys (AES, DES formats)
|
|
104
|
+
# Format: username:aes256-cts-hmac-sha1-96:hexkey
|
|
105
|
+
kerb_key_pattern = (
|
|
106
|
+
r"([^:\s]+):(aes\d+-cts-hmac-sha1-\d+|des-cbc-md5):([0-9a-fA-F]+)"
|
|
107
|
+
)
|
|
108
|
+
for match in re.finditer(kerb_key_pattern, content):
|
|
109
|
+
username, key_type, key_value = match.groups()
|
|
110
|
+
kerberos_keys.append(
|
|
111
|
+
{"username": username, "key_type": key_type, "key": key_value}
|
|
112
|
+
)
|
|
60
113
|
|
|
61
|
-
hashes.append({
|
|
62
|
-
'username': username,
|
|
63
|
-
'rid': rid,
|
|
64
|
-
'lm_hash': lm_hash,
|
|
65
|
-
'nt_hash': nt_hash,
|
|
66
|
-
'hash_type': 'NTLM'
|
|
67
|
-
})
|
|
68
|
-
|
|
69
114
|
# Parse plaintext passwords with multiple format support
|
|
70
115
|
# Format 1: DOMAIN\username:password
|
|
71
116
|
# Format 2: DOMAIN\\username:password (escaped backslash)
|
|
@@ -73,9 +118,9 @@ def parse_secretsdump(log_path: str, target: str) -> Dict[str, Any]:
|
|
|
73
118
|
# Format 4: [*] DOMAIN\username:password (with prefix)
|
|
74
119
|
|
|
75
120
|
plaintext_patterns = [
|
|
76
|
-
r
|
|
77
|
-
r
|
|
78
|
-
r
|
|
121
|
+
r"([^\\:\s]+)[\\]+([^:\s]+):([^\n\r]+)", # DOMAIN\user:pass
|
|
122
|
+
r"([^@:\s]+)@([^:\s]+):([^\n\r]+)", # user@DOMAIN:pass
|
|
123
|
+
r"\[\*\]\s*([^\\:\s]+)[\\]+([^:\s]+):([^\n\r]+)", # [*] DOMAIN\user:pass
|
|
79
124
|
]
|
|
80
125
|
|
|
81
126
|
for pattern in plaintext_patterns:
|
|
@@ -86,78 +131,85 @@ def parse_secretsdump(log_path: str, target: str) -> Dict[str, Any]:
|
|
|
86
131
|
password = password.strip()
|
|
87
132
|
|
|
88
133
|
# Skip null/empty passwords and hash-like values
|
|
89
|
-
if not password or password.startswith(
|
|
134
|
+
if not password or password.startswith("(null)"):
|
|
90
135
|
continue
|
|
91
136
|
# Skip if password looks like a hash (32+ hex chars)
|
|
92
|
-
if re.match(r
|
|
137
|
+
if re.match(r"^[0-9a-fA-F]{32,}$", password):
|
|
138
|
+
continue
|
|
139
|
+
# Skip Kerberos key formats
|
|
140
|
+
if re.match(r"^(aes\d+|des)-", password):
|
|
93
141
|
continue
|
|
94
142
|
|
|
95
143
|
# Determine domain/username based on pattern
|
|
96
|
-
if
|
|
144
|
+
if "@" in match.group(0):
|
|
97
145
|
username, domain = part1, part2
|
|
98
146
|
else:
|
|
99
147
|
domain, username = part1, part2
|
|
100
148
|
|
|
101
|
-
credentials.append(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
149
|
+
credentials.append(
|
|
150
|
+
{
|
|
151
|
+
"domain": domain,
|
|
152
|
+
"username": username,
|
|
153
|
+
"password": password,
|
|
154
|
+
"credential_type": "plaintext",
|
|
155
|
+
}
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Parse Kerberos tickets (format: username:$krb5...)
|
|
159
|
+
krb_pattern = r"([^:\s]+):(\$krb5[^\s]+)"
|
|
110
160
|
for match in re.finditer(krb_pattern, content):
|
|
111
161
|
username, krb_key = match.groups()
|
|
112
|
-
|
|
113
|
-
tickets.append(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
})
|
|
118
|
-
|
|
162
|
+
|
|
163
|
+
tickets.append(
|
|
164
|
+
{"username": username, "ticket": krb_key, "ticket_type": "Kerberos"}
|
|
165
|
+
)
|
|
166
|
+
|
|
119
167
|
except FileNotFoundError:
|
|
120
168
|
return {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
169
|
+
"tool": "secretsdump",
|
|
170
|
+
"target": target,
|
|
171
|
+
"error": "Log file not found",
|
|
172
|
+
"credentials_count": 0,
|
|
173
|
+
"hashes_count": 0,
|
|
126
174
|
}
|
|
127
175
|
except Exception as e:
|
|
128
176
|
return {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
177
|
+
"tool": "secretsdump",
|
|
178
|
+
"target": target,
|
|
179
|
+
"error": str(e),
|
|
180
|
+
"credentials_count": 0,
|
|
181
|
+
"hashes_count": 0,
|
|
134
182
|
}
|
|
135
|
-
|
|
183
|
+
|
|
136
184
|
return {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
185
|
+
"tool": "secretsdump",
|
|
186
|
+
"target": target,
|
|
187
|
+
"credentials_count": len(credentials),
|
|
188
|
+
"hashes_count": len(hashes),
|
|
189
|
+
"tickets_count": len(tickets),
|
|
190
|
+
"lsa_secrets_count": len(lsa_secrets),
|
|
191
|
+
"kerberos_keys_count": len(kerberos_keys),
|
|
192
|
+
"credentials": credentials,
|
|
193
|
+
"hashes": hashes,
|
|
194
|
+
"tickets": tickets,
|
|
195
|
+
"lsa_secrets": lsa_secrets,
|
|
196
|
+
"kerberos_keys": kerberos_keys,
|
|
145
197
|
}
|
|
146
198
|
|
|
147
199
|
|
|
148
200
|
def parse_getnpusers(log_path: str, target: str) -> Dict[str, Any]:
|
|
149
201
|
"""
|
|
150
202
|
Parse GetNPUsers output for AS-REP roastable hashes.
|
|
151
|
-
|
|
203
|
+
|
|
152
204
|
Output format:
|
|
153
205
|
$krb5asrep$23$user@DOMAIN:hash...
|
|
154
206
|
"""
|
|
155
207
|
hashes = []
|
|
156
|
-
|
|
208
|
+
|
|
157
209
|
try:
|
|
158
|
-
with open(log_path,
|
|
210
|
+
with open(log_path, "r", encoding="utf-8") as f:
|
|
159
211
|
content = f.read()
|
|
160
|
-
|
|
212
|
+
|
|
161
213
|
# Parse AS-REP hashes with multiple format support
|
|
162
214
|
# Format 1: $krb5asrep$23$user@DOMAIN:hash (etype 23)
|
|
163
215
|
# Format 2: $krb5asrep$18$user@DOMAIN:hash (etype 18)
|
|
@@ -166,8 +218,8 @@ def parse_getnpusers(log_path: str, target: str) -> Dict[str, Any]:
|
|
|
166
218
|
|
|
167
219
|
# Full format with etype: $krb5asrep$ETYPE$user@DOMAIN:hash
|
|
168
220
|
hash_patterns = [
|
|
169
|
-
r
|
|
170
|
-
r
|
|
221
|
+
r"\$krb5asrep\$(\d+)\$([^@]+)@([^:]+):([^\s]+)", # With etype
|
|
222
|
+
r"\$krb5asrep\$([^@$]+)@([^:]+):([^\s]+)", # Without etype
|
|
171
223
|
]
|
|
172
224
|
|
|
173
225
|
for pattern in hash_patterns:
|
|
@@ -175,57 +227,61 @@ def parse_getnpusers(log_path: str, target: str) -> Dict[str, Any]:
|
|
|
175
227
|
groups = match.groups()
|
|
176
228
|
if len(groups) == 4:
|
|
177
229
|
etype, username, domain, hash_value = groups
|
|
178
|
-
full_hash = f
|
|
230
|
+
full_hash = f"$krb5asrep${etype}${username}@{domain}:{hash_value}"
|
|
179
231
|
elif len(groups) == 3:
|
|
180
232
|
username, domain, hash_value = groups
|
|
181
|
-
etype =
|
|
182
|
-
full_hash = f
|
|
233
|
+
etype = "23" # Default etype
|
|
234
|
+
full_hash = f"$krb5asrep${username}@{domain}:{hash_value}"
|
|
183
235
|
else:
|
|
184
236
|
continue
|
|
185
237
|
|
|
186
|
-
hashes.append(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
238
|
+
hashes.append(
|
|
239
|
+
{
|
|
240
|
+
"username": username,
|
|
241
|
+
"domain": domain,
|
|
242
|
+
"hash": full_hash,
|
|
243
|
+
"hash_type": "AS-REP",
|
|
244
|
+
"etype": etype,
|
|
245
|
+
"crackable": True,
|
|
246
|
+
}
|
|
247
|
+
)
|
|
194
248
|
|
|
195
249
|
# Also check for simple format (username:hash)
|
|
196
250
|
if not hashes:
|
|
197
|
-
simple_pattern = r
|
|
251
|
+
simple_pattern = r"^([^:\s]+):(\$krb5asrep[^\s]+)"
|
|
198
252
|
for match in re.finditer(simple_pattern, content, re.MULTILINE):
|
|
199
253
|
username, hash_value = match.groups()
|
|
200
254
|
|
|
201
|
-
hashes.append(
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
255
|
+
hashes.append(
|
|
256
|
+
{
|
|
257
|
+
"username": username,
|
|
258
|
+
"hash": hash_value,
|
|
259
|
+
"hash_type": "AS-REP",
|
|
260
|
+
"crackable": True,
|
|
261
|
+
}
|
|
262
|
+
)
|
|
263
|
+
|
|
208
264
|
except FileNotFoundError:
|
|
209
265
|
return {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
266
|
+
"tool": "GetNPUsers",
|
|
267
|
+
"target": target,
|
|
268
|
+
"error": "Log file not found",
|
|
269
|
+
"hashes_count": 0,
|
|
214
270
|
}
|
|
215
271
|
except Exception as e:
|
|
216
272
|
return {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
273
|
+
"tool": "GetNPUsers",
|
|
274
|
+
"target": target,
|
|
275
|
+
"error": str(e),
|
|
276
|
+
"hashes_count": 0,
|
|
221
277
|
}
|
|
222
|
-
|
|
278
|
+
|
|
223
279
|
return {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
280
|
+
"tool": "GetNPUsers",
|
|
281
|
+
"target": target,
|
|
282
|
+
"hashes_count": len(hashes),
|
|
283
|
+
"hashes": hashes,
|
|
284
|
+
"asrep_hashes": hashes, # For auto-chaining to hashcat
|
|
229
285
|
}
|
|
230
286
|
|
|
231
287
|
|
|
@@ -235,52 +291,49 @@ def parse_psexec(log_path: str, target: str) -> Dict[str, Any]:
|
|
|
235
291
|
"""
|
|
236
292
|
output_lines = []
|
|
237
293
|
success = False
|
|
238
|
-
|
|
294
|
+
|
|
239
295
|
try:
|
|
240
|
-
with open(log_path,
|
|
296
|
+
with open(log_path, "r", encoding="utf-8") as f:
|
|
241
297
|
content = f.read()
|
|
242
|
-
|
|
298
|
+
|
|
243
299
|
# Check for successful connection with multiple indicators
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
300
|
+
# Use simple string matching for literal strings, regex for patterns
|
|
301
|
+
literal_indicators = [
|
|
302
|
+
"[*] Requesting shares on",
|
|
303
|
+
"[*] Uploading",
|
|
304
|
+
"[*] Opening SVCManager",
|
|
305
|
+
"[*] Starting service",
|
|
306
|
+
"C:\\Windows\\system32>",
|
|
307
|
+
"C:\\WINDOWS\\system32>",
|
|
308
|
+
"Microsoft Windows",
|
|
309
|
+
"nt authority\\system",
|
|
310
|
+
"ErrorCode: 0",
|
|
253
311
|
]
|
|
254
312
|
|
|
255
|
-
for indicator in
|
|
256
|
-
if
|
|
313
|
+
for indicator in literal_indicators:
|
|
314
|
+
if indicator.lower() in content.lower():
|
|
257
315
|
success = True
|
|
258
316
|
break
|
|
259
|
-
|
|
317
|
+
|
|
260
318
|
# Extract command output (everything after the prompt)
|
|
261
|
-
output_lines = [line for line in content.split(
|
|
262
|
-
|
|
319
|
+
output_lines = [line for line in content.split("\n") if line.strip()]
|
|
320
|
+
|
|
263
321
|
except FileNotFoundError:
|
|
264
322
|
return {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
323
|
+
"tool": "psexec",
|
|
324
|
+
"target": target,
|
|
325
|
+
"error": "Log file not found",
|
|
326
|
+
"success": False,
|
|
269
327
|
}
|
|
270
328
|
except Exception as e:
|
|
271
|
-
return {
|
|
272
|
-
|
|
273
|
-
'target': target,
|
|
274
|
-
'error': str(e),
|
|
275
|
-
'success': False
|
|
276
|
-
}
|
|
277
|
-
|
|
329
|
+
return {"tool": "psexec", "target": target, "error": str(e), "success": False}
|
|
330
|
+
|
|
278
331
|
return {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
332
|
+
"tool": "psexec",
|
|
333
|
+
"target": target,
|
|
334
|
+
"success": success,
|
|
335
|
+
"output_lines": len(output_lines),
|
|
336
|
+
"output": "\n".join(output_lines),
|
|
284
337
|
}
|
|
285
338
|
|
|
286
339
|
|
|
@@ -291,89 +344,89 @@ def parse_smbclient(log_path: str, target: str) -> Dict[str, Any]:
|
|
|
291
344
|
shares = []
|
|
292
345
|
files = []
|
|
293
346
|
success = False
|
|
294
|
-
|
|
347
|
+
|
|
295
348
|
try:
|
|
296
|
-
with open(log_path,
|
|
349
|
+
with open(log_path, "r", encoding="utf-8") as f:
|
|
297
350
|
content = f.read()
|
|
298
|
-
|
|
351
|
+
|
|
299
352
|
# Check for successful connection
|
|
300
|
-
if
|
|
353
|
+
if "Type Client" in content or "smb:" in content:
|
|
301
354
|
success = True
|
|
302
|
-
|
|
355
|
+
|
|
303
356
|
# Parse share listings
|
|
304
|
-
share_pattern = r
|
|
357
|
+
share_pattern = r"^\s*([A-Z$]+)\s+(Disk|Printer|Device|IPC)\s*(.*)$"
|
|
305
358
|
for match in re.finditer(share_pattern, content, re.MULTILINE):
|
|
306
359
|
share_name, share_type, comment = match.groups()
|
|
307
|
-
|
|
308
|
-
shares.append(
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
360
|
+
|
|
361
|
+
shares.append(
|
|
362
|
+
{
|
|
363
|
+
"name": share_name.strip(),
|
|
364
|
+
"type": share_type.strip(),
|
|
365
|
+
"comment": comment.strip(),
|
|
366
|
+
}
|
|
367
|
+
)
|
|
368
|
+
|
|
314
369
|
# Parse file listings (basic)
|
|
315
|
-
file_pattern = r
|
|
370
|
+
file_pattern = r"^\s*([^\s]+)\s+([DAH]+)\s+(\d+)\s+"
|
|
316
371
|
for match in re.finditer(file_pattern, content, re.MULTILINE):
|
|
317
372
|
filename, attributes, size = match.groups()
|
|
318
|
-
|
|
319
|
-
if filename not in [
|
|
320
|
-
files.append(
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
})
|
|
325
|
-
|
|
373
|
+
|
|
374
|
+
if filename not in [".", ".."]:
|
|
375
|
+
files.append(
|
|
376
|
+
{"name": filename, "attributes": attributes, "size": int(size)}
|
|
377
|
+
)
|
|
378
|
+
|
|
326
379
|
except FileNotFoundError:
|
|
327
380
|
return {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
381
|
+
"tool": "smbclient",
|
|
382
|
+
"target": target,
|
|
383
|
+
"error": "Log file not found",
|
|
384
|
+
"success": False,
|
|
332
385
|
}
|
|
333
386
|
except Exception as e:
|
|
334
387
|
return {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
388
|
+
"tool": "smbclient",
|
|
389
|
+
"target": target,
|
|
390
|
+
"error": str(e),
|
|
391
|
+
"success": False,
|
|
339
392
|
}
|
|
340
|
-
|
|
393
|
+
|
|
341
394
|
return {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
395
|
+
"tool": "smbclient",
|
|
396
|
+
"target": target,
|
|
397
|
+
"success": success,
|
|
398
|
+
"shares_count": len(shares),
|
|
399
|
+
"files_count": len(files),
|
|
400
|
+
"shares": shares,
|
|
401
|
+
"files": files,
|
|
349
402
|
}
|
|
350
403
|
|
|
351
404
|
|
|
352
405
|
def parse_impacket(log_path: str, target: str, tool: str) -> Dict[str, Any]:
|
|
353
406
|
"""
|
|
354
407
|
Unified parser that routes to specific Impacket tool parsers.
|
|
355
|
-
|
|
408
|
+
|
|
356
409
|
Args:
|
|
357
410
|
log_path: Path to tool output file
|
|
358
411
|
target: Target host/domain
|
|
359
412
|
tool: Specific Impacket tool name
|
|
360
|
-
|
|
413
|
+
|
|
361
414
|
Returns:
|
|
362
415
|
Parsed results dictionary
|
|
363
416
|
"""
|
|
364
|
-
tool_lower = tool.lower().replace(
|
|
365
|
-
|
|
366
|
-
if
|
|
417
|
+
tool_lower = tool.lower().replace("impacket-", "")
|
|
418
|
+
|
|
419
|
+
if "secretsdump" in tool_lower:
|
|
367
420
|
return parse_secretsdump(log_path, target)
|
|
368
|
-
elif
|
|
421
|
+
elif "getnpusers" in tool_lower:
|
|
369
422
|
return parse_getnpusers(log_path, target)
|
|
370
|
-
elif
|
|
423
|
+
elif "psexec" in tool_lower:
|
|
371
424
|
return parse_psexec(log_path, target)
|
|
372
|
-
elif
|
|
425
|
+
elif "smbclient" in tool_lower:
|
|
373
426
|
return parse_smbclient(log_path, target)
|
|
374
427
|
else:
|
|
375
428
|
return {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
429
|
+
"tool": tool,
|
|
430
|
+
"target": target,
|
|
431
|
+
"error": f"Unknown Impacket tool: {tool}",
|
|
379
432
|
}
|