mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00

增强用户体验,让用户清楚了解漏洞扫描时启用的具体插件: 功能特点: • 在"开始漏洞扫描"后显示实际启用的插件列表 • 智能过滤,只显示针对发现端口的适用插件 • 完整国际化支持,中英文界面均正常显示 • 格式与现有"使用服务插件"保持一致 实现细节: * Core/ServiceScanner.go: 新增LogVulnerabilityPluginInfo函数 - 使用与实际扫描相同的插件过滤逻辑 - 确保显示的插件列表与实际执行的插件一致 - 支持插件去重和格式化显示 * Common/i18n/messages.go: 添加漏洞扫描相关国际化文本 - scan_vulnerability_start: "开始漏洞扫描" / "Starting vulnerability scan" - scan_vulnerability_plugins: "使用漏洞扫描插件: %s" / "Using vulnerability scan plugins: %s" - scan_no_vulnerability_plugins: "未找到可用的漏洞扫描插件" / "No available vulnerability scan plugins found" 显示效果: - 针对SMB端口(445,135): 显示"ms17010, smb, smb2, smbghost, findnet" - 针对Web端口(80,443): 显示"webpoc, webtitle" - 根据实际发现的开放端口智能显示相关插件 提升用户对扫描过程的可见性和控制感,便于调试和性能优化。
262 lines
7.3 KiB
Go
262 lines
7.3 KiB
Go
package core
|
||
|
||
import (
|
||
"fmt"
|
||
"github.com/shadow1ng/fscan/common"
|
||
"github.com/shadow1ng/fscan/common/i18n"
|
||
"github.com/shadow1ng/fscan/common/parsers"
|
||
"strconv"
|
||
"strings"
|
||
"sync"
|
||
)
|
||
|
||
// ServiceScanStrategy 服务扫描策略
|
||
type ServiceScanStrategy struct{}
|
||
|
||
// NewServiceScanStrategy 创建新的服务扫描策略
|
||
func NewServiceScanStrategy() *ServiceScanStrategy {
|
||
return &ServiceScanStrategy{}
|
||
}
|
||
|
||
// Name 返回策略名称
|
||
func (s *ServiceScanStrategy) Name() string {
|
||
return "服务扫描"
|
||
}
|
||
|
||
// Description 返回策略描述
|
||
func (s *ServiceScanStrategy) Description() string {
|
||
return "扫描主机服务和漏洞"
|
||
}
|
||
|
||
// Execute 执行服务扫描策略
|
||
func (s *ServiceScanStrategy) Execute(info common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
|
||
// 验证扫描目标
|
||
if info.Host == "" {
|
||
common.LogError("未指定扫描目标")
|
||
return
|
||
}
|
||
|
||
// 验证插件配置
|
||
if err := validateScanPlugins(); err != nil {
|
||
common.LogError(err.Error())
|
||
return
|
||
}
|
||
|
||
// 解析目标主机
|
||
hosts, err := parsers.ParseIP(info.Host, common.HostsFile, common.ExcludeHosts)
|
||
if err != nil {
|
||
common.LogError(fmt.Sprintf("解析主机错误: %v", err))
|
||
return
|
||
}
|
||
|
||
common.LogBase(i18n.GetText("scan_host_start"))
|
||
|
||
// 输出插件信息
|
||
s.LogPluginInfo()
|
||
|
||
// 执行主机扫描流程
|
||
s.performHostScan(hosts, info, ch, wg)
|
||
}
|
||
|
||
// performHostScan 执行主机扫描的完整流程
|
||
func (s *ServiceScanStrategy) performHostScan(hosts []string, info common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
|
||
var targetInfos []common.HostInfo
|
||
|
||
// 主机存活性检测和端口扫描
|
||
if len(hosts) > 0 || len(common.HostPort) > 0 {
|
||
// 主机存活检测
|
||
if s.shouldPerformLivenessCheck(hosts) {
|
||
hosts = CheckLive(hosts, common.UsePing)
|
||
common.LogBase(fmt.Sprintf("存活主机数量: %d", len(hosts)))
|
||
}
|
||
|
||
// 端口扫描
|
||
alivePorts := s.discoverAlivePorts(hosts)
|
||
if len(alivePorts) > 0 {
|
||
targetInfos = s.convertToTargetInfos(alivePorts, info)
|
||
}
|
||
}
|
||
|
||
// 执行漏洞扫描
|
||
if len(targetInfos) > 0 {
|
||
common.LogBase(i18n.GetText("scan_vulnerability_start"))
|
||
// 显示即将使用的漏洞扫描插件
|
||
s.LogVulnerabilityPluginInfo(targetInfos)
|
||
ExecuteScanTasks(targetInfos, s, ch, wg)
|
||
}
|
||
}
|
||
|
||
// shouldPerformLivenessCheck 判断是否需要执行存活性检测
|
||
func (s *ServiceScanStrategy) shouldPerformLivenessCheck(hosts []string) bool {
|
||
return common.DisablePing == false && len(hosts) > 1
|
||
}
|
||
|
||
// discoverAlivePorts 发现存活的端口
|
||
func (s *ServiceScanStrategy) discoverAlivePorts(hosts []string) []string {
|
||
var alivePorts []string
|
||
|
||
// 根据扫描模式选择端口扫描方式
|
||
if len(hosts) > 0 {
|
||
alivePorts = EnhancedPortScan(hosts, common.Ports, common.Timeout)
|
||
common.LogBase(i18n.GetText("scan_alive_ports_count", len(alivePorts)))
|
||
}
|
||
|
||
// 合并额外指定的端口
|
||
if len(common.HostPort) > 0 {
|
||
alivePorts = append(alivePorts, common.HostPort...)
|
||
alivePorts = common.RemoveDuplicate(alivePorts)
|
||
common.HostPort = nil
|
||
common.LogBase(i18n.GetText("scan_alive_ports_count", len(alivePorts)))
|
||
}
|
||
|
||
return alivePorts
|
||
}
|
||
|
||
// PrepareTargets 准备目标信息
|
||
func (s *ServiceScanStrategy) PrepareTargets(info common.HostInfo) []common.HostInfo {
|
||
// 解析目标主机
|
||
hosts, err := parsers.ParseIP(info.Host, common.HostsFile, common.ExcludeHosts)
|
||
if err != nil {
|
||
common.LogError(fmt.Sprintf("解析主机错误: %v", err))
|
||
return nil
|
||
}
|
||
|
||
var targetInfos []common.HostInfo
|
||
|
||
// 主机存活性检测和端口扫描
|
||
if len(hosts) > 0 || len(common.HostPort) > 0 {
|
||
// 主机存活检测
|
||
if s.shouldPerformLivenessCheck(hosts) {
|
||
hosts = CheckLive(hosts, common.UsePing)
|
||
}
|
||
|
||
// 端口扫描
|
||
alivePorts := s.discoverAlivePorts(hosts)
|
||
if len(alivePorts) > 0 {
|
||
targetInfos = s.convertToTargetInfos(alivePorts, info)
|
||
}
|
||
}
|
||
|
||
return targetInfos
|
||
}
|
||
|
||
// convertToTargetInfos 将端口列表转换为目标信息
|
||
func (s *ServiceScanStrategy) convertToTargetInfos(ports []string, baseInfo common.HostInfo) []common.HostInfo {
|
||
var infos []common.HostInfo
|
||
|
||
for _, targetIP := range ports {
|
||
hostParts := strings.Split(targetIP, ":")
|
||
if len(hostParts) != 2 {
|
||
common.LogError(fmt.Sprintf("无效的目标地址格式: %s", targetIP))
|
||
continue
|
||
}
|
||
|
||
info := baseInfo
|
||
info.Host = hostParts[0]
|
||
info.Ports = hostParts[1]
|
||
infos = append(infos, info)
|
||
}
|
||
|
||
return infos
|
||
}
|
||
|
||
// GetPlugins 获取服务扫描插件列表
|
||
func (s *ServiceScanStrategy) GetPlugins() ([]string, bool) {
|
||
// 如果指定了插件列表且不是"all"
|
||
if common.ScanMode != "" && common.ScanMode != "all" {
|
||
plugins := parsePluginList(common.ScanMode)
|
||
if len(plugins) > 0 {
|
||
return plugins, true
|
||
}
|
||
return []string{common.ScanMode}, true
|
||
}
|
||
|
||
// 未指定或使用"all":获取所有插件,由IsPluginApplicable做类型过滤
|
||
return GetAllPlugins(), false
|
||
}
|
||
|
||
// LogPluginInfo 输出服务扫描插件信息
|
||
func (s *ServiceScanStrategy) LogPluginInfo() {
|
||
allPlugins, isCustomMode := s.GetPlugins()
|
||
|
||
// 如果是自定义模式,直接显示用户指定的插件
|
||
if isCustomMode {
|
||
common.LogBase(fmt.Sprintf("使用指定插件: %s", strings.Join(allPlugins, ", ")))
|
||
return
|
||
}
|
||
|
||
// 在自动模式下,过滤掉本地插件,只显示服务类型插件
|
||
var applicablePlugins []string
|
||
for _, pluginName := range allPlugins {
|
||
plugin, exists := common.PluginManager[pluginName]
|
||
if exists && !plugin.HasType(common.PluginTypeLocal) {
|
||
applicablePlugins = append(applicablePlugins, pluginName)
|
||
}
|
||
}
|
||
|
||
if len(applicablePlugins) > 0 {
|
||
common.LogBase(fmt.Sprintf("使用服务插件: %s", strings.Join(applicablePlugins, ", ")))
|
||
} else {
|
||
common.LogBase(i18n.GetText("scan_no_service_plugins"))
|
||
}
|
||
}
|
||
|
||
// LogVulnerabilityPluginInfo 输出漏洞扫描插件信息
|
||
func (s *ServiceScanStrategy) LogVulnerabilityPluginInfo(targets []common.HostInfo) {
|
||
allPlugins, isCustomMode := s.GetPlugins()
|
||
|
||
// 获取实际会被使用的插件列表
|
||
var vulnerabilityPlugins []string
|
||
pluginUsed := make(map[string]bool)
|
||
|
||
for _, target := range targets {
|
||
targetPort := 0
|
||
if target.Ports != "" {
|
||
targetPort, _ = strconv.Atoi(target.Ports)
|
||
}
|
||
|
||
for _, pluginName := range allPlugins {
|
||
plugin, exists := common.PluginManager[pluginName]
|
||
if !exists {
|
||
continue
|
||
}
|
||
|
||
// 检查插件是否适用于当前目标(使用与ExecuteScanTasks相同的逻辑)
|
||
if s.IsPluginApplicable(plugin, targetPort, isCustomMode) {
|
||
if !pluginUsed[pluginName] {
|
||
vulnerabilityPlugins = append(vulnerabilityPlugins, pluginName)
|
||
pluginUsed[pluginName] = true
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 输出插件信息
|
||
if len(vulnerabilityPlugins) > 0 {
|
||
common.LogBase(fmt.Sprintf(i18n.GetText("scan_vulnerability_plugins"), strings.Join(vulnerabilityPlugins, ", ")))
|
||
} else {
|
||
common.LogBase(i18n.GetText("scan_no_vulnerability_plugins"))
|
||
}
|
||
}
|
||
|
||
// IsPluginApplicable 判断插件是否适用于服务扫描
|
||
func (s *ServiceScanStrategy) IsPluginApplicable(plugin common.ScanPlugin, targetPort int, isCustomMode bool) bool {
|
||
// 自定义模式下运行所有明确指定的插件
|
||
if isCustomMode {
|
||
return true
|
||
}
|
||
|
||
// 非自定义模式下,排除本地插件
|
||
if plugin.HasType(common.PluginTypeLocal) {
|
||
return false
|
||
}
|
||
|
||
// 检查端口是否匹配
|
||
if len(plugin.Ports) > 0 && targetPort > 0 {
|
||
return plugin.HasPort(targetPort)
|
||
}
|
||
|
||
// 无端口限制的插件或适用于服务扫描的插件
|
||
return len(plugin.Ports) == 0 || plugin.HasType(common.PluginTypeService)
|
||
}
|