Files
fscan/plugins/init.go
ZacharyZcR 71b92d4408 feat: v2.1.0 核心重构与功能增强
## 架构重构
- 全局变量消除,迁移至 Config/State 对象
- SMB 插件融合(smb/smb2/smbghost/smbinfo)
- 服务探测重构,实现 Nmap 风格 fallback 机制
- 输出系统重构,TXT 实时刷盘 + 双写机制
- i18n 框架升级至 go-i18n

## 性能优化
- 正则表达式预编译
- 内存优化 map[string]struct{}
- 并发指纹匹配
- SOCKS5 连接复用
- 滑动窗口调度 + 自适应线程池

## 新功能
- Web 管理界面
- 多格式 POC 适配(xray/afrog)
- 增强指纹库(3139条)
- Favicon hash 指纹识别
- 插件选择性编译(Build Tags)
- fscan-lab 靶场环境
- 默认端口扩展(62→133)

## 构建系统
- 添加 no_local tag 支持排除本地插件
- 多版本构建:fscan/fscan-nolocal/fscan-web
- CI 添加 snapshot 模式支持仅测试构建

## Bug 修复
- 修复 120+ 个问题,包括 RDP panic、批量扫描漏报、
  JSON 输出格式、Redis 检测、Context 超时等

## 测试增强
- 单元测试覆盖率 74-100%
- 并发安全测试
- 集成测试(Web/端口/服务/SSH/ICMP)
2026-01-11 20:16:23 +08:00

213 lines
4.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package plugins
import (
"context"
"strings"
"sync"
"github.com/shadow1ng/fscan/common"
)
// Plugin 统一插件接口
type Plugin interface {
Name() string
Scan(ctx context.Context, info *common.HostInfo, config *common.Config, state *common.State) *Result
}
// BasePlugin 基础插件结构提供通用的name字段
type BasePlugin struct {
name string
}
// NewBasePlugin 创建基础插件
func NewBasePlugin(name string) BasePlugin {
return BasePlugin{name: name}
}
// Name 实现Plugin接口
func (b BasePlugin) Name() string {
return b.name
}
// ResultType 结果类型
type ResultType string
const (
ResultTypeCredential ResultType = "credential" // 弱密码发现
ResultTypeService ResultType = "service" // 服务识别
ResultTypeVuln ResultType = "vuln" // 漏洞发现
ResultTypeWeb ResultType = "web" // Web识别
)
// Result 统一结果结构
type Result struct {
Type ResultType
Success bool
Skipped bool // 扫描被跳过,不应输出结果
Service string
Username string
Password string
Banner string
Output string // web/local插件使用
Error error
// Web插件字段
Title string // 网页标题
Status int // HTTP状态码
Server string // 服务器信息
Length int // 响应长度
VulInfo string // 漏洞信息
Fingerprints []string // 指纹信息
}
// Exploiter 利用接口
type Exploiter interface {
Exploit(ctx context.Context, info *common.HostInfo, creds Credential, config *common.Config) *ExploitResult
}
// ExploitResult 利用结果
type ExploitResult struct {
Success bool
Output string
Error error
}
// Credential 认证凭据
type Credential struct {
Username string
Password string
KeyData []byte
}
// PluginInfo 插件信息结构
type PluginInfo struct {
factory func() Plugin
ports []int
types []string // 插件类型标签
}
// 插件类型常量
const (
PluginTypeWeb = "web" // Web类型插件
PluginTypeLocal = "local" // 本地类型插件
PluginTypeService = "service" // 服务类型插件
)
var (
plugins = make(map[string]*PluginInfo)
mutex sync.RWMutex
)
// RegisterWithPorts 注册带端口信息的插件
func RegisterWithPorts(name string, factory func() Plugin, ports []int) {
RegisterWithTypes(name, factory, ports, []string{PluginTypeService})
}
// RegisterWithTypes 注册带类型标签的插件
func RegisterWithTypes(name string, factory func() Plugin, ports []int, types []string) {
mutex.Lock()
defer mutex.Unlock()
plugins[name] = &PluginInfo{
factory: factory,
ports: ports,
types: types,
}
}
// HasType 检查插件是否具有指定类型
func HasType(pluginName string, typeName string) bool {
mutex.RLock()
defer mutex.RUnlock()
if info, exists := plugins[pluginName]; exists {
for _, t := range info.types {
if t == typeName {
return true
}
}
}
return false
}
// Get 获取插件实例
func Get(name string) Plugin {
mutex.RLock()
defer mutex.RUnlock()
if info, exists := plugins[name]; exists {
return info.factory()
}
return nil
}
// All 获取所有插件名称
func All() []string {
mutex.RLock()
defer mutex.RUnlock()
names := make([]string, 0, len(plugins))
for name := range plugins {
names = append(names, name)
}
return names
}
// Exists 检查插件是否存在
func Exists(name string) bool {
mutex.RLock()
defer mutex.RUnlock()
_, exists := plugins[name]
return exists
}
// GetPluginPorts 获取插件端口列表
func GetPluginPorts(name string) []int {
mutex.RLock()
defer mutex.RUnlock()
if info, exists := plugins[name]; exists {
return info.ports
}
return []int{} // 返回空列表表示适用于所有端口
}
// GenerateCredentials 生成测试凭据
func GenerateCredentials(service string, config *common.Config) []Credential {
var credentials []Credential
credConfig := config.Credentials
// 优先使用精确的用户密码对
if len(credConfig.UserPassPairs) > 0 {
for _, pair := range credConfig.UserPassPairs {
credentials = append(credentials, Credential{
Username: pair.Username,
Password: pair.Password,
})
}
return credentials
}
// 否则使用笛卡尔积方式
users := credConfig.Userdict[service]
if len(users) == 0 {
users = []string{"admin", "root", "administrator", "user", "guest", ""}
}
passwords := credConfig.Passwords
if len(passwords) == 0 {
passwords = []string{"", "admin", "root", "password", "123456"}
}
for _, user := range users {
for _, pass := range passwords {
actualPass := strings.ReplaceAll(pass, "{user}", user)
credentials = append(credentials, Credential{
Username: user,
Password: actualPass,
})
}
}
return credentials
}