mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-07-13 12:52:44 +08:00
fix: 修复扫描逻辑
This commit is contained in:
parent
a8bd8ca508
commit
e58a48ba9b
@ -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.Int64Var(&Timeout, "time", 3, GetText("flag_timeout"))
|
||||
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(&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.Int64Var(&Timeout, "time", 3, GetText("flag_timeout"))
|
||||
fs.IntVar(&ModuleThreadNum, "mt", 10, GetText("flag_module_thread_num"))
|
||||
|
@ -8,13 +8,32 @@ type HostInfo struct {
|
||||
Infostr []string
|
||||
}
|
||||
|
||||
// 在 Common/const.go 中添加
|
||||
// 插件类型常量
|
||||
const (
|
||||
PluginTypeService = "service" // 服务类型插件
|
||||
PluginTypeWeb = "web" // Web类型插件
|
||||
PluginTypeLocal = "local" // 本地类型插件
|
||||
)
|
||||
|
||||
// ScanPlugin 定义扫描插件的结构
|
||||
type ScanPlugin struct {
|
||||
Name string // 插件名称
|
||||
Ports []int // 关联的端口列表,空切片表示特殊扫描类型
|
||||
Ports []int // 适用端口
|
||||
Types []string // 插件类型标签,一个插件可以有多个类型
|
||||
ScanFunc func(*HostInfo) error // 扫描函数
|
||||
}
|
||||
|
||||
// 添加一个用于检查插件类型的辅助方法
|
||||
func (p ScanPlugin) HasType(typeName string) bool {
|
||||
for _, t := range p.Types {
|
||||
if t == typeName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HasPort 检查插件是否支持指定端口
|
||||
func (p *ScanPlugin) HasPort(port int) bool {
|
||||
// 如果没有指定端口列表,表示支持所有端口
|
||||
|
112
Core/LocalScanner.go
Normal file
112
Core/LocalScanner.go
Normal 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
71
Core/PluginUtils.go
Normal 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
|
||||
}
|
@ -15,18 +15,21 @@ func init() {
|
||||
Name: "FTP",
|
||||
Ports: []int{21},
|
||||
ScanFunc: Plugins.FtpScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("ssh", Common.ScanPlugin{
|
||||
Name: "SSH",
|
||||
Ports: []int{22, 2222},
|
||||
ScanFunc: Plugins.SshScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("telnet", Common.ScanPlugin{
|
||||
Name: "Telnet",
|
||||
Ports: []int{23},
|
||||
ScanFunc: Plugins.TelnetScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
// Windows网络服务
|
||||
@ -34,18 +37,21 @@ func init() {
|
||||
Name: "FindNet",
|
||||
Ports: []int{135},
|
||||
ScanFunc: Plugins.Findnet,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("netbios", Common.ScanPlugin{
|
||||
Name: "NetBIOS",
|
||||
Ports: []int{139},
|
||||
ScanFunc: Plugins.NetBIOS,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("smb", Common.ScanPlugin{
|
||||
Name: "SMB",
|
||||
Ports: []int{445},
|
||||
ScanFunc: Plugins.SmbScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
// 数据库服务
|
||||
@ -53,18 +59,21 @@ func init() {
|
||||
Name: "MSSQL",
|
||||
Ports: []int{1433, 1434},
|
||||
ScanFunc: Plugins.MssqlScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("oracle", Common.ScanPlugin{
|
||||
Name: "Oracle",
|
||||
Ports: []int{1521, 1522, 1526},
|
||||
ScanFunc: Plugins.OracleScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("mysql", Common.ScanPlugin{
|
||||
Name: "MySQL",
|
||||
Ports: []int{3306, 3307, 13306, 33306},
|
||||
ScanFunc: Plugins.MysqlScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
// 中间件和消息队列服务
|
||||
@ -72,24 +81,28 @@ func init() {
|
||||
Name: "Elasticsearch",
|
||||
Ports: []int{9200, 9300},
|
||||
ScanFunc: Plugins.ElasticScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("rabbitmq", Common.ScanPlugin{
|
||||
Name: "RabbitMQ",
|
||||
Ports: []int{5672, 5671, 15672, 15671},
|
||||
ScanFunc: Plugins.RabbitMQScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("kafka", Common.ScanPlugin{
|
||||
Name: "Kafka",
|
||||
Ports: []int{9092, 9093},
|
||||
ScanFunc: Plugins.KafkaScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("activemq", Common.ScanPlugin{
|
||||
Name: "ActiveMQ",
|
||||
Ports: []int{61613},
|
||||
ScanFunc: Plugins.ActiveMQScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
// 目录和认证服务
|
||||
@ -97,6 +110,7 @@ func init() {
|
||||
Name: "LDAP",
|
||||
Ports: []int{389, 636},
|
||||
ScanFunc: Plugins.LDAPScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
// 邮件服务
|
||||
@ -104,18 +118,21 @@ func init() {
|
||||
Name: "SMTP",
|
||||
Ports: []int{25, 465, 587},
|
||||
ScanFunc: Plugins.SmtpScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("imap", Common.ScanPlugin{
|
||||
Name: "IMAP",
|
||||
Ports: []int{143, 993},
|
||||
ScanFunc: Plugins.IMAPScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("pop3", Common.ScanPlugin{
|
||||
Name: "POP3",
|
||||
Ports: []int{110, 995},
|
||||
ScanFunc: Plugins.POP3Scan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
// 网络管理和监控服务
|
||||
@ -123,12 +140,14 @@ func init() {
|
||||
Name: "SNMP",
|
||||
Ports: []int{161, 162},
|
||||
ScanFunc: Plugins.SNMPScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("modbus", Common.ScanPlugin{
|
||||
Name: "Modbus",
|
||||
Ports: []int{502, 5020},
|
||||
ScanFunc: Plugins.ModbusScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
// 数据同步和备份服务
|
||||
@ -136,6 +155,7 @@ func init() {
|
||||
Name: "Rsync",
|
||||
Ports: []int{873},
|
||||
ScanFunc: Plugins.RsyncScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
// NoSQL数据库
|
||||
@ -143,12 +163,14 @@ func init() {
|
||||
Name: "Cassandra",
|
||||
Ports: []int{9042},
|
||||
ScanFunc: Plugins.CassandraScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("neo4j", Common.ScanPlugin{
|
||||
Name: "Neo4j",
|
||||
Ports: []int{7687},
|
||||
ScanFunc: Plugins.Neo4jScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
// 远程桌面和显示服务
|
||||
@ -156,18 +178,21 @@ func init() {
|
||||
Name: "RDP",
|
||||
Ports: []int{3389, 13389, 33389},
|
||||
ScanFunc: Plugins.RdpScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("postgres", Common.ScanPlugin{
|
||||
Name: "PostgreSQL",
|
||||
Ports: []int{5432, 5433},
|
||||
ScanFunc: Plugins.PostgresScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("vnc", Common.ScanPlugin{
|
||||
Name: "VNC",
|
||||
Ports: []int{5900, 5901, 5902},
|
||||
ScanFunc: Plugins.VncScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
// 缓存和键值存储服务
|
||||
@ -175,18 +200,21 @@ func init() {
|
||||
Name: "Redis",
|
||||
Ports: []int{6379, 6380, 16379},
|
||||
ScanFunc: Plugins.RedisScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("memcached", Common.ScanPlugin{
|
||||
Name: "Memcached",
|
||||
Ports: []int{11211},
|
||||
ScanFunc: Plugins.MemcachedScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("mongodb", Common.ScanPlugin{
|
||||
Name: "MongoDB",
|
||||
Ports: []int{27017, 27018},
|
||||
ScanFunc: Plugins.MongodbScan,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
// 2. 特殊漏洞扫描插件
|
||||
@ -194,12 +222,14 @@ func init() {
|
||||
Name: "MS17010",
|
||||
Ports: []int{445},
|
||||
ScanFunc: Plugins.MS17010,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("smbghost", Common.ScanPlugin{
|
||||
Name: "SMBGhost",
|
||||
Ports: []int{445},
|
||||
ScanFunc: Plugins.SmbGhost,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
// 3. Web应用扫描插件
|
||||
@ -207,12 +237,14 @@ func init() {
|
||||
Name: "WebTitle",
|
||||
Ports: Common.ParsePortsFromString(Common.WebPorts),
|
||||
ScanFunc: Plugins.WebTitle,
|
||||
Types: []string{Common.PluginTypeWeb},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("webpoc", Common.ScanPlugin{
|
||||
Name: "WebPoc",
|
||||
Ports: Common.ParsePortsFromString(Common.WebPorts),
|
||||
ScanFunc: Plugins.WebPoc,
|
||||
Types: []string{Common.PluginTypeWeb},
|
||||
})
|
||||
|
||||
// 4. Windows系统专用插件
|
||||
@ -220,6 +252,7 @@ func init() {
|
||||
Name: "SMBScan2",
|
||||
Ports: []int{445},
|
||||
ScanFunc: Plugins.SmbScan2,
|
||||
Types: []string{Common.PluginTypeService},
|
||||
})
|
||||
|
||||
// 5. 本地信息收集插件
|
||||
@ -227,33 +260,30 @@ func init() {
|
||||
Name: "LocalInfo",
|
||||
Ports: []int{},
|
||||
ScanFunc: Plugins.LocalInfoScan,
|
||||
Types: []string{Common.PluginTypeLocal},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("dcinfo", Common.ScanPlugin{
|
||||
Name: "DCInfo",
|
||||
Ports: []int{},
|
||||
ScanFunc: Plugins.DCInfoScan,
|
||||
Types: []string{Common.PluginTypeLocal},
|
||||
})
|
||||
|
||||
Common.RegisterPlugin("minidump", Common.ScanPlugin{
|
||||
Name: "MiniDump",
|
||||
Ports: []int{},
|
||||
ScanFunc: Plugins.MiniDump,
|
||||
Types: []string{Common.PluginTypeLocal},
|
||||
})
|
||||
}
|
||||
|
||||
// GetAllPlugins 返回所有已注册插件的名称列表
|
||||
// 当用户未指定特定插件或使用"All"模式时使用
|
||||
func GetAllPlugins() []string {
|
||||
pluginNames := make([]string, 0, len(Common.PluginManager))
|
||||
|
||||
// 遍历插件管理器,获取所有插件名称
|
||||
for name := range Common.PluginManager {
|
||||
pluginNames = append(pluginNames, name)
|
||||
}
|
||||
|
||||
// 对插件名称进行排序,使输出更加一致
|
||||
sort.Strings(pluginNames)
|
||||
|
||||
return pluginNames
|
||||
}
|
||||
|
555
Core/Scanner.go
555
Core/Scanner.go
@ -5,7 +5,6 @@ import (
|
||||
"github.com/schollz/progressbar/v3"
|
||||
"github.com/shadow1ng/fscan/Common"
|
||||
"github.com/shadow1ng/fscan/WebScan/lib"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -13,31 +12,59 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// 全局状态
|
||||
var (
|
||||
LocalScan bool // 本地扫描模式标识
|
||||
WebScan bool // Web扫描模式标识
|
||||
)
|
||||
|
||||
// ScanTask 表示单个扫描任务
|
||||
type ScanTask struct {
|
||||
pluginName string // 插件名称
|
||||
target Common.HostInfo // 目标信息
|
||||
}
|
||||
|
||||
// 添加一个本地插件集合,用于识别哪些插件是本地信息收集插件
|
||||
var localPlugins = map[string]bool{
|
||||
"localinfo": true,
|
||||
"dcinfo": true,
|
||||
"minidump": true,
|
||||
// ScanStrategy 定义扫描策略接口
|
||||
type ScanStrategy interface {
|
||||
// 名称和描述
|
||||
Name() string
|
||||
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 执行整体扫描流程的入口函数
|
||||
func Scan(info Common.HostInfo) {
|
||||
// NewScanner 创建新的扫描器并选择合适的策略
|
||||
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("开始信息扫描")
|
||||
lib.Inithttp()
|
||||
|
||||
@ -45,30 +72,16 @@ func Scan(info Common.HostInfo) {
|
||||
ch := make(chan struct{}, Common.ThreadNum)
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
// 选择并执行扫描模式
|
||||
selectScanMode(info, &ch, &wg)
|
||||
// 执行策略
|
||||
s.strategy.Execute(info, &ch, &wg)
|
||||
|
||||
// 等待所有扫描完成
|
||||
wg.Wait()
|
||||
finishScan()
|
||||
s.finishScan()
|
||||
}
|
||||
|
||||
// 根据配置选择扫描模式
|
||||
func selectScanMode(info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
|
||||
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() {
|
||||
// finishScan 完成扫描并输出结果
|
||||
func (s *Scanner) finishScan() {
|
||||
if Common.ProgressBar != nil {
|
||||
Common.ProgressBar.Finish()
|
||||
fmt.Println()
|
||||
@ -76,391 +89,13 @@ func finishScan() {
|
||||
Common.LogSuccess(fmt.Sprintf("扫描已完成: %v/%v", Common.End, Common.Num))
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// 三种扫描模式实现
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// 执行本地信息收集
|
||||
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) {
|
||||
// 任务执行通用框架
|
||||
func ExecuteScanTasks(targets []Common.HostInfo, strategy ScanStrategy, 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 {
|
||||
@ -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 输出扫描计划信息
|
||||
func logScanPlan(tasks []ScanTask) {
|
||||
// 统计每个插件的目标数量
|
||||
@ -497,55 +161,6 @@ func logScanPlan(tasks []ScanTask) {
|
||||
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) {
|
||||
Common.ProgressBar = progressbar.NewOptions(totalTasks,
|
||||
@ -623,3 +238,9 @@ func updateProgress() {
|
||||
Common.ProgressBar.Add(1)
|
||||
}
|
||||
}
|
||||
|
||||
// 入口函数,向后兼容旧的调用方式
|
||||
func Scan(info Common.HostInfo) {
|
||||
scanner := NewScanner(info)
|
||||
scanner.Scan(info)
|
||||
}
|
||||
|
218
Core/ServiceScanner.go
Normal file
218
Core/ServiceScanner.go
Normal 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
125
Core/WebScanner.go
Normal 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)
|
||||
}
|
Loading…
Reference in New Issue
Block a user