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() }