mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00

新增功能: - 基于STOMP协议的ActiveMQ弱密码检测 - 完整的多语言i18n支持(中英文) - 自动信息收集和权限识别 - 队列枚举和管理权限检测 - 优化的Docker测试环境配置 技术特性: - 支持端口61613(STOMP)和61614(STOMP+SSL) - 智能用户权限分析 - 异步利用执行机制 - 统一的插件架构设计 - 完善的错误处理和日志记录 测试环境: - 简化的ActiveMQ Docker配置 - 预配置多种测试凭据 - 专注STOMP协议,提升性能
383 lines
11 KiB
Go
383 lines
11 KiB
Go
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
|
||
} |