package core import ( "fmt" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/plugins" "strings" ) // PluginFilterType 插件过滤类型 type PluginFilterType int const ( FilterNone PluginFilterType = iota // 不过滤 FilterLocal // 仅本地插件 FilterService // 仅服务插件(排除本地) FilterWeb // 仅Web插件 ) // BaseScanStrategy 扫描策略基础类 type BaseScanStrategy struct { strategyName string filterType PluginFilterType } // NewBaseScanStrategy 创建基础扫描策略 func NewBaseScanStrategy(name string, filterType PluginFilterType) *BaseScanStrategy { return &BaseScanStrategy{ strategyName: name, filterType: filterType, } } // GetPlugins 获取插件列表(简化版) func (b *BaseScanStrategy) GetPlugins() ([]string, bool) { // 如果指定了特定插件且不是"all" if common.ScanMode != "" && common.ScanMode != "all" { requestedPlugins := parsePluginList(common.ScanMode) if len(requestedPlugins) == 0 { requestedPlugins = []string{common.ScanMode} } // 验证插件是否存在 var validPlugins []string for _, name := range requestedPlugins { if b.pluginExists(name) { validPlugins = append(validPlugins, name) } } return validPlugins, true } // 未指定或使用"all":根据策略类型获取对应插件 return b.getPluginsByFilterType(), false } // IsPluginApplicable 判断插件是否适用(传统接口兼容) func (b *BaseScanStrategy) IsPluginApplicable(plugin common.ScanPlugin, targetPort int, isCustomMode bool) bool { // 自定义模式下运行所有明确指定的插件 if isCustomMode { return true } // 检查插件类型过滤 switch b.filterType { case FilterLocal: return plugin.HasType(common.PluginTypeLocal) case FilterService: // 服务扫描排除本地插件,允许服务和Web插件 return !plugin.HasType(common.PluginTypeLocal) case FilterWeb: return plugin.HasType(common.PluginTypeWeb) default: return true } } // IsPluginApplicableByName 根据插件名称判断是否适用(新接口) func (b *BaseScanStrategy) IsPluginApplicableByName(pluginName string, targetHost string, targetPort int, isCustomMode bool) bool { // 首先检查插件是否存在,但不创建实例 if !b.pluginExists(pluginName) { return false } // 自定义模式下强制运行所有明确指定的插件(n*m调用) if isCustomMode { return true } // 应用过滤器类型检查,确保与传统接口逻辑一致 switch b.filterType { case FilterLocal: // 本地扫描策略:只允许本地插件且必须通过-local参数明确指定 if b.isLocalPlugin(pluginName) { result := b.isLocalPluginExplicitlySpecified(pluginName) common.LogDebug(fmt.Sprintf("本地插件 %s 检查结果: %v (LocalPlugin='%s')", pluginName, result, common.LocalPlugin)) return result } return false case FilterService: // 服务扫描策略:排除本地插件,本地插件不应在端口扫描中被调用 if b.isLocalPlugin(pluginName) { common.LogDebug(fmt.Sprintf("本地插件 %s 被服务扫描策略过滤(本地插件不应与端口关联)", pluginName)) return false } case FilterWeb: // Web扫描策略:只允许Web插件 if !b.isWebPlugin(pluginName) { return false } default: // 无过滤器:允许所有插件类型 if b.isLocalPlugin(pluginName) { result := b.isLocalPluginExplicitlySpecified(pluginName) common.LogDebug(fmt.Sprintf("本地插件 %s 检查结果: %v (LocalPlugin='%s')", pluginName, result, common.LocalPlugin)) return result } } // 检查插件端口匹配(特殊端口自动调用具体插件) pluginPorts := b.getPluginPorts(pluginName) // Web插件特殊处理:仅在用户未指定端口范围时才进行智能检测 // 如果用户明确指定了端口(如 -p 3306),则只对该端口进行检测,不使用预定义端口列表 if len(pluginPorts) == 0 && b.isWebPlugin(pluginName) { // 检查用户是否指定了具体端口范围 if common.Ports != "" && common.Ports != "all" { // 用户指定了端口,进行实际HTTP协议检测而不是预定义端口匹配 return b.isWebServicePortByProtocol(targetHost, targetPort) } else { // 用户未指定端口,使用智能检测(包含预定义端口) return b.isWebServicePort(targetHost, targetPort) } } // 无端口限制的其他插件适用于所有端口 if len(pluginPorts) == 0 { return true } // 有端口限制的插件:检查端口匹配 if targetPort > 0 { for _, port := range pluginPorts { if port == targetPort { return true } } } return false } // pluginExists 检查插件是否存在,不创建实例 func (b *BaseScanStrategy) pluginExists(pluginName string) bool { // 使用统一插件系统的Exists方法,避免创建实例和遍历 return plugins.Exists(pluginName) } // getPluginPorts 获取插件端口列表 func (b *BaseScanStrategy) getPluginPorts(pluginName string) []int { // 使用统一插件系统获取端口信息 return plugins.GetPluginPorts(pluginName) } // isWebPlugin 判断是否为Web插件 func (b *BaseScanStrategy) isWebPlugin(pluginName string) bool { // 已知的Web插件列表 webPlugins := []string{"webtitle", "webpoc"} for _, webPlugin := range webPlugins { if pluginName == webPlugin { return true } } return false } // isLocalPlugin 判断是否为本地插件 func (b *BaseScanStrategy) isLocalPlugin(pluginName string) bool { // 已知的本地插件列表(从RegisterLocalPlugin调用中获取) localPlugins := []string{ "avdetect", "crontask", "cleaner", "dcinfo", "envinfo", "forwardshell", "minidump", "socks5proxy", "shellenv", "downloader", "reverseshell", "systemdservice", "fileinfo", "keylogger", "winwmi", "winstartup", "winschtask", "systeminfo", "winregistry", "ldpreload", "winservice", } for _, localPlugin := range localPlugins { if pluginName == localPlugin { return true } } return false } // isWebServicePort 使用智能检测判断端口是否运行Web服务(含预定义端口) func (b *BaseScanStrategy) isWebServicePort(host string, port int) bool { // 创建Web端口检测器实例 detector := NewWebPortDetector() return detector.IsWebService(host, port) } // isWebServicePortByProtocol 仅通过HTTP协议检测判断端口是否运行Web服务(不使用预定义端口列表) func (b *BaseScanStrategy) isWebServicePortByProtocol(host string, port int) bool { // 创建Web端口检测器实例 detector := NewWebPortDetector() // 直接调用协议检测,跳过预定义端口检查 return detector.DetectHTTPServiceOnly(host, port) } // isLocalPluginExplicitlySpecified 检查本地插件是否明确通过-local参数指定 func (b *BaseScanStrategy) isLocalPluginExplicitlySpecified(pluginName string) bool { // 只有通过-local参数明确指定的单个插件才能调用 return common.LocalPlugin == pluginName } // LogPluginInfo 输出插件信息(简化版,将被各Strategy重写) func (b *BaseScanStrategy) LogPluginInfo() { // 基础实现:显示所有插件(无端口过滤) // 各个具体Strategy应该重写这个方法以提供更精确的显示 allPlugins, isCustomMode := b.GetPlugins() var prefix string switch b.filterType { case FilterLocal: prefix = "本地插件" case FilterService: prefix = "服务插件" case FilterWeb: prefix = "Web插件" default: prefix = "插件" } if len(allPlugins) > 0 { if isCustomMode { common.LogBase(fmt.Sprintf("%s: 自定义指定 (%s)", prefix, strings.Join(allPlugins, ", "))) } else { common.LogBase(fmt.Sprintf("%s: %s", prefix, strings.Join(allPlugins, ", "))) } } else { common.LogBase(fmt.Sprintf("%s: 无可用插件", prefix)) } } // LogPluginInfoWithPort 带端口信息的插件显示(供子类使用) func (b *BaseScanStrategy) LogPluginInfoWithPort(targetPort int) { allPlugins, isCustomMode := b.GetPlugins() var prefix string switch b.filterType { case FilterLocal: prefix = "本地插件" case FilterService: prefix = "服务插件" case FilterWeb: prefix = "Web插件" default: prefix = "插件" } // 过滤适用的插件 var applicablePlugins []string for _, pluginName := range allPlugins { if b.pluginExists(pluginName) { if b.IsPluginApplicableByName(pluginName, "127.0.0.1", targetPort, isCustomMode) { applicablePlugins = append(applicablePlugins, pluginName) } } } if len(applicablePlugins) > 0 { if isCustomMode { common.LogBase(fmt.Sprintf("%s: 自定义指定 (%s)", prefix, strings.Join(applicablePlugins, ", "))) } else { common.LogBase(fmt.Sprintf("%s: %s", prefix, strings.Join(applicablePlugins, ", "))) } } else { common.LogBase(fmt.Sprintf("%s: 无可用插件", prefix)) } } // ValidateConfiguration 验证扫描配置 func (b *BaseScanStrategy) ValidateConfiguration() error { return nil } // LogScanStart 输出扫描开始信息 func (b *BaseScanStrategy) LogScanStart() { switch b.filterType { case FilterLocal: common.LogBase("开始本地扫描") case FilterService: common.LogBase("开始服务扫描") case FilterWeb: common.LogBase("开始Web扫描") default: common.LogBase("开始扫描") } } // getPluginsByFilterType 根据过滤器类型获取插件列表 func (b *BaseScanStrategy) getPluginsByFilterType() []string { allPlugins := plugins.All() var filteredPlugins []string switch b.filterType { case FilterLocal: // 本地扫描策略:只返回本地插件 for _, pluginName := range allPlugins { if b.isLocalPlugin(pluginName) { filteredPlugins = append(filteredPlugins, pluginName) } } case FilterService: // 服务扫描策略:排除本地插件和纯Web插件,保留服务插件 for _, pluginName := range allPlugins { if !b.isLocalPlugin(pluginName) { filteredPlugins = append(filteredPlugins, pluginName) } } case FilterWeb: // Web扫描策略:只返回Web插件 for _, pluginName := range allPlugins { if b.isWebPlugin(pluginName) { filteredPlugins = append(filteredPlugins, pluginName) } } default: // 无过滤器:返回所有插件 filteredPlugins = allPlugins } return filteredPlugins } // parsePluginList 解析插件列表字符串 func parsePluginList(pluginStr string) []string { if pluginStr == "" { return []string{} } // 支持逗号分隔的插件列表 plugins := strings.Split(pluginStr, ",") var result []string for _, plugin := range plugins { plugin = strings.TrimSpace(plugin) if plugin != "" { result = append(result, plugin) } } return result }