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

278 lines
8.2 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"
"net"
"strings"
"time"
"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.DisableExploit { // 使用DisableExploit控制利用功能
go p.autoExploit(context.Background(), info, nil) // 未授权访问不需要凭据
}
return unauthorizedResult, nil
}
// 如果未授权访问失败,在-nobr模式下进行基础服务识别
if common.DisableBrute {
return p.performServiceIdentification(ctx, info)
}
// 执行基础的暴力破解扫描
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.DisableExploit {
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 方法
// performServiceIdentification 执行Redis服务识别-nobr模式
func (p *RedisPlugin) performServiceIdentification(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
// 尝试连接到Redis服务
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()
// 发送INFO命令获取Redis服务器信息
redisInfo, isRedis := p.identifyRedisService(conn)
if isRedis {
// 记录服务识别成功
common.LogSuccess(i18n.GetText("redis_service_identified", target, redisInfo))
return &base.ScanResult{
Success: true,
Service: "Redis",
Banner: redisInfo,
Extra: map[string]interface{}{
"service": "Redis",
"port": info.Ports,
"info": redisInfo,
},
}, nil
}
// 如果无法识别为Redis返回失败
return &base.ScanResult{
Success: false,
Error: fmt.Errorf("无法识别为Redis服务"),
}, nil
}
// identifyRedisService 通过INFO命令识别Redis服务
func (p *RedisPlugin) identifyRedisService(conn net.Conn) (string, bool) {
// 发送INFO命令
infoCmd := "INFO server\r\n"
conn.SetWriteDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second))
if _, err := conn.Write([]byte(infoCmd)); err != nil {
return "", false
}
// 读取响应
conn.SetReadDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second))
response := make([]byte, 2048)
n, err := conn.Read(response)
if err != nil || n < 10 {
return "", false
}
responseStr := string(response[:n])
// 检查是否为Redis响应
if strings.Contains(responseStr, "redis_version:") {
// 提取Redis版本信息
lines := strings.Split(responseStr, "\r\n")
for _, line := range lines {
if strings.HasPrefix(line, "redis_version:") {
version := strings.TrimPrefix(line, "redis_version:")
return fmt.Sprintf("Redis版本: %s", version), true
}
}
return "Redis服务版本未知", true
} else if strings.Contains(responseStr, "-NOAUTH") {
// 需要认证的Redis
return "Redis服务需要认证", true
} else if strings.Contains(responseStr, "+PONG") || strings.Contains(responseStr, "$") {
// 通过RESP协议特征识别
return "Redis服务", true
}
return "", false
}
// =============================================================================
// 插件注册
// =============================================================================
// 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()
}