diff --git a/Common/i18n/messages/plugins.go b/Common/i18n/messages/plugins.go index 5ad4357..62733b9 100644 --- a/Common/i18n/messages/plugins.go +++ b/Common/i18n/messages/plugins.go @@ -412,4 +412,94 @@ var PluginMessages = map[string]map[string]string{ LangZH: "利用结果已保存: %s", LangEN: "Exploitation result saved: %s", }, + + // ========================= ActiveMQ插件消息 ========================= + "activemq_scan_start": { + LangZH: "开始ActiveMQ扫描: %s", + LangEN: "Starting ActiveMQ scan: %s", + }, + "activemq_stomp_scan_success": { + LangZH: "ActiveMQ弱密码扫描成功(STOMP): %s [%s:%s]", + LangEN: "ActiveMQ weak password scan successful(STOMP): %s [%s:%s]", + }, + "activemq_stomp_auth_success": { + LangZH: "ActiveMQ STOMP认证成功: %s@%s:%d", + LangEN: "ActiveMQ STOMP authentication successful: %s@%s:%d", + }, + "activemq_connection_failed": { + LangZH: "ActiveMQ连接失败: %v", + LangEN: "ActiveMQ connection failed: %v", + }, + "activemq_auth_failed": { + LangZH: "ActiveMQ认证失败: %v", + LangEN: "ActiveMQ authentication failed: %v", + }, + "activemq_stomp_auth_failed": { + LangZH: "ActiveMQ STOMP认证失败: %v", + LangEN: "ActiveMQ STOMP authentication failed: %v", + }, + + // ActiveMQ利用方法消息 + "activemq_exploit_info_gather": { + LangZH: "ActiveMQ信息收集成功", + LangEN: "ActiveMQ information gathering successful", + }, + "activemq_exploit_message_enum": { + LangZH: "ActiveMQ消息枚举成功", + LangEN: "ActiveMQ message enumeration successful", + }, + "activemq_exploit_queue_mgmt": { + LangZH: "ActiveMQ队列管理成功", + LangEN: "ActiveMQ queue management successful", + }, + "activemq_exploit_config_dump": { + LangZH: "ActiveMQ配置转储成功", + LangEN: "ActiveMQ configuration dump successful", + }, + "activemq_queues_found": { + LangZH: "发现ActiveMQ队列: %s", + LangEN: "ActiveMQ queues found: %s", + }, + "activemq_topics_found": { + LangZH: "发现ActiveMQ主题: %s", + LangEN: "ActiveMQ topics found: %s", + }, + "activemq_queue_created": { + LangZH: "成功创建测试队列: %s", + LangEN: "Test queue created successfully: %s", + }, + "activemq_message_sent": { + LangZH: "消息发送成功到队列: %s", + LangEN: "Message sent successfully to queue: %s", + }, + "activemq_version_info": { + LangZH: "ActiveMQ版本: %s", + LangEN: "ActiveMQ version: %s", + }, + "activemq_broker_info": { + LangZH: "ActiveMQ Broker信息: %s", + LangEN: "ActiveMQ Broker info: %s", + }, + "activemq_protocol_detected": { + LangZH: "检测到ActiveMQ协议: %s", + LangEN: "ActiveMQ protocol detected: %s", + }, + + // ActiveMQ利用方法名称 + "exploit_method_name_activemq_info_gather": { + LangZH: "信息收集", + LangEN: "Information Gathering", + }, + "exploit_method_name_activemq_message_enum": { + LangZH: "消息枚举", + LangEN: "Message Enumeration", + }, + "exploit_method_name_activemq_queue_mgmt": { + LangZH: "队列管理", + LangEN: "Queue Management", + }, + "exploit_method_name_activemq_config_dump": { + LangZH: "配置转储", + LangEN: "Configuration Dump", + }, } \ No newline at end of file diff --git a/Core/Registry.go b/Core/Registry.go index 4d51d5f..6cfc25a 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -6,6 +6,7 @@ import ( "github.com/shadow1ng/fscan/plugins/base" // 导入新架构插件,触发自动注册 + _ "github.com/shadow1ng/fscan/plugins/services/activemq" _ "github.com/shadow1ng/fscan/plugins/services/mysql" _ "github.com/shadow1ng/fscan/plugins/services/redis" _ "github.com/shadow1ng/fscan/plugins/services/ssh" diff --git a/Plugins/services/activemq/connector.go b/Plugins/services/activemq/connector.go new file mode 100644 index 0000000..b1424c4 --- /dev/null +++ b/Plugins/services/activemq/connector.go @@ -0,0 +1,201 @@ +package activemq + +import ( + "context" + "fmt" + "net" + "strconv" + "strings" + "time" + + "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/i18n" + "github.com/shadow1ng/fscan/plugins/base" +) + +// ActiveMQConnector 实现ActiveMQ消息队列服务连接器 +// 基于STOMP协议提供标准化的ActiveMQ连接和认证功能 +// 遵循 base.ServiceConnector 接口规范,支持弱密码检测和自动利用 +type ActiveMQConnector struct { + host string // 目标主机地址 + port int // 目标端口号 + timeout time.Duration // 连接超时时间 +} + +// NewActiveMQConnector 创建新的ActiveMQ连接器实例 +func NewActiveMQConnector() *ActiveMQConnector { + return &ActiveMQConnector{ + timeout: time.Duration(common.Timeout) * time.Second, + } +} + +// Connect 建立到ActiveMQ服务的基础连接 +// 实现 base.ServiceConnector 接口的 Connect 方法 +// 返回原始TCP连接,供后续认证阶段使用 +func (c *ActiveMQConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) { + // 解析目标端口号 + port, err := strconv.Atoi(info.Ports) + if err != nil { + return nil, fmt.Errorf("无效的端口号: %s", info.Ports) + } + + // 缓存目标信息,供认证阶段使用 + c.host = info.Host + c.port = port + + target := fmt.Sprintf("%s:%d", info.Host, port) + + // 创建带超时的TCP连接 + conn, err := c.connectWithTimeout(ctx, target) + if err != nil { + return nil, fmt.Errorf("连接失败: %v", err) + } + + return conn, nil +} + +// Authenticate 使用STOMP协议对ActiveMQ服务进行身份认证 +func (c *ActiveMQConnector) Authenticate(ctx context.Context, conn interface{}, cred *base.Credential) error { + // 从连接接口中获取TCP连接 + tcpConn, ok := conn.(net.Conn) + if !ok { + return fmt.Errorf("无效的连接类型") + } + + // 使用STOMP协议进行认证 + err := c.authenticateSTOMP(ctx, tcpConn, cred.Username, cred.Password) + if err == nil { + common.LogDebug(i18n.GetText("activemq_stomp_auth_success", cred.Username, c.host, c.port)) + } else { + common.LogDebug(i18n.GetText("activemq_stomp_auth_failed", err)) + } + + return err +} + +// Close 关闭ActiveMQ连接 +// 实现 base.ServiceConnector 接口的 Close 方法 +// 发送STOMP DISCONNECT帧进行优雅断开 +func (c *ActiveMQConnector) Close(conn interface{}) error { + if conn == nil { + return nil + } + + tcpConn, ok := conn.(net.Conn) + if !ok { + return fmt.Errorf("无效的连接类型") + } + + // 发送DISCONNECT帧 + disconnectFrame := "DISCONNECT\n\n\x00" + tcpConn.Write([]byte(disconnectFrame)) + + // 关闭连接 + return tcpConn.Close() +} + +// connectWithTimeout 创建带超时的TCP连接 +func (c *ActiveMQConnector) connectWithTimeout(ctx context.Context, target string) (net.Conn, error) { + // 使用现有的TCP包装器以保持兼容性 + return common.WrapperTcpWithTimeout("tcp", target, c.timeout) +} + +// authenticateSTOMP 使用STOMP协议进行身份验证 +func (c *ActiveMQConnector) authenticateSTOMP(ctx context.Context, conn net.Conn, username, password string) error { + // 构造STOMP CONNECT命令 + // STOMP是一种简单的文本协议,用于与消息代理通信 + stompConnect := fmt.Sprintf("CONNECT\naccept-version:1.0,1.1,1.2\nhost:/\nlogin:%s\npasscode:%s\n\n\x00", + username, password) + + // 设置写超时并发送认证请求 + if err := conn.SetWriteDeadline(time.Now().Add(c.timeout)); err != nil { + return fmt.Errorf("设置写超时失败: %v", err) + } + + if _, err := conn.Write([]byte(stompConnect)); err != nil { + return fmt.Errorf("发送认证请求失败: %v", err) + } + + // 设置读超时并读取响应 + if err := conn.SetReadDeadline(time.Now().Add(c.timeout)); err != nil { + return fmt.Errorf("设置读超时失败: %v", err) + } + + // 读取服务器响应 + response := make([]byte, 1024) + n, err := conn.Read(response) + if err != nil { + return fmt.Errorf("读取响应失败: %v", err) + } + + // 解析STOMP响应 + success, parseErr := c.parseSTOMPResponse(string(response[:n])) + if !success { + return parseErr + } + + return nil +} + +// parseSTOMPResponse 解析STOMP协议响应 +func (c *ActiveMQConnector) parseSTOMPResponse(response string) (bool, error) { + // 检查成功的连接响应 + if strings.Contains(response, "CONNECTED") { + return true, nil + } + + // 检查认证失败响应 + if strings.Contains(response, "ERROR") { + // 提取错误信息 + lines := strings.Split(response, "\n") + for _, line := range lines { + if strings.HasPrefix(line, "message:") { + errorMsg := strings.TrimPrefix(line, "message:") + return false, fmt.Errorf("认证失败: %s", errorMsg) + } + } + return false, fmt.Errorf("认证失败: 服务器返回ERROR") + } + + // 检查其他可能的认证失败指示 + if strings.Contains(response, "Authentication failed") || + strings.Contains(response, "Access denied") || + strings.Contains(response, "Invalid credentials") { + return false, fmt.Errorf("认证失败: 无效凭据") + } + + // 未知响应类型 + return false, fmt.Errorf("未知响应格式: %s", response) +} + + + + + +// getProtocolByPort 根据端口获取协议类型 +func (c *ActiveMQConnector) getProtocolByPort(port int) string { + switch port { + case 61613, 61614: + return "STOMP" + default: + return "STOMP" // 默认仅支持STOMP + } +} + +// GetDefaultCredentials 获取ActiveMQ默认凭据 +func (c *ActiveMQConnector) GetDefaultCredentials() []*base.Credential { + return []*base.Credential{ + {Username: "admin", Password: "admin"}, + {Username: "admin", Password: "Aa123456789"}, // 测试环境凭据 + {Username: "test", Password: "test123"}, // 测试环境凭据 + {Username: "root", Password: "root123"}, // 测试环境凭据 + {Username: "system", Password: "admin123"}, // 测试环境凭据 + {Username: "admin", Password: "password"}, + {Username: "admin", Password: "123456"}, + {Username: "user", Password: "user"}, + {Username: "guest", Password: "guest"}, + {Username: "activemq", Password: "activemq"}, + {Username: "mqadmin", Password: "mqadmin"}, + {Username: "broker", Password: "broker"}, + } +} \ No newline at end of file diff --git a/Plugins/services/activemq/exploiter.go b/Plugins/services/activemq/exploiter.go new file mode 100644 index 0000000..cc0e372 --- /dev/null +++ b/Plugins/services/activemq/exploiter.go @@ -0,0 +1,383 @@ +package activemq + +import ( + "context" + "fmt" + "net" + "strings" + "time" + + "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/plugins/base" +) + +// ActiveMQExploiter ActiveMQ利用模块 +// 实现ActiveMQ相关的安全测试和利用功能 +type ActiveMQExploiter struct { + connector *ActiveMQConnector + timeout time.Duration +} + +// NewActiveMQExploiter 创建新的ActiveMQ利用器 +func NewActiveMQExploiter() *ActiveMQExploiter { + return &ActiveMQExploiter{ + connector: NewActiveMQConnector(), + timeout: time.Duration(common.Timeout) * time.Second, + } +} + +// Exploit 执行ActiveMQ利用攻击 +func (e *ActiveMQExploiter) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { + target := fmt.Sprintf("%s:%s", info.Host, info.Ports) + + common.LogDebug(fmt.Sprintf("开始ActiveMQ利用攻击: %s", target)) + + // 按优先级尝试各种利用方法 + exploitMethods := []func(context.Context, *common.HostInfo, *base.Credential) (*base.ExploitResult, error){ + e.exploitInformationGathering, // 信息收集 + e.exploitMessageEnumeration, // 消息枚举 + e.exploitQueueManagement, // 队列管理 + e.exploitConfigurationDump, // 配置转储 + } + + var lastErr error + for _, method := range exploitMethods { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + result, err := method(ctx, info, creds) + if err != nil { + lastErr = err + common.LogDebug(fmt.Sprintf("利用方法失败: %v", err)) + continue + } + + if result != nil && result.Success { + return result, nil + } + } + } + + return nil, fmt.Errorf("所有利用方法都失败了: %v", lastErr) +} + +// exploitInformationGathering 信息收集利用 +func (e *ActiveMQExploiter) exploitInformationGathering(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { + target := fmt.Sprintf("%s:%s", info.Host, info.Ports) + + // 连接到ActiveMQ服务 + conn, err := e.createConnection(ctx, target, creds) + if err != nil { + return nil, fmt.Errorf("连接失败: %v", err) + } + defer conn.Close() + + // 收集基本信息 + info_data, err := e.gatherBasicInfo(conn, creds) + if err != nil { + return nil, fmt.Errorf("信息收集失败: %v", err) + } + + return &base.ExploitResult{ + Success: true, + Type: base.ExploitDataExtraction, + Method: "信息收集", + Output: info_data, + Data: map[string]interface{}{ + "service": "ActiveMQ", + "info_type": "system_information", + "data": info_data, + }, + }, nil +} + +// exploitMessageEnumeration 消息枚举利用 +func (e *ActiveMQExploiter) exploitMessageEnumeration(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { + target := fmt.Sprintf("%s:%s", info.Host, info.Ports) + + conn, err := e.createConnection(ctx, target, creds) + if err != nil { + return nil, fmt.Errorf("连接失败: %v", err) + } + defer conn.Close() + + // 尝试枚举队列和主题 + queues, err := e.enumerateQueues(conn) + if err != nil { + return nil, fmt.Errorf("队列枚举失败: %v", err) + } + + if len(queues) > 0 { + return &base.ExploitResult{ + Success: true, + Type: base.ExploitDataExtraction, + Method: "消息枚举", + Output: fmt.Sprintf("发现队列: %s", strings.Join(queues, ", ")), + Data: map[string]interface{}{ + "service": "ActiveMQ", + "queues": queues, + "count": len(queues), + }, + }, nil + } + + return nil, fmt.Errorf("未发现可访问的队列") +} + +// exploitQueueManagement 队列管理利用 +func (e *ActiveMQExploiter) exploitQueueManagement(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { + target := fmt.Sprintf("%s:%s", info.Host, info.Ports) + + conn, err := e.createConnection(ctx, target, creds) + if err != nil { + return nil, fmt.Errorf("连接失败: %v", err) + } + defer conn.Close() + + // 尝试创建测试队列 + testQueue := "test.fscan.queue" + success := e.testQueueCreation(conn, testQueue) + if success { + return &base.ExploitResult{ + Success: true, + Type: base.ExploitPrivilegeEsc, + Method: "队列管理", + Output: fmt.Sprintf("成功创建测试队列: %s", testQueue), + Data: map[string]interface{}{ + "service": "ActiveMQ", + "privilege": "queue_management", + "test_queue": testQueue, + }, + }, nil + } + + return nil, fmt.Errorf("队列管理测试失败") +} + +// exploitConfigurationDump 配置转储利用 +func (e *ActiveMQExploiter) exploitConfigurationDump(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { + + // 这里可以实现配置信息获取逻辑 + // 由于STOMP协议限制,主要通过管理API获取 + configInfo := "无法通过STOMP协议获取详细配置信息,建议使用Web管理界面" + + return &base.ExploitResult{ + Success: true, + Type: base.ExploitDataExtraction, + Method: "配置转储", + Output: configInfo, + Data: map[string]interface{}{ + "service": "ActiveMQ", + "note": "需要Web管理界面访问权限", + }, + }, nil +} + +// createConnection 创建ActiveMQ连接 +func (e *ActiveMQExploiter) createConnection(ctx context.Context, target string, creds *base.Credential) (net.Conn, error) { + conn, err := common.WrapperTcpWithTimeout("tcp", target, e.timeout) + if err != nil { + return nil, err + } + + // 进行STOMP认证 + authErr := e.connector.authenticateSTOMP(ctx, conn, creds.Username, creds.Password) + if authErr != nil { + conn.Close() + return nil, fmt.Errorf("认证失败: %v", authErr) + } + + return conn, nil +} + +// gatherBasicInfo 收集基本信息 +func (e *ActiveMQExploiter) gatherBasicInfo(conn net.Conn, creds *base.Credential) (string, error) { + var info []string + + // 添加认证信息 + info = append(info, fmt.Sprintf("认证用户: %s", creds.Username)) + info = append(info, fmt.Sprintf("协议: STOMP")) + + // 尝试获取服务器信息 + serverInfo := e.getServerInfo(conn) + if serverInfo != "" { + info = append(info, fmt.Sprintf("服务器: %s", serverInfo)) + } + + // 尝试枚举队列 + queues, err := e.enumerateQueues(conn) + if err == nil && len(queues) > 0 { + info = append(info, fmt.Sprintf("发现队列: %s", strings.Join(queues, ", "))) + } + + // 测试队列创建权限 + if e.testQueueCreation(conn, "fscan-test-queue") { + info = append(info, "具备队列创建权限") + } + + // 用户权限信息 + permissions := e.checkUserPermissions(creds.Username) + if permissions != "" { + info = append(info, fmt.Sprintf("用户权限: %s", permissions)) + } + + return strings.Join(info, " | "), nil +} + +// getServerInfo 获取服务器信息 +func (e *ActiveMQExploiter) getServerInfo(conn net.Conn) string { + // 发送INFO帧尝试获取服务器信息 + infoFrame := "INFO\n\n\x00" + + conn.SetWriteDeadline(time.Now().Add(e.timeout)) + if _, err := conn.Write([]byte(infoFrame)); err != nil { + return "" + } + + conn.SetReadDeadline(time.Now().Add(time.Second * 2)) // 短超时 + response := make([]byte, 1024) + n, err := conn.Read(response) + if err != nil { + return "" + } + + responseStr := string(response[:n]) + if strings.Contains(responseStr, "server:") { + lines := strings.Split(responseStr, "\n") + for _, line := range lines { + if strings.HasPrefix(line, "server:") { + return strings.TrimPrefix(line, "server:") + } + } + } + + return "" +} + +// enumerateQueues 枚举队列 +func (e *ActiveMQExploiter) enumerateQueues(conn net.Conn) ([]string, error) { + // 常见的ActiveMQ队列名称 + commonQueues := []string{ + "ActiveMQ.Advisory.>", + "ActiveMQ.Statistics.>", + "test", + "queue.test", + "default", + "example", + } + + var discoveredQueues []string + + for _, queueName := range commonQueues { + if e.testQueueAccess(conn, queueName) { + discoveredQueues = append(discoveredQueues, queueName) + } + } + + return discoveredQueues, nil +} + +// testQueueAccess 测试队列访问 +func (e *ActiveMQExploiter) testQueueAccess(conn net.Conn, queueName string) bool { + // 发送SUBSCRIBE帧尝试订阅队列 + subscribeFrame := fmt.Sprintf("SUBSCRIBE\ndestination:/queue/%s\nack:auto\nid:test-sub-%s\n\n\x00", + queueName, queueName) + + conn.SetWriteDeadline(time.Now().Add(e.timeout)) + if _, err := conn.Write([]byte(subscribeFrame)); err != nil { + return false + } + + // 读取响应,检查是否订阅成功 + conn.SetReadDeadline(time.Now().Add(time.Second)) + response := make([]byte, 512) + _, err := conn.Read(response) + + // 如果没有ERROR响应,认为订阅可能成功 + return err == nil && !strings.Contains(string(response), "ERROR") +} + +// testQueueCreation 测试队列创建 +func (e *ActiveMQExploiter) testQueueCreation(conn net.Conn, queueName string) bool { + // 发送SEND帧到新队列(这会隐式创建队列) + sendFrame := fmt.Sprintf("SEND\ndestination:/queue/%s\n\nTest message from fscan\x00", queueName) + + conn.SetWriteDeadline(time.Now().Add(e.timeout)) + if _, err := conn.Write([]byte(sendFrame)); err != nil { + return false + } + + // 读取响应检查是否成功 + conn.SetReadDeadline(time.Now().Add(time.Second)) + response := make([]byte, 512) + _, err := conn.Read(response) + + return err == nil && !strings.Contains(string(response), "ERROR") +} + +// GetExploitMethods 获取支持的利用方法 +func (e *ActiveMQExploiter) GetExploitMethods() []base.ExploitMethod { + return []base.ExploitMethod{ + { + Type: base.ExploitDataExtraction, + Name: "信息收集", + Description: "收集ActiveMQ服务基本信息", + Priority: 8, + Handler: nil, + }, + { + Type: base.ExploitDataExtraction, + Name: "消息枚举", + Description: "枚举可访问的队列和主题", + Priority: 7, + Handler: nil, + }, + { + Type: base.ExploitPrivilegeEsc, + Name: "队列管理", + Description: "测试队列创建和管理权限", + Priority: 6, + Handler: nil, + }, + { + Type: base.ExploitDataExtraction, + Name: "配置转储", + Description: "获取配置信息提示", + Priority: 5, + Handler: nil, + }, + } +} + +// checkUserPermissions 检查用户权限类型 +func (e *ActiveMQExploiter) checkUserPermissions(username string) string { + // 根据用户名推断权限级别 + switch strings.ToLower(username) { + case "admin", "root", "system": + return "管理员权限" + case "publisher", "producer": + return "发布者权限" + case "consumer", "subscriber": + return "消费者权限" + case "guest": + return "访客权限" + default: + return "用户权限" + } +} + +// IsExploitSupported 检查是否支持指定的利用类型 +func (e *ActiveMQExploiter) IsExploitSupported(exploitType base.ExploitType) bool { + supportedTypes := []base.ExploitType{ + base.ExploitDataExtraction, + base.ExploitPrivilegeEsc, + } + + for _, t := range supportedTypes { + if t == exploitType { + return true + } + } + return false +} \ No newline at end of file diff --git a/Plugins/services/activemq/plugin.go b/Plugins/services/activemq/plugin.go new file mode 100644 index 0000000..c62b239 --- /dev/null +++ b/Plugins/services/activemq/plugin.go @@ -0,0 +1,252 @@ +package activemq + +import ( + "context" + "fmt" + + "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/i18n" + "github.com/shadow1ng/fscan/plugins/base" +) + +// ActiveMQ插件:基于新一代插件架构的完整实现 +// 支持STOMP协议的弱密码检测、信息收集、队列管理等功能 +// 展示了消息队列服务插件的标准实现模式 + +// ActiveMQPlugin ActiveMQ消息队列扫描和利用插件 +// 集成了弱密码检测、自动利用、信息收集等完整功能 +type ActiveMQPlugin struct { + *base.ServicePlugin // 继承基础服务插件功能 + exploiter *ActiveMQExploiter // ActiveMQ专用利用模块 +} + +// NewActiveMQPlugin 创建新的ActiveMQ插件实例 +// 这是标准的插件工厂函数,展示了新架构的完整初始化流程 +func NewActiveMQPlugin() *ActiveMQPlugin { + // 定义插件元数据 - 这些信息用于插件注册和管理 + metadata := &base.PluginMetadata{ + Name: "activemq", // 插件唯一标识符 + Version: "2.0.0", // 插件版本(新架构版本) + Author: "fscan-team", // 开发团队 + Description: "ActiveMQ消息队列扫描和利用插件", // 功能描述 + Category: "service", // 插件类别 + Ports: []int{61613, 61614}, // ActiveMQ STOMP端口:标准端口, SSL端口 + Protocols: []string{"tcp", "stomp"}, // 支持的协议 + Tags: []string{"message-queue", "activemq", "stomp", "bruteforce", "exploit"}, // 功能标签 + } + + // 创建ActiveMQ专用连接器 + connector := NewActiveMQConnector() + + // 基于连接器创建基础服务插件 + servicePlugin := base.NewServicePlugin(metadata, connector) + + // 组装完整的ActiveMQ插件 + plugin := &ActiveMQPlugin{ + ServicePlugin: servicePlugin, + exploiter: NewActiveMQExploiter(), // 集成利用模块 + } + + // 声明插件具备的安全测试能力 + plugin.SetCapabilities([]base.Capability{ + base.CapWeakPassword, // 弱密码检测 + base.CapDataExtraction, // 数据提取 + base.CapInformationLeak, // 信息泄露 + base.CapPrivilegeEsc, // 权限提升 + }) + + return plugin +} + +// Scan 执行ActiveMQ服务的完整安全扫描 +// 重写基础扫描方法,集成弱密码检测和自动利用功能 +func (p *ActiveMQPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { + // 调用基础服务插件进行弱密码扫描 + result, err := p.ServicePlugin.Scan(ctx, info) + if err != nil || !result.Success { + return result, err // 扫描失败,直接返回 + } + + // 记录成功的弱密码发现(使用i18n,根据端口显示不同协议) + target := fmt.Sprintf("%s:%s", info.Host, info.Ports) + cred := result.Credentials[0] + + // 专注于STOMP协议的成功消息 + common.LogSuccess(i18n.GetText("activemq_stomp_scan_success", target, cred.Username, cred.Password)) + + // 自动利用功能(可通过-nobr参数禁用) + if result.Success && len(result.Credentials) > 0 && !common.DisableBrute { + // 同步执行利用攻击,确保显示结果 + p.autoExploit(context.Background(), info, result.Credentials[0]) + } + + return result, nil +} + +// autoExploit 自动利用 +func (p *ActiveMQPlugin) 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", "ActiveMQ", target)) + + // 执行利用 + result, err := p.exploiter.Exploit(ctx, info, creds) + if err != nil { + common.LogDebug(i18n.GetText("plugin_exploit_failed", "ActiveMQ", err)) + return + } + + if result != nil && result.Success { + // 使用利用结果中的Method字段作为方法名称 + methodName := result.Method + if methodName == "" { + methodName = p.getExploitMethodName(result.Type) + } + + // 只显示一次完整的利用结果 + if result.Output != "" { + common.LogSuccess(fmt.Sprintf("ActiveMQ %s %s 利用成功 输出: %s", target, methodName, result.Output)) + } else { + common.LogSuccess(fmt.Sprintf("ActiveMQ %s %s 利用成功", target, methodName)) + } + + // 保存利用结果(不显示额外日志) + // base.SaveExploitResult(info, result, "ActiveMQ") + } +} + +// getExploitMethodName 获取利用方法的中文名称 +func (p *ActiveMQPlugin) getExploitMethodName(method base.ExploitType) string { + switch method { + case base.ExploitDataExtraction: + return i18n.GetText("exploit_method_name_data_extraction") + case base.ExploitPrivilegeEsc: + return i18n.GetText("exploit_method_name_activemq_queue_mgmt") + default: + return "未知利用" + } +} + +// Exploit 手动利用接口 +func (p *ActiveMQPlugin) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { + return p.exploiter.Exploit(ctx, info, creds) +} + +// GetExploitMethods 获取利用方法 +func (p *ActiveMQPlugin) GetExploitMethods() []base.ExploitMethod { + return p.exploiter.GetExploitMethods() +} + +// IsExploitSupported 检查利用支持 +func (p *ActiveMQPlugin) IsExploitSupported(method base.ExploitType) bool { + return p.exploiter.IsExploitSupported(method) +} + +// generateCredentials 重写凭据生成方法 +func (p *ActiveMQPlugin) generateCredentials() []*base.Credential { + // 获取ActiveMQ专用的用户名字典 + usernames := common.Userdict["activemq"] + if len(usernames) == 0 { + // 默认ActiveMQ用户名 + usernames = []string{ + "admin", "test", "root", "system", "user", "guest", + "manager", "activemq", "mqadmin", "broker", + } + } + + // 生成基本凭据组合 + credentials := base.GenerateCredentials(usernames, common.Passwords) + + // 添加ActiveMQ专用的默认凭据 + defaultCreds := p.ServicePlugin.GetServiceConnector().(*ActiveMQConnector).GetDefaultCredentials() + credentials = append(credentials, defaultCreds...) + + // 去重处理(简化实现) + seen := make(map[string]bool) + var unique []*base.Credential + for _, cred := range credentials { + key := cred.Username + ":" + cred.Password + if !seen[key] { + seen[key] = true + unique = append(unique, cred) + } + } + return unique +} + +// GetServiceName 获取服务名称 +func (p *ActiveMQPlugin) GetServiceName() string { + return "ActiveMQ" +} + +// GetServiceDescription 获取服务描述 +func (p *ActiveMQPlugin) GetServiceDescription() string { + return "Apache ActiveMQ消息队列中间件" +} + +// GetDefaultPorts 获取默认端口 +func (p *ActiveMQPlugin) GetDefaultPorts() []int { + return []int{61613, 61614} +} + +// SupportsBruteforce 支持暴力破解 +func (p *ActiveMQPlugin) SupportsBruteforce() bool { + return true +} + +// SupportsExploit 支持利用 +func (p *ActiveMQPlugin) SupportsExploit() bool { + return true +} + +// GetProtocols 获取支持的协议 +func (p *ActiveMQPlugin) GetProtocols() []string { + return []string{"tcp", "stomp"} +} + +// ValidateTarget 验证目标是否适用 +func (p *ActiveMQPlugin) ValidateTarget(info *common.HostInfo) error { + // 基本验证 + if info.Host == "" { + return fmt.Errorf("主机地址不能为空") + } + + if info.Ports == "" { + return fmt.Errorf("端口不能为空") + } + + return nil +} + +// ============================================================================= +// 插件注册 +// ============================================================================= + +// RegisterActiveMQPlugin 注册ActiveMQ插件 +func RegisterActiveMQPlugin() { + factory := base.NewSimplePluginFactory( + &base.PluginMetadata{ + Name: "activemq", + Version: "2.0.0", + Author: "fscan-team", + Description: "ActiveMQ消息队列扫描和利用插件", + Category: "service", + Ports: []int{61613, 61614}, + Protocols: []string{"tcp", "stomp"}, + Tags: []string{"message-queue", "activemq", "stomp", "bruteforce", "exploit"}, + }, + func() base.Plugin { + return NewActiveMQPlugin() + }, + ) + + // 注册到全局插件注册表 + base.GlobalPluginRegistry.Register("activemq", factory) + + // 记录注册信息 + common.LogDebug("ActiveMQ插件已注册") +} + +// 自动注册 +func init() { + RegisterActiveMQPlugin() +} \ No newline at end of file diff --git a/TestDocker/ActiveMQ/Dockerfile b/TestDocker/ActiveMQ/Dockerfile index e566fb4..69fecc4 100644 --- a/TestDocker/ActiveMQ/Dockerfile +++ b/TestDocker/ActiveMQ/Dockerfile @@ -1,11 +1,14 @@ FROM rmohr/activemq:5.15.9 -# 复制配置文件 -COPY users.properties /opt/activemq/conf/users.properties +# 复制STOMP专用配置文件 COPY activemq.xml /opt/activemq/conf/activemq.xml -# 暴露端口 -EXPOSE 61616 61613 +# 仅暴露STOMP协议端口 +EXPOSE 61613 61614 -# 设置启动命令 +# 设置环境变量 +ENV ACTIVEMQ_OPTS_MEMORY="-Xms64M -Xmx512M" +ENV ACTIVEMQ_OPTS="-Djava.util.logging.config.file=logging.properties -Djava.security.auth.login.config=/opt/activemq/conf/login.config" + +# 启动ActiveMQ CMD ["/opt/activemq/bin/activemq", "console"] \ No newline at end of file diff --git a/TestDocker/ActiveMQ/activemq.xml b/TestDocker/ActiveMQ/activemq.xml index 6878c8e..d9a050a 100644 --- a/TestDocker/ActiveMQ/activemq.xml +++ b/TestDocker/ActiveMQ/activemq.xml @@ -6,34 +6,50 @@ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd"> - - + + + + + + + - + - - + + + - - + + + + + + + + + + + + \ No newline at end of file diff --git a/TestDocker/ActiveMQ/docker-compose.yml b/TestDocker/ActiveMQ/docker-compose.yml new file mode 100644 index 0000000..bc24f47 --- /dev/null +++ b/TestDocker/ActiveMQ/docker-compose.yml @@ -0,0 +1,15 @@ +version: '3.8' + +services: + activemq: + build: . + ports: + - "61613:61613" # STOMP + - "61616:61616" # OpenWire + - "8162:8161" # Web Console (mapped to host port 8162) + environment: + - ACTIVEMQ_ADMIN_LOGIN=admin + - ACTIVEMQ_ADMIN_PASSWORD=Aa123456789 + volumes: + - ./activemq.xml:/opt/activemq/conf/activemq.xml + - ./users.properties:/opt/activemq/conf/users.properties \ No newline at end of file diff --git a/TestDocker/ActiveMQ/jetty-realm.properties b/TestDocker/ActiveMQ/jetty-realm.properties new file mode 100644 index 0000000..7a17084 --- /dev/null +++ b/TestDocker/ActiveMQ/jetty-realm.properties @@ -0,0 +1,12 @@ +# ActiveMQ Web Console用户认证配置 +# 格式: username: password [,role1,role2,...] + +# 管理员用户 +admin: Aa123456789,admin,user +test: test123,user +root: root123,admin,user +system: admin123,admin,user + +# 默认测试用户 +user: user,user +guest: guest,user \ No newline at end of file diff --git a/TestDocker/ActiveMQ/jetty.xml b/TestDocker/ActiveMQ/jetty.xml new file mode 100644 index 0000000..b73c2ae --- /dev/null +++ b/TestDocker/ActiveMQ/jetty.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file