fix: 修复扫描逻辑

This commit is contained in:
ZacharyZcR 2025-04-26 06:18:01 +08:00
parent a8bd8ca508
commit e58a48ba9b
8 changed files with 672 additions and 476 deletions

View File

@ -73,7 +73,7 @@ func Flag(Info *HostInfo) {
// ═════════════════════════════════════════════════ // ═════════════════════════════════════════════════
// 扫描控制参数 // 扫描控制参数
// ═════════════════════════════════════════════════ // ═════════════════════════════════════════════════
flag.StringVar(&ScanMode, "m", "All", GetText("flag_scan_mode")) flag.StringVar(&ScanMode, "m", "all", GetText("flag_scan_mode"))
flag.IntVar(&ThreadNum, "t", 10, GetText("flag_thread_num")) flag.IntVar(&ThreadNum, "t", 10, GetText("flag_thread_num"))
flag.Int64Var(&Timeout, "time", 3, GetText("flag_timeout")) flag.Int64Var(&Timeout, "time", 3, GetText("flag_timeout"))
flag.IntVar(&ModuleThreadNum, "mt", 10, GetText("flag_module_thread_num")) flag.IntVar(&ModuleThreadNum, "mt", 10, GetText("flag_module_thread_num"))
@ -183,7 +183,7 @@ func FlagFromRemote(info *HostInfo, argString string) error {
fs.StringVar(&HostsFile, "hf", "", GetText("flag_hosts_file")) fs.StringVar(&HostsFile, "hf", "", GetText("flag_hosts_file"))
fs.StringVar(&PortsFile, "pf", "", GetText("flag_ports_file")) fs.StringVar(&PortsFile, "pf", "", GetText("flag_ports_file"))
fs.StringVar(&ScanMode, "m", "All", GetText("flag_scan_mode")) fs.StringVar(&ScanMode, "m", "all", GetText("flag_scan_mode"))
fs.IntVar(&ThreadNum, "t", 10, GetText("flag_thread_num")) fs.IntVar(&ThreadNum, "t", 10, GetText("flag_thread_num"))
fs.Int64Var(&Timeout, "time", 3, GetText("flag_timeout")) fs.Int64Var(&Timeout, "time", 3, GetText("flag_timeout"))
fs.IntVar(&ModuleThreadNum, "mt", 10, GetText("flag_module_thread_num")) fs.IntVar(&ModuleThreadNum, "mt", 10, GetText("flag_module_thread_num"))

View File

@ -8,13 +8,32 @@ type HostInfo struct {
Infostr []string Infostr []string
} }
// 在 Common/const.go 中添加
// 插件类型常量
const (
PluginTypeService = "service" // 服务类型插件
PluginTypeWeb = "web" // Web类型插件
PluginTypeLocal = "local" // 本地类型插件
)
// ScanPlugin 定义扫描插件的结构 // ScanPlugin 定义扫描插件的结构
type ScanPlugin struct { type ScanPlugin struct {
Name string // 插件名称 Name string // 插件名称
Ports []int // 关联的端口列表,空切片表示特殊扫描类型 Ports []int // 适用端口
Types []string // 插件类型标签,一个插件可以有多个类型
ScanFunc func(*HostInfo) error // 扫描函数 ScanFunc func(*HostInfo) error // 扫描函数
} }
// 添加一个用于检查插件类型的辅助方法
func (p ScanPlugin) HasType(typeName string) bool {
for _, t := range p.Types {
if t == typeName {
return true
}
}
return false
}
// HasPort 检查插件是否支持指定端口 // HasPort 检查插件是否支持指定端口
func (p *ScanPlugin) HasPort(port int) bool { func (p *ScanPlugin) HasPort(port int) bool {
// 如果没有指定端口列表,表示支持所有端口 // 如果没有指定端口列表,表示支持所有端口

112
Core/LocalScanner.go Normal file
View File

@ -0,0 +1,112 @@
package Core
import (
"fmt"
"github.com/shadow1ng/fscan/Common"
"strings"
"sync"
)
// LocalScanStrategy 本地扫描策略
type LocalScanStrategy struct{}
// NewLocalScanStrategy 创建新的本地扫描策略
func NewLocalScanStrategy() *LocalScanStrategy {
return &LocalScanStrategy{}
}
// Name 返回策略名称
func (s *LocalScanStrategy) Name() string {
return "本地扫描"
}
// Description 返回策略描述
func (s *LocalScanStrategy) Description() string {
return "收集本地系统信息"
}
// Execute 执行本地扫描策略
func (s *LocalScanStrategy) Execute(info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
Common.LogInfo("执行本地信息收集")
// 验证插件配置
if err := validateScanPlugins(); err != nil {
Common.LogError(err.Error())
return
}
// 输出插件信息
s.LogPluginInfo()
// 准备目标(本地扫描通常只有一个目标,即本机)
targets := s.PrepareTargets(info)
// 执行扫描任务
ExecuteScanTasks(targets, s, ch, wg)
}
// PrepareTargets 准备本地扫描目标
func (s *LocalScanStrategy) PrepareTargets(info Common.HostInfo) []Common.HostInfo {
// 本地扫描只使用传入的目标信息,不做额外处理
return []Common.HostInfo{info}
}
// GetPlugins 获取本地扫描插件列表
func (s *LocalScanStrategy) GetPlugins() ([]string, bool) {
// 如果指定了特定插件且不是"all"
if Common.ScanMode != "" && Common.ScanMode != "all" {
requestedPlugins := parsePluginList(Common.ScanMode)
if len(requestedPlugins) == 0 {
requestedPlugins = []string{Common.ScanMode}
}
// 验证插件是否存在不做Local类型过滤
var validPlugins []string
for _, name := range requestedPlugins {
if _, exists := Common.PluginManager[name]; exists {
validPlugins = append(validPlugins, name)
}
}
return validPlugins, true
}
// 未指定或使用"all"获取所有插件由IsPluginApplicable做类型过滤
return GetAllPlugins(), false
}
// LogPluginInfo 输出本地扫描插件信息
func (s *LocalScanStrategy) LogPluginInfo() {
allPlugins, isCustomMode := s.GetPlugins()
// 如果是自定义模式,直接显示用户指定的插件
if isCustomMode {
Common.LogInfo(fmt.Sprintf("本地模式: 使用指定插件: %s", strings.Join(allPlugins, ", ")))
return
}
// 在自动模式下只显示Local类型的插件
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.LogInfo(fmt.Sprintf("本地模式: 使用本地插件: %s", strings.Join(applicablePlugins, ", ")))
} else {
Common.LogInfo("本地模式: 未找到可用的本地插件")
}
}
// IsPluginApplicable 判断插件是否适用于本地扫描
func (s *LocalScanStrategy) IsPluginApplicable(plugin Common.ScanPlugin, targetPort int, isCustomMode bool) bool {
// 自定义模式下运行所有明确指定的插件
if isCustomMode {
return true
}
// 非自定义模式下只运行Local类型插件
return plugin.HasType(Common.PluginTypeLocal)
}

71
Core/PluginUtils.go Normal file
View File

@ -0,0 +1,71 @@
package Core
import (
"fmt"
"github.com/shadow1ng/fscan/Common"
"sort"
"strings"
)
// 插件列表解析和验证
func parsePluginList(pluginStr string) []string {
if pluginStr == "" {
return nil
}
// 按逗号分割并去除每个插件名称两端的空白
plugins := strings.Split(pluginStr, ",")
for i, p := range plugins {
plugins[i] = strings.TrimSpace(p)
}
// 过滤空字符串
var result []string
for _, p := range plugins {
if p != "" {
result = append(result, p)
}
}
return result
}
// 验证扫描插件的有效性
func validateScanPlugins() error {
// 如果未指定扫描模式或使用All模式则无需验证
if Common.ScanMode == "" || Common.ScanMode == "all" {
return nil
}
// 解析插件列表
plugins := parsePluginList(Common.ScanMode)
if len(plugins) == 0 {
plugins = []string{Common.ScanMode}
}
// 验证每个插件是否有效
var invalidPlugins []string
for _, plugin := range plugins {
if _, exists := Common.PluginManager[plugin]; !exists {
invalidPlugins = append(invalidPlugins, plugin)
}
}
if len(invalidPlugins) > 0 {
return fmt.Errorf("无效的插件: %s", strings.Join(invalidPlugins, ", "))
}
return nil
}
// 根据类型获取插件列表
func GetPluginsByType(typeName string) []string {
var result []string
for name, plugin := range Common.PluginManager {
if plugin.HasType(typeName) {
result = append(result, name)
}
}
sort.Strings(result)
return result
}

View File

@ -15,18 +15,21 @@ func init() {
Name: "FTP", Name: "FTP",
Ports: []int{21}, Ports: []int{21},
ScanFunc: Plugins.FtpScan, ScanFunc: Plugins.FtpScan,
Types: []string{Common.PluginTypeService},
}) })
Common.RegisterPlugin("ssh", Common.ScanPlugin{ Common.RegisterPlugin("ssh", Common.ScanPlugin{
Name: "SSH", Name: "SSH",
Ports: []int{22, 2222}, Ports: []int{22, 2222},
ScanFunc: Plugins.SshScan, ScanFunc: Plugins.SshScan,
Types: []string{Common.PluginTypeService},
}) })
Common.RegisterPlugin("telnet", Common.ScanPlugin{ Common.RegisterPlugin("telnet", Common.ScanPlugin{
Name: "Telnet", Name: "Telnet",
Ports: []int{23}, Ports: []int{23},
ScanFunc: Plugins.TelnetScan, ScanFunc: Plugins.TelnetScan,
Types: []string{Common.PluginTypeService},
}) })
// Windows网络服务 // Windows网络服务
@ -34,18 +37,21 @@ func init() {
Name: "FindNet", Name: "FindNet",
Ports: []int{135}, Ports: []int{135},
ScanFunc: Plugins.Findnet, ScanFunc: Plugins.Findnet,
Types: []string{Common.PluginTypeService},
}) })
Common.RegisterPlugin("netbios", Common.ScanPlugin{ Common.RegisterPlugin("netbios", Common.ScanPlugin{
Name: "NetBIOS", Name: "NetBIOS",
Ports: []int{139}, Ports: []int{139},
ScanFunc: Plugins.NetBIOS, ScanFunc: Plugins.NetBIOS,
Types: []string{Common.PluginTypeService},
}) })
Common.RegisterPlugin("smb", Common.ScanPlugin{ Common.RegisterPlugin("smb", Common.ScanPlugin{
Name: "SMB", Name: "SMB",
Ports: []int{445}, Ports: []int{445},
ScanFunc: Plugins.SmbScan, ScanFunc: Plugins.SmbScan,
Types: []string{Common.PluginTypeService},
}) })
// 数据库服务 // 数据库服务
@ -53,18 +59,21 @@ func init() {
Name: "MSSQL", Name: "MSSQL",
Ports: []int{1433, 1434}, Ports: []int{1433, 1434},
ScanFunc: Plugins.MssqlScan, ScanFunc: Plugins.MssqlScan,
Types: []string{Common.PluginTypeService},
}) })
Common.RegisterPlugin("oracle", Common.ScanPlugin{ Common.RegisterPlugin("oracle", Common.ScanPlugin{
Name: "Oracle", Name: "Oracle",
Ports: []int{1521, 1522, 1526}, Ports: []int{1521, 1522, 1526},
ScanFunc: Plugins.OracleScan, ScanFunc: Plugins.OracleScan,
Types: []string{Common.PluginTypeService},
}) })
Common.RegisterPlugin("mysql", Common.ScanPlugin{ Common.RegisterPlugin("mysql", Common.ScanPlugin{
Name: "MySQL", Name: "MySQL",
Ports: []int{3306, 3307, 13306, 33306}, Ports: []int{3306, 3307, 13306, 33306},
ScanFunc: Plugins.MysqlScan, ScanFunc: Plugins.MysqlScan,
Types: []string{Common.PluginTypeService},
}) })
// 中间件和消息队列服务 // 中间件和消息队列服务
@ -72,24 +81,28 @@ func init() {
Name: "Elasticsearch", Name: "Elasticsearch",
Ports: []int{9200, 9300}, Ports: []int{9200, 9300},
ScanFunc: Plugins.ElasticScan, ScanFunc: Plugins.ElasticScan,
Types: []string{Common.PluginTypeService},
}) })
Common.RegisterPlugin("rabbitmq", Common.ScanPlugin{ Common.RegisterPlugin("rabbitmq", Common.ScanPlugin{
Name: "RabbitMQ", Name: "RabbitMQ",
Ports: []int{5672, 5671, 15672, 15671}, Ports: []int{5672, 5671, 15672, 15671},
ScanFunc: Plugins.RabbitMQScan, ScanFunc: Plugins.RabbitMQScan,
Types: []string{Common.PluginTypeService},
}) })
Common.RegisterPlugin("kafka", Common.ScanPlugin{ Common.RegisterPlugin("kafka", Common.ScanPlugin{
Name: "Kafka", Name: "Kafka",
Ports: []int{9092, 9093}, Ports: []int{9092, 9093},
ScanFunc: Plugins.KafkaScan, ScanFunc: Plugins.KafkaScan,
Types: []string{Common.PluginTypeService},
}) })
Common.RegisterPlugin("activemq", Common.ScanPlugin{ Common.RegisterPlugin("activemq", Common.ScanPlugin{
Name: "ActiveMQ", Name: "ActiveMQ",
Ports: []int{61613}, Ports: []int{61613},
ScanFunc: Plugins.ActiveMQScan, ScanFunc: Plugins.ActiveMQScan,
Types: []string{Common.PluginTypeService},
}) })
// 目录和认证服务 // 目录和认证服务
@ -97,6 +110,7 @@ func init() {
Name: "LDAP", Name: "LDAP",
Ports: []int{389, 636}, Ports: []int{389, 636},
ScanFunc: Plugins.LDAPScan, ScanFunc: Plugins.LDAPScan,
Types: []string{Common.PluginTypeService},
}) })
// 邮件服务 // 邮件服务
@ -104,18 +118,21 @@ func init() {
Name: "SMTP", Name: "SMTP",
Ports: []int{25, 465, 587}, Ports: []int{25, 465, 587},
ScanFunc: Plugins.SmtpScan, ScanFunc: Plugins.SmtpScan,
Types: []string{Common.PluginTypeService},
}) })
Common.RegisterPlugin("imap", Common.ScanPlugin{ Common.RegisterPlugin("imap", Common.ScanPlugin{
Name: "IMAP", Name: "IMAP",
Ports: []int{143, 993}, Ports: []int{143, 993},
ScanFunc: Plugins.IMAPScan, ScanFunc: Plugins.IMAPScan,
Types: []string{Common.PluginTypeService},
}) })
Common.RegisterPlugin("pop3", Common.ScanPlugin{ Common.RegisterPlugin("pop3", Common.ScanPlugin{
Name: "POP3", Name: "POP3",
Ports: []int{110, 995}, Ports: []int{110, 995},
ScanFunc: Plugins.POP3Scan, ScanFunc: Plugins.POP3Scan,
Types: []string{Common.PluginTypeService},
}) })
// 网络管理和监控服务 // 网络管理和监控服务
@ -123,12 +140,14 @@ func init() {
Name: "SNMP", Name: "SNMP",
Ports: []int{161, 162}, Ports: []int{161, 162},
ScanFunc: Plugins.SNMPScan, ScanFunc: Plugins.SNMPScan,
Types: []string{Common.PluginTypeService},
}) })
Common.RegisterPlugin("modbus", Common.ScanPlugin{ Common.RegisterPlugin("modbus", Common.ScanPlugin{
Name: "Modbus", Name: "Modbus",
Ports: []int{502, 5020}, Ports: []int{502, 5020},
ScanFunc: Plugins.ModbusScan, ScanFunc: Plugins.ModbusScan,
Types: []string{Common.PluginTypeService},
}) })
// 数据同步和备份服务 // 数据同步和备份服务
@ -136,6 +155,7 @@ func init() {
Name: "Rsync", Name: "Rsync",
Ports: []int{873}, Ports: []int{873},
ScanFunc: Plugins.RsyncScan, ScanFunc: Plugins.RsyncScan,
Types: []string{Common.PluginTypeService},
}) })
// NoSQL数据库 // NoSQL数据库
@ -143,12 +163,14 @@ func init() {
Name: "Cassandra", Name: "Cassandra",
Ports: []int{9042}, Ports: []int{9042},
ScanFunc: Plugins.CassandraScan, ScanFunc: Plugins.CassandraScan,
Types: []string{Common.PluginTypeService},
}) })
Common.RegisterPlugin("neo4j", Common.ScanPlugin{ Common.RegisterPlugin("neo4j", Common.ScanPlugin{
Name: "Neo4j", Name: "Neo4j",
Ports: []int{7687}, Ports: []int{7687},
ScanFunc: Plugins.Neo4jScan, ScanFunc: Plugins.Neo4jScan,
Types: []string{Common.PluginTypeService},
}) })
// 远程桌面和显示服务 // 远程桌面和显示服务
@ -156,18 +178,21 @@ func init() {
Name: "RDP", Name: "RDP",
Ports: []int{3389, 13389, 33389}, Ports: []int{3389, 13389, 33389},
ScanFunc: Plugins.RdpScan, ScanFunc: Plugins.RdpScan,
Types: []string{Common.PluginTypeService},
}) })
Common.RegisterPlugin("postgres", Common.ScanPlugin{ Common.RegisterPlugin("postgres", Common.ScanPlugin{
Name: "PostgreSQL", Name: "PostgreSQL",
Ports: []int{5432, 5433}, Ports: []int{5432, 5433},
ScanFunc: Plugins.PostgresScan, ScanFunc: Plugins.PostgresScan,
Types: []string{Common.PluginTypeService},
}) })
Common.RegisterPlugin("vnc", Common.ScanPlugin{ Common.RegisterPlugin("vnc", Common.ScanPlugin{
Name: "VNC", Name: "VNC",
Ports: []int{5900, 5901, 5902}, Ports: []int{5900, 5901, 5902},
ScanFunc: Plugins.VncScan, ScanFunc: Plugins.VncScan,
Types: []string{Common.PluginTypeService},
}) })
// 缓存和键值存储服务 // 缓存和键值存储服务
@ -175,18 +200,21 @@ func init() {
Name: "Redis", Name: "Redis",
Ports: []int{6379, 6380, 16379}, Ports: []int{6379, 6380, 16379},
ScanFunc: Plugins.RedisScan, ScanFunc: Plugins.RedisScan,
Types: []string{Common.PluginTypeService},
}) })
Common.RegisterPlugin("memcached", Common.ScanPlugin{ Common.RegisterPlugin("memcached", Common.ScanPlugin{
Name: "Memcached", Name: "Memcached",
Ports: []int{11211}, Ports: []int{11211},
ScanFunc: Plugins.MemcachedScan, ScanFunc: Plugins.MemcachedScan,
Types: []string{Common.PluginTypeService},
}) })
Common.RegisterPlugin("mongodb", Common.ScanPlugin{ Common.RegisterPlugin("mongodb", Common.ScanPlugin{
Name: "MongoDB", Name: "MongoDB",
Ports: []int{27017, 27018}, Ports: []int{27017, 27018},
ScanFunc: Plugins.MongodbScan, ScanFunc: Plugins.MongodbScan,
Types: []string{Common.PluginTypeService},
}) })
// 2. 特殊漏洞扫描插件 // 2. 特殊漏洞扫描插件
@ -194,12 +222,14 @@ func init() {
Name: "MS17010", Name: "MS17010",
Ports: []int{445}, Ports: []int{445},
ScanFunc: Plugins.MS17010, ScanFunc: Plugins.MS17010,
Types: []string{Common.PluginTypeService},
}) })
Common.RegisterPlugin("smbghost", Common.ScanPlugin{ Common.RegisterPlugin("smbghost", Common.ScanPlugin{
Name: "SMBGhost", Name: "SMBGhost",
Ports: []int{445}, Ports: []int{445},
ScanFunc: Plugins.SmbGhost, ScanFunc: Plugins.SmbGhost,
Types: []string{Common.PluginTypeService},
}) })
// 3. Web应用扫描插件 // 3. Web应用扫描插件
@ -207,12 +237,14 @@ func init() {
Name: "WebTitle", Name: "WebTitle",
Ports: Common.ParsePortsFromString(Common.WebPorts), Ports: Common.ParsePortsFromString(Common.WebPorts),
ScanFunc: Plugins.WebTitle, ScanFunc: Plugins.WebTitle,
Types: []string{Common.PluginTypeWeb},
}) })
Common.RegisterPlugin("webpoc", Common.ScanPlugin{ Common.RegisterPlugin("webpoc", Common.ScanPlugin{
Name: "WebPoc", Name: "WebPoc",
Ports: Common.ParsePortsFromString(Common.WebPorts), Ports: Common.ParsePortsFromString(Common.WebPorts),
ScanFunc: Plugins.WebPoc, ScanFunc: Plugins.WebPoc,
Types: []string{Common.PluginTypeWeb},
}) })
// 4. Windows系统专用插件 // 4. Windows系统专用插件
@ -220,6 +252,7 @@ func init() {
Name: "SMBScan2", Name: "SMBScan2",
Ports: []int{445}, Ports: []int{445},
ScanFunc: Plugins.SmbScan2, ScanFunc: Plugins.SmbScan2,
Types: []string{Common.PluginTypeService},
}) })
// 5. 本地信息收集插件 // 5. 本地信息收集插件
@ -227,33 +260,30 @@ func init() {
Name: "LocalInfo", Name: "LocalInfo",
Ports: []int{}, Ports: []int{},
ScanFunc: Plugins.LocalInfoScan, ScanFunc: Plugins.LocalInfoScan,
Types: []string{Common.PluginTypeLocal},
}) })
Common.RegisterPlugin("dcinfo", Common.ScanPlugin{ Common.RegisterPlugin("dcinfo", Common.ScanPlugin{
Name: "DCInfo", Name: "DCInfo",
Ports: []int{}, Ports: []int{},
ScanFunc: Plugins.DCInfoScan, ScanFunc: Plugins.DCInfoScan,
Types: []string{Common.PluginTypeLocal},
}) })
Common.RegisterPlugin("minidump", Common.ScanPlugin{ Common.RegisterPlugin("minidump", Common.ScanPlugin{
Name: "MiniDump", Name: "MiniDump",
Ports: []int{}, Ports: []int{},
ScanFunc: Plugins.MiniDump, ScanFunc: Plugins.MiniDump,
Types: []string{Common.PluginTypeLocal},
}) })
} }
// GetAllPlugins 返回所有已注册插件的名称列表 // GetAllPlugins 返回所有已注册插件的名称列表
// 当用户未指定特定插件或使用"All"模式时使用
func GetAllPlugins() []string { func GetAllPlugins() []string {
pluginNames := make([]string, 0, len(Common.PluginManager)) pluginNames := make([]string, 0, len(Common.PluginManager))
// 遍历插件管理器,获取所有插件名称
for name := range Common.PluginManager { for name := range Common.PluginManager {
pluginNames = append(pluginNames, name) pluginNames = append(pluginNames, name)
} }
// 对插件名称进行排序,使输出更加一致
sort.Strings(pluginNames) sort.Strings(pluginNames)
return pluginNames return pluginNames
} }

View File

@ -5,7 +5,6 @@ import (
"github.com/schollz/progressbar/v3" "github.com/schollz/progressbar/v3"
"github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/WebScan/lib" "github.com/shadow1ng/fscan/WebScan/lib"
"sort"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -13,31 +12,59 @@ import (
"time" "time"
) )
// 全局状态
var (
LocalScan bool // 本地扫描模式标识
WebScan bool // Web扫描模式标识
)
// ScanTask 表示单个扫描任务 // ScanTask 表示单个扫描任务
type ScanTask struct { type ScanTask struct {
pluginName string // 插件名称 pluginName string // 插件名称
target Common.HostInfo // 目标信息 target Common.HostInfo // 目标信息
} }
// 添加一个本地插件集合,用于识别哪些插件是本地信息收集插件 // ScanStrategy 定义扫描策略接口
var localPlugins = map[string]bool{ type ScanStrategy interface {
"localinfo": true, // 名称和描述
"dcinfo": true, Name() string
"minidump": true, Description() string
// 执行扫描的主要方法
Execute(info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup)
// 插件管理方法
GetPlugins() ([]string, bool)
LogPluginInfo()
// 任务准备方法
PrepareTargets(info Common.HostInfo) []Common.HostInfo
IsPluginApplicable(plugin Common.ScanPlugin, targetPort int, isCustomMode bool) bool
} }
// ----------------------------------------------------------------------------- // Scanner 扫描器结构体
// 主扫描流程 type Scanner struct {
// ----------------------------------------------------------------------------- strategy ScanStrategy
}
// Scan 执行整体扫描流程的入口函数 // NewScanner 创建新的扫描器并选择合适的策略
func Scan(info Common.HostInfo) { func NewScanner(info Common.HostInfo) *Scanner {
scanner := &Scanner{}
scanner.selectStrategy(info)
return scanner
}
// selectStrategy 根据扫描配置选择适当的扫描策略
func (s *Scanner) selectStrategy(info Common.HostInfo) {
switch {
case Common.LocalMode:
s.strategy = NewLocalScanStrategy()
Common.LogInfo("已选择本地扫描模式")
case len(Common.URLs) > 0:
s.strategy = NewWebScanStrategy()
Common.LogInfo("已选择Web扫描模式")
default:
s.strategy = NewServiceScanStrategy()
Common.LogInfo("已选择服务扫描模式")
}
}
// Scan 执行整体扫描流程
func (s *Scanner) Scan(info Common.HostInfo) {
Common.LogInfo("开始信息扫描") Common.LogInfo("开始信息扫描")
lib.Inithttp() lib.Inithttp()
@ -45,30 +72,16 @@ func Scan(info Common.HostInfo) {
ch := make(chan struct{}, Common.ThreadNum) ch := make(chan struct{}, Common.ThreadNum)
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
// 选择并执行扫描模式 // 执行策略
selectScanMode(info, &ch, &wg) s.strategy.Execute(info, &ch, &wg)
// 等待所有扫描完成 // 等待所有扫描完成
wg.Wait() wg.Wait()
finishScan() s.finishScan()
} }
// 根据配置选择扫描模式 // finishScan 完成扫描并输出结果
func selectScanMode(info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { func (s *Scanner) finishScan() {
switch {
case Common.LocalMode:
LocalScan = true
executeLocalScan(info, ch, wg)
case len(Common.URLs) > 0:
WebScan = true
executeWebScan(info, ch, wg)
default:
executeHostScan(info, ch, wg)
}
}
// 完成扫描并输出结果
func finishScan() {
if Common.ProgressBar != nil { if Common.ProgressBar != nil {
Common.ProgressBar.Finish() Common.ProgressBar.Finish()
fmt.Println() fmt.Println()
@ -76,391 +89,13 @@ func finishScan() {
Common.LogSuccess(fmt.Sprintf("扫描已完成: %v/%v", Common.End, Common.Num)) Common.LogSuccess(fmt.Sprintf("扫描已完成: %v/%v", Common.End, Common.Num))
} }
// ----------------------------------------------------------------------------- // 任务执行通用框架
// 三种扫描模式实现 func ExecuteScanTasks(targets []Common.HostInfo, strategy ScanStrategy, ch *chan struct{}, wg *sync.WaitGroup) {
// -----------------------------------------------------------------------------
// 执行本地信息收集
func executeLocalScan(info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
Common.LogInfo("执行本地信息收集")
// 验证插件配置
if err := validateScanPlugins(); err != nil {
Common.LogError(err.Error())
return
}
// 输出插件信息
logPluginInfo()
// 执行扫描任务
executeScanTasks([]Common.HostInfo{info}, ch, wg)
}
// 执行Web扫描
func executeWebScan(info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
Common.LogInfo("开始Web扫描")
// 验证插件配置
if err := validateScanPlugins(); err != nil {
Common.LogError(err.Error())
return
}
// 准备URL目标
targetInfos := prepareURLTargets(info)
// 输出插件信息
logPluginInfo()
// 执行扫描任务
executeScanTasks(targetInfos, ch, wg)
}
// 执行主机扫描
func executeHostScan(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 := Common.ParseIP(info.Host, Common.HostsFile, Common.ExcludeHosts)
if err != nil {
Common.LogError(fmt.Sprintf("解析主机错误: %v", err))
return
}
Common.LogInfo("开始主机扫描")
// 输出插件信息
logPluginInfo()
// 执行主机扫描
performHostScan(hosts, info, ch, wg)
}
// -----------------------------------------------------------------------------
// 主机扫描流程详细实现
// -----------------------------------------------------------------------------
// 执行主机扫描的完整流程
func 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 shouldPerformLivenessCheck(hosts) {
hosts = CheckLive(hosts, Common.UsePing)
Common.LogInfo(fmt.Sprintf("存活主机数量: %d", len(hosts)))
}
// 端口扫描
targetInfos = scanPortsAndPrepareTargets(hosts, info)
}
// 添加URL目标
targetInfos = appendURLTargets(targetInfos, info)
// 执行漏洞扫描
if len(targetInfos) > 0 {
Common.LogInfo("开始漏洞扫描")
executeScanTasks(targetInfos, ch, wg)
}
}
// 判断是否需要执行存活性检测
func shouldPerformLivenessCheck(hosts []string) bool {
return Common.DisablePing == false && len(hosts) > 1
}
// 扫描端口并准备目标信息
func scanPortsAndPrepareTargets(hosts []string, info Common.HostInfo) []Common.HostInfo {
// 扫描存活端口
alivePorts := discoverAlivePorts(hosts)
if len(alivePorts) == 0 {
return nil
}
// 转换为目标信息
return convertToTargetInfos(alivePorts, info)
}
// 发现存活的端口
func discoverAlivePorts(hosts []string) []string {
var alivePorts []string
// 根据扫描模式选择端口扫描方式
if WebScan || len(Common.URLs) > 0 {
alivePorts = NoPortScan(hosts, Common.Ports)
} else if len(hosts) > 0 {
alivePorts = PortScan(hosts, Common.Ports, Common.Timeout)
Common.LogInfo(fmt.Sprintf("存活端口数量: %d", len(alivePorts)))
}
// 合并额外指定的端口
if len(Common.HostPort) > 0 {
alivePorts = append(alivePorts, Common.HostPort...)
alivePorts = Common.RemoveDuplicate(alivePorts)
Common.HostPort = nil
Common.LogInfo(fmt.Sprintf("存活端口数量: %d", len(alivePorts)))
}
return alivePorts
}
// -----------------------------------------------------------------------------
// 插件管理和解析
// -----------------------------------------------------------------------------
// getAllLocalPlugins 返回所有本地插件的名称列表
func getAllLocalPlugins() []string {
var localPluginList []string
for plugin := range localPlugins {
localPluginList = append(localPluginList, plugin)
}
sort.Strings(localPluginList)
return localPluginList
}
// parsePluginList 解析逗号分隔的插件列表
// pluginStr: 逗号分隔的插件字符串,如 "ssh,ftp,telnet"
// 返回: 插件名称的字符串切片
func parsePluginList(pluginStr string) []string {
if pluginStr == "" {
return nil
}
// 按逗号分割并去除每个插件名称两端的空白
plugins := strings.Split(pluginStr, ",")
for i, p := range plugins {
plugins[i] = strings.TrimSpace(p)
}
// 过滤空字符串
var result []string
for _, p := range plugins {
if p != "" {
result = append(result, p)
}
}
return result
}
// validateScanPlugins 验证扫描插件的有效性
// 返回: 错误信息
func validateScanPlugins() error {
// 如果未指定扫描模式或使用All模式则无需验证
if Common.ScanMode == "" || Common.ScanMode == "All" {
return nil
}
// 解析插件列表
plugins := parsePluginList(Common.ScanMode)
if len(plugins) == 0 {
plugins = []string{Common.ScanMode}
}
// 验证每个插件是否有效
var invalidPlugins []string
for _, plugin := range plugins {
if _, exists := Common.PluginManager[plugin]; !exists {
invalidPlugins = append(invalidPlugins, plugin)
}
}
if len(invalidPlugins) > 0 {
return fmt.Errorf("无效的插件: %s", strings.Join(invalidPlugins, ", "))
}
// 如果是本地模式,验证是否包含非本地插件
if Common.LocalMode {
var nonLocalPlugins []string
for _, plugin := range plugins {
if !isLocalPlugin(plugin) {
nonLocalPlugins = append(nonLocalPlugins, plugin)
}
}
if len(nonLocalPlugins) > 0 {
Common.LogInfo(fmt.Sprintf("本地模式下,以下非本地插件将被忽略: %s", strings.Join(nonLocalPlugins, ", ")))
}
}
return nil
}
// isLocalPlugin 判断插件是否为本地信息收集插件
func isLocalPlugin(pluginName string) bool {
return localPlugins[pluginName]
}
// getPluginsToRun 获取要执行的插件列表
// 返回: 插件列表和是否为自定义插件模式
func getPluginsToRun() ([]string, bool) {
// 本地模式处理
if Common.LocalMode {
// 在本地模式下只执行本地插件
// 如果指定了特定插件(单个或多个)
if Common.ScanMode != "" && Common.ScanMode != "All" {
requestedPlugins := parsePluginList(Common.ScanMode)
if len(requestedPlugins) == 0 {
requestedPlugins = []string{Common.ScanMode}
}
// 过滤出本地插件
var localPluginsToRun []string
for _, plugin := range requestedPlugins {
if isLocalPlugin(plugin) {
localPluginsToRun = append(localPluginsToRun, plugin)
}
}
return localPluginsToRun, true
}
// 如果是All模式或未指定则返回所有本地插件
return getAllLocalPlugins(), true
}
// 非本地模式处理(保持原有行为)
// 如果指定了插件列表(逗号分隔)
if Common.ScanMode != "" && Common.ScanMode != "All" {
plugins := parsePluginList(Common.ScanMode)
if len(plugins) > 0 {
return plugins, true
}
return []string{Common.ScanMode}, true
}
// 默认情况:使用所有非本地插件
allPlugins := GetAllPlugins()
filteredPlugins := make([]string, 0, len(allPlugins))
for _, plugin := range allPlugins {
if !isLocalPlugin(plugin) {
filteredPlugins = append(filteredPlugins, plugin)
}
}
return filteredPlugins, false
}
// logPluginInfo 输出插件信息
func logPluginInfo() {
if Common.LocalMode {
if Common.ScanMode == "" || Common.ScanMode == "All" {
Common.LogInfo("本地模式: 使用所有本地信息收集插件")
} else {
plugins := parsePluginList(Common.ScanMode)
if len(plugins) == 0 {
plugins = []string{Common.ScanMode}
}
// 过滤出本地插件
var localPluginsToRun []string
for _, plugin := range plugins {
if isLocalPlugin(plugin) {
localPluginsToRun = append(localPluginsToRun, plugin)
}
}
if len(localPluginsToRun) > 1 {
Common.LogInfo(fmt.Sprintf("本地模式: 使用本地插件: %s", strings.Join(localPluginsToRun, ", ")))
} else if len(localPluginsToRun) == 1 {
Common.LogInfo(fmt.Sprintf("本地模式: 使用本地插件: %s", localPluginsToRun[0]))
} else {
Common.LogInfo("本地模式: 未指定有效的本地插件,将不执行任何扫描")
}
}
return
}
// 非本地模式的原有逻辑
if Common.ScanMode == "" || Common.ScanMode == "All" {
Common.LogInfo("使用所有可用插件(已排除本地敏感插件)")
} else {
plugins := parsePluginList(Common.ScanMode)
if len(plugins) > 1 {
Common.LogInfo(fmt.Sprintf("使用插件: %s", strings.Join(plugins, ", ")))
} else {
Common.LogInfo(fmt.Sprintf("使用插件: %s", Common.ScanMode))
}
}
}
// -----------------------------------------------------------------------------
// 目标准备
// -----------------------------------------------------------------------------
// 准备URL目标列表
func prepareURLTargets(baseInfo Common.HostInfo) []Common.HostInfo {
var targetInfos []Common.HostInfo
for _, url := range Common.URLs {
urlInfo := baseInfo
// 确保URL包含协议头
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
url = "http://" + url
}
urlInfo.Url = url
targetInfos = append(targetInfos, urlInfo)
}
return targetInfos
}
// 将端口列表转换为目标信息
func 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
}
// 添加URL扫描目标
func appendURLTargets(targetInfos []Common.HostInfo, baseInfo Common.HostInfo) []Common.HostInfo {
for _, url := range Common.URLs {
urlInfo := baseInfo
urlInfo.Url = url
targetInfos = append(targetInfos, urlInfo)
}
return targetInfos
}
// -----------------------------------------------------------------------------
// 任务执行
// -----------------------------------------------------------------------------
// 执行扫描任务集合
func executeScanTasks(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
// 获取要执行的插件 // 获取要执行的插件
pluginsToRun, isCustomMode := getPluginsToRun() pluginsToRun, isCustomMode := strategy.GetPlugins()
// 准备扫描任务 // 准备扫描任务
tasks := prepareScanTasks(targets, pluginsToRun, isCustomMode) tasks := prepareScanTasks(targets, pluginsToRun, isCustomMode, strategy)
// 输出扫描计划 // 输出扫描计划
if Common.ShowScanPlan && len(tasks) > 0 { if Common.ShowScanPlan && len(tasks) > 0 {
@ -478,6 +113,35 @@ func executeScanTasks(targets []Common.HostInfo, ch *chan struct{}, wg *sync.Wai
} }
} }
// 准备扫描任务列表
func prepareScanTasks(targets []Common.HostInfo, pluginsToRun []string, isCustomMode bool, strategy ScanStrategy) []ScanTask {
var tasks []ScanTask
for _, target := range targets {
targetPort := 0
if target.Ports != "" {
targetPort, _ = strconv.Atoi(target.Ports)
}
for _, pluginName := range pluginsToRun {
plugin, exists := Common.PluginManager[pluginName]
if !exists {
continue
}
// 检查插件是否适用于当前目标 (通过策略判断)
if strategy.IsPluginApplicable(plugin, targetPort, isCustomMode) {
tasks = append(tasks, ScanTask{
pluginName: pluginName,
target: target,
})
}
}
}
return tasks
}
// logScanPlan 输出扫描计划信息 // logScanPlan 输出扫描计划信息
func logScanPlan(tasks []ScanTask) { func logScanPlan(tasks []ScanTask) {
// 统计每个插件的目标数量 // 统计每个插件的目标数量
@ -497,55 +161,6 @@ func logScanPlan(tasks []ScanTask) {
Common.LogInfo(planInfo.String()) Common.LogInfo(planInfo.String())
} }
// 准备扫描任务列表
func prepareScanTasks(targets []Common.HostInfo, pluginsToRun []string, isCustomMode bool) []ScanTask {
var tasks []ScanTask
for _, target := range targets {
targetPort, _ := strconv.Atoi(target.Ports)
for _, pluginName := range pluginsToRun {
plugin, exists := Common.PluginManager[pluginName]
if !exists {
continue
}
// 检查插件是否适用于当前目标
if isPluginApplicable(plugin, targetPort, isCustomMode, pluginName) {
tasks = append(tasks, ScanTask{
pluginName: pluginName,
target: target,
})
}
}
}
return tasks
}
// isPluginApplicable 判断插件是否适用于目标
func isPluginApplicable(plugin Common.ScanPlugin, targetPort int, isCustomMode bool, pluginName string) bool {
// 本地模式下,只执行本地插件
if LocalScan {
return isLocalPlugin(pluginName)
}
// 非本地模式下,本地插件特殊处理
if isLocalPlugin(pluginName) {
// 只有在自定义模式下明确指定时才执行本地插件
return isCustomMode
}
// 特殊扫描模式下的处理
if WebScan || isCustomMode {
return true
}
// 端口匹配检查
// 无端口限制的插件或端口匹配的插件
return len(plugin.Ports) == 0 || plugin.HasPort(targetPort)
}
// 初始化进度条 // 初始化进度条
func initProgressBar(totalTasks int) { func initProgressBar(totalTasks int) {
Common.ProgressBar = progressbar.NewOptions(totalTasks, Common.ProgressBar = progressbar.NewOptions(totalTasks,
@ -623,3 +238,9 @@ func updateProgress() {
Common.ProgressBar.Add(1) Common.ProgressBar.Add(1)
} }
} }
// 入口函数,向后兼容旧的调用方式
func Scan(info Common.HostInfo) {
scanner := NewScanner(info)
scanner.Scan(info)
}

218
Core/ServiceScanner.go Normal file
View File

@ -0,0 +1,218 @@
package Core
import (
"fmt"
"github.com/shadow1ng/fscan/Common"
"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 := Common.ParseIP(info.Host, Common.HostsFile, Common.ExcludeHosts)
if err != nil {
Common.LogError(fmt.Sprintf("解析主机错误: %v", err))
return
}
Common.LogInfo("开始主机扫描")
// 输出插件信息
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.LogInfo(fmt.Sprintf("存活主机数量: %d", len(hosts)))
}
// 端口扫描
alivePorts := s.discoverAlivePorts(hosts)
if len(alivePorts) > 0 {
targetInfos = s.convertToTargetInfos(alivePorts, info)
}
}
// 执行漏洞扫描
if len(targetInfos) > 0 {
Common.LogInfo("开始漏洞扫描")
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 = PortScan(hosts, Common.Ports, Common.Timeout)
Common.LogInfo(fmt.Sprintf("存活端口数量: %d", len(alivePorts)))
}
// 合并额外指定的端口
if len(Common.HostPort) > 0 {
alivePorts = append(alivePorts, Common.HostPort...)
alivePorts = Common.RemoveDuplicate(alivePorts)
Common.HostPort = nil
Common.LogInfo(fmt.Sprintf("存活端口数量: %d", len(alivePorts)))
}
return alivePorts
}
// PrepareTargets 准备目标信息
func (s *ServiceScanStrategy) PrepareTargets(info Common.HostInfo) []Common.HostInfo {
// 解析目标主机
hosts, err := Common.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.LogInfo(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.LogInfo(fmt.Sprintf("使用服务插件: %s", strings.Join(applicablePlugins, ", ")))
} else {
Common.LogInfo("未找到可用的服务插件")
}
}
// 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)
}

125
Core/WebScanner.go Normal file
View File

@ -0,0 +1,125 @@
package Core
import (
"fmt"
"github.com/shadow1ng/fscan/Common"
"strings"
"sync"
)
// WebScanStrategy Web扫描策略
type WebScanStrategy struct{}
// NewWebScanStrategy 创建新的Web扫描策略
func NewWebScanStrategy() *WebScanStrategy {
return &WebScanStrategy{}
}
// Name 返回策略名称
func (s *WebScanStrategy) Name() string {
return "Web扫描"
}
// Description 返回策略描述
func (s *WebScanStrategy) Description() string {
return "扫描Web应用漏洞和信息"
}
// Execute 执行Web扫描策略
func (s *WebScanStrategy) Execute(info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
Common.LogInfo("开始Web扫描")
// 验证插件配置
if err := validateScanPlugins(); err != nil {
Common.LogError(err.Error())
return
}
// 准备URL目标
targets := s.PrepareTargets(info)
// 输出插件信息
s.LogPluginInfo()
// 执行扫描任务
ExecuteScanTasks(targets, s, ch, wg)
}
// PrepareTargets 准备URL目标列表
func (s *WebScanStrategy) PrepareTargets(baseInfo Common.HostInfo) []Common.HostInfo {
var targetInfos []Common.HostInfo
for _, url := range Common.URLs {
urlInfo := baseInfo
// 确保URL包含协议头
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
url = "http://" + url
}
urlInfo.Url = url
targetInfos = append(targetInfos, urlInfo)
}
return targetInfos
}
// GetPlugins 获取Web扫描插件列表
func (s *WebScanStrategy) GetPlugins() ([]string, bool) {
// 如果指定了自定义插件并且不是"all"
if Common.ScanMode != "" && Common.ScanMode != "all" {
requestedPlugins := parsePluginList(Common.ScanMode)
if len(requestedPlugins) == 0 {
requestedPlugins = []string{Common.ScanMode}
}
// 验证插件是否存在不做Web类型过滤
var validPlugins []string
for _, name := range requestedPlugins {
if _, exists := Common.PluginManager[name]; exists {
validPlugins = append(validPlugins, name)
}
}
if len(validPlugins) > 0 {
return validPlugins, true
}
}
// 未指定或使用"all"获取所有插件由IsPluginApplicable做类型过滤
return GetAllPlugins(), false
}
// LogPluginInfo 输出Web扫描插件信息
func (s *WebScanStrategy) LogPluginInfo() {
allPlugins, isCustomMode := s.GetPlugins()
// 如果是自定义模式,直接显示用户指定的插件
if isCustomMode {
Common.LogInfo(fmt.Sprintf("Web扫描模式: 使用指定插件: %s", strings.Join(allPlugins, ", ")))
return
}
// 在自动模式下只显示Web类型的插件
var applicablePlugins []string
for _, pluginName := range allPlugins {
plugin, exists := Common.PluginManager[pluginName]
if exists && plugin.HasType(Common.PluginTypeWeb) {
applicablePlugins = append(applicablePlugins, pluginName)
}
}
if len(applicablePlugins) > 0 {
Common.LogInfo(fmt.Sprintf("Web扫描模式: 使用Web插件: %s", strings.Join(applicablePlugins, ", ")))
} else {
Common.LogInfo("Web扫描模式: 未找到可用的Web插件")
}
}
// IsPluginApplicable 判断插件是否适用于Web扫描
func (s *WebScanStrategy) IsPluginApplicable(plugin Common.ScanPlugin, targetPort int, isCustomMode bool) bool {
// 自定义模式下运行所有明确指定的插件
if isCustomMode {
return true
}
// 非自定义模式下只运行Web类型插件
return plugin.HasType(Common.PluginTypeWeb)
}