package services import ( "context" "encoding/json" "fmt" "io" "net/http" "strings" "time" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/common/i18n" ) // RabbitMQPlugin RabbitMQ消息队列扫描和利用插件 - 包含队列信息提取利用功能 type RabbitMQPlugin struct { name string ports []int } // NewRabbitMQPlugin 创建RabbitMQ插件 func NewRabbitMQPlugin() *RabbitMQPlugin { return &RabbitMQPlugin{ name: "rabbitmq", ports: []int{5672, 15672, 5671}, // AMQP、管理界面、AMQPS端口 } } // GetName 实现Plugin接口 func (p *RabbitMQPlugin) GetName() string { return p.name } // GetPorts 实现Plugin接口 func (p *RabbitMQPlugin) GetPorts() []int { return p.ports } // Scan 执行RabbitMQ扫描 - 弱密码检测 func (p *RabbitMQPlugin) Scan(ctx context.Context, info *common.HostInfo) *ScanResult { target := fmt.Sprintf("%s:%s", info.Host, info.Ports) // 如果禁用暴力破解,只做服务识别 if common.DisableBrute { return p.identifyService(ctx, info) } // RabbitMQ管理端口通常是15672,AMQP端口是5672 if info.Ports == "5672" || info.Ports == "5671" { // AMQP端口,进行协议检测 return p.testAMQPProtocol(ctx, info) } // 生成测试凭据 credentials := GenerateCredentials("rabbitmq") if len(credentials) == 0 { // RabbitMQ默认凭据 credentials = []Credential{ {Username: "guest", Password: "guest"}, {Username: "admin", Password: "admin"}, {Username: "admin", Password: "password"}, {Username: "admin", Password: "123456"}, {Username: "rabbitmq", Password: "rabbitmq"}, {Username: "user", Password: "user"}, } } // 逐个测试凭据(针对管理接口) for _, cred := range credentials { // 检查Context是否被取消 select { case <-ctx.Done(): return &ScanResult{ Success: false, Service: "rabbitmq", Error: ctx.Err(), } default: } // 测试凭据 if p.testCredential(ctx, info, cred) { // RabbitMQ认证成功 common.LogSuccess(i18n.GetText("rabbitmq_scan_success", target, cred.Username, cred.Password)) return &ScanResult{ Success: true, Service: "rabbitmq", Username: cred.Username, Password: cred.Password, } } } // 所有凭据都失败 return &ScanResult{ Success: false, Service: "rabbitmq", Error: fmt.Errorf("未发现弱密码"), } } // testAMQPProtocol 测试AMQP协议 func (p *RabbitMQPlugin) testAMQPProtocol(ctx context.Context, info *common.HostInfo) *ScanResult { // 对于AMQP端口,我们只做协议识别 // AMQP协议检测比较复杂,这里简化处理 return &ScanResult{ Success: true, Service: "rabbitmq", Banner: "RabbitMQ AMQP协议端口", } } // testCredential 测试单个凭据(通过管理API) func (p *RabbitMQPlugin) testCredential(ctx context.Context, info *common.HostInfo, cred Credential) bool { // 构建管理API URL baseURL := fmt.Sprintf("http://%s:%s", info.Host, info.Ports) client := &http.Client{ Timeout: time.Duration(common.Timeout) * time.Second, } // 尝试访问API overview req, err := http.NewRequestWithContext(ctx, "GET", baseURL+"/api/overview", nil) if err != nil { return false } req.SetBasicAuth(cred.Username, cred.Password) req.Header.Set("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { return false } defer resp.Body.Close() return resp.StatusCode == 200 } // makeAPIRequest 执行API请求 func (p *RabbitMQPlugin) makeAPIRequest(ctx context.Context, info *common.HostInfo, creds Credential, endpoint string) (map[string]interface{}, error) { baseURL := fmt.Sprintf("http://%s:%s", info.Host, info.Ports) client := &http.Client{ Timeout: time.Duration(common.Timeout) * time.Second, } req, err := http.NewRequestWithContext(ctx, "GET", baseURL+endpoint, nil) if err != nil { return nil, err } req.SetBasicAuth(creds.Username, creds.Password) req.Header.Set("Accept", "application/json") resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != 200 { return nil, fmt.Errorf("API请求失败,状态码: %d", resp.StatusCode) } body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } var result map[string]interface{} err = json.Unmarshal(body, &result) if err != nil { // 尝试解析为数组 var arrayResult []map[string]interface{} if err2 := json.Unmarshal(body, &arrayResult); err2 == nil { // 如果是数组,返回包装的结果 return map[string]interface{}{"data": arrayResult}, nil } return nil, err } return result, nil } // getClusterInfo 获取集群信息 func (p *RabbitMQPlugin) getClusterInfo(ctx context.Context, info *common.HostInfo, creds Credential) string { result, err := p.makeAPIRequest(ctx, info, creds, "/api/overview") if err != nil { return "" } var info_str strings.Builder if managementVersion, ok := result["management_version"]; ok { info_str.WriteString(fmt.Sprintf("管理版本: %v\n", managementVersion)) } if rabbitMQVersion, ok := result["rabbitmq_version"]; ok { info_str.WriteString(fmt.Sprintf("RabbitMQ版本: %v\n", rabbitMQVersion)) } if clusterName, ok := result["cluster_name"]; ok { info_str.WriteString(fmt.Sprintf("集群名称: %v\n", clusterName)) } return info_str.String() } // getNodes 获取节点列表 func (p *RabbitMQPlugin) getNodes(ctx context.Context, info *common.HostInfo, creds Credential) []string { result, err := p.makeAPIRequest(ctx, info, creds, "/api/nodes") if err != nil { return nil } var nodes []string if data, ok := result["data"].([]interface{}); ok { for _, item := range data { if node, ok := item.(map[string]interface{}); ok { if name, ok := node["name"]; ok { if nameStr, ok := name.(string); ok { nodes = append(nodes, nameStr) } } } } } return nodes } // getVHosts 获取虚拟主机列表 func (p *RabbitMQPlugin) getVHosts(ctx context.Context, info *common.HostInfo, creds Credential) []string { result, err := p.makeAPIRequest(ctx, info, creds, "/api/vhosts") if err != nil { return nil } var vhosts []string if data, ok := result["data"].([]interface{}); ok { for _, item := range data { if vhost, ok := item.(map[string]interface{}); ok { if name, ok := vhost["name"]; ok { if nameStr, ok := name.(string); ok { vhosts = append(vhosts, nameStr) } } } } } return vhosts } // getUsers 获取用户列表 func (p *RabbitMQPlugin) getUsers(ctx context.Context, info *common.HostInfo, creds Credential) []string { result, err := p.makeAPIRequest(ctx, info, creds, "/api/users") if err != nil { return nil } var users []string if data, ok := result["data"].([]interface{}); ok { for _, item := range data { if user, ok := item.(map[string]interface{}); ok { if name, ok := user["name"]; ok { if nameStr, ok := name.(string); ok { // 获取用户标签信息 var tags string if tagsInterface, ok := user["tags"]; ok { if tagsStr, ok := tagsInterface.(string); ok { tags = fmt.Sprintf(" [%s]", tagsStr) } } users = append(users, nameStr+tags) } } } } } return users } // getQueues 获取队列列表 func (p *RabbitMQPlugin) getQueues(ctx context.Context, info *common.HostInfo, creds Credential) []string { result, err := p.makeAPIRequest(ctx, info, creds, "/api/queues") if err != nil { return nil } var queues []string if data, ok := result["data"].([]interface{}); ok { for _, item := range data { if queue, ok := item.(map[string]interface{}); ok { if name, ok := queue["name"]; ok { if nameStr, ok := name.(string); ok { // 获取队列消息数 var messages string if messagesInterface, ok := queue["messages"]; ok { messages = fmt.Sprintf(" (消息数: %v)", messagesInterface) } queues = append(queues, nameStr+messages) } } } } } return queues } // getExchanges 获取交换器列表 func (p *RabbitMQPlugin) getExchanges(ctx context.Context, info *common.HostInfo, creds Credential) []string { result, err := p.makeAPIRequest(ctx, info, creds, "/api/exchanges") if err != nil { return nil } var exchanges []string if data, ok := result["data"].([]interface{}); ok { for _, item := range data { if exchange, ok := item.(map[string]interface{}); ok { if name, ok := exchange["name"]; ok { if nameStr, ok := name.(string); ok { // 过滤掉空名称的默认交换器 if nameStr == "" { nameStr = "(default)" } // 获取交换器类型 var exchType string if typeInterface, ok := exchange["type"]; ok { if typeStr, ok := typeInterface.(string); ok { exchType = fmt.Sprintf(" [%s]", typeStr) } } exchanges = append(exchanges, nameStr+exchType) } } } } } return exchanges } // identifyService 服务识别 - 检测RabbitMQ服务 func (p *RabbitMQPlugin) identifyService(ctx context.Context, info *common.HostInfo) *ScanResult { target := fmt.Sprintf("%s:%s", info.Host, info.Ports) // 根据端口类型进行不同的识别 if info.Ports == "5672" || info.Ports == "5671" { // AMQP端口 return &ScanResult{ Success: true, Service: "rabbitmq", Banner: "RabbitMQ AMQP协议服务", } } // 管理端口,尝试HTTP请求 baseURL := fmt.Sprintf("http://%s:%s", info.Host, info.Ports) client := &http.Client{ Timeout: time.Duration(common.Timeout) * time.Second, } req, err := http.NewRequestWithContext(ctx, "GET", baseURL, nil) if err != nil { return &ScanResult{ Success: false, Service: "rabbitmq", Error: err, } } resp, err := client.Do(req) if err != nil { return &ScanResult{ Success: false, Service: "rabbitmq", Error: err, } } defer resp.Body.Close() // 检查响应是否包含RabbitMQ特征 var banner string if resp.StatusCode == 200 || resp.StatusCode == 401 { body, _ := io.ReadAll(resp.Body) bodyStr := strings.ToLower(string(body)) if strings.Contains(bodyStr, "rabbitmq") { banner = "RabbitMQ管理界面" } else if strings.Contains(bodyStr, "management") { banner = "RabbitMQ服务 (管理端口)" } else { banner = "RabbitMQ消息队列服务" } } else { return &ScanResult{ Success: false, Service: "rabbitmq", Error: fmt.Errorf("无法识别为RabbitMQ服务"), } } common.LogSuccess(i18n.GetText("rabbitmq_service_identified", target, banner)) return &ScanResult{ Success: true, Service: "rabbitmq", Banner: banner, } } // init 自动注册插件 func init() { RegisterPlugin("rabbitmq", func() Plugin { return NewRabbitMQPlugin() }) }