mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 05:56:46 +08:00
193 lines
5.7 KiB
Go
193 lines
5.7 KiB
Go
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"},
|
||
}
|
||
} |