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
|
@@ -9,128 +9,136 @@ from typing import Dict, List, Optional
|
|
|
9
9
|
|
|
10
10
|
class ServiceVersionExtractor:
|
|
11
11
|
"""Extract and normalize service version information from multiple sources."""
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
def __init__(self):
|
|
14
14
|
# Version extraction patterns
|
|
15
15
|
self.version_patterns = [
|
|
16
16
|
# Apache: "Apache/2.4.41 (Ubuntu)"
|
|
17
|
-
r
|
|
17
|
+
r"Apache/(\d+\.\d+(?:\.\d+)?)",
|
|
18
18
|
# nginx: "nginx/1.18.0"
|
|
19
|
-
r
|
|
19
|
+
r"nginx/(\d+\.\d+(?:\.\d+)?)",
|
|
20
20
|
# OpenSSH: "OpenSSH 8.2p1 Ubuntu"
|
|
21
|
-
r
|
|
21
|
+
r"OpenSSH[_\s](\d+\.\d+)(?:p\d+)?",
|
|
22
22
|
# vsftpd: "vsftpd 2.3.4"
|
|
23
|
-
r
|
|
23
|
+
r"vsftpd[_\s](\d+\.\d+(?:\.\d+)?)",
|
|
24
24
|
# MySQL: "MySQL 5.0.51a"
|
|
25
|
-
r
|
|
25
|
+
r"MySQL[_\s](\d+\.\d+(?:\.\d+)?)",
|
|
26
26
|
# ProFTPD: "ProFTPD 1.3.5"
|
|
27
|
-
r
|
|
27
|
+
r"ProFTPD[_\s](\d+\.\d+(?:\.\d+)?)",
|
|
28
28
|
# Samba: "Samba 3.0.20"
|
|
29
|
-
r
|
|
29
|
+
r"Samba[_\s](\d+\.\d+(?:\.\d+)?)",
|
|
30
30
|
# PostgreSQL: "PostgreSQL 9.6.1"
|
|
31
|
-
r
|
|
31
|
+
r"PostgreSQL[_\s](\d+\.\d+(?:\.\d+)?)",
|
|
32
32
|
# Generic version pattern (last resort)
|
|
33
|
-
r
|
|
33
|
+
r"v?(\d+\.\d+(?:\.\d+)?(?:\.\d+)?)",
|
|
34
34
|
]
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
def extract_from_services_table(self, host_id: int) -> List[Dict]:
|
|
37
37
|
"""
|
|
38
38
|
Get services from database with version info.
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
Returns:
|
|
41
41
|
List of service dicts with normalized version info
|
|
42
42
|
"""
|
|
43
|
-
from souleyez.storage.database import
|
|
44
|
-
|
|
45
|
-
db =
|
|
43
|
+
from souleyez.storage.database import get_db
|
|
44
|
+
|
|
45
|
+
db = get_db() # Use singleton instead of creating new connection
|
|
46
46
|
services = []
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
with db.get_connection() as conn:
|
|
49
49
|
cursor = conn.cursor()
|
|
50
|
-
cursor.execute(
|
|
50
|
+
cursor.execute(
|
|
51
|
+
"""
|
|
51
52
|
SELECT port, service_name, service_version, service_product
|
|
52
53
|
FROM services
|
|
53
54
|
WHERE host_id = ?
|
|
54
55
|
ORDER BY port
|
|
55
|
-
""",
|
|
56
|
-
|
|
56
|
+
""",
|
|
57
|
+
(host_id,),
|
|
58
|
+
)
|
|
59
|
+
|
|
57
60
|
for row in cursor.fetchall():
|
|
58
61
|
port, service_name, service_version, service_product = row
|
|
59
|
-
|
|
62
|
+
|
|
60
63
|
# Parse version string from service_version or service_name
|
|
61
|
-
version_string = service_version or service_name or
|
|
64
|
+
version_string = service_version or service_name or ""
|
|
62
65
|
# Clean the version string to remove Nmap metadata
|
|
63
66
|
cleaned_version = self.clean_version_string(version_string)
|
|
64
67
|
parsed = self.parse_version_string(cleaned_version)
|
|
65
|
-
|
|
66
|
-
services.append(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
|
|
69
|
+
services.append(
|
|
70
|
+
{
|
|
71
|
+
"port": port,
|
|
72
|
+
"service": service_name or "unknown",
|
|
73
|
+
"version": cleaned_version or parsed.get("version", "unknown"),
|
|
74
|
+
"product": service_product
|
|
75
|
+
or parsed.get("product", service_name or "unknown"),
|
|
76
|
+
"version_number": parsed.get("version", "unknown"),
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
|
|
74
80
|
return services
|
|
75
|
-
|
|
81
|
+
|
|
76
82
|
def extract_from_findings(self, findings: List[Dict]) -> List[Dict]:
|
|
77
83
|
"""
|
|
78
84
|
Parse version info from finding titles/descriptions.
|
|
79
|
-
|
|
85
|
+
|
|
80
86
|
Example findings:
|
|
81
87
|
- "Port 21: vsftpd 2.3.4 - Backdoor Command Execution"
|
|
82
88
|
- "MySQL 5.0.51a - Remote Code Execution"
|
|
83
|
-
|
|
89
|
+
|
|
84
90
|
Returns:
|
|
85
91
|
List of service dicts
|
|
86
92
|
"""
|
|
87
93
|
services = []
|
|
88
|
-
|
|
94
|
+
|
|
89
95
|
for finding in findings:
|
|
90
|
-
title = finding.get(
|
|
91
|
-
description = finding.get(
|
|
92
|
-
host = finding.get(
|
|
93
|
-
|
|
96
|
+
title = finding.get("title", "")
|
|
97
|
+
description = finding.get("description", "")
|
|
98
|
+
host = finding.get("host", "")
|
|
99
|
+
|
|
94
100
|
# Try to extract port
|
|
95
|
-
port_match = re.search(r
|
|
101
|
+
port_match = re.search(r"[Pp]ort\s+(\d+)", title)
|
|
96
102
|
port = int(port_match.group(1)) if port_match else None
|
|
97
|
-
|
|
103
|
+
|
|
98
104
|
# Parse version from title
|
|
99
105
|
parsed = self.parse_version_string(title)
|
|
100
|
-
if parsed.get(
|
|
101
|
-
services.append(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
if parsed.get("product") and parsed.get("version"):
|
|
107
|
+
services.append(
|
|
108
|
+
{
|
|
109
|
+
"port": port,
|
|
110
|
+
"service": self._guess_service(parsed["product"], port),
|
|
111
|
+
"version": f"{parsed['product']} {parsed['version']}",
|
|
112
|
+
"product": parsed["product"],
|
|
113
|
+
"version_number": parsed["version"],
|
|
114
|
+
"source": "finding",
|
|
115
|
+
"finding_id": finding.get("id"),
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
|
|
111
119
|
return services
|
|
112
|
-
|
|
120
|
+
|
|
113
121
|
def clean_version_string(self, version_str: str) -> str:
|
|
114
122
|
"""
|
|
115
123
|
Remove Nmap metadata from version strings.
|
|
116
|
-
|
|
124
|
+
|
|
117
125
|
Input: "syn-ack ttl 64 vsftpd 2.3.4"
|
|
118
126
|
Output: "vsftpd 2.3.4"
|
|
119
127
|
"""
|
|
120
128
|
if not version_str:
|
|
121
129
|
return version_str
|
|
122
|
-
|
|
130
|
+
|
|
123
131
|
# Remove common Nmap prefixes
|
|
124
|
-
version_str = re.sub(r
|
|
125
|
-
version_str = re.sub(r
|
|
126
|
-
version_str = re.sub(r
|
|
127
|
-
|
|
132
|
+
version_str = re.sub(r"^syn-ack\s+ttl\s+\d+\s+", "", version_str)
|
|
133
|
+
version_str = re.sub(r"^no-response\s+", "", version_str)
|
|
134
|
+
version_str = re.sub(r"^reset\s+ttl\s+\d+\s+", "", version_str)
|
|
135
|
+
|
|
128
136
|
return version_str.strip()
|
|
129
|
-
|
|
137
|
+
|
|
130
138
|
def parse_version_string(self, version_str: str) -> Dict:
|
|
131
139
|
"""
|
|
132
140
|
Parse version string into structured data.
|
|
133
|
-
|
|
141
|
+
|
|
134
142
|
Input: "OpenSSH 8.2p1 Ubuntu 4ubuntu0.1"
|
|
135
143
|
Output: {
|
|
136
144
|
'product': 'OpenSSH',
|
|
@@ -139,49 +147,53 @@ class ServiceVersionExtractor:
|
|
|
139
147
|
'build': 'Ubuntu 4ubuntu0.1'
|
|
140
148
|
}
|
|
141
149
|
"""
|
|
142
|
-
result = {
|
|
143
|
-
|
|
144
|
-
'version': None,
|
|
145
|
-
'patch': None,
|
|
146
|
-
'build': None
|
|
147
|
-
}
|
|
148
|
-
|
|
150
|
+
result = {"product": None, "version": None, "patch": None, "build": None}
|
|
151
|
+
|
|
149
152
|
if not version_str:
|
|
150
153
|
return result
|
|
151
|
-
|
|
154
|
+
|
|
152
155
|
# Clean the version string first
|
|
153
156
|
version_str = self.clean_version_string(version_str)
|
|
154
|
-
|
|
157
|
+
|
|
155
158
|
# Known products
|
|
156
159
|
products = [
|
|
157
|
-
|
|
158
|
-
|
|
160
|
+
"OpenSSH",
|
|
161
|
+
"Apache",
|
|
162
|
+
"nginx",
|
|
163
|
+
"vsftpd",
|
|
164
|
+
"MySQL",
|
|
165
|
+
"ProFTPD",
|
|
166
|
+
"Samba",
|
|
167
|
+
"PostgreSQL",
|
|
168
|
+
"Microsoft",
|
|
169
|
+
"Windows",
|
|
170
|
+
"OpenSSL",
|
|
159
171
|
]
|
|
160
|
-
|
|
172
|
+
|
|
161
173
|
for product in products:
|
|
162
174
|
if product.lower() in version_str.lower():
|
|
163
|
-
result[
|
|
175
|
+
result["product"] = product
|
|
164
176
|
break
|
|
165
|
-
|
|
177
|
+
|
|
166
178
|
# Extract version number
|
|
167
179
|
for pattern in self.version_patterns:
|
|
168
180
|
match = re.search(pattern, version_str, re.IGNORECASE)
|
|
169
181
|
if match:
|
|
170
|
-
result[
|
|
182
|
+
result["version"] = match.group(1)
|
|
171
183
|
break
|
|
172
|
-
|
|
184
|
+
|
|
173
185
|
# Extract patch level (e.g., p1)
|
|
174
|
-
patch_match = re.search(r
|
|
186
|
+
patch_match = re.search(r"p(\d+)", version_str, re.IGNORECASE)
|
|
175
187
|
if patch_match:
|
|
176
|
-
result[
|
|
177
|
-
|
|
188
|
+
result["patch"] = patch_match.group(1)
|
|
189
|
+
|
|
178
190
|
# Extract build info
|
|
179
|
-
build_match = re.search(r
|
|
191
|
+
build_match = re.search(r"\((.*?)\)", version_str)
|
|
180
192
|
if build_match:
|
|
181
|
-
result[
|
|
182
|
-
|
|
193
|
+
result["build"] = build_match.group(1)
|
|
194
|
+
|
|
183
195
|
return result
|
|
184
|
-
|
|
196
|
+
|
|
185
197
|
def combine_sources(self, host_id: int, findings: List[Dict]) -> List[Dict]:
|
|
186
198
|
"""
|
|
187
199
|
Merge version info from all sources.
|
|
@@ -189,67 +201,69 @@ class ServiceVersionExtractor:
|
|
|
189
201
|
"""
|
|
190
202
|
# Get from services table
|
|
191
203
|
services_from_db = self.extract_from_services_table(host_id)
|
|
192
|
-
|
|
204
|
+
|
|
193
205
|
# Get from findings
|
|
194
206
|
services_from_findings = self.extract_from_findings(findings)
|
|
195
|
-
|
|
207
|
+
|
|
196
208
|
# Merge by port
|
|
197
209
|
merged = {}
|
|
198
|
-
|
|
210
|
+
|
|
199
211
|
# Add DB services first (highest priority)
|
|
200
212
|
for svc in services_from_db:
|
|
201
|
-
key = svc[
|
|
213
|
+
key = svc["port"]
|
|
202
214
|
merged[key] = svc
|
|
203
|
-
|
|
215
|
+
|
|
204
216
|
# Augment with finding data if better version info
|
|
205
217
|
for svc in services_from_findings:
|
|
206
|
-
key = svc.get(
|
|
218
|
+
key = svc.get("port")
|
|
207
219
|
if key is None:
|
|
208
220
|
continue
|
|
209
|
-
|
|
221
|
+
|
|
210
222
|
if key not in merged:
|
|
211
223
|
merged[key] = svc
|
|
212
224
|
else:
|
|
213
225
|
# Update if finding has more specific version
|
|
214
|
-
if (
|
|
215
|
-
|
|
216
|
-
merged[key][
|
|
217
|
-
|
|
218
|
-
|
|
226
|
+
if (
|
|
227
|
+
svc["version_number"] != "unknown"
|
|
228
|
+
and merged[key]["version_number"] == "unknown"
|
|
229
|
+
):
|
|
230
|
+
merged[key]["version_number"] = svc["version_number"]
|
|
231
|
+
merged[key]["product"] = svc["product"]
|
|
232
|
+
|
|
219
233
|
return list(merged.values())
|
|
220
|
-
|
|
234
|
+
|
|
221
235
|
def _guess_service(self, product: str, port: Optional[int]) -> str:
|
|
222
236
|
"""Guess service type from product name and port."""
|
|
223
237
|
product_lower = product.lower()
|
|
224
|
-
|
|
238
|
+
|
|
225
239
|
# Map products to service types
|
|
226
|
-
if
|
|
227
|
-
return
|
|
228
|
-
elif
|
|
229
|
-
return
|
|
230
|
-
elif
|
|
231
|
-
return
|
|
232
|
-
elif
|
|
233
|
-
return
|
|
234
|
-
elif
|
|
235
|
-
return
|
|
236
|
-
elif
|
|
237
|
-
return
|
|
238
|
-
|
|
240
|
+
if "ssh" in product_lower:
|
|
241
|
+
return "ssh"
|
|
242
|
+
elif "ftp" in product_lower:
|
|
243
|
+
return "ftp"
|
|
244
|
+
elif "mysql" in product_lower:
|
|
245
|
+
return "mysql"
|
|
246
|
+
elif "postgres" in product_lower:
|
|
247
|
+
return "postgres"
|
|
248
|
+
elif "apache" in product_lower or "nginx" in product_lower:
|
|
249
|
+
return "http"
|
|
250
|
+
elif "samba" in product_lower or "smb" in product_lower:
|
|
251
|
+
return "smb"
|
|
252
|
+
|
|
239
253
|
# Fallback to port-based guess
|
|
240
254
|
if port:
|
|
241
255
|
port_map = {
|
|
242
|
-
21:
|
|
243
|
-
22:
|
|
244
|
-
23:
|
|
245
|
-
25:
|
|
246
|
-
80:
|
|
247
|
-
443:
|
|
248
|
-
445:
|
|
249
|
-
3306:
|
|
250
|
-
5432:
|
|
251
|
-
8080:
|
|
256
|
+
21: "ftp",
|
|
257
|
+
22: "ssh",
|
|
258
|
+
23: "telnet",
|
|
259
|
+
25: "smtp",
|
|
260
|
+
80: "http",
|
|
261
|
+
443: "https",
|
|
262
|
+
445: "smb",
|
|
263
|
+
3306: "mysql",
|
|
264
|
+
5432: "postgres",
|
|
265
|
+
8080: "http",
|
|
252
266
|
}
|
|
253
|
-
return port_map.get(port,
|
|
254
|
-
|
|
255
|
-
return
|
|
267
|
+
return port_map.get(port, "unknown")
|
|
268
|
+
|
|
269
|
+
return "unknown"
|