fscan/core/ServiceScanner.go
ZacharyZcR 3b50f42474 fix: 修复服务扫描插件显示bug,正确显示所有适用插件
问题:
- "使用服务扫描插件" 只显示一个错误插件名(如ldap)
- 原因:只检查第一个端口来判断所有插件适用性
- 实际扫描有多个端口,每个端口适用不同插件组合

修复:
- 改为检查所有发现端口的插件适用性匹配
- 使用Set去重,收集适用于任意端口的所有插件
- 正确显示实际会被使用的插件列表

修复前: 使用服务扫描插件: ldap
修复后: 使用服务扫描插件: webtitle, ssh, mysql, smtp, webpoc

验证:
- SSH插件 → 端口22 ✓
- SMTP插件 → 端口25 ✓
- MySQL插件 → 端口3306 ✓
- WebTitle/WebPOC → 端口8080 ✓
2025-09-02 06:52:16 +00:00

212 lines
5.7 KiB
Go

package core
import (
"fmt"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"strconv"
"strings"
"sync"
)
// ServiceScanStrategy 服务扫描策略
type ServiceScanStrategy struct {
*BaseScanStrategy
portDiscovery *PortDiscoveryService
}
// NewServiceScanStrategy 创建新的服务扫描策略
func NewServiceScanStrategy() *ServiceScanStrategy {
return &ServiceScanStrategy{
BaseScanStrategy: NewBaseScanStrategy("服务扫描", FilterService),
portDiscovery: NewPortDiscoveryService(),
}
}
// LogPluginInfo 重写以提供基于端口的插件过滤
func (s *ServiceScanStrategy) LogPluginInfo() {
// 需要从命令行参数获取端口信息来进行过滤
// 如果没有指定端口,使用默认端口进行过滤显示
if common.Ports == "" || common.Ports == "all" {
// 默认端口扫描:显示所有插件
s.BaseScanStrategy.LogPluginInfo()
} else {
// 指定端口扫描:只显示匹配的插件
s.showPluginsForSpecifiedPorts()
}
}
// showPluginsForSpecifiedPorts 显示指定端口的匹配插件
func (s *ServiceScanStrategy) showPluginsForSpecifiedPorts() {
allPlugins, isCustomMode := s.GetPlugins()
// 解析端口
ports := s.parsePortList(common.Ports)
if len(ports) == 0 {
s.BaseScanStrategy.LogPluginInfo()
return
}
// 收集所有匹配的插件(去重)
pluginSet := make(map[string]bool)
for _, port := range ports {
for _, pluginName := range allPlugins {
if s.pluginExists(pluginName) {
if s.IsPluginApplicableByName(pluginName, "127.0.0.1", port, isCustomMode) {
pluginSet[pluginName] = true
}
}
}
}
// 转换为列表
var applicablePlugins []string
for pluginName := range pluginSet {
applicablePlugins = append(applicablePlugins, pluginName)
}
// 输出结果
if len(applicablePlugins) > 0 {
if isCustomMode {
common.LogBase(fmt.Sprintf("服务插件: 自定义指定 (%s)", strings.Join(applicablePlugins, ", ")))
} else {
common.LogBase(fmt.Sprintf("服务插件: %s", strings.Join(applicablePlugins, ", ")))
}
} else {
common.LogBase("服务插件: 无可用插件")
}
}
// parsePortList 解析端口列表
func (s *ServiceScanStrategy) parsePortList(portStr string) []int {
if portStr == "" || portStr == "all" {
return []int{}
}
var ports []int
parts := strings.Split(portStr, ",")
for _, part := range parts {
part = strings.TrimSpace(part)
if port, err := strconv.Atoi(part); err == nil {
ports = append(ports, port)
}
}
return ports
}
// Name 返回策略名称
func (s *ServiceScanStrategy) Name() string {
return i18n.GetText("scan_strategy_service_name")
}
// Description 返回策略描述
func (s *ServiceScanStrategy) Description() string {
return i18n.GetText("scan_strategy_service_desc")
}
// Execute 执行服务扫描策略
func (s *ServiceScanStrategy) Execute(info common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
// 验证扫描目标
if info.Host == "" {
common.LogError(i18n.GetText("parse_error_target_empty"))
return
}
// 输出扫描开始信息
s.LogScanStart()
// 验证插件配置
if err := s.ValidateConfiguration(); err != nil {
common.LogError(err.Error())
return
}
common.LogBase(i18n.GetText("scan_host_start"))
// 输出插件信息(重写以提供端口过滤)
s.LogPluginInfo()
// 执行主机扫描流程
s.performHostScan(info, ch, wg)
}
// performHostScan 执行主机扫描的完整流程
func (s *ServiceScanStrategy) performHostScan(info common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
// 使用端口发现服务发现目标
targetInfos, err := s.portDiscovery.DiscoverTargets(info.Host, info)
if err != nil {
common.LogError(err.Error())
return
}
// 执行漏洞扫描
if len(targetInfos) > 0 {
common.LogBase(i18n.GetText("scan_vulnerability_start"))
// 显示即将使用的漏洞扫描插件
s.LogVulnerabilityPluginInfo(targetInfos)
ExecuteScanTasks(targetInfos, s, ch, wg)
}
}
// PrepareTargets 准备目标信息
func (s *ServiceScanStrategy) PrepareTargets(info common.HostInfo) []common.HostInfo {
// 使用端口发现服务发现目标
targetInfos, err := s.portDiscovery.DiscoverTargets(info.Host, info)
if err != nil {
common.LogError(err.Error())
return nil
}
return targetInfos
}
// LogVulnerabilityPluginInfo 输出服务扫描插件信息
func (s *ServiceScanStrategy) LogVulnerabilityPluginInfo(targets []common.HostInfo) {
allPlugins, isCustomMode := s.GetPlugins()
// 收集所有目标端口用于插件适用性检查
portSet := make(map[int]bool)
for _, target := range targets {
if target.Ports != "" {
if port, err := strconv.Atoi(target.Ports); err == nil {
portSet[port] = true
}
}
}
// 获取实际会被使用的插件列表(考虑所有发现端口的匹配)
servicePluginSet := make(map[string]bool)
// 提取所有目标端口用于插件适用性检查
var allPorts []int
for port := range portSet {
allPorts = append(allPorts, port)
}
for _, pluginName := range allPlugins {
// 使用统一插件系统检查插件存在性
if s.pluginExists(pluginName) {
// 检查插件是否适用于任意一个发现的端口
for _, port := range allPorts {
if s.IsPluginApplicableByName(pluginName, "127.0.0.1", port, isCustomMode) {
servicePluginSet[pluginName] = true
break // 只要适用于一个端口就添加
}
}
}
}
// 转换为切片
var servicePlugins []string
for pluginName := range servicePluginSet {
servicePlugins = append(servicePlugins, pluginName)
}
// 输出插件信息
if len(servicePlugins) > 0 {
common.LogBase(i18n.GetText("scan_service_plugins", strings.Join(servicePlugins, ", ")))
} else {
common.LogBase(i18n.GetText("scan_no_service_plugins"))
}
}