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

193 lines
5.7 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 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 方法
// 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"},
}
}