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
|
@@ -28,7 +28,7 @@ class CredentialTester:
|
|
|
28
28
|
service: str,
|
|
29
29
|
username: str,
|
|
30
30
|
password: str,
|
|
31
|
-
protocol: str =
|
|
31
|
+
protocol: str = "tcp",
|
|
32
32
|
) -> Dict:
|
|
33
33
|
"""
|
|
34
34
|
Test a single credential against a service.
|
|
@@ -45,44 +45,44 @@ class CredentialTester:
|
|
|
45
45
|
Dict with test results
|
|
46
46
|
"""
|
|
47
47
|
result = {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
"host": host,
|
|
49
|
+
"port": port,
|
|
50
|
+
"service": service,
|
|
51
|
+
"username": username,
|
|
52
|
+
"status": "unknown",
|
|
53
|
+
"message": "",
|
|
54
|
+
"tested_at": None,
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
# Check if port is open first
|
|
58
58
|
if not self._check_port(host, port, protocol):
|
|
59
|
-
result[
|
|
60
|
-
result[
|
|
59
|
+
result["status"] = "port_closed"
|
|
60
|
+
result["message"] = f"Port {port}/{protocol} is not open"
|
|
61
61
|
return result
|
|
62
62
|
|
|
63
63
|
# Route to appropriate tester
|
|
64
64
|
service_lower = service.lower()
|
|
65
65
|
|
|
66
|
-
if service_lower in [
|
|
66
|
+
if service_lower in ["ssh", "ssh-2"]:
|
|
67
67
|
return self._test_ssh(host, port, username, password)
|
|
68
|
-
elif service_lower in [
|
|
68
|
+
elif service_lower in ["smb", "microsoft-ds", "netbios-ssn"]:
|
|
69
69
|
return self._test_smb(host, username, password)
|
|
70
|
-
elif service_lower in [
|
|
70
|
+
elif service_lower in ["mysql", "mariadb"]:
|
|
71
71
|
return self._test_mysql(host, port, username, password)
|
|
72
|
-
elif service_lower in [
|
|
72
|
+
elif service_lower in ["postgresql", "postgres"]:
|
|
73
73
|
return self._test_postgresql(host, port, username, password)
|
|
74
|
-
elif service_lower in [
|
|
74
|
+
elif service_lower in ["rdp", "ms-wbt-server", "terminal-server"]:
|
|
75
75
|
return self._test_rdp(host, port, username, password)
|
|
76
|
-
elif service_lower in [
|
|
76
|
+
elif service_lower in ["ftp", "ftps"]:
|
|
77
77
|
return self._test_ftp(host, port, username, password)
|
|
78
|
-
elif service_lower in [
|
|
78
|
+
elif service_lower in ["telnet"]:
|
|
79
79
|
return self._test_telnet(host, port, username, password)
|
|
80
80
|
else:
|
|
81
|
-
result[
|
|
82
|
-
result[
|
|
81
|
+
result["status"] = "unsupported"
|
|
82
|
+
result["message"] = f"Service '{service}' testing not yet supported"
|
|
83
83
|
return result
|
|
84
84
|
|
|
85
|
-
def _check_port(self, host: str, port: int, protocol: str =
|
|
85
|
+
def _check_port(self, host: str, port: int, protocol: str = "tcp") -> bool:
|
|
86
86
|
"""Check if a port is open."""
|
|
87
87
|
try:
|
|
88
88
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
@@ -96,315 +96,324 @@ class CredentialTester:
|
|
|
96
96
|
def _test_ssh(self, host: str, port: int, username: str, password: str) -> Dict:
|
|
97
97
|
"""Test SSH credentials using sshpass."""
|
|
98
98
|
result = {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
99
|
+
"host": host,
|
|
100
|
+
"port": port,
|
|
101
|
+
"service": "ssh",
|
|
102
|
+
"username": username,
|
|
103
|
+
"status": "unknown",
|
|
104
|
+
"message": "",
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
try:
|
|
108
108
|
# Use sshpass with ssh
|
|
109
109
|
cmd = [
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
110
|
+
"sshpass",
|
|
111
|
+
"-p",
|
|
112
|
+
password,
|
|
113
|
+
"ssh",
|
|
114
|
+
"-o",
|
|
115
|
+
"StrictHostKeyChecking=no",
|
|
116
|
+
"-o",
|
|
117
|
+
"UserKnownHostsFile=/dev/null",
|
|
118
|
+
"-o",
|
|
119
|
+
f"ConnectTimeout={self.timeout}",
|
|
120
|
+
"-p",
|
|
121
|
+
str(port),
|
|
122
|
+
f"{username}@{host}",
|
|
123
|
+
'echo "SUCCESS"',
|
|
118
124
|
]
|
|
119
125
|
|
|
120
126
|
proc = subprocess.run(
|
|
121
|
-
cmd,
|
|
122
|
-
capture_output=True,
|
|
123
|
-
timeout=self.timeout + 5,
|
|
124
|
-
text=True
|
|
127
|
+
cmd, capture_output=True, timeout=self.timeout + 5, text=True
|
|
125
128
|
)
|
|
126
129
|
|
|
127
|
-
if
|
|
128
|
-
result[
|
|
129
|
-
result[
|
|
130
|
-
elif
|
|
131
|
-
result[
|
|
132
|
-
result[
|
|
130
|
+
if "SUCCESS" in proc.stdout:
|
|
131
|
+
result["status"] = "valid"
|
|
132
|
+
result["message"] = "Authentication successful"
|
|
133
|
+
elif "Permission denied" in proc.stderr:
|
|
134
|
+
result["status"] = "invalid"
|
|
135
|
+
result["message"] = "Invalid credentials"
|
|
133
136
|
else:
|
|
134
|
-
result[
|
|
135
|
-
result[
|
|
137
|
+
result["status"] = "error"
|
|
138
|
+
result["message"] = f"Error: {proc.stderr[:100]}"
|
|
136
139
|
|
|
137
140
|
except FileNotFoundError:
|
|
138
|
-
result[
|
|
139
|
-
result[
|
|
141
|
+
result["status"] = "error"
|
|
142
|
+
result["message"] = "sshpass not installed (apt install sshpass)"
|
|
140
143
|
except subprocess.TimeoutExpired:
|
|
141
|
-
result[
|
|
142
|
-
result[
|
|
144
|
+
result["status"] = "timeout"
|
|
145
|
+
result["message"] = "Connection timeout"
|
|
143
146
|
except Exception as e:
|
|
144
|
-
result[
|
|
145
|
-
result[
|
|
147
|
+
result["status"] = "error"
|
|
148
|
+
result["message"] = f"Error: {str(e)[:100]}"
|
|
146
149
|
|
|
147
150
|
return result
|
|
148
151
|
|
|
149
152
|
def _test_smb(self, host: str, username: str, password: str) -> Dict:
|
|
150
153
|
"""Test SMB credentials using smbclient."""
|
|
151
154
|
result = {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
155
|
+
"host": host,
|
|
156
|
+
"port": 445,
|
|
157
|
+
"service": "smb",
|
|
158
|
+
"username": username,
|
|
159
|
+
"status": "unknown",
|
|
160
|
+
"message": "",
|
|
158
161
|
}
|
|
159
162
|
|
|
160
163
|
try:
|
|
161
164
|
# Use smbclient to test
|
|
162
165
|
cmd = [
|
|
163
|
-
|
|
164
|
-
f
|
|
165
|
-
|
|
166
|
-
|
|
166
|
+
"smbclient",
|
|
167
|
+
f"//{host}/IPC$",
|
|
168
|
+
"-U",
|
|
169
|
+
f"{username}%{password}",
|
|
170
|
+
"-c",
|
|
171
|
+
"quit",
|
|
167
172
|
]
|
|
168
173
|
|
|
169
174
|
proc = subprocess.run(
|
|
170
|
-
cmd,
|
|
171
|
-
capture_output=True,
|
|
172
|
-
timeout=self.timeout + 5,
|
|
173
|
-
text=True
|
|
175
|
+
cmd, capture_output=True, timeout=self.timeout + 5, text=True
|
|
174
176
|
)
|
|
175
177
|
|
|
176
178
|
if proc.returncode == 0:
|
|
177
|
-
result[
|
|
178
|
-
result[
|
|
179
|
-
elif
|
|
180
|
-
result[
|
|
181
|
-
result[
|
|
182
|
-
elif
|
|
183
|
-
result[
|
|
184
|
-
result[
|
|
179
|
+
result["status"] = "valid"
|
|
180
|
+
result["message"] = "Authentication successful"
|
|
181
|
+
elif "NT_STATUS_LOGON_FAILURE" in proc.stderr:
|
|
182
|
+
result["status"] = "invalid"
|
|
183
|
+
result["message"] = "Invalid credentials"
|
|
184
|
+
elif "NT_STATUS_ACCOUNT_DISABLED" in proc.stderr:
|
|
185
|
+
result["status"] = "account_disabled"
|
|
186
|
+
result["message"] = "Account is disabled"
|
|
185
187
|
else:
|
|
186
|
-
result[
|
|
187
|
-
result[
|
|
188
|
+
result["status"] = "error"
|
|
189
|
+
result["message"] = f"Error: {proc.stderr[:100]}"
|
|
188
190
|
|
|
189
191
|
except FileNotFoundError:
|
|
190
|
-
result[
|
|
191
|
-
result[
|
|
192
|
+
result["status"] = "error"
|
|
193
|
+
result["message"] = "smbclient not installed (apt install smbclient)"
|
|
192
194
|
except subprocess.TimeoutExpired:
|
|
193
|
-
result[
|
|
194
|
-
result[
|
|
195
|
+
result["status"] = "timeout"
|
|
196
|
+
result["message"] = "Connection timeout"
|
|
195
197
|
except Exception as e:
|
|
196
|
-
result[
|
|
197
|
-
result[
|
|
198
|
+
result["status"] = "error"
|
|
199
|
+
result["message"] = f"Error: {str(e)[:100]}"
|
|
198
200
|
|
|
199
201
|
return result
|
|
200
202
|
|
|
201
203
|
def _test_mysql(self, host: str, port: int, username: str, password: str) -> Dict:
|
|
202
204
|
"""Test MySQL credentials."""
|
|
203
205
|
result = {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
206
|
+
"host": host,
|
|
207
|
+
"port": port,
|
|
208
|
+
"service": "mysql",
|
|
209
|
+
"username": username,
|
|
210
|
+
"status": "unknown",
|
|
211
|
+
"message": "",
|
|
210
212
|
}
|
|
211
213
|
|
|
212
214
|
try:
|
|
213
215
|
cmd = [
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
216
|
+
"mysql",
|
|
217
|
+
"-h",
|
|
218
|
+
host,
|
|
219
|
+
"-P",
|
|
220
|
+
str(port),
|
|
221
|
+
"-u",
|
|
222
|
+
username,
|
|
223
|
+
f"-p{password}",
|
|
224
|
+
"-e",
|
|
225
|
+
"SELECT 1;",
|
|
220
226
|
]
|
|
221
227
|
|
|
222
228
|
proc = subprocess.run(
|
|
223
|
-
cmd,
|
|
224
|
-
capture_output=True,
|
|
225
|
-
timeout=self.timeout + 5,
|
|
226
|
-
text=True
|
|
229
|
+
cmd, capture_output=True, timeout=self.timeout + 5, text=True
|
|
227
230
|
)
|
|
228
231
|
|
|
229
232
|
if proc.returncode == 0:
|
|
230
|
-
result[
|
|
231
|
-
result[
|
|
232
|
-
elif
|
|
233
|
-
result[
|
|
234
|
-
result[
|
|
233
|
+
result["status"] = "valid"
|
|
234
|
+
result["message"] = "Authentication successful"
|
|
235
|
+
elif "Access denied" in proc.stderr:
|
|
236
|
+
result["status"] = "invalid"
|
|
237
|
+
result["message"] = "Invalid credentials"
|
|
235
238
|
else:
|
|
236
|
-
result[
|
|
237
|
-
result[
|
|
239
|
+
result["status"] = "error"
|
|
240
|
+
result["message"] = f"Error: {proc.stderr[:100]}"
|
|
238
241
|
|
|
239
242
|
except FileNotFoundError:
|
|
240
|
-
result[
|
|
241
|
-
result[
|
|
243
|
+
result["status"] = "error"
|
|
244
|
+
result["message"] = "mysql client not installed (apt install mysql-client)"
|
|
242
245
|
except subprocess.TimeoutExpired:
|
|
243
|
-
result[
|
|
244
|
-
result[
|
|
246
|
+
result["status"] = "timeout"
|
|
247
|
+
result["message"] = "Connection timeout"
|
|
245
248
|
except Exception as e:
|
|
246
|
-
result[
|
|
247
|
-
result[
|
|
249
|
+
result["status"] = "error"
|
|
250
|
+
result["message"] = f"Error: {str(e)[:100]}"
|
|
248
251
|
|
|
249
252
|
return result
|
|
250
253
|
|
|
251
|
-
def _test_postgresql(
|
|
254
|
+
def _test_postgresql(
|
|
255
|
+
self, host: str, port: int, username: str, password: str
|
|
256
|
+
) -> Dict:
|
|
252
257
|
"""Test PostgreSQL credentials."""
|
|
253
258
|
result = {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
259
|
+
"host": host,
|
|
260
|
+
"port": port,
|
|
261
|
+
"service": "postgresql",
|
|
262
|
+
"username": username,
|
|
263
|
+
"status": "unknown",
|
|
264
|
+
"message": "",
|
|
260
265
|
}
|
|
261
266
|
|
|
262
267
|
try:
|
|
263
268
|
# Set PGPASSWORD environment variable
|
|
264
269
|
import os
|
|
270
|
+
|
|
265
271
|
env = os.environ.copy()
|
|
266
|
-
env[
|
|
272
|
+
env["PGPASSWORD"] = password
|
|
267
273
|
|
|
268
274
|
cmd = [
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
+
"psql",
|
|
276
|
+
"-h",
|
|
277
|
+
host,
|
|
278
|
+
"-p",
|
|
279
|
+
str(port),
|
|
280
|
+
"-U",
|
|
281
|
+
username,
|
|
282
|
+
"-d",
|
|
283
|
+
"postgres",
|
|
284
|
+
"-c",
|
|
285
|
+
"SELECT 1;",
|
|
275
286
|
]
|
|
276
287
|
|
|
277
288
|
proc = subprocess.run(
|
|
278
|
-
cmd,
|
|
279
|
-
capture_output=True,
|
|
280
|
-
timeout=self.timeout + 5,
|
|
281
|
-
text=True,
|
|
282
|
-
env=env
|
|
289
|
+
cmd, capture_output=True, timeout=self.timeout + 5, text=True, env=env
|
|
283
290
|
)
|
|
284
291
|
|
|
285
292
|
if proc.returncode == 0:
|
|
286
|
-
result[
|
|
287
|
-
result[
|
|
288
|
-
elif
|
|
289
|
-
result[
|
|
290
|
-
result[
|
|
293
|
+
result["status"] = "valid"
|
|
294
|
+
result["message"] = "Authentication successful"
|
|
295
|
+
elif "authentication failed" in proc.stderr.lower():
|
|
296
|
+
result["status"] = "invalid"
|
|
297
|
+
result["message"] = "Invalid credentials"
|
|
291
298
|
else:
|
|
292
|
-
result[
|
|
293
|
-
result[
|
|
299
|
+
result["status"] = "error"
|
|
300
|
+
result["message"] = f"Error: {proc.stderr[:100]}"
|
|
294
301
|
|
|
295
302
|
except FileNotFoundError:
|
|
296
|
-
result[
|
|
297
|
-
result[
|
|
303
|
+
result["status"] = "error"
|
|
304
|
+
result["message"] = "psql not installed (apt install postgresql-client)"
|
|
298
305
|
except subprocess.TimeoutExpired:
|
|
299
|
-
result[
|
|
300
|
-
result[
|
|
306
|
+
result["status"] = "timeout"
|
|
307
|
+
result["message"] = "Connection timeout"
|
|
301
308
|
except Exception as e:
|
|
302
|
-
result[
|
|
303
|
-
result[
|
|
309
|
+
result["status"] = "error"
|
|
310
|
+
result["message"] = f"Error: {str(e)[:100]}"
|
|
304
311
|
|
|
305
312
|
return result
|
|
306
313
|
|
|
307
314
|
def _test_rdp(self, host: str, port: int, username: str, password: str) -> Dict:
|
|
308
315
|
"""Test RDP credentials using xfreerdp."""
|
|
309
316
|
result = {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
317
|
+
"host": host,
|
|
318
|
+
"port": port,
|
|
319
|
+
"service": "rdp",
|
|
320
|
+
"username": username,
|
|
321
|
+
"status": "unknown",
|
|
322
|
+
"message": "",
|
|
316
323
|
}
|
|
317
324
|
|
|
318
325
|
try:
|
|
319
326
|
cmd = [
|
|
320
|
-
|
|
321
|
-
f
|
|
322
|
-
f
|
|
323
|
-
f
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
+
"xfreerdp",
|
|
328
|
+
f"/v:{host}:{port}",
|
|
329
|
+
f"/u:{username}",
|
|
330
|
+
f"/p:{password}",
|
|
331
|
+
"/cert:ignore",
|
|
332
|
+
"+auth-only",
|
|
333
|
+
"/timeout:5000",
|
|
327
334
|
]
|
|
328
335
|
|
|
329
336
|
proc = subprocess.run(
|
|
330
|
-
cmd,
|
|
331
|
-
capture_output=True,
|
|
332
|
-
timeout=self.timeout + 5,
|
|
333
|
-
text=True
|
|
337
|
+
cmd, capture_output=True, timeout=self.timeout + 5, text=True
|
|
334
338
|
)
|
|
335
339
|
|
|
336
340
|
# xfreerdp returns 0 on successful auth
|
|
337
341
|
if proc.returncode == 0:
|
|
338
|
-
result[
|
|
339
|
-
result[
|
|
340
|
-
elif
|
|
341
|
-
|
|
342
|
-
|
|
342
|
+
result["status"] = "valid"
|
|
343
|
+
result["message"] = "Authentication successful"
|
|
344
|
+
elif (
|
|
345
|
+
"Authentication failure" in proc.stderr
|
|
346
|
+
or "ERRCONNECT_LOGON_FAILURE" in proc.stderr
|
|
347
|
+
):
|
|
348
|
+
result["status"] = "invalid"
|
|
349
|
+
result["message"] = "Invalid credentials"
|
|
343
350
|
else:
|
|
344
|
-
result[
|
|
345
|
-
result[
|
|
351
|
+
result["status"] = "error"
|
|
352
|
+
result["message"] = f"Error: {proc.stderr[:100]}"
|
|
346
353
|
|
|
347
354
|
except FileNotFoundError:
|
|
348
|
-
result[
|
|
349
|
-
result[
|
|
355
|
+
result["status"] = "error"
|
|
356
|
+
result["message"] = "xfreerdp not installed (apt install freerdp2-x11)"
|
|
350
357
|
except subprocess.TimeoutExpired:
|
|
351
|
-
result[
|
|
352
|
-
result[
|
|
358
|
+
result["status"] = "timeout"
|
|
359
|
+
result["message"] = "Connection timeout"
|
|
353
360
|
except Exception as e:
|
|
354
|
-
result[
|
|
355
|
-
result[
|
|
361
|
+
result["status"] = "error"
|
|
362
|
+
result["message"] = f"Error: {str(e)[:100]}"
|
|
356
363
|
|
|
357
364
|
return result
|
|
358
365
|
|
|
359
366
|
def _test_ftp(self, host: str, port: int, username: str, password: str) -> Dict:
|
|
360
367
|
"""Test FTP credentials."""
|
|
361
368
|
result = {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
369
|
+
"host": host,
|
|
370
|
+
"port": port,
|
|
371
|
+
"service": "ftp",
|
|
372
|
+
"username": username,
|
|
373
|
+
"status": "unknown",
|
|
374
|
+
"message": "",
|
|
368
375
|
}
|
|
369
376
|
|
|
370
377
|
try:
|
|
371
378
|
from ftplib import FTP # nosec B402 - Intentionally testing FTP credentials
|
|
372
379
|
|
|
373
|
-
ftp = FTP() # nosec B321 - Penetration testing tool,
|
|
380
|
+
ftp = FTP() # nosec B321 - Penetration testing tool, FTP cred testing
|
|
374
381
|
ftp.connect(host, port, timeout=self.timeout)
|
|
375
382
|
ftp.login(username, password)
|
|
376
383
|
ftp.quit()
|
|
377
384
|
|
|
378
|
-
result[
|
|
379
|
-
result[
|
|
385
|
+
result["status"] = "valid"
|
|
386
|
+
result["message"] = "Authentication successful"
|
|
380
387
|
|
|
381
388
|
except Exception as e:
|
|
382
389
|
error_str = str(e).lower()
|
|
383
|
-
if
|
|
384
|
-
result[
|
|
385
|
-
result[
|
|
386
|
-
elif
|
|
387
|
-
result[
|
|
388
|
-
result[
|
|
390
|
+
if "530" in error_str or "login" in error_str:
|
|
391
|
+
result["status"] = "invalid"
|
|
392
|
+
result["message"] = "Invalid credentials"
|
|
393
|
+
elif "timeout" in error_str:
|
|
394
|
+
result["status"] = "timeout"
|
|
395
|
+
result["message"] = "Connection timeout"
|
|
389
396
|
else:
|
|
390
|
-
result[
|
|
391
|
-
result[
|
|
397
|
+
result["status"] = "error"
|
|
398
|
+
result["message"] = f"Error: {str(e)[:100]}"
|
|
392
399
|
|
|
393
400
|
return result
|
|
394
401
|
|
|
395
402
|
def _test_telnet(self, host: str, port: int, username: str, password: str) -> Dict:
|
|
396
403
|
"""Test Telnet credentials."""
|
|
397
404
|
result = {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
405
|
+
"host": host,
|
|
406
|
+
"port": port,
|
|
407
|
+
"service": "telnet",
|
|
408
|
+
"username": username,
|
|
409
|
+
"status": "unsupported",
|
|
410
|
+
"message": "Telnet testing requires interactive session (not yet implemented)",
|
|
404
411
|
}
|
|
405
412
|
return result
|
|
406
413
|
|
|
407
|
-
def batch_test(
|
|
414
|
+
def batch_test(
|
|
415
|
+
self, credentials: List[Dict], services: List[Dict], verbose: bool = False
|
|
416
|
+
) -> List[Dict]:
|
|
408
417
|
"""
|
|
409
418
|
Batch test credentials against multiple services.
|
|
410
419
|
|
|
@@ -421,28 +430,35 @@ class CredentialTester:
|
|
|
421
430
|
current = 0
|
|
422
431
|
|
|
423
432
|
for cred in credentials:
|
|
424
|
-
username = cred.get(
|
|
425
|
-
password = cred.get(
|
|
433
|
+
username = cred.get("username")
|
|
434
|
+
password = cred.get("password")
|
|
426
435
|
|
|
427
436
|
if not username or not password:
|
|
428
437
|
continue
|
|
429
438
|
|
|
430
439
|
for svc in services:
|
|
431
440
|
current += 1
|
|
432
|
-
host = svc.get(
|
|
433
|
-
port = svc.get(
|
|
434
|
-
service = svc.get(
|
|
441
|
+
host = svc.get("host") or svc.get("ip_address")
|
|
442
|
+
port = svc.get("port")
|
|
443
|
+
service = svc.get("service") or svc.get("service_name")
|
|
435
444
|
|
|
436
445
|
if not all([host, port, service]):
|
|
437
446
|
continue
|
|
438
447
|
|
|
439
448
|
if verbose:
|
|
440
|
-
click.echo(
|
|
449
|
+
click.echo(
|
|
450
|
+
f"[{current}/{total}] Testing {username} @ {host}:{port} ({service})"
|
|
451
|
+
)
|
|
441
452
|
|
|
442
453
|
result = self.test_credential(host, port, service, username, password)
|
|
443
454
|
results.append(result)
|
|
444
455
|
|
|
445
|
-
if verbose and result[
|
|
446
|
-
click.echo(
|
|
456
|
+
if verbose and result["status"] == "valid":
|
|
457
|
+
click.echo(
|
|
458
|
+
click.style(
|
|
459
|
+
f" ✓ Valid: {username}:{password} @ {host}:{port}",
|
|
460
|
+
fg="green",
|
|
461
|
+
)
|
|
462
|
+
)
|
|
447
463
|
|
|
448
464
|
return results
|
souleyez/ui/__init__.py
CHANGED