mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 05:56:46 +08:00

- 重构插件注册架构采用现代工厂模式和自动发现机制 - 新增完整的插件元数据管理系统支持版本能力标签等信息 - 实现智能插件适配器提供向后兼容的桥接功能 - 建立MySQL Redis SSH三个标准插件作为新架构参考实现 - 优化插件扫描逻辑支持按端口按类型的智能查询和过滤 - 添加国际化支持和完善的文档体系 - 代码量减少67%维护成本大幅降低扩展性显著提升 新架构特点: - 零配置插件注册import即用 - 工厂模式延迟初始化和依赖注入 - 丰富元数据系统和能力声明 - 完全解耦的模块化设计 - 面向未来的可扩展架构 测试验证: MySQL和Redis插件功能完整包括弱密码检测未授权访问检测和自动利用攻击
177 lines
5.3 KiB
Go
177 lines
5.3 KiB
Go
package adapter
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"github.com/shadow1ng/fscan/common"
|
||
"github.com/shadow1ng/fscan/common/i18n"
|
||
"github.com/shadow1ng/fscan/common/output"
|
||
"github.com/shadow1ng/fscan/plugins/base"
|
||
"time"
|
||
|
||
// 导入新插件以触发注册
|
||
_ "github.com/shadow1ng/fscan/plugins/services/mysql"
|
||
_ "github.com/shadow1ng/fscan/plugins/services/redis"
|
||
_ "github.com/shadow1ng/fscan/plugins/services/ssh"
|
||
)
|
||
|
||
// PluginAdapter 插件适配器,将新插件架构与旧系统集成
|
||
type PluginAdapter struct {
|
||
registry *base.PluginRegistry
|
||
}
|
||
|
||
// NewPluginAdapter 创建插件适配器
|
||
func NewPluginAdapter() *PluginAdapter {
|
||
return &PluginAdapter{
|
||
registry: base.GlobalPluginRegistry,
|
||
}
|
||
}
|
||
|
||
// AdaptPluginScan 适配插件扫描调用
|
||
// 将传统的插件扫描函数调用转换为新架构的插件调用
|
||
func (a *PluginAdapter) AdaptPluginScan(pluginName string, info *common.HostInfo) error {
|
||
// 创建插件实例
|
||
plugin, err := a.registry.Create(pluginName)
|
||
if err != nil {
|
||
// 如果新架构中没有该插件,返回错误让旧系统处理
|
||
return fmt.Errorf("plugin %s not found in new architecture: %v", pluginName, err)
|
||
}
|
||
|
||
// 初始化插件
|
||
if err := plugin.Initialize(); err != nil {
|
||
return fmt.Errorf("plugin %s initialization failed: %v", pluginName, err)
|
||
}
|
||
|
||
// 设置全局超时上下文
|
||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
|
||
defer cancel()
|
||
|
||
// 执行扫描
|
||
result, err := plugin.Scan(ctx, info)
|
||
if err != nil {
|
||
common.LogError(fmt.Sprintf("Plugin %s scan failed: %v", pluginName, err))
|
||
return err
|
||
}
|
||
|
||
// 如果扫描成功,记录结果
|
||
if result != nil && result.Success {
|
||
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
|
||
|
||
if len(result.Credentials) > 0 {
|
||
// 有凭据的情况
|
||
cred := result.Credentials[0]
|
||
if cred.Username != "" {
|
||
common.LogSuccess(fmt.Sprintf("%s successful login: %s [%s:%s]",
|
||
pluginName, target, cred.Username, cred.Password))
|
||
} else {
|
||
// 仅密码的情况(如Redis)
|
||
common.LogSuccess(fmt.Sprintf("%s successful login: %s [%s]",
|
||
pluginName, target, cred.Password))
|
||
}
|
||
} else {
|
||
// 未授权访问的情况
|
||
common.LogSuccess(fmt.Sprintf("%s unauthorized access: %s", pluginName, target))
|
||
}
|
||
|
||
// 保存结果到文件
|
||
a.saveResult(info, result, pluginName)
|
||
|
||
// 如果有漏洞信息,也记录下来
|
||
for _, vuln := range result.Vulnerabilities {
|
||
common.LogError(fmt.Sprintf("%s vulnerability found: %s - %s",
|
||
pluginName, vuln.ID, vuln.Description))
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// saveResult 保存扫描结果
|
||
func (a *PluginAdapter) saveResult(info *common.HostInfo, result *base.ScanResult, pluginName string) {
|
||
// 使用原有的结果保存机制
|
||
|
||
vulnResult := &output.ScanResult{
|
||
Time: time.Now(),
|
||
Type: output.TypeVuln,
|
||
Target: info.Host,
|
||
Status: "vulnerable",
|
||
Details: map[string]interface{}{
|
||
"plugin": pluginName,
|
||
"port": info.Ports,
|
||
"host": info.Host,
|
||
},
|
||
}
|
||
|
||
if len(result.Credentials) > 0 {
|
||
cred := result.Credentials[0]
|
||
if cred.Username != "" {
|
||
vulnResult.Details["username"] = cred.Username
|
||
vulnResult.Details["password"] = cred.Password
|
||
vulnResult.Details["credentials"] = fmt.Sprintf("%s:%s", cred.Username, cred.Password)
|
||
} else {
|
||
vulnResult.Details["password"] = cred.Password
|
||
vulnResult.Details["credentials"] = cred.Password
|
||
}
|
||
} else {
|
||
// 未授权访问
|
||
vulnResult.Details["type"] = "unauthorized"
|
||
}
|
||
|
||
// 保存结果
|
||
common.SaveResult(vulnResult)
|
||
}
|
||
|
||
// IsPluginSupported 检查插件是否在新架构中支持
|
||
func (a *PluginAdapter) IsPluginSupported(pluginName string) bool {
|
||
plugins := a.registry.GetAll()
|
||
for _, name := range plugins {
|
||
if name == pluginName {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
// GetSupportedPlugins 获取新架构支持的插件列表
|
||
func (a *PluginAdapter) GetSupportedPlugins() []string {
|
||
return a.registry.GetAll()
|
||
}
|
||
|
||
// GetPluginMetadata 获取插件元数据
|
||
func (a *PluginAdapter) GetPluginMetadata(pluginName string) (*base.PluginMetadata, error) {
|
||
metadata := a.registry.GetMetadata(pluginName)
|
||
if metadata == nil {
|
||
return nil, fmt.Errorf("plugin %s not found", pluginName)
|
||
}
|
||
return metadata, nil
|
||
}
|
||
|
||
// =============================================================================
|
||
// 全局适配器实例
|
||
// =============================================================================
|
||
|
||
// GlobalAdapter 全局插件适配器实例
|
||
var GlobalAdapter = NewPluginAdapter()
|
||
|
||
// =============================================================================
|
||
// 便捷函数
|
||
// =============================================================================
|
||
|
||
// TryNewArchitecture 尝试使用新架构执行插件扫描
|
||
// 如果新架构支持该插件,则使用新架构;否则返回false让调用方使用旧插件
|
||
func TryNewArchitecture(pluginName string, info *common.HostInfo) bool {
|
||
if !GlobalAdapter.IsPluginSupported(pluginName) {
|
||
common.LogDebug(i18n.GetText("plugin_legacy_using", pluginName))
|
||
return false
|
||
}
|
||
|
||
common.LogDebug(i18n.GetText("plugin_new_arch_trying", pluginName))
|
||
err := GlobalAdapter.AdaptPluginScan(pluginName, info)
|
||
if err != nil {
|
||
common.LogError(i18n.GetText("plugin_new_arch_fallback", pluginName, err))
|
||
return false
|
||
}
|
||
|
||
common.LogDebug(i18n.GetText("plugin_new_arch_success", pluginName))
|
||
return true
|
||
} |