fscan/Core/ServiceScanner.go
ZacharyZcR 51735c4e25 feat: 实现-nobr/-ne参数和服务识别功能
新增功能:
- 添加-ne参数禁用利用攻击,实现弱密码检测和利用攻击的分离控制
- 实现-nobr模式下所有插件的服务识别功能,单数据包获取最大信息
- 修复端口插件匹配逻辑,只调用适配端口的插件提升扫描效率

插件改造:
- MySQL: 通过握手包识别获取版本信息
- Redis: INFO命令识别版本,优先检测未授权访问
- SSH: Banner识别获取协议和服务器版本
- ActiveMQ: STOMP协议识别获取版本和认证状态

技术改进:
- 新增端口匹配算法确保精准插件调用
- 完善i18n国际化支持所有新功能
- 统一服务识别接口设计便于扩展
2025-08-08 03:32:00 +08:00

190 lines
5.0 KiB
Go

package core
import (
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/plugins/base"
"strconv"
"strings"
"sync"
)
// ServiceScanStrategy 服务扫描策略
type ServiceScanStrategy struct {
*BaseScanStrategy
portDiscovery *PortDiscoveryService
}
// NewServiceScanStrategy 创建新的服务扫描策略
func NewServiceScanStrategy() *ServiceScanStrategy {
return &ServiceScanStrategy{
BaseScanStrategy: NewBaseScanStrategy("服务扫描", FilterService),
portDiscovery: NewPortDiscoveryService(),
}
}
// 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
}
}
}
// 获取实际会被使用的插件列表(包括新插件架构和传统插件)
var servicePlugins []string
// 检查新插件架构
for _, pluginName := range allPlugins {
// 首先检查新插件架构
if factory := base.GlobalPluginRegistry.GetFactory(pluginName); factory != nil {
// 获取插件元数据检查端口匹配
metadata := factory.GetMetadata()
if s.isNewPluginApplicableToAnyPort(metadata, portSet, isCustomMode) {
servicePlugins = append(servicePlugins, pluginName)
}
continue
}
// 然后检查传统插件系统
plugin, exists := common.PluginManager[pluginName]
if !exists {
continue
}
// 检查传统插件是否对任何目标端口适用
if s.isPluginApplicableToAnyPort(plugin, portSet, isCustomMode) {
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"))
}
}
// isPluginApplicableToAnyPort 检查插件是否对任何端口适用(性能优化)
func (s *ServiceScanStrategy) isPluginApplicableToAnyPort(plugin common.ScanPlugin, portSet map[int]bool, isCustomMode bool) bool {
// 自定义模式下运行所有明确指定的插件
if isCustomMode {
return true
}
// 非自定义模式下,排除本地插件
if plugin.HasType(common.PluginTypeLocal) {
return false
}
// 无端口限制的插件适用于所有端口
if len(plugin.Ports) == 0 {
return true
}
// 有端口限制的插件:检查是否匹配任何目标端口
for port := range portSet {
if plugin.HasPort(port) {
return true
}
}
return false
}
// isNewPluginApplicableToAnyPort 检查新插件架构的插件是否对任何端口适用
func (s *ServiceScanStrategy) isNewPluginApplicableToAnyPort(metadata *base.PluginMetadata, portSet map[int]bool, isCustomMode bool) bool {
// 自定义模式下运行所有明确指定的插件
if isCustomMode {
return true
}
// 无端口限制的插件适用于所有端口
if len(metadata.Ports) == 0 {
return true
}
// 有端口限制的插件:检查是否匹配任何目标端口
for port := range portSet {
for _, pluginPort := range metadata.Ports {
if pluginPort == port {
return true
}
}
}
return false
}