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 +}