fscan/plugins/services/mysql/plugin.go
ZacharyZcR 4a3f281b6b refactor: 统一Plugins目录大小写为小写
- 将所有Plugins路径重命名为plugins
- 修复Git索引与实际文件系统大小写不一致问题
- 确保跨平台兼容性和路径一致性
2025-08-12 13:08:06 +08:00

235 lines
7.4 KiB
Go
Raw 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 mysql
import (
"context"
"fmt"
"net"
"regexp"
"time"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/plugins/base"
)
// MySQL插件新一代插件架构的完整实现示例
// 展示了如何正确实现服务扫描、凭据爆破、自动利用等功能
// 本插件可作为其他数据库插件迁移的标准参考模板
// MySQLPlugin MySQL数据库扫描和利用插件
// 集成了弱密码检测、自动利用、信息收集等完整功能
type MySQLPlugin struct {
*base.ServicePlugin // 继承基础服务插件功能
exploiter *MySQLExploiter // MySQL专用利用模块
}
// NewMySQLPlugin 创建新的MySQL插件实例
// 这是标准的插件工厂函数,展示了新架构的完整初始化流程
func NewMySQLPlugin() *MySQLPlugin {
// 定义插件元数据 - 这些信息用于插件注册和管理
metadata := &base.PluginMetadata{
Name: "mysql", // 插件唯一标识符
Version: "2.0.0", // 插件版本(新架构版本)
Author: "fscan-team", // 开发团队
Description: "MySQL数据库扫描和利用插件", // 功能描述
Category: "service", // 插件类别
Ports: []int{3306, 3307, 33060, 33061, 33062}, // MySQL常用端口包括默认端口和备用端口
Protocols: []string{"tcp"}, // 支持的协议
Tags: []string{"database", "mysql", "bruteforce", "exploit"}, // 功能标签
}
// 创建MySQL专用连接器
connector := NewMySQLConnector()
// 基于连接器创建基础服务插件
servicePlugin := base.NewServicePlugin(metadata, connector)
// 组装完整的MySQL插件
plugin := &MySQLPlugin{
ServicePlugin: servicePlugin,
exploiter: NewMySQLExploiter(), // 集成利用模块
}
// 声明插件具备的安全测试能力
plugin.SetCapabilities([]base.Capability{
base.CapWeakPassword, // 弱密码检测
base.CapDataExtraction, // 数据提取
base.CapFileWrite, // 文件写入
base.CapSQLInjection, // SQL注入
base.CapInformationLeak, // 信息泄露
})
return plugin
}
// Scan 执行MySQL服务的完整安全扫描
// 重写基础扫描方法,集成弱密码检测和自动利用功能
func (p *MySQLPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
// 如果禁用暴力破解,则进行基础服务识别
if common.DisableBrute {
return p.performServiceIdentification(ctx, info)
}
// 调用基础服务插件进行弱密码扫描
result, err := p.ServicePlugin.Scan(ctx, info)
if err != nil || !result.Success {
return result, err // 扫描失败,直接返回
}
// 记录成功的弱密码发现使用i18n
cred := result.Credentials[0]
common.LogSuccess(i18n.GetText("mysql_scan_success", target, cred.Username, cred.Password))
// 自动利用功能(可通过-ne参数禁用
if result.Success && len(result.Credentials) > 0 && !common.DisableExploit {
// 异步执行利用攻击,避免阻塞扫描进程
go p.autoExploit(context.Background(), info, result.Credentials[0])
}
return result, nil
}
// autoExploit 自动利用
func (p *MySQLPlugin) autoExploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
common.LogDebug(i18n.GetText("plugin_exploit_start", "MySQL", target))
// 执行利用
result, err := p.exploiter.Exploit(ctx, info, creds)
if err != nil {
common.LogError(i18n.GetText("plugin_exploit_failed", "MySQL", err))
return
}
if result != nil && result.Success {
common.LogSuccess(i18n.GetText("plugin_exploit_success", "MySQL", i18n.GetExploitMethodName(result.Method)))
base.SaveExploitResult(info, result, "MySQL")
}
}
// Exploit 手动利用接口
func (p *MySQLPlugin) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) {
return p.exploiter.Exploit(ctx, info, creds)
}
// GetExploitMethods 获取利用方法
func (p *MySQLPlugin) GetExploitMethods() []base.ExploitMethod {
return p.exploiter.GetExploitMethods()
}
// IsExploitSupported 检查利用支持
func (p *MySQLPlugin) IsExploitSupported(method base.ExploitType) bool {
return p.exploiter.IsExploitSupported(method)
}
// 已移除未使用的 generateCredentials 方法
// performServiceIdentification 执行MySQL服务识别-nobr模式
func (p *MySQLPlugin) performServiceIdentification(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
// 尝试连接到MySQL服务获取握手包
conn, err := common.WrapperTcpWithTimeout("tcp", target, time.Duration(common.Timeout)*time.Second)
if err != nil {
return &base.ScanResult{
Success: false,
Error: err,
}, nil
}
defer conn.Close()
// 读取MySQL握手包
mysqlInfo, isMySQL := p.identifyMySQLService(conn)
if isMySQL {
// 记录服务识别成功
common.LogSuccess(i18n.GetText("mysql_service_identified", target, mysqlInfo))
return &base.ScanResult{
Success: true,
Service: "MySQL",
Banner: mysqlInfo,
Extra: map[string]interface{}{
"service": "MySQL",
"port": info.Ports,
"info": mysqlInfo,
},
}, nil
}
// 如果无法识别为MySQL返回失败
return &base.ScanResult{
Success: false,
Error: fmt.Errorf("无法识别为MySQL服务"),
}, nil
}
// identifyMySQLService 通过握手包识别MySQL服务
func (p *MySQLPlugin) identifyMySQLService(conn net.Conn) (string, bool) {
// 设置读取超时
conn.SetReadDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second))
// MySQL服务器在连接后会主动发送握手包
handshake := make([]byte, 1024)
n, err := conn.Read(handshake)
if err != nil || n < 10 {
return "", false
}
// 检查MySQL握手包格式
// MySQL握手包开始: 包长度(3字节) + 序号(1字节) + 协议版本(1字节)
if handshake[4] != 10 { // MySQL 协议版本通常是10
return "", false
}
// 提取版本字符串从第5字节开始到第一个0结束
versionStart := 5
versionEnd := versionStart
for versionEnd < n && handshake[versionEnd] != 0 {
versionEnd++
}
if versionEnd <= versionStart {
return "", false
}
versionStr := string(handshake[versionStart:versionEnd])
// 验证版本字符串是否包含MySQL标识
if len(versionStr) > 0 && (regexp.MustCompile(`\d+\.\d+`).MatchString(versionStr)) {
return fmt.Sprintf("MySQL版本: %s", versionStr), true
}
return "", false
}
// =============================================================================
// 插件注册
// =============================================================================
// RegisterMySQLPlugin 注册MySQL插件
func RegisterMySQLPlugin() {
factory := base.NewSimplePluginFactory(
&base.PluginMetadata{
Name: "mysql",
Version: "2.0.0",
Author: "fscan-team",
Description: "MySQL数据库扫描和利用插件",
Category: "service",
Ports: []int{3306, 3307, 33060, 33061, 33062},
Protocols: []string{"tcp"},
Tags: []string{"database", "mysql", "bruteforce", "exploit"},
},
func() base.Plugin {
return NewMySQLPlugin()
},
)
base.GlobalPluginRegistry.Register("mysql", factory)
}
// 自动注册
func init() {
RegisterMySQLPlugin()
}