mqqm文件函数CQmPacket-CQmPacket分析
字数 1734 2025-08-22 12:22:24

MSMQ协议CQmPacket::CQmPacket函数分析与漏洞研究

1. 概述

本文档详细分析MSMQ协议中CQmPacket::CQmPacket函数的实现原理、数据结构处理机制以及其中存在的安全漏洞。该函数主要用于处理UserMessage数据包,包含多个必需和可选的消息头结构。

2. 函数参数分析

CQmPacket::CQmPacket函数主要参数:

  • 参数一:原始数据包指针
  • 参数二:BaseHeader头指针
  • 返回值:处理后的数据包结构

3. BaseHeader结构定义

根据微软官方文档定义,BaseHeader结构如下:

typedef struct {
    uint8_t VersionNumber;        // 版本号
    uint8_t Reserved;             // 保留字段
    uint16_t Flags;               // 标志位
    uint32_t Signature;           // 签名(固定值0x524F494C)
    uint32_t PacketSize;          // 数据包大小
    uint32_t TimeToReachQueue;    // 到达队列时间
    // 其他字段...
} BaseHeader;

4. UserMessage数据包结构

UserMessage数据包包含以下部分:

必需头:

  • BaseHeader
  • UserHeader
  • MessagePropertiesHeader

可选头:

  • TransactionHeader
  • SecurityHeader
  • DebugHeader
  • SoapHeader
  • MultiQueueFormatHeader
  • SessionHeader

5. 头结构验证机制

5.1 CBaseHeader::SectionIsValid验证流程

验证函数接收三个参数:

  1. baseHeader指针
  2. 消息最大限制值
  3. 固定布尔值1

验证步骤:

  1. 检查BaseHeader大小是否超过最大限制
  2. 验证BaseHeader数据体是否超出范围
  3. 验证签名是否为固定值0x524F494C
  4. 验证版本号是否为0x10

5.2 其他头结构验证

类似验证流程,包括:

  • 固定值验证
  • 范围检查
  • 合理性检查

6. GetNextSection函数

用于获取下一个节(头结构):

void* GetNextSection(void* prevHeader, int param=0);

工作原理:

  1. 从上一个头结构中获取数据大小
  2. 在当前头结构地址上偏移该大小
  3. 返回下一个头结构的地址

7. CQmPacket结构定义

完整结构定义如下:

typedef struct {
    int64_t field1;                 // 'this'指针
    int64_t field2;                 // 跳过初始化
    int64_t field3;                 // 跳过初始化
    int64_t pPacket;                // 原始数据包指针(由a3参数赋值)
    int64_t pBaseHeader;            // BaseHeader指针
    int64_t pUserHeader;            // UserHeader指针(this+6)
    int64_t pXactHeader;            // 事务头(this+7)
    int64_t pSecurityHeader;        // 安全头(this+8)
    int64_t pPropertyHeader;        // 属性头(this+9)
    int64_t pDebugSection;          // 调试节(this+10)
    int64_t pBaseMqfHeader;         // MQF基础头(this+11)
    int64_t pBaseMqfHeader2;        // MQF基础头(this+12)
    int64_t pBaseMqfHeader3;        // MQF基础头(this+13)
    int64_t pMqfSignatureHeader;    // MQF签名头(this+14)
    int64_t pSRMPEnvelopeHeader;    // SRMP信封头(this+15,未验证)
    int64_t pCompoundMessageHeader; // 复合消息头(this+16,未验证)
    int64_t unKnownReservedX31;     // 保留位X31(this+17,未验证)
    int64_t unKnownReservedX32;     // 保留位X32(this+18,未验证)
    int64_t SoapHeader;             // SOAP头(this+19,未验证)
    int64_t SoapHeaderBody;         // SOAP头体(this+20,未验证)
    int64_t unKnownReservedX41;     // 保留位X41(this+21,未验证)
    int64_t unKnown22;              // 未知头22(this+22,未验证)
    int64_t pMsgGroupHeader;        // 消息组头(this+23,已验证)
    int64_t pExtensionHeader;       // 扩展头(this+24,未验证)
    int64_t pMsgDeadletterHeader;   // 死信头(this+25,未验证)
    int64_t pSubqueueHeader;        // 子队列头(this+26,未验证)
    int64_t pExtendedAddressHeader; // 扩展地址头(this+27,未验证)
    int64_t pSessionHeader;         // 会话头(this+28,未验证)
} CQmPacket;

8. 标志位解析

通过UserHeader的标志位识别其他数据结构:

  • 0x2000000 (J标志位):指示数据包是否通过HTTP发送

    • 设置:使用HTTP(SRMP消息)
    • 未设置:使用二进制协议
    • 设置时会多出SRMPEnvelopeHeader和CompoundMessageHeader
  • 0x4000000 (X3标志位):保留位1

  • 0x8000000 (X3标志位):保留位2

  • 0x10000000 (K标志位):对应SoapHeader

  • 0x20000000 (X4标志位):保留位第一位

9. 漏洞原理

9.1 漏洞成因

部分头结构未进行验证,攻击者可:

  1. 伪造结构体大小与实际大小的差异
  2. 误导后续节获取的指针地址
  3. 导致访问未分配内存而崩溃

9.2 漏洞利用

选择第三个未验证的结构体(unKnownReservedX31)进行伪造:

  1. 需要使if语句为真进入循环
  2. this+6(UserHeader)偏移44对应Flags字段(16进制数据中04字节)
  3. 指针地址计算公式:指针地址 + (指针地址+4) + (指针地址+8) + 15
  4. 可随意设置(指针地址+4)和(指针地址+8)处的值
  5. 最终导致访问未分配地址而崩溃

10. POC构造

10.1 必要通信流程

  1. 发送EstablishConnection Packet
  2. 发送ConnectionParameters Packet
  3. 发送特制的User Message包

10.2 User Message包构造要点

  1. 保留必选头(BaseHeader, UserHeader, MessagePropertiesHeader)
  2. 添加触发漏洞的头结构(unKnownReservedX31)
  3. 设置UserHeader Flags字段的特定标志位(0x2000000等)

10.3 参考数据包

11. 防御建议

  1. 对所有头结构进行完整验证
  2. 检查指针计算过程中的边界条件
  3. 更新MSMQ协议实现,修复未验证头结构的问题
  4. 实施输入数据的完整性检查
MSMQ协议CQmPacket::CQmPacket函数分析与漏洞研究 1. 概述 本文档详细分析MSMQ协议中 CQmPacket::CQmPacket 函数的实现原理、数据结构处理机制以及其中存在的安全漏洞。该函数主要用于处理UserMessage数据包,包含多个必需和可选的消息头结构。 2. 函数参数分析 CQmPacket::CQmPacket 函数主要参数: 参数一 :原始数据包指针 参数二 :BaseHeader头指针 返回值 :处理后的数据包结构 3. BaseHeader结构定义 根据微软官方文档定义,BaseHeader结构如下: 4. UserMessage数据包结构 UserMessage数据包包含以下部分: 必需头: BaseHeader UserHeader MessagePropertiesHeader 可选头: TransactionHeader SecurityHeader DebugHeader SoapHeader MultiQueueFormatHeader SessionHeader 5. 头结构验证机制 5.1 CBaseHeader::SectionIsValid验证流程 验证函数接收三个参数: baseHeader指针 消息最大限制值 固定布尔值1 验证步骤: 检查BaseHeader大小是否超过最大限制 验证BaseHeader数据体是否超出范围 验证签名是否为固定值0x524F494C 验证版本号是否为0x10 5.2 其他头结构验证 类似验证流程,包括: 固定值验证 范围检查 合理性检查 6. GetNextSection函数 用于获取下一个节(头结构): 工作原理: 从上一个头结构中获取数据大小 在当前头结构地址上偏移该大小 返回下一个头结构的地址 7. CQmPacket结构定义 完整结构定义如下: 8. 标志位解析 通过UserHeader的标志位识别其他数据结构: 0x2000000 (J标志位) :指示数据包是否通过HTTP发送 设置:使用HTTP(SRMP消息) 未设置:使用二进制协议 设置时会多出SRMPEnvelopeHeader和CompoundMessageHeader 0x4000000 (X3标志位) :保留位1 0x8000000 (X3标志位) :保留位2 0x10000000 (K标志位) :对应SoapHeader 0x20000000 (X4标志位) :保留位第一位 9. 漏洞原理 9.1 漏洞成因 部分头结构未进行验证,攻击者可: 伪造结构体大小与实际大小的差异 误导后续节获取的指针地址 导致访问未分配内存而崩溃 9.2 漏洞利用 选择第三个未验证的结构体(unKnownReservedX31)进行伪造: 需要使if语句为真进入循环 this+6(UserHeader)偏移44对应Flags字段(16进制数据中04字节) 指针地址计算公式: 指针地址 + (指针地址+4) + (指针地址+8) + 15 可随意设置(指针地址+4)和(指针地址+8)处的值 最终导致访问未分配地址而崩溃 10. POC构造 10.1 必要通信流程 发送EstablishConnection Packet 发送ConnectionParameters Packet 发送特制的User Message包 10.2 User Message包构造要点 保留必选头(BaseHeader, UserHeader, MessagePropertiesHeader) 添加触发漏洞的头结构(unKnownReservedX31) 设置UserHeader Flags字段的特定标志位(0x2000000等) 10.3 参考数据包 Establish Connection Request: 微软文档 Connection Parameters Request: 微软文档 User Message: 微软文档 11. 防御建议 对所有头结构进行完整验证 检查指针计算过程中的边界条件 更新MSMQ协议实现,修复未验证头结构的问题 实施输入数据的完整性检查