fscan/Plugins/services/redis/plugin.go
ZacharyZcR de286026e8 refactor: 统一插件超时机制实现Context-First架构
- 重构SSH/MySQL/Redis插件超时控制,移除第三方库超时依赖
- 统一使用Go Context超时机制,提升超时控制可靠性和精确度
- 扩展MySQL/Redis/SSH插件默认端口支持,提升扫描覆盖率
- 修复插件系统中ConcurrentScanConfig超时配置缺失问题
- 优化插件检测逻辑,正确识别新架构插件并显示准确状态信息
- 解决插件在错误端口上长时间等待问题,显著提升扫描效率
2025-08-07 14:01:50 +08:00

201 lines
6.1 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 redis
import (
"context"
"fmt"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/plugins/base"
)
// Redis插件展示如何实现未授权访问检测和弱密码爆破
// 作为NoSQL数据库插件的标准参考实现
// 重点展示了自定义扫描逻辑和未授权访问检测模式
// RedisPlugin Redis插件实现
type RedisPlugin struct {
*base.ServicePlugin
exploiter *RedisExploiter
}
// NewRedisPlugin 创建Redis插件
func NewRedisPlugin() *RedisPlugin {
// 插件元数据
metadata := &base.PluginMetadata{
Name: "redis",
Version: "2.0.0",
Author: "fscan-team",
Description: "Redis数据库扫描和利用插件",
Category: "service",
Ports: []int{6379, 6380, 6381, 16379, 26379}, // Redis常用端口包括默认端口、集群端口和备用端口
Protocols: []string{"tcp"},
Tags: []string{"database", "redis", "bruteforce", "exploit", "unauthorized"},
}
// 创建连接器和服务插件
connector := NewRedisConnector()
servicePlugin := base.NewServicePlugin(metadata, connector)
// 创建Redis插件
plugin := &RedisPlugin{
ServicePlugin: servicePlugin,
exploiter: NewRedisExploiter(),
}
// 设置能力
plugin.SetCapabilities([]base.Capability{
base.CapWeakPassword,
base.CapUnauthorized,
base.CapFileWrite,
base.CapCommandExecution,
base.CapDataExtraction,
base.CapInformationLeak,
})
return plugin
}
// Scan 重写扫描方法以支持未授权访问检测和后续利用
func (p *RedisPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
common.LogDebug(i18n.GetText("redis_scan_start", target))
// 先检查未授权访问
unauthorizedResult := p.checkUnauthorizedAccess(ctx, info)
if unauthorizedResult != nil && unauthorizedResult.Success {
common.LogSuccess(i18n.GetText("redis_unauth_success", target))
// 如果启用了利用功能,执行自动利用
if !common.DisableBrute { // 使用DisableBrute作为替代用户可以通过-nobr禁用利用功能
go p.autoExploit(context.Background(), info, nil) // 未授权访问不需要凭据
}
return unauthorizedResult, nil
}
// 如果未授权访问失败,尝试暴力破解
if common.DisableBrute {
return &base.ScanResult{
Success: false,
Error: fmt.Errorf("暴力破解已禁用且无未授权访问"),
}, nil
}
// 执行基础的暴力破解扫描
result, err := p.ServicePlugin.Scan(ctx, info)
if err != nil || !result.Success {
return result, err
}
common.LogSuccess(i18n.GetText("redis_weak_pwd_success",
target, result.Credentials[0].Password))
// 如果扫描成功并且启用了利用功能,执行自动利用
if result.Success && len(result.Credentials) > 0 && !common.DisableBrute {
go p.autoExploit(context.Background(), info, result.Credentials[0])
}
return result, nil
}
// checkUnauthorizedAccess 检查未授权访问
func (p *RedisPlugin) checkUnauthorizedAccess(ctx context.Context, info *common.HostInfo) *base.ScanResult {
conn, err := p.ServicePlugin.GetServiceConnector().Connect(ctx, info)
if err != nil {
return nil
}
defer p.ServicePlugin.GetServiceConnector().Close(conn)
// 尝试无密码认证
err = p.ServicePlugin.GetServiceConnector().Authenticate(ctx, conn, nil)
if err != nil {
return nil
}
// 未授权访问成功
return &base.ScanResult{
Success: true,
Service: "redis",
Credentials: []*base.Credential{}, // 未授权访问无凭据
Vulnerabilities: []base.Vulnerability{
{
ID: "REDIS-UNAUTH",
Name: "Redis未授权访问",
Severity: "High",
Description: "Redis服务允许未授权访问攻击者可以读取、修改数据或执行命令",
References: []string{"https://redis.io/topics/security"},
},
},
Extra: make(map[string]interface{}),
}
}
// autoExploit 自动利用
func (p *RedisPlugin) 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", "Redis", target))
// 执行利用
result, err := p.exploiter.Exploit(ctx, info, creds)
if err != nil {
common.LogError(i18n.GetText("plugin_exploit_failed", "Redis", err))
return
}
if result != nil && result.Success {
common.LogSuccess(i18n.GetText("plugin_exploit_success", "Redis", result.Method))
base.SaveExploitResult(info, result, "Redis")
}
}
// Exploit 手动利用接口
func (p *RedisPlugin) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) {
return p.exploiter.Exploit(ctx, info, creds)
}
// GetExploitMethods 获取利用方法
func (p *RedisPlugin) GetExploitMethods() []base.ExploitMethod {
return p.exploiter.GetExploitMethods()
}
// IsExploitSupported 检查利用支持
func (p *RedisPlugin) IsExploitSupported(method base.ExploitType) bool {
return p.exploiter.IsExploitSupported(method)
}
// generateCredentials 重写凭据生成方法Redis只需要密码
func (p *RedisPlugin) generateCredentials() []*base.Credential {
// Redis通常只需要密码不需要用户名
return base.GeneratePasswordOnlyCredentials(common.Passwords)
}
// =============================================================================
// 插件注册
// =============================================================================
// RegisterRedisPlugin 注册Redis插件
func RegisterRedisPlugin() {
factory := base.NewSimplePluginFactory(
&base.PluginMetadata{
Name: "redis",
Version: "2.0.0",
Author: "fscan-team",
Description: "Redis数据库扫描和利用插件",
Category: "service",
Ports: []int{6379, 6380, 6381, 16379, 26379},
Protocols: []string{"tcp"},
Tags: []string{"database", "redis", "bruteforce", "exploit", "unauthorized"},
},
func() base.Plugin {
return NewRedisPlugin()
},
)
base.GlobalPluginRegistry.Register("redis", factory)
}
// 自动注册
func init() {
RegisterRedisPlugin()
}