From 2ab7c4d9b2e6f016ed050b9e10cafaa7c2297c6b Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Fri, 12 Jun 2026 19:31:56 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=9D=9E=E6=A0=87=E5=87=86=E7=AB=AF?= =?UTF-8?q?=E5=8F=A3=E7=9A=84=E6=9C=8D=E5=8A=A1=E6=97=A0=E6=B3=95=E5=8C=B9?= =?UTF-8?q?=E9=85=8D=E5=AF=B9=E5=BA=94=E6=8F=92=E4=BB=B6=20#588?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 端口扫描识别到 8881 上运行 SSH,但 SSH 插件只注册了 [22,2222,2200,22222], 端口不匹配导致插件不执行。 新增服务名称缓存:端口扫描阶段记录 host:port → serviceName, 插件匹配时端口不命中则回退到服务名称匹配。 --- core/base_scan_strategy.go | 11 +++++++++++ core/port_scan.go | 14 ++++++++++++-- core/service_cache.go | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 core/service_cache.go diff --git a/core/base_scan_strategy.go b/core/base_scan_strategy.go index 9bf9abc..a1912ab 100644 --- a/core/base_scan_strategy.go +++ b/core/base_scan_strategy.go @@ -115,6 +115,7 @@ func (b *BaseScanStrategy) isLocalPluginExplicitlySpecified(pluginName string, c } // isPluginApplicableToPortWithHost 检查插件是否适用于指定端口 +// 匹配策略:端口匹配 → 服务名称匹配(解决非标准端口问题) func (b *BaseScanStrategy) isPluginApplicableToPortWithHost(pluginName string, targetHost string, targetPort int) bool { if b.isWebPlugin(pluginName) { return IsMarkedWebService(targetHost, targetPort) @@ -136,6 +137,16 @@ func (b *BaseScanStrategy) isPluginApplicableToPortWithHost(pluginName string, t } } + // 端口不匹配时,按服务识别结果匹配 + // 例:8881 端口上识别到 ssh 服务 → ssh 插件应该执行 + if targetHost != "" && targetPort > 0 { + if svcName, ok := GetServiceName(targetHost, targetPort); ok { + if strings.EqualFold(svcName, pluginName) { + return true + } + } + } + return false } diff --git a/core/port_scan.go b/core/port_scan.go index 150d717..a816c2c 100644 --- a/core/port_scan.go +++ b/core/port_scan.go @@ -472,14 +472,21 @@ func buildWebServiceURL(addr string, serviceInfo *ServiceInfo) string { return fmt.Sprintf("%s://%s", protocol, addr) } if protocol == "http" && port == "80" { - return fmt.Sprintf("http://%s", host) + return fmt.Sprintf("http://%s", urlHost(host)) } if protocol == "https" && port == "443" { - return fmt.Sprintf("https://%s", host) + return fmt.Sprintf("https://%s", urlHost(host)) } return fmt.Sprintf("%s://%s", protocol, net.JoinHostPort(host, port)) } +func urlHost(host string) string { + if strings.Contains(host, ":") && !strings.HasPrefix(host, "[") { + return "[" + host + "]" + } + return host +} + // scanSinglePort 扫描单个端口并进行服务识别(重构后的简洁版本) func scanSinglePort(ctx context.Context, host string, port int, addr string, adaptiveTO *AdaptiveTimeout, metrics *ScanMetrics, count *atomic.Int64, collector *resultCollector, failedCollector *failedPortCollector, session *common.ScanSession) { config := session.Config @@ -700,6 +707,9 @@ func processServiceResult(ctx context.Context, host string, port int, addr strin return } + // 缓存服务名称,供插件按服务类型匹配(解决非标准端口问题) + MarkServiceName(host, port, serviceInfo.Name) + // 保存并输出服务信息 details := buildServiceDetails(port, serviceInfo) isWeb := IsWebServiceByFingerprint(serviceInfo) diff --git a/core/service_cache.go b/core/service_cache.go new file mode 100644 index 0000000..4f1054b --- /dev/null +++ b/core/service_cache.go @@ -0,0 +1,36 @@ +package core + +import ( + "net" + "strconv" + "strings" + "sync" +) + +// 服务识别缓存:host:port → 服务名称 +// 端口扫描阶段写入,插件匹配阶段读取 +// 解决非标准端口上的服务无法匹配对应插件的问题 +var ( + serviceNameCache = make(map[string]string) + serviceCacheMu sync.RWMutex +) + +// MarkServiceName 记录端口上识别到的服务名称 +func MarkServiceName(host string, port int, serviceName string) { + if serviceName == "" || serviceName == "unknown" { + return + } + key := net.JoinHostPort(host, strconv.Itoa(port)) + serviceCacheMu.Lock() + serviceNameCache[key] = strings.ToLower(serviceName) + serviceCacheMu.Unlock() +} + +// GetServiceName 查询端口上的服务名称 +func GetServiceName(host string, port int) (string, bool) { + key := net.JoinHostPort(host, strconv.Itoa(port)) + serviceCacheMu.RLock() + name, ok := serviceNameCache[key] + serviceCacheMu.RUnlock() + return name, ok +}