fscan/Plugins/services/activemq/plugin.go
ZacharyZcR 51735c4e25 feat: 实现-nobr/-ne参数和服务识别功能
新增功能:
- 添加-ne参数禁用利用攻击,实现弱密码检测和利用攻击的分离控制
- 实现-nobr模式下所有插件的服务识别功能,单数据包获取最大信息
- 修复端口插件匹配逻辑,只调用适配端口的插件提升扫描效率

插件改造:
- MySQL: 通过握手包识别获取版本信息
- Redis: INFO命令识别版本,优先检测未授权访问
- SSH: Banner识别获取协议和服务器版本
- ActiveMQ: STOMP协议识别获取版本和认证状态

技术改进:
- 新增端口匹配算法确保精准插件调用
- 完善i18n国际化支持所有新功能
- 统一服务识别接口设计便于扩展
2025-08-08 03:32:00 +08:00

344 lines
10 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"
"strings"
"time"
"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) {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
// 如果禁用暴力破解,则进行基础服务识别
if common.DisableBrute {
return p.performServiceIdentification(ctx, info)
}
// 调用基础服务插件进行弱密码扫描
result, err := p.ServicePlugin.Scan(ctx, info)
if err != nil || !result.Success {
return result, err // 扫描失败,直接返回
}
// 记录成功的弱密码发现使用i18n根据端口显示不同协议
cred := result.Credentials[0]
// 专注于STOMP协议的成功消息
common.LogSuccess(i18n.GetText("activemq_stomp_scan_success", target, cred.Username, cred.Password))
// 自动利用功能(可通过-ne参数禁用
if result.Success && len(result.Credentials) > 0 && !common.DisableExploit {
// 同步执行利用攻击,确保显示结果
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
}
// performServiceIdentification 执行服务识别(-nobr模式
func (p *ActiveMQPlugin) performServiceIdentification(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
// 尝试连接到ActiveMQ服务进行基础识别
conn, err := common.WrapperTcpWithTimeout("tcp", target, time.Duration(common.Timeout)*time.Second)
if err != nil {
return &base.ScanResult{
Success: false,
Error: err,
}, nil
}
defer conn.Close()
// 尝试STOMP协议识别
stompInfo, isActiveMQ := p.identifySTOMPService(conn)
if isActiveMQ {
// 记录服务识别成功
common.LogSuccess(i18n.GetText("activemq_service_identified", target, "STOMP", stompInfo))
return &base.ScanResult{
Success: true,
Service: "ActiveMQ",
Banner: stompInfo,
Extra: map[string]interface{}{
"service": "ActiveMQ",
"protocol": "STOMP",
"port": info.Ports,
"info": stompInfo,
},
}, nil
}
// 如果无法识别为ActiveMQ返回一般服务信息
return &base.ScanResult{
Success: false,
Error: fmt.Errorf("无法识别为ActiveMQ服务"),
}, nil
}
// identifySTOMPService 识别STOMP协议服务
func (p *ActiveMQPlugin) identifySTOMPService(conn net.Conn) (string, bool) {
// 发送STOMP CONNECT帧进行协议识别不提供凭据
connectFrame := "CONNECT\naccept-version:1.0,1.1,1.2\nhost:localhost\n\n\x00"
conn.SetWriteDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second))
if _, err := conn.Write([]byte(connectFrame)); err != nil {
return "", false
}
// 读取响应
conn.SetReadDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second))
response := make([]byte, 1024)
n, err := conn.Read(response)
if err != nil {
return "", false
}
responseStr := string(response[:n])
// 检查是否为STOMP协议响应
if strings.Contains(responseStr, "CONNECTED") {
// 提取版本信息
version := "unknown"
if strings.Contains(responseStr, "version:") {
lines := strings.Split(responseStr, "\n")
for _, line := range lines {
if strings.HasPrefix(line, "version:") {
version = strings.TrimPrefix(line, "version:")
break
}
}
}
return fmt.Sprintf("STOMP协议版本: %s", version), true
} else if strings.Contains(responseStr, "ERROR") {
// 即使返回错误但能识别STOMP协议格式
return "STOMP协议需要认证", true
}
return "", false
}
// 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()
}