mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00
feat: 实现Web插件智能检测,增强服务扫描模式
当服务扫描模式检测到常见Web端口时,自动包含WebPoc和WebTitle插件, 提升Web服务扫描覆盖率的同时保持完全向后兼容性。 主要改进: - 新增WebPortDetector智能检测Web服务 - 服务扫描模式自动包含Web插件当检测到Web端口时 - 创建WebPoc和WebTitle插件适配器 - 保持所有现有功能和参数行为不变
This commit is contained in:
parent
798d4e211a
commit
143801ff58
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/shadow1ng/fscan/common"
|
||||
"github.com/shadow1ng/fscan/common/i18n"
|
||||
"github.com/shadow1ng/fscan/plugins/base"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -131,6 +132,11 @@ func (b *BaseScanStrategy) IsPluginApplicableByName(pluginName string, targetPor
|
||||
return false
|
||||
}
|
||||
|
||||
// 智能Web插件检测:如果是Web插件且检测到Web服务,则包含Web插件
|
||||
if b.shouldIncludeWebPlugin(metadata, targetPort) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查类型匹配
|
||||
if !b.isPluginTypeMatchedByName(pluginName) {
|
||||
return false
|
||||
@ -149,6 +155,35 @@ func (b *BaseScanStrategy) IsPluginApplicableByName(pluginName string, targetPor
|
||||
return true
|
||||
}
|
||||
|
||||
// shouldIncludeWebPlugin 判断是否应该包含Web插件(智能检测)
|
||||
func (b *BaseScanStrategy) shouldIncludeWebPlugin(metadata *base.PluginMetadata, targetPort int) bool {
|
||||
// 只对服务扫描策略启用Web插件智能检测
|
||||
if b.filterType != FilterService {
|
||||
return false
|
||||
}
|
||||
|
||||
// 只对Web类别的插件进行检测
|
||||
if metadata.Category != "web" {
|
||||
return false
|
||||
}
|
||||
|
||||
// 如果没有指定端口,跳过检测
|
||||
if targetPort <= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 获取Web检测器实例(延迟初始化)
|
||||
if globalWebDetector == nil {
|
||||
globalWebDetector = NewWebPortDetector()
|
||||
}
|
||||
|
||||
// 检测是否为Web服务(这里暂时只检查常见端口,避免每次都进行HTTP探测)
|
||||
return globalWebDetector.IsCommonWebPort(targetPort)
|
||||
}
|
||||
|
||||
// globalWebDetector Web检测器全局实例
|
||||
var globalWebDetector *WebPortDetector
|
||||
|
||||
// IsPluginApplicable 判断插件是否适用(通用实现)
|
||||
func (b *BaseScanStrategy) IsPluginApplicable(plugin common.ScanPlugin, targetPort int, isCustomMode bool) bool {
|
||||
// 自定义模式下运行所有明确指定的插件
|
||||
|
145
Core/WebDetection.go
Normal file
145
Core/WebDetection.go
Normal file
@ -0,0 +1,145 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// WebPortDetector Web端口检测器
|
||||
type WebPortDetector struct {
|
||||
// 常见Web端口列表
|
||||
commonWebPorts map[int]bool
|
||||
// HTTP检测超时时间
|
||||
httpTimeout time.Duration
|
||||
}
|
||||
|
||||
// NewWebPortDetector 创建Web端口检测器
|
||||
func NewWebPortDetector() *WebPortDetector {
|
||||
// 定义常见Web端口
|
||||
commonPorts := map[int]bool{
|
||||
80: true, // HTTP
|
||||
443: true, // HTTPS
|
||||
8080: true, // HTTP alternate
|
||||
8443: true, // HTTPS alternate
|
||||
8000: true, // Development server
|
||||
8888: true, // Common dev port
|
||||
9000: true, // Common dev port
|
||||
9090: true, // Common dev port
|
||||
3000: true, // Node.js dev server
|
||||
4000: true, // Ruby dev server
|
||||
5000: true, // Python dev server
|
||||
8081: true, // HTTP alternate
|
||||
8082: true, // HTTP alternate
|
||||
8083: true, // HTTP alternate
|
||||
8084: true, // HTTP alternate
|
||||
8085: true, // HTTP alternate
|
||||
8086: true, // HTTP alternate
|
||||
8087: true, // HTTP alternate
|
||||
8088: true, // HTTP alternate
|
||||
8089: true, // HTTP alternate
|
||||
}
|
||||
|
||||
return &WebPortDetector{
|
||||
commonWebPorts: commonPorts,
|
||||
httpTimeout: 2 * time.Second, // 2秒超时,快速检测
|
||||
}
|
||||
}
|
||||
|
||||
// IsWebService 检测指定主机端口是否为Web服务
|
||||
func (w *WebPortDetector) IsWebService(host string, port int) bool {
|
||||
// 1. 首先检查是否为常见Web端口
|
||||
if w.commonWebPorts[port] {
|
||||
return true
|
||||
}
|
||||
|
||||
// 2. 对于非常见端口,进行HTTP协议检测
|
||||
return w.detectHTTPService(host, port)
|
||||
}
|
||||
|
||||
// IsCommonWebPort 检查是否为常见Web端口
|
||||
func (w *WebPortDetector) IsCommonWebPort(port int) bool {
|
||||
return w.commonWebPorts[port]
|
||||
}
|
||||
|
||||
// detectHTTPService 检测HTTP服务(轻量级探测)
|
||||
func (w *WebPortDetector) detectHTTPService(host string, port int) bool {
|
||||
// 创建检测上下文,避免长时间阻塞
|
||||
ctx, cancel := context.WithTimeout(context.Background(), w.httpTimeout)
|
||||
defer cancel()
|
||||
|
||||
// 尝试HTTP连接
|
||||
if w.tryHTTPConnection(ctx, host, port, "http") {
|
||||
return true
|
||||
}
|
||||
|
||||
// 尝试HTTPS连接(对于高端口常见)
|
||||
if w.tryHTTPConnection(ctx, host, port, "https") {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// tryHTTPConnection 尝试HTTP连接
|
||||
func (w *WebPortDetector) tryHTTPConnection(ctx context.Context, host string, port int, protocol string) bool {
|
||||
url := ""
|
||||
if port == 80 && protocol == "http" {
|
||||
url = "http://" + host
|
||||
} else if port == 443 && protocol == "https" {
|
||||
url = "https://" + host
|
||||
} else {
|
||||
url = protocol + "://" + host + ":" + string(rune(port))
|
||||
}
|
||||
|
||||
// 创建HTTP客户端,快速检测
|
||||
client := &http.Client{
|
||||
Timeout: w.httpTimeout,
|
||||
Transport: &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: w.httpTimeout,
|
||||
}).DialContext,
|
||||
TLSHandshakeTimeout: w.httpTimeout,
|
||||
ResponseHeaderTimeout: w.httpTimeout,
|
||||
DisableKeepAlives: true,
|
||||
},
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse // 不跟随重定向,加快检测速度
|
||||
},
|
||||
}
|
||||
|
||||
// 发送HEAD请求,最小化网络开销
|
||||
req, err := http.NewRequestWithContext(ctx, "HEAD", url, nil)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", "fscan-web-detector/2.0")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
// 检查错误类型,某些错误也表明是HTTP服务
|
||||
errStr := strings.ToLower(err.Error())
|
||||
if strings.Contains(errStr, "http") ||
|
||||
strings.Contains(errStr, "malformed http") ||
|
||||
strings.Contains(errStr, "server closed") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 任何HTTP响应都表明这是Web服务
|
||||
return resp.StatusCode > 0
|
||||
}
|
||||
|
||||
// GetCommonWebPorts 获取常见Web端口列表
|
||||
func (w *WebPortDetector) GetCommonWebPorts() []int {
|
||||
ports := make([]int, 0, len(w.commonWebPorts))
|
||||
for port := range w.commonWebPorts {
|
||||
ports = append(ports, port)
|
||||
}
|
||||
return ports
|
||||
}
|
54
Plugins/legacy/webpoc/plugin.go
Normal file
54
Plugins/legacy/webpoc/plugin.go
Normal file
@ -0,0 +1,54 @@
|
||||
package webpoc
|
||||
|
||||
import (
|
||||
"github.com/shadow1ng/fscan/plugins/adapters"
|
||||
"github.com/shadow1ng/fscan/plugins/base"
|
||||
LegacyPlugins "github.com/shadow1ng/fscan/plugins/legacy"
|
||||
)
|
||||
|
||||
// NewWebPocPlugin 创建WebPoc漏洞扫描插件
|
||||
func NewWebPocPlugin() base.Plugin {
|
||||
// 插件元数据
|
||||
metadata := &base.PluginMetadata{
|
||||
Name: "webpoc",
|
||||
Version: "1.0.0",
|
||||
Author: "fscan-team",
|
||||
Description: "Web应用漏洞POC扫描检测",
|
||||
Category: "web",
|
||||
Ports: []int{}, // Web插件不限制端口,支持任意端口的URL
|
||||
Protocols: []string{"http", "https"},
|
||||
Tags: []string{"web", "poc", "vulnerability", "exploit"},
|
||||
}
|
||||
|
||||
// 适配器选项
|
||||
options := &adapters.LegacyPluginOptions{
|
||||
CheckBruteFlag: false, // WebPoc不依赖暴力破解标志
|
||||
IsVulnPlugin: true, // 这是漏洞检测插件
|
||||
IsInfoPlugin: false,
|
||||
CustomPorts: []int{}, // Web插件不限制端口,支持任意端口的URL
|
||||
}
|
||||
|
||||
// 创建适配器,使用老版本的WebPoc函数
|
||||
return adapters.NewLegacyPlugin(metadata, LegacyPlugins.WebPoc, options)
|
||||
}
|
||||
|
||||
// init 自动注册WebPoc插件
|
||||
func init() {
|
||||
// 创建插件工厂
|
||||
metadata := &base.PluginMetadata{
|
||||
Name: "webpoc",
|
||||
Version: "1.0.0",
|
||||
Author: "fscan-team",
|
||||
Description: "Web应用漏洞POC扫描检测",
|
||||
Category: "web",
|
||||
Ports: []int{}, // Web插件不限制端口,支持任意端口的URL
|
||||
Protocols: []string{"http", "https"},
|
||||
Tags: []string{"web", "poc", "vulnerability", "exploit"},
|
||||
}
|
||||
|
||||
factory := base.NewSimplePluginFactory(metadata, func() base.Plugin {
|
||||
return NewWebPocPlugin()
|
||||
})
|
||||
|
||||
base.GlobalPluginRegistry.Register("webpoc", factory)
|
||||
}
|
54
Plugins/legacy/webtitle/plugin.go
Normal file
54
Plugins/legacy/webtitle/plugin.go
Normal file
@ -0,0 +1,54 @@
|
||||
package webtitle
|
||||
|
||||
import (
|
||||
"github.com/shadow1ng/fscan/plugins/adapters"
|
||||
"github.com/shadow1ng/fscan/plugins/base"
|
||||
LegacyPlugins "github.com/shadow1ng/fscan/plugins/legacy"
|
||||
)
|
||||
|
||||
// NewWebTitlePlugin 创建WebTitle网站标题获取插件
|
||||
func NewWebTitlePlugin() base.Plugin {
|
||||
// 插件元数据
|
||||
metadata := &base.PluginMetadata{
|
||||
Name: "webtitle",
|
||||
Version: "1.0.0",
|
||||
Author: "fscan-team",
|
||||
Description: "Web网站标题和指纹识别扫描",
|
||||
Category: "web",
|
||||
Ports: []int{}, // Web插件不限制端口,支持任意端口的URL
|
||||
Protocols: []string{"http", "https"},
|
||||
Tags: []string{"web", "title", "fingerprint", "information-gathering"},
|
||||
}
|
||||
|
||||
// 适配器选项
|
||||
options := &adapters.LegacyPluginOptions{
|
||||
CheckBruteFlag: false, // WebTitle不依赖暴力破解标志
|
||||
IsVulnPlugin: false, // 这不是漏洞检测插件
|
||||
IsInfoPlugin: true, // 这是信息收集插件
|
||||
CustomPorts: []int{}, // Web插件不限制端口,支持任意端口的URL
|
||||
}
|
||||
|
||||
// 创建适配器,使用老版本的WebTitle函数
|
||||
return adapters.NewLegacyPlugin(metadata, LegacyPlugins.WebTitle, options)
|
||||
}
|
||||
|
||||
// init 自动注册WebTitle插件
|
||||
func init() {
|
||||
// 创建插件工厂
|
||||
metadata := &base.PluginMetadata{
|
||||
Name: "webtitle",
|
||||
Version: "1.0.0",
|
||||
Author: "fscan-team",
|
||||
Description: "Web网站标题和指纹识别扫描",
|
||||
Category: "web",
|
||||
Ports: []int{}, // Web插件不限制端口,支持任意端口的URL
|
||||
Protocols: []string{"http", "https"},
|
||||
Tags: []string{"web", "title", "fingerprint", "information-gathering"},
|
||||
}
|
||||
|
||||
factory := base.NewSimplePluginFactory(metadata, func() base.Plugin {
|
||||
return NewWebTitlePlugin()
|
||||
})
|
||||
|
||||
base.GlobalPluginRegistry.Register("webtitle", factory)
|
||||
}
|
Loading…
Reference in New Issue
Block a user