souleyez 2.43.29__py3-none-any.whl → 3.0.0__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 +9564 -2881
- 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 +564 -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 +409 -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 +417 -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 +913 -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 +219 -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 +237 -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 +23034 -10679
- 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-3.0.0.dist-info}/METADATA +2 -2
- souleyez-3.0.0.dist-info/RECORD +443 -0
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/WHEEL +1 -1
- souleyez-2.43.29.dist-info/RECORD +0 -379
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/entry_points.txt +0 -0
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Base handler class for tool result parsing and display.
|
|
4
|
+
|
|
5
|
+
All tool handlers inherit from BaseToolHandler and implement
|
|
6
|
+
the required methods for their specific tool.
|
|
7
|
+
"""
|
|
8
|
+
import logging
|
|
9
|
+
from abc import ABC, abstractmethod
|
|
10
|
+
from typing import Any, Dict, Optional
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BaseToolHandler(ABC):
|
|
18
|
+
"""
|
|
19
|
+
Abstract base class for tool handlers.
|
|
20
|
+
|
|
21
|
+
Each handler consolidates:
|
|
22
|
+
- Parsing logic (parse_job)
|
|
23
|
+
- Display logic (display_done, display_warning, etc.)
|
|
24
|
+
- Capability flags (has_warning_handler, etc.)
|
|
25
|
+
|
|
26
|
+
Subclasses MUST define:
|
|
27
|
+
- tool_name: The tool identifier (e.g., 'msf_exploit')
|
|
28
|
+
- display_name: Human-readable name (e.g., 'Metasploit Exploit')
|
|
29
|
+
- parse_job(): Parse job output and write to database
|
|
30
|
+
- display_done(): Show successful results
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
# Tool identifier - MUST be overridden
|
|
34
|
+
tool_name: str = "base"
|
|
35
|
+
display_name: str = "Unknown Tool"
|
|
36
|
+
|
|
37
|
+
# Capability flags - registry checks these instead of manual lists
|
|
38
|
+
# Override in subclass if the handler doesn't support a status
|
|
39
|
+
has_error_handler: bool = True
|
|
40
|
+
has_warning_handler: bool = True
|
|
41
|
+
has_no_results_handler: bool = True
|
|
42
|
+
has_done_handler: bool = True
|
|
43
|
+
|
|
44
|
+
@abstractmethod
|
|
45
|
+
def parse_job(
|
|
46
|
+
self,
|
|
47
|
+
engagement_id: int,
|
|
48
|
+
log_path: str,
|
|
49
|
+
job: Dict[str, Any],
|
|
50
|
+
host_manager: Optional[Any] = None,
|
|
51
|
+
findings_manager: Optional[Any] = None,
|
|
52
|
+
credentials_manager: Optional[Any] = None,
|
|
53
|
+
) -> Dict[str, Any]:
|
|
54
|
+
"""
|
|
55
|
+
Parse job output and write findings/credentials to database.
|
|
56
|
+
|
|
57
|
+
This replaces parse_*_job() functions in result_handler.py.
|
|
58
|
+
Called ONCE when job completes.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
engagement_id: The engagement ID
|
|
62
|
+
log_path: Path to the job's log file
|
|
63
|
+
job: The job dict from background system
|
|
64
|
+
host_manager: Optional HostManager for DB operations
|
|
65
|
+
findings_manager: Optional FindingsManager for DB operations
|
|
66
|
+
credentials_manager: Optional CredentialsManager for DB operations
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
parse_result dict with at minimum:
|
|
70
|
+
- 'status': 'done' | 'warning' | 'no_results' | 'error'
|
|
71
|
+
- 'summary': Human-readable summary
|
|
72
|
+
"""
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
@abstractmethod
|
|
76
|
+
def display_done(
|
|
77
|
+
self,
|
|
78
|
+
job: Dict[str, Any],
|
|
79
|
+
log_path: str,
|
|
80
|
+
show_all: bool = False,
|
|
81
|
+
show_passwords: bool = False,
|
|
82
|
+
) -> None:
|
|
83
|
+
"""
|
|
84
|
+
Display successful job results.
|
|
85
|
+
|
|
86
|
+
Called when job status is 'done' and handler.has_done_handler is True.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
job: The job dict (includes parse_result)
|
|
90
|
+
log_path: Path to the job's log file
|
|
91
|
+
show_all: Whether to show all results (not just top N)
|
|
92
|
+
show_passwords: Whether to reveal passwords
|
|
93
|
+
"""
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
def display_warning(
|
|
97
|
+
self,
|
|
98
|
+
job: Dict[str, Any],
|
|
99
|
+
log_path: str,
|
|
100
|
+
log_content: Optional[str] = None,
|
|
101
|
+
) -> None:
|
|
102
|
+
"""
|
|
103
|
+
Display warning status results.
|
|
104
|
+
|
|
105
|
+
Default implementation shows a generic warning block.
|
|
106
|
+
Override for tool-specific warning display.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
job: The job dict (includes parse_result)
|
|
110
|
+
log_path: Path to the job's log file
|
|
111
|
+
log_content: Optional pre-read log content
|
|
112
|
+
"""
|
|
113
|
+
parse_result = job.get("parse_result", {})
|
|
114
|
+
summary = "Warning"
|
|
115
|
+
if isinstance(parse_result, dict):
|
|
116
|
+
summary = parse_result.get("summary", "Warning")
|
|
117
|
+
|
|
118
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
119
|
+
click.echo(
|
|
120
|
+
click.style(
|
|
121
|
+
f"[WARNING] {self.display_name.upper()}", bold=True, fg="yellow"
|
|
122
|
+
)
|
|
123
|
+
)
|
|
124
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
125
|
+
click.echo()
|
|
126
|
+
click.echo(f" {summary}")
|
|
127
|
+
click.echo()
|
|
128
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
129
|
+
click.echo()
|
|
130
|
+
|
|
131
|
+
def display_error(
|
|
132
|
+
self,
|
|
133
|
+
job: Dict[str, Any],
|
|
134
|
+
log_path: str,
|
|
135
|
+
log_content: Optional[str] = None,
|
|
136
|
+
) -> None:
|
|
137
|
+
"""
|
|
138
|
+
Display error status results.
|
|
139
|
+
|
|
140
|
+
Default implementation shows a generic error block.
|
|
141
|
+
Override for tool-specific error display.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
job: The job dict (includes parse_result)
|
|
145
|
+
log_path: Path to the job's log file
|
|
146
|
+
log_content: Optional pre-read log content
|
|
147
|
+
"""
|
|
148
|
+
parse_result = job.get("parse_result", {})
|
|
149
|
+
summary = "An error occurred"
|
|
150
|
+
if isinstance(parse_result, dict):
|
|
151
|
+
summary = parse_result.get("summary", "An error occurred")
|
|
152
|
+
|
|
153
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
154
|
+
click.echo(
|
|
155
|
+
click.style(f"[ERROR] {self.display_name.upper()}", bold=True, fg="red")
|
|
156
|
+
)
|
|
157
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
158
|
+
click.echo()
|
|
159
|
+
click.echo(f" {summary}")
|
|
160
|
+
click.echo()
|
|
161
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
162
|
+
click.echo()
|
|
163
|
+
|
|
164
|
+
def display_no_results(
|
|
165
|
+
self,
|
|
166
|
+
job: Dict[str, Any],
|
|
167
|
+
log_path: str,
|
|
168
|
+
) -> None:
|
|
169
|
+
"""
|
|
170
|
+
Display no_results status.
|
|
171
|
+
|
|
172
|
+
Default implementation shows a generic no results block.
|
|
173
|
+
Override for tool-specific no results display.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
job: The job dict (includes parse_result)
|
|
177
|
+
log_path: Path to the job's log file
|
|
178
|
+
"""
|
|
179
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
180
|
+
click.echo(
|
|
181
|
+
click.style(f"{self.display_name.upper()} RESULTS", bold=True, fg="yellow")
|
|
182
|
+
)
|
|
183
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
184
|
+
click.echo()
|
|
185
|
+
click.echo(" No results found.")
|
|
186
|
+
click.echo()
|
|
187
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
188
|
+
click.echo()
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Bash handler.
|
|
4
|
+
|
|
5
|
+
Handles bash script execution results, particularly for auto-generated
|
|
6
|
+
scripts like web login credential tests.
|
|
7
|
+
"""
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
from typing import Any, Dict, Optional
|
|
12
|
+
|
|
13
|
+
import click
|
|
14
|
+
|
|
15
|
+
from souleyez.engine.job_status import STATUS_DONE, STATUS_ERROR, STATUS_NO_RESULTS
|
|
16
|
+
from souleyez.handlers.base import BaseToolHandler
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BashHandler(BaseToolHandler):
|
|
22
|
+
"""Handler for bash script execution jobs."""
|
|
23
|
+
|
|
24
|
+
tool_name = "bash"
|
|
25
|
+
display_name = "Bash Script"
|
|
26
|
+
|
|
27
|
+
has_error_handler = True
|
|
28
|
+
has_warning_handler = False
|
|
29
|
+
has_no_results_handler = True
|
|
30
|
+
has_done_handler = True
|
|
31
|
+
|
|
32
|
+
def parse_job(
|
|
33
|
+
self,
|
|
34
|
+
engagement_id: int,
|
|
35
|
+
log_path: str,
|
|
36
|
+
job: Dict[str, Any],
|
|
37
|
+
host_manager: Optional[Any] = None,
|
|
38
|
+
findings_manager: Optional[Any] = None,
|
|
39
|
+
credentials_manager: Optional[Any] = None,
|
|
40
|
+
) -> Dict[str, Any]:
|
|
41
|
+
"""
|
|
42
|
+
Parse bash job results.
|
|
43
|
+
|
|
44
|
+
Detects web login test scripts and parses their output.
|
|
45
|
+
"""
|
|
46
|
+
try:
|
|
47
|
+
if not log_path or not os.path.exists(log_path):
|
|
48
|
+
return {"error": "Log file not found"}
|
|
49
|
+
|
|
50
|
+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
51
|
+
log_content = f.read()
|
|
52
|
+
|
|
53
|
+
# Check if this is a web login test script
|
|
54
|
+
args = job.get("args", [])
|
|
55
|
+
is_web_login_test = any("web_login_test" in str(arg) for arg in args)
|
|
56
|
+
|
|
57
|
+
if is_web_login_test:
|
|
58
|
+
return self._parse_web_login_test(log_content, job)
|
|
59
|
+
|
|
60
|
+
# Generic bash script - just check exit code
|
|
61
|
+
exit_code = self._extract_exit_code(log_content)
|
|
62
|
+
if exit_code == 0:
|
|
63
|
+
return {
|
|
64
|
+
"tool": "bash",
|
|
65
|
+
"status": STATUS_DONE,
|
|
66
|
+
"summary": "Script completed successfully",
|
|
67
|
+
}
|
|
68
|
+
else:
|
|
69
|
+
return {
|
|
70
|
+
"tool": "bash",
|
|
71
|
+
"status": STATUS_ERROR,
|
|
72
|
+
"summary": f"Script failed (exit code {exit_code})",
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
except Exception as e:
|
|
76
|
+
logger.error(f"Error parsing bash job: {e}")
|
|
77
|
+
return {"error": str(e)}
|
|
78
|
+
|
|
79
|
+
def _parse_web_login_test(
|
|
80
|
+
self, log_content: str, job: Dict[str, Any]
|
|
81
|
+
) -> Dict[str, Any]:
|
|
82
|
+
"""Parse web login credential test output."""
|
|
83
|
+
result = {
|
|
84
|
+
"tool": "bash",
|
|
85
|
+
"status": STATUS_NO_RESULTS,
|
|
86
|
+
"summary": "Credential test completed",
|
|
87
|
+
"login_success": False,
|
|
88
|
+
"username": None,
|
|
89
|
+
"response": None,
|
|
90
|
+
"http_code": None,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# Extract username from "Testing user:pass against URL"
|
|
94
|
+
test_match = re.search(r"Testing (\S+):(\S+) against", log_content)
|
|
95
|
+
if test_match:
|
|
96
|
+
result["username"] = test_match.group(1)
|
|
97
|
+
|
|
98
|
+
# Extract HTTP response code
|
|
99
|
+
http_match = re.search(r"HTTP Code: (\d+)", log_content)
|
|
100
|
+
if http_match:
|
|
101
|
+
result["http_code"] = http_match.group(1)
|
|
102
|
+
|
|
103
|
+
# Extract response message
|
|
104
|
+
resp_match = re.search(
|
|
105
|
+
r"Response: (.+?)(?:\n|HTTP_CODE)", log_content, re.DOTALL
|
|
106
|
+
)
|
|
107
|
+
if resp_match:
|
|
108
|
+
result["response"] = resp_match.group(1).strip()[:100]
|
|
109
|
+
|
|
110
|
+
# Check for success indicators
|
|
111
|
+
if "[+] LOGIN SUCCESS" in log_content:
|
|
112
|
+
result["status"] = STATUS_DONE
|
|
113
|
+
result["login_success"] = True
|
|
114
|
+
result["summary"] = f"Login successful: {result['username']}"
|
|
115
|
+
elif "[+] POSSIBLE SUCCESS" in log_content:
|
|
116
|
+
result["status"] = STATUS_DONE
|
|
117
|
+
result["login_success"] = True
|
|
118
|
+
result["summary"] = (
|
|
119
|
+
f"Possible login success: {result['username']} (HTTP {result['http_code']})"
|
|
120
|
+
)
|
|
121
|
+
elif "[-] Login failed" in log_content:
|
|
122
|
+
result["status"] = STATUS_NO_RESULTS
|
|
123
|
+
# Extract failure reason from response
|
|
124
|
+
if result["response"]:
|
|
125
|
+
# Truncate long responses
|
|
126
|
+
reason = result["response"]
|
|
127
|
+
if len(reason) > 50:
|
|
128
|
+
reason = reason[:47] + "..."
|
|
129
|
+
result["summary"] = f"Login failed: {reason}"
|
|
130
|
+
else:
|
|
131
|
+
result["summary"] = (
|
|
132
|
+
f"Login failed (HTTP {result['http_code'] or 'unknown'})"
|
|
133
|
+
)
|
|
134
|
+
else:
|
|
135
|
+
# Unknown result
|
|
136
|
+
result["summary"] = "Credential test completed (check logs)"
|
|
137
|
+
|
|
138
|
+
return result
|
|
139
|
+
|
|
140
|
+
def _extract_exit_code(self, log_content: str) -> int:
|
|
141
|
+
"""Extract exit code from log content."""
|
|
142
|
+
match = re.search(r"Exit Code: (\d+)", log_content)
|
|
143
|
+
if match:
|
|
144
|
+
return int(match.group(1))
|
|
145
|
+
return -1
|
|
146
|
+
|
|
147
|
+
def display_done(
|
|
148
|
+
self,
|
|
149
|
+
job: Dict[str, Any],
|
|
150
|
+
log_path: str,
|
|
151
|
+
show_all: bool = False,
|
|
152
|
+
show_passwords: bool = False,
|
|
153
|
+
) -> None:
|
|
154
|
+
"""Display successful bash results."""
|
|
155
|
+
args = job.get("args", [])
|
|
156
|
+
is_web_login_test = any("web_login_test" in str(arg) for arg in args)
|
|
157
|
+
|
|
158
|
+
if is_web_login_test:
|
|
159
|
+
self._display_web_login_result(job, log_path, success=True)
|
|
160
|
+
else:
|
|
161
|
+
click.echo(click.style("=" * 60, fg="green"))
|
|
162
|
+
click.echo(click.style("BASH SCRIPT COMPLETED", bold=True, fg="green"))
|
|
163
|
+
click.echo(click.style("=" * 60, fg="green"))
|
|
164
|
+
click.echo()
|
|
165
|
+
click.echo(" Script executed successfully.")
|
|
166
|
+
click.echo()
|
|
167
|
+
click.echo(click.style("=" * 60, fg="green"))
|
|
168
|
+
click.echo()
|
|
169
|
+
|
|
170
|
+
def display_no_results(
|
|
171
|
+
self,
|
|
172
|
+
job: Dict[str, Any],
|
|
173
|
+
log_path: str,
|
|
174
|
+
) -> None:
|
|
175
|
+
"""Display no_results status for bash."""
|
|
176
|
+
args = job.get("args", [])
|
|
177
|
+
is_web_login_test = any("web_login_test" in str(arg) for arg in args)
|
|
178
|
+
|
|
179
|
+
if is_web_login_test:
|
|
180
|
+
self._display_web_login_result(job, log_path, success=False)
|
|
181
|
+
else:
|
|
182
|
+
click.echo(click.style("=" * 60, fg="yellow"))
|
|
183
|
+
click.echo(click.style("BASH SCRIPT - NO RESULTS", bold=True, fg="yellow"))
|
|
184
|
+
click.echo(click.style("=" * 60, fg="yellow"))
|
|
185
|
+
click.echo()
|
|
186
|
+
click.echo(" Script completed but produced no actionable results.")
|
|
187
|
+
click.echo()
|
|
188
|
+
click.echo(click.style("=" * 60, fg="yellow"))
|
|
189
|
+
click.echo()
|
|
190
|
+
|
|
191
|
+
def display_error(
|
|
192
|
+
self,
|
|
193
|
+
job: Dict[str, Any],
|
|
194
|
+
log_path: str,
|
|
195
|
+
log_content: Optional[str] = None,
|
|
196
|
+
) -> None:
|
|
197
|
+
"""Display error status for bash."""
|
|
198
|
+
args = job.get("args", [])
|
|
199
|
+
is_web_login_test = any("web_login_test" in str(arg) for arg in args)
|
|
200
|
+
|
|
201
|
+
if is_web_login_test:
|
|
202
|
+
self._display_web_login_result(job, log_path, success=False)
|
|
203
|
+
else:
|
|
204
|
+
click.echo(click.style("=" * 60, fg="red"))
|
|
205
|
+
click.echo(click.style("[ERROR] BASH SCRIPT FAILED", bold=True, fg="red"))
|
|
206
|
+
click.echo(click.style("=" * 60, fg="red"))
|
|
207
|
+
click.echo()
|
|
208
|
+
click.echo(" Script execution failed.")
|
|
209
|
+
click.echo(" Check raw logs for details (press 'r').")
|
|
210
|
+
click.echo()
|
|
211
|
+
click.echo(click.style("=" * 60, fg="red"))
|
|
212
|
+
click.echo()
|
|
213
|
+
|
|
214
|
+
def _display_web_login_result(
|
|
215
|
+
self, job: Dict[str, Any], log_path: str, success: bool
|
|
216
|
+
) -> None:
|
|
217
|
+
"""Display web login credential test result."""
|
|
218
|
+
# Parse the log to get details
|
|
219
|
+
username = None
|
|
220
|
+
password = "***"
|
|
221
|
+
response = None
|
|
222
|
+
http_code = None
|
|
223
|
+
|
|
224
|
+
if log_path and os.path.exists(log_path):
|
|
225
|
+
try:
|
|
226
|
+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
227
|
+
log_content = f.read()
|
|
228
|
+
|
|
229
|
+
# Extract details
|
|
230
|
+
test_match = re.search(r"Testing (\S+):(\S+) against", log_content)
|
|
231
|
+
if test_match:
|
|
232
|
+
username = test_match.group(1)
|
|
233
|
+
|
|
234
|
+
http_match = re.search(r"HTTP Code: (\d+)", log_content)
|
|
235
|
+
if http_match:
|
|
236
|
+
http_code = http_match.group(1)
|
|
237
|
+
|
|
238
|
+
resp_match = re.search(
|
|
239
|
+
r"Response: (.+?)(?:\nHTTP|$)", log_content, re.DOTALL
|
|
240
|
+
)
|
|
241
|
+
if resp_match:
|
|
242
|
+
response = resp_match.group(1).strip()
|
|
243
|
+
|
|
244
|
+
except Exception:
|
|
245
|
+
pass
|
|
246
|
+
|
|
247
|
+
target = job.get("target", "unknown")
|
|
248
|
+
|
|
249
|
+
if success or "[+]" in (response or ""):
|
|
250
|
+
click.echo(click.style("=" * 60, fg="green"))
|
|
251
|
+
click.echo(click.style("WEB LOGIN TEST - SUCCESS", bold=True, fg="green"))
|
|
252
|
+
click.echo(click.style("=" * 60, fg="green"))
|
|
253
|
+
click.echo()
|
|
254
|
+
click.echo(f" Target: {target}")
|
|
255
|
+
click.echo(click.style(f" Credential: {username}:{password}", fg="green"))
|
|
256
|
+
click.echo(f" HTTP Code: {http_code or 'unknown'}")
|
|
257
|
+
click.echo()
|
|
258
|
+
click.echo(click.style("=" * 60, fg="green"))
|
|
259
|
+
else:
|
|
260
|
+
click.echo(click.style("=" * 60, fg="yellow"))
|
|
261
|
+
click.echo(click.style("WEB LOGIN TEST", bold=True, fg="yellow"))
|
|
262
|
+
click.echo(click.style("=" * 60, fg="yellow"))
|
|
263
|
+
click.echo()
|
|
264
|
+
click.echo(f" Target: {target}")
|
|
265
|
+
click.echo(f" Credential: {username}:{password}")
|
|
266
|
+
click.echo(f" HTTP Code: {http_code or 'unknown'}")
|
|
267
|
+
click.echo()
|
|
268
|
+
if response:
|
|
269
|
+
# Show truncated response
|
|
270
|
+
if len(response) > 60:
|
|
271
|
+
response = response[:57] + "..."
|
|
272
|
+
click.echo(f" Response: {response}")
|
|
273
|
+
click.echo()
|
|
274
|
+
click.echo(click.style(" Result: Invalid credentials", fg="yellow"))
|
|
275
|
+
click.echo()
|
|
276
|
+
click.echo(click.style("=" * 60, fg="yellow"))
|
|
277
|
+
click.echo()
|