souleyez 3.0.0__py3-none-any.whl → 3.0.7__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 -1
- souleyez/ai/__init__.py +7 -7
- souleyez/ai/action_mapper.py +3 -2
- souleyez/ai/chain_advisor.py +2 -1
- souleyez/ai/claude_provider.py +2 -2
- souleyez/ai/context_builder.py +4 -2
- souleyez/ai/executor.py +9 -6
- souleyez/ai/feedback_handler.py +4 -2
- souleyez/ai/llm_provider.py +2 -2
- souleyez/ai/ollama_provider.py +2 -2
- souleyez/ai/ollama_service.py +10 -26
- souleyez/ai/path_scorer.py +2 -1
- souleyez/ai/recommender.py +6 -4
- souleyez/ai/report_context.py +2 -2
- souleyez/ai/report_service.py +5 -5
- souleyez/ai/result_parser.py +3 -2
- souleyez/ai/safety.py +5 -2
- souleyez/auth/__init__.py +6 -6
- souleyez/auth/audit.py +2 -2
- souleyez/auth/engagement_access.py +5 -7
- souleyez/auth/permissions.py +1 -1
- souleyez/auth/session_manager.py +5 -5
- souleyez/auth/user_manager.py +4 -5
- souleyez/commands/audit.py +6 -5
- souleyez/commands/auth.py +6 -5
- souleyez/commands/deliverables.py +2 -3
- souleyez/commands/engagement.py +3 -3
- souleyez/commands/license.py +3 -2
- souleyez/commands/screenshots.py +5 -4
- souleyez/commands/user.py +10 -8
- souleyez/config.py +4 -2
- souleyez/core/credential_tester.py +4 -2
- souleyez/core/cve_mappings.py +2 -1
- souleyez/core/cve_matcher.py +2 -1
- souleyez/core/msf_auto_mapper.py +2 -0
- souleyez/core/msf_chain_engine.py +3 -1
- souleyez/core/msf_database.py +7 -13
- souleyez/core/msf_integration.py +2 -2
- souleyez/core/msf_rpc_client.py +3 -2
- souleyez/core/msf_rpc_manager.py +4 -4
- souleyez/core/msf_sync_manager.py +7 -7
- souleyez/core/network_utils.py +1 -1
- souleyez/core/parser_handler.py +2 -1
- souleyez/core/pending_chains.py +4 -3
- souleyez/core/templates.py +5 -2
- souleyez/core/tool_chaining.py +101 -70
- souleyez/core/version_utils.py +1 -0
- souleyez/core/vuln_correlation.py +3 -2
- souleyez/core/web_utils.py +2 -1
- souleyez/detection/__init__.py +1 -1
- souleyez/detection/attack_signatures.py +1 -1
- souleyez/detection/mitre_mappings.py +1 -2
- souleyez/detection/validator.py +5 -4
- souleyez/devtools.py +4 -2
- souleyez/docs/README.md +2 -2
- souleyez/engine/background.py +168 -7
- souleyez/engine/base.py +2 -1
- souleyez/engine/loader.py +4 -2
- souleyez/engine/log_sanitizer.py +1 -0
- souleyez/engine/manager.py +3 -1
- souleyez/engine/result_handler.py +50 -67
- souleyez/engine/worker_manager.py +6 -4
- souleyez/export/evidence_bundle.py +1 -0
- souleyez/handlers/base.py +1 -0
- souleyez/handlers/bash_handler.py +1 -0
- souleyez/handlers/bloodhound_handler.py +1 -0
- souleyez/handlers/certipy_handler.py +1 -0
- souleyez/handlers/crackmapexec_handler.py +2 -20
- souleyez/handlers/dnsrecon_handler.py +2 -1
- souleyez/handlers/enum4linux_handler.py +65 -37
- souleyez/handlers/evil_winrm_handler.py +1 -0
- souleyez/handlers/ffuf_handler.py +3 -1
- souleyez/handlers/gobuster_handler.py +7 -6
- souleyez/handlers/gpp_extract_handler.py +1 -0
- souleyez/handlers/hashcat_handler.py +1 -0
- souleyez/handlers/hydra_handler.py +5 -2
- souleyez/handlers/impacket_getuserspns_handler.py +1 -0
- souleyez/handlers/impacket_psexec_handler.py +1 -0
- souleyez/handlers/impacket_secretsdump_handler.py +1 -0
- souleyez/handlers/john_handler.py +1 -0
- souleyez/handlers/katana_handler.py +39 -2
- souleyez/handlers/kerbrute_handler.py +1 -0
- souleyez/handlers/ldapsearch_handler.py +90 -17
- souleyez/handlers/lfi_extract_handler.py +1 -0
- souleyez/handlers/msf_auxiliary_handler.py +1 -0
- souleyez/handlers/msf_exploit_handler.py +1 -0
- souleyez/handlers/nikto_handler.py +2 -1
- souleyez/handlers/nmap_handler.py +2 -1
- souleyez/handlers/nuclei_handler.py +2 -1
- souleyez/handlers/nxc_handler.py +3 -18
- souleyez/handlers/rdp_sec_check_handler.py +1 -0
- souleyez/handlers/registry.py +1 -0
- souleyez/handlers/responder_handler.py +1 -0
- souleyez/handlers/service_explorer_handler.py +2 -1
- souleyez/handlers/smbclient_handler.py +1 -0
- souleyez/handlers/smbmap_handler.py +3 -2
- souleyez/handlers/sqlmap_handler.py +6 -4
- souleyez/handlers/theharvester_handler.py +2 -1
- souleyez/handlers/web_login_test_handler.py +1 -0
- souleyez/handlers/whois_handler.py +3 -2
- souleyez/handlers/wpscan_handler.py +2 -1
- souleyez/history.py +4 -3
- souleyez/importers/msf_importer.py +5 -3
- souleyez/importers/smart_importer.py +6 -4
- souleyez/integrations/siem/__init__.py +6 -6
- souleyez/integrations/siem/base.py +1 -1
- souleyez/integrations/siem/elastic.py +3 -3
- souleyez/integrations/siem/factory.py +1 -2
- souleyez/integrations/siem/googlesecops.py +4 -4
- souleyez/integrations/siem/rule_mappings/wazuh_rules.py +1 -1
- souleyez/integrations/siem/sentinel.py +3 -3
- souleyez/integrations/siem/splunk.py +3 -3
- souleyez/integrations/siem/wazuh.py +4 -4
- souleyez/integrations/wazuh/__init__.py +1 -1
- souleyez/integrations/wazuh/client.py +3 -2
- souleyez/integrations/wazuh/config.py +3 -2
- souleyez/integrations/wazuh/host_mapper.py +3 -1
- souleyez/integrations/wazuh/sync.py +4 -1
- souleyez/intelligence/__init__.py +1 -1
- souleyez/intelligence/correlation_analyzer.py +6 -5
- souleyez/intelligence/exploit_knowledge.py +4 -4
- souleyez/intelligence/exploit_suggestions.py +4 -3
- souleyez/intelligence/gap_analyzer.py +5 -3
- souleyez/intelligence/gap_detector.py +2 -0
- souleyez/intelligence/sensitive_tables.py +1 -1
- souleyez/intelligence/service_parser.py +1 -0
- souleyez/intelligence/surface_analyzer.py +9 -9
- souleyez/intelligence/target_parser.py +1 -0
- souleyez/licensing/__init__.py +3 -3
- souleyez/main.py +25 -18
- souleyez/migrations/fix_job_counter.py +2 -1
- souleyez/parsers/bloodhound_parser.py +1 -0
- souleyez/parsers/crackmapexec_parser.py +2 -1
- souleyez/parsers/dalfox_parser.py +3 -2
- souleyez/parsers/dnsrecon_parser.py +2 -1
- souleyez/parsers/enum4linux_parser.py +2 -1
- souleyez/parsers/ffuf_parser.py +2 -1
- souleyez/parsers/gobuster_parser.py +2 -1
- souleyez/parsers/hashcat_parser.py +3 -2
- souleyez/parsers/http_fingerprint_parser.py +2 -1
- souleyez/parsers/hydra_parser.py +2 -1
- souleyez/parsers/impacket_parser.py +2 -1
- souleyez/parsers/john_parser.py +4 -3
- souleyez/parsers/katana_parser.py +134 -2
- souleyez/parsers/msf_parser.py +2 -1
- souleyez/parsers/nikto_parser.py +2 -1
- souleyez/parsers/nmap_parser.py +14 -3
- souleyez/parsers/nuclei_parser.py +3 -2
- souleyez/parsers/responder_parser.py +1 -0
- souleyez/parsers/searchsploit_parser.py +3 -2
- souleyez/parsers/service_explorer_parser.py +1 -0
- souleyez/parsers/smbmap_parser.py +2 -1
- souleyez/parsers/sqlmap_parser.py +36 -2
- souleyez/parsers/theharvester_parser.py +2 -1
- souleyez/parsers/whois_parser.py +2 -1
- souleyez/parsers/wpscan_parser.py +3 -2
- souleyez/plugins/afp.py +3 -1
- souleyez/plugins/afp_brute.py +3 -1
- souleyez/plugins/ard.py +3 -1
- souleyez/plugins/bloodhound.py +3 -2
- souleyez/plugins/certipy.py +1 -0
- souleyez/plugins/crackmapexec.py +11 -7
- souleyez/plugins/dalfox.py +5 -2
- souleyez/plugins/dns_hijack.py +3 -1
- souleyez/plugins/dnsrecon.py +3 -1
- souleyez/plugins/enum4linux.py +3 -1
- souleyez/plugins/evil_winrm.py +1 -0
- souleyez/plugins/ffuf.py +3 -1
- souleyez/plugins/firmware_extract.py +3 -2
- souleyez/plugins/gobuster.py +6 -3
- souleyez/plugins/gpp_extract.py +1 -0
- souleyez/plugins/hashcat.py +2 -1
- souleyez/plugins/http_fingerprint.py +57 -7
- souleyez/plugins/hydra.py +5 -3
- souleyez/plugins/impacket_common.py +40 -0
- souleyez/plugins/impacket_getnpusers.py +19 -2
- souleyez/plugins/impacket_getuserspns.py +158 -0
- souleyez/plugins/impacket_psexec.py +19 -2
- souleyez/plugins/impacket_secretsdump.py +19 -2
- souleyez/plugins/impacket_smbclient.py +19 -2
- souleyez/plugins/john.py +2 -1
- souleyez/plugins/katana.py +48 -6
- souleyez/plugins/kerbrute.py +1 -0
- souleyez/plugins/lfi_extract.py +1 -0
- souleyez/plugins/macos_ssh.py +3 -1
- souleyez/plugins/mdns.py +3 -1
- souleyez/plugins/msf_auxiliary.py +3 -2
- souleyez/plugins/msf_exploit.py +6 -5
- souleyez/plugins/nikto.py +5 -2
- souleyez/plugins/nmap.py +6 -4
- souleyez/plugins/nuclei.py +3 -1
- souleyez/plugins/nxc.py +1 -0
- souleyez/plugins/plugin_base.py +3 -2
- souleyez/plugins/plugin_template.py +3 -2
- souleyez/plugins/rdp_sec_check.py +1 -0
- souleyez/plugins/responder.py +2 -1
- souleyez/plugins/router_http_brute.py +3 -1
- souleyez/plugins/router_ssh_brute.py +3 -1
- souleyez/plugins/router_telnet_brute.py +3 -1
- souleyez/plugins/routersploit.py +5 -3
- souleyez/plugins/routersploit_exploit.py +5 -3
- souleyez/plugins/searchsploit.py +1 -0
- souleyez/plugins/service_explorer.py +2 -1
- souleyez/plugins/smbmap.py +3 -1
- souleyez/plugins/smbpasswd.py +1 -0
- souleyez/plugins/sqlmap.py +3 -1
- souleyez/plugins/theharvester.py +3 -1
- souleyez/plugins/tr069.py +3 -1
- souleyez/plugins/upnp.py +3 -1
- souleyez/plugins/upnp_abuse.py +4 -2
- souleyez/plugins/vnc_access.py +4 -2
- souleyez/plugins/vnc_brute.py +3 -1
- souleyez/plugins/web_login_test.py +1 -0
- souleyez/plugins/whois.py +3 -1
- souleyez/plugins/wpscan.py +3 -1
- souleyez/reporting/attack_chain.py +2 -1
- souleyez/reporting/charts.py +1 -0
- souleyez/reporting/compliance_mappings.py +1 -0
- souleyez/reporting/detection_report.py +10 -10
- souleyez/reporting/formatters.py +7 -12
- souleyez/reporting/generator.py +34 -46
- souleyez/reporting/metrics.py +2 -1
- souleyez/scanner.py +6 -3
- souleyez/security/__init__.py +7 -5
- souleyez/security/scope_validator.py +5 -4
- souleyez/security.py +5 -2
- souleyez/storage/credentials.py +14 -19
- souleyez/storage/crypto.py +7 -4
- souleyez/storage/database.py +6 -6
- souleyez/storage/db.py +8 -8
- souleyez/storage/deliverable_evidence.py +2 -1
- souleyez/storage/deliverable_exporter.py +3 -2
- souleyez/storage/deliverable_templates.py +2 -1
- souleyez/storage/deliverables.py +2 -1
- souleyez/storage/engagements.py +6 -4
- souleyez/storage/evidence.py +5 -4
- souleyez/storage/execution_log.py +4 -2
- souleyez/storage/exploit_attempts.py +3 -2
- souleyez/storage/exploits.py +3 -1
- souleyez/storage/findings.py +3 -1
- souleyez/storage/hosts.py +5 -2
- souleyez/storage/migrate_to_engagements.py +14 -24
- souleyez/storage/migrations/_001_add_credential_enhancements.py +12 -21
- souleyez/storage/migrations/_003_add_execution_log.py +8 -13
- souleyez/storage/migrations/_005_screenshots.py +2 -4
- souleyez/storage/migrations/_006_deliverables.py +2 -4
- souleyez/storage/migrations/_007_deliverable_templates.py +4 -8
- souleyez/storage/migrations/_008_add_nuclei_table.py +2 -4
- souleyez/storage/migrations/_010_evidence_linking.py +6 -12
- souleyez/storage/migrations/_012_team_collaboration.py +12 -24
- souleyez/storage/migrations/_013_add_host_tags.py +2 -4
- souleyez/storage/migrations/_014_exploit_attempts.py +10 -20
- souleyez/storage/migrations/_015_add_mac_os_fields.py +4 -8
- souleyez/storage/migrations/_016_add_domain_field.py +2 -4
- souleyez/storage/migrations/_017_msf_sessions.py +8 -16
- souleyez/storage/migrations/_018_add_osint_target.py +4 -8
- souleyez/storage/migrations/_019_add_engagement_type.py +4 -8
- souleyez/storage/migrations/_020_add_rbac.py +9 -17
- souleyez/storage/migrations/_021_wazuh_integration.py +4 -8
- souleyez/storage/migrations/_023_fix_detection_results_fk.py +2 -4
- souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +4 -8
- souleyez/storage/migrations/_026_add_engagement_scope.py +4 -8
- souleyez/storage/migrations/_027_multi_siem_persistence.py +8 -16
- souleyez/storage/migrations/__init__.py +1 -4
- souleyez/storage/migrations/migration_manager.py +6 -9
- souleyez/storage/msf_sessions.py +1 -1
- souleyez/storage/osint.py +3 -1
- souleyez/storage/recommendation_engine.py +3 -2
- souleyez/storage/screenshots.py +2 -1
- souleyez/storage/smb_shares.py +3 -1
- souleyez/storage/sqlmap_data.py +6 -4
- souleyez/storage/team_collaboration.py +3 -2
- souleyez/storage/timeline_tracker.py +2 -1
- souleyez/storage/wazuh_vulns.py +3 -1
- souleyez/storage/web_paths.py +3 -1
- souleyez/testing/credential_tester.py +2 -0
- souleyez/ui/__init__.py +2 -1
- souleyez/ui/ai_quotes.py +1 -1
- souleyez/ui/attack_surface.py +50 -28
- souleyez/ui/chain_rules_view.py +6 -3
- souleyez/ui/correlation_view.py +3 -2
- souleyez/ui/dashboard.py +85 -139
- souleyez/ui/deliverables_view.py +1 -1
- souleyez/ui/design_system.py +5 -3
- souleyez/ui/errors.py +3 -1
- souleyez/ui/evidence_linking_view.py +2 -1
- souleyez/ui/evidence_vault.py +11 -6
- souleyez/ui/exploit_suggestions_view.py +11 -7
- souleyez/ui/export_view.py +3 -1
- souleyez/ui/gap_analysis_view.py +6 -3
- souleyez/ui/help_system.py +4 -1
- souleyez/ui/intelligence_view.py +7 -3
- souleyez/ui/interactive.py +1280 -558
- souleyez/ui/interactive_selector.py +3 -2
- souleyez/ui/log_formatter.py +1 -0
- souleyez/ui/menu_components.py +3 -1
- souleyez/ui/msf_auxiliary_menu.py +4 -1
- souleyez/ui/pending_chains_view.py +15 -12
- souleyez/ui/progress_indicators.py +5 -2
- souleyez/ui/recommendations_view.py +4 -2
- souleyez/ui/rule_builder.py +4 -1
- souleyez/ui/setup_wizard.py +10 -8
- souleyez/ui/shortcuts.py +1 -1
- souleyez/ui/splunk_gap_analysis_view.py +7 -4
- souleyez/ui/splunk_vulns_view.py +4 -1
- souleyez/ui/team_dashboard.py +7 -5
- souleyez/ui/template_selector.py +2 -1
- souleyez/ui/terminal.py +3 -2
- souleyez/ui/timeline_view.py +2 -1
- souleyez/ui/tool_setup.py +92 -31
- souleyez/ui/tutorial.py +7 -4
- souleyez/ui/tutorial_state.py +3 -2
- souleyez/ui/wazuh_vulns_view.py +5 -2
- souleyez/ui/wordlist_browser.py +4 -3
- souleyez/ui.py +13 -7
- souleyez/utils/tool_checker.py +61 -12
- souleyez/utils.py +4 -4
- souleyez/wordlists.py +1 -0
- {souleyez-3.0.0.dist-info → souleyez-3.0.7.dist-info}/METADATA +1 -1
- souleyez-3.0.7.dist-info/RECORD +445 -0
- souleyez-3.0.0.dist-info/RECORD +0 -443
- {souleyez-3.0.0.dist-info → souleyez-3.0.7.dist-info}/WHEEL +0 -0
- {souleyez-3.0.0.dist-info → souleyez-3.0.7.dist-info}/entry_points.txt +0 -0
- {souleyez-3.0.0.dist-info → souleyez-3.0.7.dist-info}/licenses/LICENSE +0 -0
- {souleyez-3.0.0.dist-info → souleyez-3.0.7.dist-info}/top_level.txt +0 -0
|
@@ -105,24 +105,8 @@ class CrackMapExecHandler(BaseToolHandler):
|
|
|
105
105
|
vulns_found = len(parsed.get("findings", {}).get("vulnerabilities", []))
|
|
106
106
|
pw_must_change = parsed.get("password_must_change", [])
|
|
107
107
|
|
|
108
|
-
#
|
|
109
|
-
|
|
110
|
-
"NETBIOS connection with the remote host timed out",
|
|
111
|
-
"Connection reset by peer",
|
|
112
|
-
"Connection timed out",
|
|
113
|
-
"Error enumerating shares",
|
|
114
|
-
]
|
|
115
|
-
log_content = ""
|
|
116
|
-
try:
|
|
117
|
-
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
118
|
-
log_content = f.read()
|
|
119
|
-
except Exception:
|
|
120
|
-
pass
|
|
121
|
-
|
|
122
|
-
has_transient_error = any(
|
|
123
|
-
err.lower() in log_content.lower() for err in transient_errors
|
|
124
|
-
)
|
|
125
|
-
|
|
108
|
+
# Determine status based on results found
|
|
109
|
+
# Retry logic is handled by background.py before parsing
|
|
126
110
|
if (
|
|
127
111
|
hosts_found > 0
|
|
128
112
|
or shares_found > 0
|
|
@@ -132,8 +116,6 @@ class CrackMapExecHandler(BaseToolHandler):
|
|
|
132
116
|
or len(pw_must_change) > 0
|
|
133
117
|
):
|
|
134
118
|
status = STATUS_DONE
|
|
135
|
-
elif has_transient_error:
|
|
136
|
-
status = STATUS_WARNING # Transient error - may be retried
|
|
137
119
|
else:
|
|
138
120
|
status = STATUS_NO_RESULTS
|
|
139
121
|
|
|
@@ -4,6 +4,7 @@ DNSRecon handler.
|
|
|
4
4
|
|
|
5
5
|
Consolidates parsing and display logic for DNSRecon DNS enumeration jobs.
|
|
6
6
|
"""
|
|
7
|
+
|
|
7
8
|
import logging
|
|
8
9
|
import os
|
|
9
10
|
import re
|
|
@@ -44,9 +45,9 @@ class DNSReconHandler(BaseToolHandler):
|
|
|
44
45
|
Extracts DNS records and stores them as OSINT data.
|
|
45
46
|
"""
|
|
46
47
|
try:
|
|
48
|
+
from souleyez.engine.result_handler import detect_tool_error
|
|
47
49
|
from souleyez.parsers.dnsrecon_parser import parse_dnsrecon_output
|
|
48
50
|
from souleyez.storage.osint import OsintManager
|
|
49
|
-
from souleyez.engine.result_handler import detect_tool_error
|
|
50
51
|
|
|
51
52
|
# Import managers if not provided
|
|
52
53
|
if host_manager is None:
|
|
@@ -4,6 +4,7 @@ enum4linux handler.
|
|
|
4
4
|
|
|
5
5
|
Consolidates parsing and display logic for enum4linux SMB/Samba enumeration jobs.
|
|
6
6
|
"""
|
|
7
|
+
|
|
7
8
|
import logging
|
|
8
9
|
import os
|
|
9
10
|
import re
|
|
@@ -11,7 +12,7 @@ from typing import Any, Dict, Optional
|
|
|
11
12
|
|
|
12
13
|
import click
|
|
13
14
|
|
|
14
|
-
from souleyez.engine.job_status import STATUS_DONE,
|
|
15
|
+
from souleyez.engine.job_status import STATUS_DONE, STATUS_NO_RESULTS, STATUS_WARNING
|
|
15
16
|
from souleyez.handlers.base import BaseToolHandler
|
|
16
17
|
|
|
17
18
|
logger = logging.getLogger(__name__)
|
|
@@ -44,12 +45,12 @@ class Enum4LinuxHandler(BaseToolHandler):
|
|
|
44
45
|
Extracts SMB users, groups, shares and stores them.
|
|
45
46
|
"""
|
|
46
47
|
try:
|
|
48
|
+
from souleyez.engine.result_handler import detect_tool_error
|
|
47
49
|
from souleyez.parsers.enum4linux_parser import (
|
|
48
|
-
parse_enum4linux_output,
|
|
49
|
-
get_smb_stats,
|
|
50
50
|
categorize_share,
|
|
51
|
+
get_smb_stats,
|
|
52
|
+
parse_enum4linux_output,
|
|
51
53
|
)
|
|
52
|
-
from souleyez.engine.result_handler import detect_tool_error
|
|
53
54
|
|
|
54
55
|
# Import managers if not provided
|
|
55
56
|
if host_manager is None:
|
|
@@ -103,46 +104,71 @@ class Enum4LinuxHandler(BaseToolHandler):
|
|
|
103
104
|
)
|
|
104
105
|
credentials_added += 1
|
|
105
106
|
|
|
106
|
-
# Store shares
|
|
107
|
+
# Store shares in smb_shares table (proper storage)
|
|
108
|
+
from souleyez.storage.smb_shares import SMBSharesManager
|
|
109
|
+
|
|
110
|
+
smb_mgr = SMBSharesManager()
|
|
111
|
+
shares_added = 0
|
|
107
112
|
findings_added = 0
|
|
113
|
+
|
|
108
114
|
for share in parsed.get("shares", []):
|
|
109
115
|
category = categorize_share(share)
|
|
110
|
-
if category == "open":
|
|
111
|
-
severity = "high"
|
|
112
|
-
elif category == "readable":
|
|
113
|
-
severity = "medium"
|
|
114
|
-
elif category == "restricted":
|
|
115
|
-
severity = "low"
|
|
116
|
-
else:
|
|
117
|
-
severity = "info"
|
|
118
|
-
|
|
119
116
|
share_name = share["name"]
|
|
120
117
|
share_type = share.get("type", "Unknown")
|
|
121
118
|
mapping = share.get("mapping", "N/A")
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
119
|
+
listing = share.get("listing", "N/A")
|
|
120
|
+
writing = share.get("writing", "N/A")
|
|
121
|
+
|
|
122
|
+
# Determine readable/writable status
|
|
123
|
+
readable = mapping == "OK" and listing in ("OK", "N/A")
|
|
124
|
+
writable = mapping == "OK" and writing == "OK"
|
|
125
|
+
|
|
126
|
+
# Store in smb_shares table
|
|
127
|
+
if host_id:
|
|
128
|
+
try:
|
|
129
|
+
smb_mgr.add_share(
|
|
130
|
+
host_id=host_id,
|
|
131
|
+
share_data={
|
|
132
|
+
"name": share_name,
|
|
133
|
+
"type": share_type,
|
|
134
|
+
"permissions": f"{mapping}/{listing}/{writing}",
|
|
135
|
+
"comment": share.get("comment", ""),
|
|
136
|
+
"readable": readable,
|
|
137
|
+
"writable": writable,
|
|
138
|
+
},
|
|
139
|
+
)
|
|
140
|
+
shares_added += 1
|
|
141
|
+
except Exception as e:
|
|
142
|
+
logger.debug(f"Could not store share in smb_shares: {e}")
|
|
143
|
+
|
|
144
|
+
# Also add a finding for high-risk shares (writable or open)
|
|
145
|
+
if category == "open" or writable:
|
|
146
|
+
if category == "open":
|
|
147
|
+
severity = "high"
|
|
148
|
+
elif writable:
|
|
149
|
+
severity = "medium"
|
|
150
|
+
else:
|
|
151
|
+
severity = "low"
|
|
152
|
+
|
|
153
|
+
title = f"Writable SMB Share: {share_name}"
|
|
154
|
+
description = (
|
|
155
|
+
f"Share: {share_name}\n"
|
|
156
|
+
f"Type: {share_type}\n"
|
|
157
|
+
f"Comment: {share.get('comment', 'N/A')}\n"
|
|
158
|
+
f"Readable: {readable}, Writable: {writable}"
|
|
128
159
|
)
|
|
129
|
-
else:
|
|
130
|
-
access_desc = f"Access denied (Mapping={mapping})"
|
|
131
160
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
port=445,
|
|
144
|
-
)
|
|
145
|
-
findings_added += 1
|
|
161
|
+
findings_manager.add_finding(
|
|
162
|
+
engagement_id=engagement_id,
|
|
163
|
+
host_id=host_id,
|
|
164
|
+
title=title,
|
|
165
|
+
finding_type="smb_share",
|
|
166
|
+
severity=severity,
|
|
167
|
+
description=description,
|
|
168
|
+
tool="enum4linux",
|
|
169
|
+
port=445,
|
|
170
|
+
)
|
|
171
|
+
findings_added += 1
|
|
146
172
|
|
|
147
173
|
stats = get_smb_stats(parsed)
|
|
148
174
|
|
|
@@ -158,7 +184,8 @@ class Enum4LinuxHandler(BaseToolHandler):
|
|
|
158
184
|
|
|
159
185
|
# Determine status
|
|
160
186
|
has_results = (
|
|
161
|
-
|
|
187
|
+
shares_added > 0
|
|
188
|
+
or findings_added > 0
|
|
162
189
|
or credentials_added > 0
|
|
163
190
|
or len(parsed.get("users", [])) > 0
|
|
164
191
|
or stats["total_shares"] > 0
|
|
@@ -177,6 +204,7 @@ class Enum4LinuxHandler(BaseToolHandler):
|
|
|
177
204
|
return {
|
|
178
205
|
"tool": "enum4linux",
|
|
179
206
|
"status": status,
|
|
207
|
+
"shares_added": shares_added,
|
|
180
208
|
"findings_added": findings_added,
|
|
181
209
|
"credentials_added": credentials_added,
|
|
182
210
|
"users_found": len(parsed.get("users", [])),
|
|
@@ -4,6 +4,7 @@ Ffuf handler.
|
|
|
4
4
|
|
|
5
5
|
Consolidates parsing and display logic for ffuf fuzzing jobs.
|
|
6
6
|
"""
|
|
7
|
+
|
|
7
8
|
import logging
|
|
8
9
|
import os
|
|
9
10
|
import re
|
|
@@ -186,8 +187,8 @@ class FfufHandler(BaseToolHandler):
|
|
|
186
187
|
Extracts discovered paths and stores them in the database.
|
|
187
188
|
"""
|
|
188
189
|
try:
|
|
189
|
-
from souleyez.parsers.ffuf_parser import parse_ffuf
|
|
190
190
|
from souleyez.engine.result_handler import detect_tool_error
|
|
191
|
+
from souleyez.parsers.ffuf_parser import parse_ffuf
|
|
191
192
|
|
|
192
193
|
# Import managers if not provided
|
|
193
194
|
if host_manager is None:
|
|
@@ -344,6 +345,7 @@ class FfufHandler(BaseToolHandler):
|
|
|
344
345
|
decode the base64, and parse for credentials.
|
|
345
346
|
"""
|
|
346
347
|
import base64
|
|
348
|
+
|
|
347
349
|
import requests
|
|
348
350
|
|
|
349
351
|
extracted_creds = []
|
|
@@ -4,18 +4,19 @@ Gobuster handler.
|
|
|
4
4
|
|
|
5
5
|
Consolidates parsing and display logic for Gobuster directory enumeration jobs.
|
|
6
6
|
"""
|
|
7
|
+
|
|
7
8
|
import logging
|
|
8
9
|
import os
|
|
9
10
|
import re
|
|
10
|
-
import ssl
|
|
11
11
|
import socket
|
|
12
|
-
import
|
|
12
|
+
import ssl
|
|
13
13
|
import urllib.error
|
|
14
|
+
import urllib.request
|
|
14
15
|
from typing import Any, Dict, List, Optional
|
|
15
|
-
from urllib.parse import
|
|
16
|
-
import defusedxml.ElementTree as ElementTree # Safe XML parsing
|
|
16
|
+
from urllib.parse import urljoin, urlparse
|
|
17
17
|
|
|
18
18
|
import click
|
|
19
|
+
import defusedxml.ElementTree as ElementTree # Safe XML parsing
|
|
19
20
|
|
|
20
21
|
from souleyez.engine.job_status import (
|
|
21
22
|
STATUS_DONE,
|
|
@@ -201,11 +202,11 @@ class GobusterHandler(BaseToolHandler):
|
|
|
201
202
|
Extracts discovered paths and stores them in the database.
|
|
202
203
|
"""
|
|
203
204
|
try:
|
|
205
|
+
from souleyez.engine.result_handler import detect_tool_error
|
|
204
206
|
from souleyez.parsers.gobuster_parser import (
|
|
205
|
-
parse_gobuster_output,
|
|
206
207
|
get_paths_stats,
|
|
208
|
+
parse_gobuster_output,
|
|
207
209
|
)
|
|
208
|
-
from souleyez.engine.result_handler import detect_tool_error
|
|
209
210
|
|
|
210
211
|
# Import managers if not provided
|
|
211
212
|
if host_manager is None:
|
|
@@ -4,6 +4,7 @@ Hydra handler.
|
|
|
4
4
|
|
|
5
5
|
Consolidates parsing and display logic for Hydra brute-force jobs.
|
|
6
6
|
"""
|
|
7
|
+
|
|
7
8
|
import logging
|
|
8
9
|
import os
|
|
9
10
|
import re
|
|
@@ -58,8 +59,8 @@ class HydraHandler(BaseToolHandler):
|
|
|
58
59
|
Extracts credentials and usernames from the output.
|
|
59
60
|
"""
|
|
60
61
|
try:
|
|
61
|
-
from souleyez.parsers.hydra_parser import parse_hydra_output
|
|
62
62
|
from souleyez.engine.result_handler import detect_tool_error
|
|
63
|
+
from souleyez.parsers.hydra_parser import parse_hydra_output
|
|
63
64
|
|
|
64
65
|
# Import managers if not provided
|
|
65
66
|
if host_manager is None:
|
|
@@ -261,7 +262,9 @@ class HydraHandler(BaseToolHandler):
|
|
|
261
262
|
"port": parsed.get("port"),
|
|
262
263
|
"credentials_found": len(parsed.get("credentials", [])),
|
|
263
264
|
"credentials_added": creds_added,
|
|
264
|
-
"credentials": parsed.get(
|
|
265
|
+
"credentials": parsed.get(
|
|
266
|
+
"credentials", []
|
|
267
|
+
), # Include actual creds for smart chains
|
|
265
268
|
"usernames_found": len(parsed.get("usernames", [])),
|
|
266
269
|
"usernames": parsed.get("usernames", []),
|
|
267
270
|
"usernames_added": usernames_added,
|
|
@@ -4,6 +4,7 @@ Katana handler.
|
|
|
4
4
|
|
|
5
5
|
Consolidates parsing and display logic for katana web crawling jobs.
|
|
6
6
|
"""
|
|
7
|
+
|
|
7
8
|
import logging
|
|
8
9
|
import os
|
|
9
10
|
from typing import Any, Dict, List, Optional
|
|
@@ -49,11 +50,11 @@ class KatanaHandler(BaseToolHandler):
|
|
|
49
50
|
Extracts discovered URLs, parameters, forms, and JS endpoints.
|
|
50
51
|
"""
|
|
51
52
|
try:
|
|
53
|
+
from souleyez.engine.result_handler import detect_tool_error
|
|
52
54
|
from souleyez.parsers.katana_parser import (
|
|
53
|
-
parse_katana_output,
|
|
54
55
|
extract_injectable_urls,
|
|
56
|
+
parse_katana_output,
|
|
55
57
|
)
|
|
56
|
-
from souleyez.engine.result_handler import detect_tool_error
|
|
57
58
|
|
|
58
59
|
# Import managers if not provided
|
|
59
60
|
if host_manager is None:
|
|
@@ -134,6 +135,42 @@ class KatanaHandler(BaseToolHandler):
|
|
|
134
135
|
|
|
135
136
|
lfi_urls = extract_lfi_urls(parsed)
|
|
136
137
|
|
|
138
|
+
# ARM64/headless workaround: If we have JS files but few parameterized URLs,
|
|
139
|
+
# extract endpoints directly from JavaScript source code
|
|
140
|
+
js_files = [u for u in parsed.get("urls", []) if u.endswith(".js")]
|
|
141
|
+
urls_with_params_list = parsed.get("urls_with_params", [])
|
|
142
|
+
sqli_candidates = parsed.get("sqli_candidate_urls", [])
|
|
143
|
+
|
|
144
|
+
if js_files and len(urls_with_params_list) < 10:
|
|
145
|
+
try:
|
|
146
|
+
from souleyez.parsers.katana_parser import (
|
|
147
|
+
fetch_and_extract_js_endpoints,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
logger.info(
|
|
151
|
+
f"Extracting endpoints from {len(js_files)} JavaScript files..."
|
|
152
|
+
)
|
|
153
|
+
js_endpoints = fetch_and_extract_js_endpoints(target, js_files)
|
|
154
|
+
|
|
155
|
+
if js_endpoints:
|
|
156
|
+
logger.info(
|
|
157
|
+
f"Found {len(js_endpoints)} additional endpoints from JavaScript"
|
|
158
|
+
)
|
|
159
|
+
# Add to parsed results
|
|
160
|
+
for ep in js_endpoints:
|
|
161
|
+
if ep not in urls_with_params_list:
|
|
162
|
+
urls_with_params_list.append(ep)
|
|
163
|
+
if ep not in sqli_candidates:
|
|
164
|
+
sqli_candidates.append(ep)
|
|
165
|
+
if ep not in injectable_urls:
|
|
166
|
+
injectable_urls.append(ep)
|
|
167
|
+
|
|
168
|
+
# Update parsed dict for display
|
|
169
|
+
parsed["urls_with_params"] = urls_with_params_list
|
|
170
|
+
parsed["sqli_candidate_urls"] = sqli_candidates
|
|
171
|
+
except Exception as e:
|
|
172
|
+
logger.warning(f"JavaScript endpoint extraction failed: {e}")
|
|
173
|
+
|
|
137
174
|
# Determine status
|
|
138
175
|
urls_list = parsed.get("urls", [])
|
|
139
176
|
urls_with_params_list = parsed.get("urls_with_params", [])
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"""
|
|
3
3
|
Handler for ldapsearch LDAP enumeration tool.
|
|
4
4
|
"""
|
|
5
|
+
|
|
5
6
|
import logging
|
|
6
7
|
import os
|
|
7
8
|
import re
|
|
@@ -177,8 +178,8 @@ class LdapsearchHandler(BaseToolHandler):
|
|
|
177
178
|
f"{user.get('sAMAccountName')} - check description field"
|
|
178
179
|
)
|
|
179
180
|
|
|
180
|
-
# Store credentials if found
|
|
181
|
-
if credentials_found and credentials_manager:
|
|
181
|
+
# Store credentials if found AND store enumerated users
|
|
182
|
+
if (credentials_found or usernames) and credentials_manager:
|
|
182
183
|
if host_manager is None:
|
|
183
184
|
from souleyez.storage.hosts import HostManager
|
|
184
185
|
|
|
@@ -186,6 +187,7 @@ class LdapsearchHandler(BaseToolHandler):
|
|
|
186
187
|
|
|
187
188
|
host = host_manager.get_host_by_ip(engagement_id, target)
|
|
188
189
|
if host:
|
|
190
|
+
# Store passwords found in descriptions
|
|
189
191
|
for cred in credentials_found:
|
|
190
192
|
credentials_manager.add_credential(
|
|
191
193
|
engagement_id=engagement_id,
|
|
@@ -199,6 +201,31 @@ class LdapsearchHandler(BaseToolHandler):
|
|
|
199
201
|
notes="Found in LDAP user description field",
|
|
200
202
|
)
|
|
201
203
|
|
|
204
|
+
# Store enumerated usernames (without passwords)
|
|
205
|
+
usernames_stored = 0
|
|
206
|
+
for username in usernames:
|
|
207
|
+
# Skip if already stored with a password
|
|
208
|
+
already_has_cred = any(
|
|
209
|
+
c["username"] == username for c in credentials_found
|
|
210
|
+
)
|
|
211
|
+
if not already_has_cred:
|
|
212
|
+
credentials_manager.add_credential(
|
|
213
|
+
engagement_id=engagement_id,
|
|
214
|
+
host_id=host["id"],
|
|
215
|
+
username=username,
|
|
216
|
+
password="",
|
|
217
|
+
service="ldap",
|
|
218
|
+
credential_type="ldap_user",
|
|
219
|
+
tool="ldapsearch",
|
|
220
|
+
status="enumerated",
|
|
221
|
+
notes="Enumerated via LDAP",
|
|
222
|
+
)
|
|
223
|
+
usernames_stored += 1
|
|
224
|
+
if usernames_stored > 0:
|
|
225
|
+
logger.info(
|
|
226
|
+
f"Stored {usernames_stored} enumerated LDAP usernames"
|
|
227
|
+
)
|
|
228
|
+
|
|
202
229
|
logger.info(
|
|
203
230
|
f"ldapsearch parse complete: {len(naming_contexts)} naming contexts, "
|
|
204
231
|
f"{len(domains)} domains, {len(users)} users, {len(credentials_found)} creds, base_dn={base_dn}"
|
|
@@ -420,8 +447,11 @@ class LdapsearchHandler(BaseToolHandler):
|
|
|
420
447
|
|
|
421
448
|
# New entry starts with "dn:"
|
|
422
449
|
if line.startswith("dn:"):
|
|
450
|
+
# Save previous entry if it's not explicitly a group
|
|
451
|
+
# Default to including (is_group=False) if no objectClass info
|
|
423
452
|
if current_user and current_user.get("sAMAccountName"):
|
|
424
|
-
|
|
453
|
+
if not current_user.get("is_group", False):
|
|
454
|
+
users.append(current_user)
|
|
425
455
|
current_user = {}
|
|
426
456
|
# Extract OU from DN
|
|
427
457
|
ou_match = re.search(r"OU=([^,]+)", line, re.IGNORECASE)
|
|
@@ -448,12 +478,43 @@ class LdapsearchHandler(BaseToolHandler):
|
|
|
448
478
|
"key ",
|
|
449
479
|
"dns",
|
|
450
480
|
"ras ",
|
|
481
|
+
"cloneable ",
|
|
451
482
|
)
|
|
452
483
|
):
|
|
453
484
|
current_user["cn_username"] = cn_name.replace(" ", ".")
|
|
454
485
|
|
|
486
|
+
elif line.startswith("objectClass:"):
|
|
487
|
+
obj_class = line.split(":", 1)[1].strip().lower()
|
|
488
|
+
# Mark as group if objectClass is group (blacklist approach)
|
|
489
|
+
if obj_class == "group":
|
|
490
|
+
current_user["is_group"] = True
|
|
491
|
+
|
|
455
492
|
elif line.startswith("sAMAccountName:"):
|
|
456
|
-
|
|
493
|
+
sam = line.split(":", 1)[1].strip()
|
|
494
|
+
current_user["sAMAccountName"] = sam
|
|
495
|
+
# Filter out obvious group names by sAMAccountName pattern
|
|
496
|
+
sam_lower = sam.lower()
|
|
497
|
+
if sam_lower in (
|
|
498
|
+
"guest",
|
|
499
|
+
"domain users",
|
|
500
|
+
"domain computers",
|
|
501
|
+
"domain guests",
|
|
502
|
+
"domain admins",
|
|
503
|
+
"enterprise admins",
|
|
504
|
+
"schema admins",
|
|
505
|
+
"cert publishers",
|
|
506
|
+
"group policy creator owners",
|
|
507
|
+
"ras and ias servers",
|
|
508
|
+
"allowed rodc password replication group",
|
|
509
|
+
"denied rodc password replication group",
|
|
510
|
+
"read-only domain controllers",
|
|
511
|
+
"enterprise read-only domain controllers",
|
|
512
|
+
"cloneable domain controllers",
|
|
513
|
+
"protected users",
|
|
514
|
+
"dnsadmins",
|
|
515
|
+
"dnsupdateproxy",
|
|
516
|
+
):
|
|
517
|
+
current_user["is_group"] = True
|
|
457
518
|
|
|
458
519
|
elif line.startswith("description:"):
|
|
459
520
|
current_user["description"] = line.split(":", 1)[1].strip()
|
|
@@ -466,9 +527,10 @@ class LdapsearchHandler(BaseToolHandler):
|
|
|
466
527
|
if cn_match:
|
|
467
528
|
current_user["memberOf"].append(cn_match.group(1))
|
|
468
529
|
|
|
469
|
-
# Don't forget last user
|
|
530
|
+
# Don't forget last user - only if it's not a group
|
|
470
531
|
if current_user and current_user.get("sAMAccountName"):
|
|
471
|
-
|
|
532
|
+
if not current_user.get("is_group", False):
|
|
533
|
+
users.append(current_user)
|
|
472
534
|
|
|
473
535
|
# Second pass: add users found only via CN (no sAMAccountName returned)
|
|
474
536
|
# This catches users from broader queries like (objectClass=*)
|
|
@@ -476,9 +538,11 @@ class LdapsearchHandler(BaseToolHandler):
|
|
|
476
538
|
for line in log_content.split("\n"):
|
|
477
539
|
line = line.strip()
|
|
478
540
|
if line.startswith("dn:"):
|
|
479
|
-
if
|
|
480
|
-
"
|
|
481
|
-
|
|
541
|
+
if (
|
|
542
|
+
current_user.get("cn_username")
|
|
543
|
+
and not current_user.get("sAMAccountName")
|
|
544
|
+
and not current_user.get("is_group", False)
|
|
545
|
+
): # Default to user if no objectClass
|
|
482
546
|
# Check if we already have this user
|
|
483
547
|
existing = [
|
|
484
548
|
u
|
|
@@ -507,24 +571,33 @@ class LdapsearchHandler(BaseToolHandler):
|
|
|
507
571
|
"key ",
|
|
508
572
|
"dns",
|
|
509
573
|
"ras ",
|
|
574
|
+
"cloneable ",
|
|
510
575
|
)
|
|
511
576
|
):
|
|
512
577
|
current_user["cn_username"] = cn_name.replace(" ", ".")
|
|
513
578
|
if ou_match:
|
|
514
579
|
current_user["ou"] = ou_match.group(1)
|
|
580
|
+
elif line.startswith("objectClass:"):
|
|
581
|
+
obj_class = line.split(":", 1)[1].strip().lower()
|
|
582
|
+
# Blacklist groups - mark as group if objectClass is group
|
|
583
|
+
if obj_class == "group":
|
|
584
|
+
current_user["is_group"] = True
|
|
515
585
|
elif line.startswith("sAMAccountName:"):
|
|
516
586
|
current_user["sAMAccountName"] = line.split(":", 1)[1].strip()
|
|
517
587
|
|
|
518
588
|
# Last entry from second pass
|
|
519
589
|
if current_user.get("cn_username") and not current_user.get("sAMAccountName"):
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
590
|
+
if not current_user.get(
|
|
591
|
+
"is_group", False
|
|
592
|
+
): # Default to user if no objectClass
|
|
593
|
+
existing = [
|
|
594
|
+
u
|
|
595
|
+
for u in users
|
|
596
|
+
if u.get("sAMAccountName") == current_user.get("cn_username")
|
|
597
|
+
]
|
|
598
|
+
if not existing:
|
|
599
|
+
current_user["sAMAccountName"] = current_user["cn_username"]
|
|
600
|
+
users.append(current_user)
|
|
528
601
|
|
|
529
602
|
return users
|
|
530
603
|
|