souleyez 2.43.29__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 +22827 -10678
- souleyez/ui/interactive_selector.py +75 -68
- souleyez/ui/log_formatter.py +47 -39
- souleyez/ui/menu_components.py +22 -13
- souleyez/ui/msf_auxiliary_menu.py +184 -133
- souleyez/ui/pending_chains_view.py +336 -172
- souleyez/ui/progress_indicators.py +5 -3
- souleyez/ui/recommendations_view.py +195 -137
- souleyez/ui/rule_builder.py +343 -225
- souleyez/ui/setup_wizard.py +678 -284
- souleyez/ui/shortcuts.py +217 -165
- souleyez/ui/splunk_gap_analysis_view.py +452 -270
- souleyez/ui/splunk_vulns_view.py +139 -86
- souleyez/ui/team_dashboard.py +498 -335
- souleyez/ui/template_selector.py +196 -105
- souleyez/ui/terminal.py +6 -6
- souleyez/ui/timeline_view.py +198 -127
- souleyez/ui/tool_setup.py +264 -164
- souleyez/ui/tutorial.py +202 -72
- souleyez/ui/tutorial_state.py +40 -40
- souleyez/ui/wazuh_vulns_view.py +235 -141
- souleyez/ui/wordlist_browser.py +260 -107
- souleyez/ui.py +464 -312
- souleyez/utils/tool_checker.py +427 -367
- souleyez/utils.py +33 -29
- souleyez/wordlists.py +134 -167
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/METADATA +1 -1
- souleyez-2.43.34.dist-info/RECORD +443 -0
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/WHEEL +1 -1
- souleyez-2.43.29.dist-info/RECORD +0 -379
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/entry_points.txt +0 -0
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/top_level.txt +0 -0
souleyez/plugins/bloodhound.py
CHANGED
|
@@ -29,51 +29,54 @@ HELP = {
|
|
|
29
29
|
"- Pre-built queries: 'Shortest path to Domain Admins'\n\n"
|
|
30
30
|
"💡 Pro tip: Run from Linux with bloodhound-python (no need to touch DC directly)\n"
|
|
31
31
|
),
|
|
32
|
-
"usage":
|
|
32
|
+
"usage": 'souleyez jobs enqueue bloodhound <dc_ip> --args "-u user -p pass -d domain.com"',
|
|
33
33
|
"examples": [
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
'souleyez jobs enqueue bloodhound 10.0.0.82 --args "-u jdoe -p Password123! -d CONTOSO.LOCAL"',
|
|
35
|
+
'souleyez jobs enqueue bloodhound 10.0.0.82 --args "-u admin -p pass -d CORP.COM -c All"',
|
|
36
|
+
'souleyez jobs enqueue bloodhound 10.0.0.82 --args "-u user@domain.com -p pass -d domain.com -c DCOnly"',
|
|
37
37
|
],
|
|
38
38
|
"presets": [
|
|
39
39
|
{
|
|
40
40
|
"name": "Full Enumeration",
|
|
41
41
|
"desc": "Complete AD collection - all data (recommended)",
|
|
42
|
-
"args": ["-c", "All", "--zip"]
|
|
42
|
+
"args": ["-c", "All", "--zip"],
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
"name": "DCOnly (Fast)",
|
|
46
46
|
"desc": "DC data only - fast, low footprint",
|
|
47
|
-
"args": ["-c", "DCOnly", "--zip"]
|
|
47
|
+
"args": ["-c", "DCOnly", "--zip"],
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
"name": "Users & Groups",
|
|
51
51
|
"desc": "User/group memberships and local admins",
|
|
52
|
-
"args": ["-c", "Group,LocalAdmin", "--zip"]
|
|
52
|
+
"args": ["-c", "Group,LocalAdmin", "--zip"],
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
"name": "Sessions Only",
|
|
56
56
|
"desc": "Active sessions and logged-in users",
|
|
57
|
-
"args": ["-c", "Session", "--zip"]
|
|
57
|
+
"args": ["-c", "Session", "--zip"],
|
|
58
58
|
},
|
|
59
59
|
{
|
|
60
60
|
"name": "Trust Relationships",
|
|
61
61
|
"desc": "Domain trusts enumeration",
|
|
62
|
-
"args": ["-c", "Trusts", "--zip"]
|
|
63
|
-
}
|
|
62
|
+
"args": ["-c", "Trusts", "--zip"],
|
|
63
|
+
},
|
|
64
64
|
],
|
|
65
65
|
"flags": [
|
|
66
66
|
["-u <username>", "Domain username (required)"],
|
|
67
67
|
["-p <password>", "Domain password (required)"],
|
|
68
68
|
["-d <domain>", "Domain name (e.g., CONTOSO.LOCAL) (required)"],
|
|
69
69
|
["-ns <dc_ip>", "Domain Controller IP (nameserver)"],
|
|
70
|
-
[
|
|
70
|
+
[
|
|
71
|
+
"-c <collection>",
|
|
72
|
+
"Collection method: All, DCOnly, Group, LocalAdmin, Session, Trusts",
|
|
73
|
+
],
|
|
71
74
|
["--zip", "Compress output to ZIP file (recommended)"],
|
|
72
75
|
],
|
|
73
76
|
"presets_explained": {
|
|
74
77
|
"Full Enumeration": "Complete AD data collection - all users, groups, computers, sessions, GPOs (slowest, most data)",
|
|
75
78
|
"Users & Groups Only": "Quick enumeration - user/group memberships, local admins, sessions (faster, good for initial recon)",
|
|
76
|
-
"DCOnly": "Domain Controller data only - fastest, minimal footprint, misses workstation relationships"
|
|
79
|
+
"DCOnly": "Domain Controller data only - fastest, minimal footprint, misses workstation relationships",
|
|
77
80
|
},
|
|
78
81
|
"notes": [
|
|
79
82
|
"Requires bloodhound-python installed: pip3 install bloodhound",
|
|
@@ -89,48 +92,68 @@ HELP = {
|
|
|
89
92
|
"title": "What is Bloodhound?",
|
|
90
93
|
"color": "cyan",
|
|
91
94
|
"content": [
|
|
92
|
-
{
|
|
93
|
-
|
|
94
|
-
"
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
"
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
{
|
|
96
|
+
"title": "Overview",
|
|
97
|
+
"desc": "Bloodhound uses graph theory to analyze Active Directory relationships and identify complex attack paths that would be impossible to find manually.",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"title": "Use Cases",
|
|
101
|
+
"desc": "Find the path from user to Domain Admin",
|
|
102
|
+
"tips": [
|
|
103
|
+
"Map all AD users, groups, computers, and GPOs",
|
|
104
|
+
"Identify shortest path to Domain Admin",
|
|
105
|
+
"Find Kerberoastable users (SPN accounts)",
|
|
106
|
+
"Discover high-value targets and misconfigurations",
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
],
|
|
100
110
|
},
|
|
101
111
|
{
|
|
102
112
|
"title": "How to Use",
|
|
103
113
|
"color": "green",
|
|
104
114
|
"content": [
|
|
105
|
-
{
|
|
106
|
-
|
|
107
|
-
"
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
"
|
|
111
|
-
|
|
112
|
-
|
|
115
|
+
{
|
|
116
|
+
"title": "Basic Workflow",
|
|
117
|
+
"desc": "1. Collect AD data with valid credentials\n 2. Import ZIP files into Bloodhound GUI\n 3. Run pre-built queries for attack paths\n 4. Identify and document exploitation path",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"title": "Collection Methods",
|
|
121
|
+
"desc": "Different levels of data collection",
|
|
122
|
+
"tips": [
|
|
123
|
+
"All: Complete AD collection (recommended)",
|
|
124
|
+
"DCOnly: Fast, minimal footprint",
|
|
125
|
+
"Group,LocalAdmin: User/group memberships only",
|
|
126
|
+
"Session: Active sessions and logged-in users",
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
],
|
|
113
130
|
},
|
|
114
131
|
{
|
|
115
132
|
"title": "Tips & Best Practices",
|
|
116
133
|
"color": "yellow",
|
|
117
134
|
"content": [
|
|
118
|
-
(
|
|
119
|
-
"
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
"
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
135
|
+
(
|
|
136
|
+
"Best Practices:",
|
|
137
|
+
[
|
|
138
|
+
"Requires valid domain credentials (any user)",
|
|
139
|
+
"Data collection takes 5-15 minutes",
|
|
140
|
+
"Use 'Full Enumeration' for complete picture",
|
|
141
|
+
"Import ZIP into Bloodhound GUI for visualization",
|
|
142
|
+
"Pre-built queries: 'Shortest path to Domain Admins'",
|
|
143
|
+
],
|
|
144
|
+
),
|
|
145
|
+
(
|
|
146
|
+
"Common Issues:",
|
|
147
|
+
[
|
|
148
|
+
"No data collected: Verify credentials and DC connectivity",
|
|
149
|
+
"Timeout: Domain may be large, increase timeout",
|
|
150
|
+
"Permission denied: Check domain user credentials",
|
|
151
|
+
"Neo4j errors: Ensure Bloodhound database is running",
|
|
152
|
+
],
|
|
153
|
+
),
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
],
|
|
134
157
|
}
|
|
135
158
|
|
|
136
159
|
|
|
@@ -162,17 +185,23 @@ class BloodhoundPlugin:
|
|
|
162
185
|
"""
|
|
163
186
|
result = subprocess.run(["which", "bloodhound-python"], capture_output=True)
|
|
164
187
|
if result.returncode != 0:
|
|
165
|
-
with open(log_path,
|
|
188
|
+
with open(log_path, "w") as f:
|
|
166
189
|
f.write("ERROR: bloodhound-python not found. Install with:\n")
|
|
167
190
|
f.write("pip3 install bloodhound\n")
|
|
168
191
|
return 1
|
|
169
192
|
|
|
170
|
-
|
|
193
|
+
# Handle both string and list args
|
|
194
|
+
if isinstance(args, list):
|
|
195
|
+
arg_list = args
|
|
196
|
+
elif isinstance(args, str):
|
|
197
|
+
arg_list = args.split() if args else []
|
|
198
|
+
else:
|
|
199
|
+
arg_list = []
|
|
171
200
|
|
|
172
201
|
username, password, domain = self._parse_creds(arg_list)
|
|
173
202
|
|
|
174
203
|
if not all([username, password, domain]):
|
|
175
|
-
with open(log_path,
|
|
204
|
+
with open(log_path, "w") as f:
|
|
176
205
|
f.write("ERROR: Missing required arguments\n")
|
|
177
206
|
f.write("Usage: -u username -p password -d domain.com\n")
|
|
178
207
|
return 1
|
|
@@ -183,15 +212,20 @@ class BloodhoundPlugin:
|
|
|
183
212
|
|
|
184
213
|
cmd = [
|
|
185
214
|
"bloodhound-python",
|
|
186
|
-
"-u",
|
|
187
|
-
|
|
188
|
-
"-
|
|
189
|
-
|
|
190
|
-
"-
|
|
191
|
-
|
|
215
|
+
"-u",
|
|
216
|
+
username,
|
|
217
|
+
"-p",
|
|
218
|
+
password,
|
|
219
|
+
"-d",
|
|
220
|
+
domain,
|
|
221
|
+
"-ns",
|
|
222
|
+
target,
|
|
223
|
+
"-c",
|
|
224
|
+
"All",
|
|
225
|
+
"--zip",
|
|
192
226
|
]
|
|
193
227
|
|
|
194
|
-
with open(log_path,
|
|
228
|
+
with open(log_path, "w") as f:
|
|
195
229
|
f.write(f"Running Bloodhound collector...\n")
|
|
196
230
|
f.write(f"Domain: {domain}\n")
|
|
197
231
|
f.write(f"DC: {target}\n")
|
|
@@ -205,10 +239,10 @@ class BloodhoundPlugin:
|
|
|
205
239
|
stdout=subprocess.PIPE,
|
|
206
240
|
stderr=subprocess.STDOUT,
|
|
207
241
|
text=True,
|
|
208
|
-
timeout=600
|
|
242
|
+
timeout=600,
|
|
209
243
|
)
|
|
210
244
|
|
|
211
|
-
with open(log_path,
|
|
245
|
+
with open(log_path, "a") as f:
|
|
212
246
|
f.write(result.stdout)
|
|
213
247
|
|
|
214
248
|
if result.returncode == 0:
|
|
@@ -222,7 +256,7 @@ class BloodhoundPlugin:
|
|
|
222
256
|
return result.returncode
|
|
223
257
|
|
|
224
258
|
except subprocess.TimeoutExpired:
|
|
225
|
-
with open(log_path,
|
|
259
|
+
with open(log_path, "a") as f:
|
|
226
260
|
f.write("\n\nERROR: Bloodhound collector timeout (10 minutes)\n")
|
|
227
261
|
return 1
|
|
228
262
|
|
|
@@ -245,16 +279,16 @@ class BloodhoundPlugin:
|
|
|
245
279
|
return {
|
|
246
280
|
"Full Enumeration": {
|
|
247
281
|
"description": "Collect all AD data (users, groups, computers, GPOs)",
|
|
248
|
-
"args": "-c All"
|
|
282
|
+
"args": "-c All",
|
|
249
283
|
},
|
|
250
284
|
"Users & Groups Only": {
|
|
251
285
|
"description": "Quick enumeration (users and group memberships)",
|
|
252
|
-
"args": "-c Group,LocalAdmin,Session"
|
|
286
|
+
"args": "-c Group,LocalAdmin,Session",
|
|
253
287
|
},
|
|
254
288
|
"DCOnly": {
|
|
255
289
|
"description": "Domain Controller data only (fastest)",
|
|
256
|
-
"args": "-c DCOnly"
|
|
257
|
-
}
|
|
290
|
+
"args": "-c DCOnly",
|
|
291
|
+
},
|
|
258
292
|
}
|
|
259
293
|
|
|
260
294
|
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
souleyez.plugins.certipy - Active Directory Certificate Services (ADCS) enumeration
|
|
4
|
+
"""
|
|
5
|
+
import subprocess
|
|
6
|
+
import time
|
|
7
|
+
from typing import List
|
|
8
|
+
|
|
9
|
+
from .plugin_base import PluginBase
|
|
10
|
+
|
|
11
|
+
HELP = {
|
|
12
|
+
"name": "Certipy - ADCS Enumeration & Exploitation",
|
|
13
|
+
"description": (
|
|
14
|
+
"Certipy enumerates and exploits Active Directory Certificate Services.\n\n"
|
|
15
|
+
"Use it for:\n"
|
|
16
|
+
"- Finding vulnerable certificate templates (ESC1-ESC10)\n"
|
|
17
|
+
"- Requesting certificates for privilege escalation\n"
|
|
18
|
+
"- NTLM relay to ADCS (ESC8)\n"
|
|
19
|
+
"- Shadow credentials attacks\n\n"
|
|
20
|
+
"Quick tips:\n"
|
|
21
|
+
"- Use 'find' to enumerate all templates\n"
|
|
22
|
+
"- Use 'req' to request certificates\n"
|
|
23
|
+
"- Use 'auth' to authenticate with certificates\n"
|
|
24
|
+
"- ESC1/ESC4/ESC7/ESC8 are high severity\n"
|
|
25
|
+
),
|
|
26
|
+
"usage": 'souleyez jobs enqueue certipy <target> --args "find -u <user>@<domain> -p <pass> -dc-ip <dc>"',
|
|
27
|
+
"examples": [
|
|
28
|
+
'souleyez jobs enqueue certipy 10.0.0.82 --args "find -u admin@contoso.local -p Password1 -dc-ip 10.0.0.82"',
|
|
29
|
+
'souleyez jobs enqueue certipy 10.0.0.82 --args "find -u admin@contoso.local -p Password1 -vulnerable"',
|
|
30
|
+
'souleyez jobs enqueue certipy 10.0.0.82 --args "req -u admin@contoso.local -p Password1 -ca CONTOSO-CA -template User"',
|
|
31
|
+
'souleyez jobs enqueue certipy 10.0.0.82 --args "auth -pfx admin.pfx -dc-ip 10.0.0.82"',
|
|
32
|
+
],
|
|
33
|
+
"flags": [
|
|
34
|
+
["find", "Enumerate certificate templates and CAs"],
|
|
35
|
+
["req", "Request a certificate"],
|
|
36
|
+
["auth", "Authenticate using a certificate"],
|
|
37
|
+
["shadow", "Shadow credentials attack"],
|
|
38
|
+
["account", "Manage user/computer accounts"],
|
|
39
|
+
["-u <user>@<domain>", "Username with domain"],
|
|
40
|
+
["-p <password>", "Password"],
|
|
41
|
+
["-H <hash>", "NTLM hash for authentication"],
|
|
42
|
+
["-dc-ip <ip>", "Domain Controller IP"],
|
|
43
|
+
["-ca <name>", "Certificate Authority name"],
|
|
44
|
+
["-template <name>", "Certificate template name"],
|
|
45
|
+
["-vulnerable", "Only show vulnerable templates"],
|
|
46
|
+
["-pfx <file>", "PFX certificate file"],
|
|
47
|
+
["-stdout", "Print output to stdout"],
|
|
48
|
+
],
|
|
49
|
+
"preset_categories": {
|
|
50
|
+
"enumeration": [
|
|
51
|
+
{
|
|
52
|
+
"name": "Full ADCS Enum",
|
|
53
|
+
"args": [
|
|
54
|
+
"find",
|
|
55
|
+
"-u",
|
|
56
|
+
"<user>@<domain>",
|
|
57
|
+
"-p",
|
|
58
|
+
"<pass>",
|
|
59
|
+
"-dc-ip",
|
|
60
|
+
"<target>",
|
|
61
|
+
],
|
|
62
|
+
"desc": "Enumerate all certificate templates and CAs",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"name": "Vulnerable Templates Only",
|
|
66
|
+
"args": [
|
|
67
|
+
"find",
|
|
68
|
+
"-u",
|
|
69
|
+
"<user>@<domain>",
|
|
70
|
+
"-p",
|
|
71
|
+
"<pass>",
|
|
72
|
+
"-dc-ip",
|
|
73
|
+
"<target>",
|
|
74
|
+
"-vulnerable",
|
|
75
|
+
],
|
|
76
|
+
"desc": "Show only vulnerable templates (ESC1-ESC10)",
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"name": "ADCS Enum (PTH)",
|
|
80
|
+
"args": [
|
|
81
|
+
"find",
|
|
82
|
+
"-u",
|
|
83
|
+
"<user>@<domain>",
|
|
84
|
+
"-hashes",
|
|
85
|
+
"<nthash>",
|
|
86
|
+
"-dc-ip",
|
|
87
|
+
"<target>",
|
|
88
|
+
],
|
|
89
|
+
"desc": "Enumerate ADCS with pass-the-hash",
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
"exploitation": [
|
|
93
|
+
{
|
|
94
|
+
"name": "ESC1 - Request Cert as Admin",
|
|
95
|
+
"args": [
|
|
96
|
+
"req",
|
|
97
|
+
"-u",
|
|
98
|
+
"<user>@<domain>",
|
|
99
|
+
"-p",
|
|
100
|
+
"<pass>",
|
|
101
|
+
"-ca",
|
|
102
|
+
"<ca_name>",
|
|
103
|
+
"-template",
|
|
104
|
+
"<template>",
|
|
105
|
+
"-upn",
|
|
106
|
+
"administrator@<domain>",
|
|
107
|
+
"-dc-ip",
|
|
108
|
+
"<target>",
|
|
109
|
+
],
|
|
110
|
+
"desc": "Request certificate as another user (ESC1)",
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"name": "Authenticate with Cert",
|
|
114
|
+
"args": ["auth", "-pfx", "<cert.pfx>", "-dc-ip", "<target>"],
|
|
115
|
+
"desc": "Get TGT using certificate",
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"name": "Shadow Credentials",
|
|
119
|
+
"args": [
|
|
120
|
+
"shadow",
|
|
121
|
+
"auto",
|
|
122
|
+
"-u",
|
|
123
|
+
"<user>@<domain>",
|
|
124
|
+
"-p",
|
|
125
|
+
"<pass>",
|
|
126
|
+
"-account",
|
|
127
|
+
"<target_user>",
|
|
128
|
+
"-dc-ip",
|
|
129
|
+
"<target>",
|
|
130
|
+
],
|
|
131
|
+
"desc": "Add shadow credentials to target user",
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
"presets": [],
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
# Flatten presets
|
|
139
|
+
for category_presets in HELP["preset_categories"].values():
|
|
140
|
+
HELP["presets"].extend(category_presets)
|
|
141
|
+
|
|
142
|
+
HELP["help_sections"] = [
|
|
143
|
+
{
|
|
144
|
+
"title": "What is Certipy?",
|
|
145
|
+
"color": "cyan",
|
|
146
|
+
"content": [
|
|
147
|
+
{
|
|
148
|
+
"title": "Overview",
|
|
149
|
+
"desc": "Certipy is a tool for enumerating and abusing Active Directory Certificate Services (ADCS) misconfigurations.",
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"title": "ESC Vulnerabilities",
|
|
153
|
+
"desc": "Common ADCS misconfigurations",
|
|
154
|
+
"tips": [
|
|
155
|
+
"ESC1: Enrollee supplies subject (request as any user)",
|
|
156
|
+
"ESC2: Any Purpose EKU (use cert for anything)",
|
|
157
|
+
"ESC3: Certificate Request Agent (enroll for others)",
|
|
158
|
+
"ESC4: Vulnerable template ACL (modify template)",
|
|
159
|
+
"ESC6: EDITF_ATTRIBUTESUBJECTALTNAME2 (CA misconfiguration)",
|
|
160
|
+
"ESC7: Vulnerable CA ACL (manage CA)",
|
|
161
|
+
"ESC8: NTLM relay to ADCS web enrollment",
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"title": "How to Use",
|
|
168
|
+
"color": "green",
|
|
169
|
+
"content": [
|
|
170
|
+
{
|
|
171
|
+
"title": "Basic Workflow",
|
|
172
|
+
"desc": "1. Run 'find' to enumerate templates\n 2. Identify vulnerable templates (ESC1-ESC10)\n 3. Request malicious certificate\n 4. Authenticate with certificate to get TGT",
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"title": "Key Commands",
|
|
176
|
+
"desc": "Essential certipy modes",
|
|
177
|
+
"tips": [
|
|
178
|
+
"find: Enumerate CAs and templates",
|
|
179
|
+
"req: Request a certificate",
|
|
180
|
+
"auth: Authenticate with certificate",
|
|
181
|
+
"shadow: Shadow credentials attack",
|
|
182
|
+
"-vulnerable: Filter for vulnerable only",
|
|
183
|
+
],
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
]
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class CertipyPlugin(PluginBase):
|
|
191
|
+
name = "Certipy"
|
|
192
|
+
tool = "certipy"
|
|
193
|
+
category = "credential_access"
|
|
194
|
+
HELP = HELP
|
|
195
|
+
|
|
196
|
+
def check_tool_available(self) -> tuple:
|
|
197
|
+
"""Check if certipy is available."""
|
|
198
|
+
import shutil
|
|
199
|
+
|
|
200
|
+
# certipy can be installed as 'certipy' or 'certipy-ad'
|
|
201
|
+
tool_path = shutil.which("certipy") or shutil.which("certipy-ad")
|
|
202
|
+
if not tool_path:
|
|
203
|
+
return False, "certipy not found. Install with: pipx install certipy-ad"
|
|
204
|
+
return True, None
|
|
205
|
+
|
|
206
|
+
def build_command(
|
|
207
|
+
self, target: str, args: List[str] = None, label: str = "", log_path: str = None
|
|
208
|
+
):
|
|
209
|
+
"""Build command for background execution with PID tracking."""
|
|
210
|
+
import shutil
|
|
211
|
+
|
|
212
|
+
args = args or []
|
|
213
|
+
|
|
214
|
+
# Replace placeholders
|
|
215
|
+
args = [arg.replace("<target>", target) for arg in args]
|
|
216
|
+
|
|
217
|
+
# Find the correct binary name
|
|
218
|
+
tool_bin = "certipy" if shutil.which("certipy") else "certipy-ad"
|
|
219
|
+
|
|
220
|
+
# Build command
|
|
221
|
+
cmd = [tool_bin]
|
|
222
|
+
cmd.extend(args)
|
|
223
|
+
|
|
224
|
+
return {"cmd": cmd, "timeout": 1800}
|
|
225
|
+
|
|
226
|
+
def run(
|
|
227
|
+
self, target: str, args: List[str] = None, label: str = "", log_path: str = None
|
|
228
|
+
) -> int:
|
|
229
|
+
"""Execute certipy and write output to log_path."""
|
|
230
|
+
import shutil
|
|
231
|
+
|
|
232
|
+
args = args or []
|
|
233
|
+
|
|
234
|
+
# Replace placeholders
|
|
235
|
+
args = [arg.replace("<target>", target) for arg in args]
|
|
236
|
+
|
|
237
|
+
# Find the correct binary name
|
|
238
|
+
tool_bin = "certipy" if shutil.which("certipy") else "certipy-ad"
|
|
239
|
+
|
|
240
|
+
# Build command
|
|
241
|
+
cmd = [tool_bin]
|
|
242
|
+
cmd.extend(args)
|
|
243
|
+
|
|
244
|
+
if not log_path:
|
|
245
|
+
try:
|
|
246
|
+
proc = subprocess.run(
|
|
247
|
+
cmd, capture_output=True, timeout=600, check=False
|
|
248
|
+
)
|
|
249
|
+
return proc.returncode
|
|
250
|
+
except Exception:
|
|
251
|
+
return 1
|
|
252
|
+
|
|
253
|
+
try:
|
|
254
|
+
# Create metadata header
|
|
255
|
+
with open(log_path, "w", encoding="utf-8", errors="replace") as fh:
|
|
256
|
+
fh.write(f"=== Plugin: Certipy (ADCS) ===\n")
|
|
257
|
+
fh.write(f"Target: {target}\n")
|
|
258
|
+
fh.write(f"Args: {args}\n")
|
|
259
|
+
fh.write(f"Label: {label}\n")
|
|
260
|
+
fh.write(
|
|
261
|
+
f"Started: {time.strftime('%Y-%m-%d %H:%M:%S UTC', time.gmtime())}\n"
|
|
262
|
+
)
|
|
263
|
+
fh.write(f"Command: {' '.join(cmd)}\n\n")
|
|
264
|
+
|
|
265
|
+
# Run certipy
|
|
266
|
+
proc = subprocess.run(
|
|
267
|
+
cmd, capture_output=True, timeout=1800, check=False, text=True
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
# Write output
|
|
271
|
+
with open(log_path, "a", encoding="utf-8", errors="replace") as fh:
|
|
272
|
+
if proc.stdout:
|
|
273
|
+
fh.write(proc.stdout)
|
|
274
|
+
|
|
275
|
+
if proc.stderr:
|
|
276
|
+
fh.write(f"\n{proc.stderr}")
|
|
277
|
+
|
|
278
|
+
fh.write(
|
|
279
|
+
f"\n\n=== Completed: {time.strftime('%Y-%m-%d %H:%M:%S UTC', time.gmtime())} ===\n"
|
|
280
|
+
)
|
|
281
|
+
fh.write(f"Exit Code: {proc.returncode}\n")
|
|
282
|
+
|
|
283
|
+
return proc.returncode
|
|
284
|
+
|
|
285
|
+
except subprocess.TimeoutExpired:
|
|
286
|
+
with open(log_path, "a", encoding="utf-8", errors="replace") as fh:
|
|
287
|
+
fh.write("\n\nERROR: certipy timed out after 1800 seconds\n")
|
|
288
|
+
return 124
|
|
289
|
+
|
|
290
|
+
except FileNotFoundError:
|
|
291
|
+
with open(log_path, "w", encoding="utf-8", errors="replace") as fh:
|
|
292
|
+
fh.write("ERROR: certipy not found in PATH\n")
|
|
293
|
+
fh.write("Install: pipx install certipy-ad\n")
|
|
294
|
+
fh.write("Or: pip install certipy-ad\n")
|
|
295
|
+
return 127
|
|
296
|
+
|
|
297
|
+
except Exception as e:
|
|
298
|
+
with open(log_path, "a", encoding="utf-8", errors="replace") as fh:
|
|
299
|
+
fh.write(f"\n\nERROR: {type(e).__name__}: {e}\n")
|
|
300
|
+
return 1
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
plugin = CertipyPlugin()
|