The security updates released by Microsoft on April 11, 2023, addressed over 90 individual vulnerabilities. Of particular note was CVE-2023-21554, dubbed QueueJumper, a remote code execution vulnerability affecting the Microsoft Message Queueing (MSMQ) service. MSMQ is an optional Windows component that enables applications to exchange messages via message queues that are reachable both locally and remotely. This analysis was performed in collaboration with the Randori and X-Force Adversary Services teams, by Valentina Palmiotti, Fabius Watson, and Aaron Portnoy.

Research motivations

The following qualities of CVE-2023-21554 drew initial attention:

  • Due to the varying usage of MSMQ queues by third-party applications, there are challenges in identifying the full breadth of vulnerability impact between and across environments.
  • Initial reports described 360,000 Internet accessible hosts with the MSMQ service exposed. Consulting Shodan at the time displayed a mere 250. This discrepancy made it even less clear the extent of possible exposure.
  • The vulnerability is classified as a remote code execution that does not require authentication and also affects a wide range of Windows platforms.
  • The only apparent requirement is that the service is reachable on TCP port 1801.

Initial triage

Identifying MSMQ endpoints

To determine how to detect the presence of MSMQ, a test system was configured with the component in a lab environment. When connecting to the MSMQ endpoint on port 1801, the server did not respond with any data. It could be assumed the server will not reply without receiving reasonably-formed data. To understand how to conduct a conversation with the endpoint, further inspection of the underlying code was required.

Locating MSMQ code

From the advisory, it is not immediately clear which binary was patched. The first step to identifying the changed code was to download the update file. This was achieved by searching for KB5025224 in the Microsoft Update Catalog.


Microsoft Update Catalog search results for KB5025224

Specifically, the cumulative update file windows10.0-kb5025224-x64_3522b1b562ed44bc949541dd1c7d08e1e967d925.msu for Windows 11 for x86-based Systems was retrieved.

The 7-Zip extractor was used to unpack the contents of the MSU file and the CAB files it contained. Searching for the string “msmq” across all files in the resulting folders yielded the Container Index file, Windows11.0-KB5025239-x64/express.psf.cix.xml, which included the following entry:

<File id="21296" name="amd64_microsoft-windows-msmq-queuemanager-core_31bf3856ad364e35_10.0.22621.1555_none_5f975f8fdb349517\f\mqqm.dll" length="34811" time="133245266510000000" attr="128">

This entry references MQQM.dll, which was identified as a target for further analysis.

Identifying patched code

To identify the changes made between the patched and unpatched versions of the MQQM.dll file, the Zynamics’ BinDiff tool was used. The following image shows the matched functions with a similarity score less than 1.00:

BinDiff results between patched and unpatched MQQM.dll files 

Analysis of these functions using Hex-Rays Decompiler revealed symbols for the patched version containing verbose function names that appear to reference Microsoft Security Research Center (MSRC) vulnerability identifiers:

Feature flag symbols within MQQM.dll

Feature flag analysis

After closer inspection, the symbols containing MSRC case numbers correspond to feature flags for vulnerability fixes. Feature flags are a component of Windows that toggle various functionality and experiments. Here, the discovered “feature” is actually a patch for a vulnerability. If the feature is enabled, the secure, patched code path is executed. Otherwise, the original unpatched code path is executed.

Decompiled code snippet showing feature flag for vulnerability fix

A feature-flag check was present for each of the vulnerabilities that comprised CVE-2023-21554 in both the user and kernel components. Analysis of the remaining binaries contained in the security update package revealed that feature flags were also introduced for most or all of the vulnerabilities fixed on April 2023 Patch Tuesday.

Root cause analysis

Cross-referencing these flags helped us quickly pinpoint the patched code locations. In particular, the changes within CQmPacket::CQmPacket which related to the MSRC76146_MSMQ_OOBRWFixes flag seemed of most interest.

The check for this feature-flag occurs eight times within the function. One such check is depicted below:

Decompiled code snippet showing vulnerability fix 

In the decompiled code snippet shown above, the patched code path obtains the pointer to the next section of the MSMQ message via a call to GetNextSectionPtrSafe. In the unpatched path, the pointer is obtained by simply adding the size of the section with no checks. The new GetNextSectionPtrSafe function verifies that the calculated pointer to the next section of the packet does not exceed the length of the packet itself.

Decompilation snippet of GetNextSectionPtrSafe function

After examining all the occurrences of this feature-flag and others, it was determined that CVE-2023-21554 consists of several bugs which insufficiently check the size of various MSMQ message section types. These vulnerabilities allow the pointer to the end of the MSMQ message to be incremented by an arbitrary 32-bit offset to outside the bounds of the originally allocated buffer.

Following packet processing, the final step in CQmPacket::CQmPacket will append an OnDiskExtensionHeader to the end of the packet buffer. Because of the aforementioned vulnerabilities, it is possible for the calculated pointer to the end of the message to go out of bounds. Thus, the appending of the OnDiskExtensionHeader can result in several out-of-bounds writes. The decompiled code below shows a patched check for the vulnerable message end pointer before the OOB writes occur:

Out of Bound memory access check for message end

Kernel variants

While evaluating the exploitability of the vulnerabilities, additional kernel vulnerabilities were identified in the corresponding kernel driver for the MSMQ service, mqac.sys. The vulnerabilities were again indicated by feature-flag symbols, which also reference MSRC case 76146 as observed in MQQM.dll.

Feature flag symbols within mqac.sys

Analysis of the KernelVariants feature flag revealed that the corresponding vulnerabilities are analogues of the ones found in the user space component. The kernel component mqac.sys is involved in various operations for the MSMQ service such as forwarding messages to dead letter queues, sending acknowledgement messages, and memory management for MSMQ packet buffers.

In order to perform the associated operations, the kernel component parses various sections of the MSMQ packet buffer. Two such instances are pictured below:

Decompiled code snippet showing feature flag for vulnerability fix in kernel driver mqac.sys

As seen in the decompiled code snippet above, if the feature-flag is enabled, the “safe” functions CPacketBuffer::MsgDeadletterHeaderSafe and CPacketBuffer::MsgOnDiskExtensionHeaderSafe are called to retrieve the corresponding headers from the packet buffer. If not enabled, the “unsafe” functions are called, which do not have sufficient checks on the size of the section.

Triggering vulnerability

Protocol reverse engineering

The majority of the code related to protocol parsing is located within the MQQM.dll library, which is loaded by the mqsvc.exe process. To reach the code referenced by MSRC76146_MSMQ_OOBRWFixes, a valid MSMQ request must be sent from the client. The MSMQ message specification and protocol is vast and complex. Initially, public code was found for scanning MSMQ servers in a GitHub gist that provided the basis for more in depth exploration of the packet processing. In addition, the Microsoft documentation was helpful for understanding the MSMQ message format.

The code within the DLL accepts data through port 1801 using a call to the WSARecv function, as can be seen in the debugging session below:

0:020> bp ws2_32!wsarecv ; g

Breakpoint 0 hit

WS2_32!WSARecv:

00007ffd`651115c0 48895c2408      mov     qword ptr [rsp+8],rbx ss:000000aa`f07fef30=0000000000000006

 

0:019> k

# Child-SP          RetAddr               Call Site

00 000000aa`f07fef28 00007ffd`57ba9cb0     WS2_32!WSARecv

01 000000aa`f07fef30 00007ffd`57b9683d    

MQQM!NoReceivePartialBuffer+0x38

02 000000aa`f07fefb0 00007ffd`57b2078e     MQQM!CWinsockConnection::ReceivePartialBuffer+0x7d

03 000000aa`f07ff010 00007ffd`57b25b28    

MQQM!CSockTransport::BeginReceive+0xe6

04 000000aa`f07ff060 00007ffd`57b2d40b    

MQQM!CSockTransport::NewSession+0x194

05 000000aa`f07ff3f0 00007ffd`57b2d367     MQQM!CSessionMgr::AcceptSockSession+0x6f

06 000000aa`f07ff430 00007ffd`645e26bd     MQQM!AcceptIPThread+0x6a7

07 000000aa`f07ff7d0 00007ffd`6650a9f8    

KERNEL32!BaseThreadInitThunk+0x1d

08 000000aa`f07ff800 00000000`00000000     ntdll!RtlUserThreadStart+0x28

 

0:019> r @$t0=@rdx

0:019> pt

WS2_32!WSARecv+0x19f:

00007ffd`6511175f c3              ret

0:019> dc poi(@$t0+8) LC

000001b5`880ff490  62f06110 524f494c 00000070 5a5a5a5a  .a.bLIORp…ZZZZ

000001b5`880ff4a0  0073006e 00610074 0063006e 00200065  n.s.t.a.n.c.e. .

000001b5`880ff4b0  00440049 00000000 00000000 00000000  I.D………….

 

0:019> ba r1 poi(@$t0+8) ; g

Breakpoint 1 hit

MQQM!CBaseHeader::SectionIsValid+0x154:

00007ffd`57b53450 7462            je     

MQQM!CBaseHeader::SectionIsValid+0x1b8 (00007ffd`57b534b4) [br=1]

The CBaseHeader::SectionIsValid function is responsible for parsing the beginning of a new message, which starts with a BaseHeader structure of the form:

Structure of the BaseHeader object

The incoming message fields are validated by SectionIsValid prior to the server sending a response:

Disassembly code for SectionIsValid within MQQM.dll 

After initial validation, the majority of the protocol parsing occurs within the CQmPacket:: CQmPacket constructor function. To trigger a CVE-2023-21554 vulnerability, a MSMQ message can be sent containing a malformed section header that is mishandled. The following example details triggering a vulnerability using an EodHeader for an Eod (exactly-once-delivery) section.

Decompilation showing vulnerability in handling of EodHeader

The above decompilation shows the patched path calling the function named CEodHeader::GetNextSectionSafe. The unpatched path simply performs pointer addition, using values within the header, to calculate the start of the next section.

The structure of an EodHeader is not documented; the flag bit indicating the presence of an EodHeader in the UserHeader is specified as “reserved” in Microsoft’s documentation, which states the flag bit should not be set when sent. Look at the disassembly can give insight on the structure of the header:

Disassembly calculating total size of EodHeader section

In the disassembly above, the pointer to the beginning of the EodHeader is loaded into the rdx register. It can be speculated that the first and second fields of the header describe integer sizes (header size and data size, or something similar). The two sizes plus 0xF indicate the total size of the Eod section in bytes. By sending a message with an EodHeader whose corresponding size fields are set to large values, an out-of-bounds write is triggered. This causes an access violation when the OnDiskExtensionHeader is written to what (the program thinks) is the end of the message:

WinDbg session showing an access violation when writing to the end of a malformed MSMQ message

Exploitability

Memory management & manipulation

To evaluate the strength of the out-of-bounds write obtained by the vulnerabilities, we first investigated the allocation and management of the underlying memory.

Before a MSMQ message packet is processed, its contents are copied to a buffer allocated in the function QmAcAllocatePacket. This function calls into the mqac.sys kernel driver via NtDeviceIoControl.

Decompilation of packet buffer allocation function

The kernel component returns a buffer that is partitioned from a mapped view of a file on disk, stored in the C:\Windows\System32\msmq\storage directory with a .mq extension. Each file can hold up to 4MB and can contain messages from multiple queues.

ProcessHacker memory layout of MQSVC process showing packet buffer memory is a file mapping

The packet buffer address, along with a randomized packet handle, is returned to user space following the allocation request via IOCTL.  The driver uses the handle to calculate the packet buffer address when handling requests from user space.

Heap grooming

The backing memory for the vulnerable buffers is within a mapped file, and thus not in a heap of the process. Additionally, the affected memory does not store any objects. The only thing that is contained in the file mapping is the contents of MSMQ messages along with metadata that is stored in the OnDiskExtension.

Furthermore, without prior knowledge of the memory layout it is not possible to know the relative positioning of the file mapping to the process’ heap in order to overwrite object pointers. Therefore, in order to feasibly obtain remote code execution, we require some method for shaping the memory into a predictable position.

After experimenting with sending legitimate MSMQ messages to the target, it was discovered that it is possible to force a new heap allocation adjacent to a .mq file mapping by spraying many messages. Messages are written sequentially in memory to when they are received, and using the MSMQ message TimeToBeReceived property, the freeing of specific messages can be carefully timed.

Memory layout of MQSVC process showing a new heap allocated adjacently to a MQ file mapping

However, this requires that the attacker have access to send messages to a least one queue on the target, otherwise, the messages will be discarded and overwritten and therefore do not occupy packet memory persistently.

In the conducted research experimentation, it was discovered that it is possible for an unprivileged user to create a queue that is open to anyone to send messages remotely, by default. The queue persists on the system indefinitely. Although most modern uses of MSMQ exist as optional components and adapters, vendors such Oracle, Veritas, and Xerox offer enterprise software that rely on MSMQ as a core dependency. Therefore, the attack scenario is not unrealistic.

Exploit primitives

The out-of-bound memory access vulnerability introduces an exploit primitive that allows a
malicious client to write an OnDiskExtensionHeader beyond the boundaries of the memory
allocated for the packet.

When assessing a vulnerability that allows for writing values to an offset, some properties
must be considered:

  • Can the offset be controlled
  • Can the values written be controlled

In the case of the QueueJumper vulnerabilities, the offset of allocated buffer to the OOB write is controllable because it is calculated using attacker-controlled data. The contents of the OOB write, however, has limited control.

Here, Address represents the corrupted pointer to the end of the packet message where the OnDiskExtension header will be written. The series of operations that occur is as follows:

  1. Value 0x000000000000000C is written to Address
  2. Value 0x00000000 is written to Address+0x2
  3. Value 0x0000000000000000 is written to Address+0x12
  4. Value 0x0000000000000000 is written to Address+0x1A
  5. Value 0x00000094 is written Address+0xE
  6. Value 0x0000 is written Address+0x22
  7. Value 0x0000 is written to Address+0x62
  8. memcpy(Address+0xA6, Source, Source->AddressLength+0x08)

The Source used in the call to memcpy above refers to a TA_ADDRESS object. The TA_ADDRESS structure defines a single transport address of a specific type (for example, NetBIOS). The definition follows:

typedef struct _TA_ADDRESS {

    USHORT AddressLength;;

    USHORT AddressType; Info;

    UCHAR Address[1];;

} TA_ADDRESS, *PTA_ADDRESS;

AddressLength

Specifies the number of bytes in an address of the specified AddressType.

AddressType

Specifies the type of the transport address.

Address

Specifies a variable-sized array containing the transport address.

In a debugging session below, TA_ADDRESS contains attacker-influenced data in the form of an IP address:

rax=0000014e1d110180 rbx=0000014e1d110180 rcx=0000014e1d110184

rdx=0000014e1ce86b10 rsi=0000014e1d110001 rdi=0000000545d7fa50

rip=00007ff843208074 rsp=0000000545d7f940 rbp=0000000545d7f980

r8=000000000000000c  r9=0000000000000002 r10=0000000000000000

r11=0000000545d7f938 r12=0000000000000002 r13=0000000000001000

r14=0000014e1ce86b10 r15=0000000000000000

iopl=0         nv up ei pl nz na po nc

cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206

MQQM!CQmPacket::CQmPacket+0x89c:

00007ff8`43208074 e8a5780a00      call    MQQM!memcpy (00007ff8`432af91e)

0:017> dc rcx

0000014e`1d110184  00010004 00000000 8620a8c0 00000000  ………. ….. 

Above, the TA_ADDRESS object has a length of 4, a type of 1, and a value of 0x8620a8c0. Converting each byte to an IPv4 octet yields the address 192.168.32.134, which is the IPv4 source address of the client.

This scenario allows for the writing of attacker-controlled data via a source address to a controlled offset from the packet allocation in memory. While very restrictive and possibly impractical, this could theoretically be used to precisely control the values written to memory by making multiple requests from varying source addresses at decrementing packet offsets.

Alternative attacks

Because of the number of limitations encountered to achieve blind remote code execution, alternative attack scenarios, such as information leaks, were investigated.

One attack that was considered was corrupting the pointer to a specific message section to point to out-of-bounds memory and then later retrieving the message to read the adjacent memory.

However, the beginning of each section parsing does a check on the position of the current section, therefore we cannot trick the parsing logic into parsing a data section that is out of bounds.

Another possible attack scenario is to overwrite the OnDiskExtensionHeader of another message to trick the recipient of the message of the origins of the received message. Similarly, it’s possible to overwrite the contents of messages intended for queues that the attacker does not have access to. Overwriting specific contents of a message would require the attacker to have information about the message, such as its size.

There is an optional section named SoapHeader that is susceptible to out-of-bounds data access, with the following format:

SoapHeader packet format

It was observed that the memory used to store MSMQ packet data is not properly cleared when the buffer is freed. As a result, sending an MSMQ packet that is smaller in size than a prior MSMQ packet will result in a memory layout contained the current packet’s data followed by data from previously processed MSMQ packets.

It was discovered that the SoapHeader could be used to abuse this characteristic in order to disclose data from previously processed packets or adjacent stored messages. An attacker may arbitrarily define the BodyDataLength and exclude the Body section entirely. This will result in an MSMQ message that includes potentially sensitive data from previously processed MSMQ packets as the Body of the attacker’s SoapHeader. The patched code path prevents this abuse case, as it validates that the Body data is within the bounds of the MSMQ packet data.

The limited control in write contents with the additional non-controlled writes corrupting adjacent memory, along with limited knowledge in message layout proved to be a big obstacle in exploitation.

Attacking via kernel

At the time of analysis, only the user space vulnerabilities involved in the initial processing of a MSMQ message packet were evaluated for exploitability. Analysis of the kernel variant vulnerabilities could uncover additional exploitation avenues. For example, it is possible including a malformed header could gain a write primitive in the kernel memory mapping of the .mq file. In some cases, checks in user space processing prevent the kernel from performing some post-processing operations.

Additionally, in the case of the kernel variants the attacker has less direct control over message contents and is also operating on the same file mapping as user space, not in a kernel pool (heap). Mitigations, such as KASLR, also present obstacles that are difficult to overcome for remote exploitation.

Detecting vulnerable servers

Due to the implementation of the patched code, a remote client can safely determine if a system has applied the update. This can be done by sending a MSMQ message that triggers the vulnerability, but does not actually cause an out of bound access, thus not crashing the service process. If the HTTP flag is set in the UserHeader of the message, a SRMPEnvelopeHeader is expected. In the patch, a check for an integer overflow is performed on the DataLength field multiplied by 2. This length can be set so that the result of the multiplication overflows to a small number equal to the actual size of the data.

Decompiled code snippet showing integer overflow check on SRMPEnvelopeHeader DataLength

If the target is patched, the vulnerability causes an exception to be thrown and the server will abort the processing of the message, thus no response is sent back to the client. If it is not patched, the server will process the message normally and send a response.

Conclusion

Due to the location backing memory of the vulnerable buffers, remote code execution seems more feasible for an attacker with the ability to send messages to any queue on the target. In this scenario, it is possible to groom so that an adjacent heap is allocated. The extremely limited control over write contents presents challenges for remote exploitation. Because of mitigations like ASLR, an information leak must also be obtained. It is possible that a single write of a constant (0xC, 0x0 – obtained with the primitive) to an object could achieve this, however, the object must contain a useful field where the adjacent fields can withstand corruption from the other writes. Additionally, the object must be predictably allocated by the attacker, the lifespan of the object must span the duration of exploitation, and some action of the object must be triggerable by the attacker. In the course of this research, no such suitable objects were discovered.

Ultimately, we conclude that while the remote exploitation of QueueJumper may not be impossible, the aforementioned requirements make it difficult to accomplish. This blog post only scratches the surface of what is a very complex message specification, service, and protocol. Further vulnerability research into both the user space and kernel component operations is warranted.

More from Adversary Services

Getting “in tune” with an enterprise: Detecting Intune lateral movement

13 min read - Organizations continue to implement cloud-based services, a shift that has led to the wider adoption of hybrid identity environments that connect on-premises Active Directory with Microsoft Entra ID (formerly Azure AD). To manage devices in these hybrid identity environments, Microsoft Intune (Intune) has emerged as one of the most popular device management solutions. Since this trusted enterprise platform can easily be integrated with on-premises Active Directory devices and services, it is a prime target for attackers to abuse for conducting…

Racing Round and Round: The Little Bug That Could

13 min read - The little bug that could: CVE-2024-30089 is a subtle kernel vulnerability I used to exploit a fully updated Windows 11 machine (with all Virtualization Based Security and hardware security mitigations enabled) and scored my first win at Pwn2Own this year. In this article, I outline my straightforward approach to bug hunting: picking a starting point and intuitively following a path until something catches my attention. This bug is interesting because it can be reliably triggered due to a logic error.…

Q&A with Valentina Palmiotti, aka chompie

4 min read - The Pwn2Own computer hacking contest has been around since 2007, and during that time, there has never been a female to score a full win — until now.This milestone was reached at Pwn2Own 2024 in Vancouver, where two women, Valentina Palmiotti and Emma Kirkpatrick, each secured full wins by exploiting kernel vulnerabilities in Microsoft Windows 11. Prior to this year, only Amy Burnett and Alisa Esage had competed in the contest's 17-year history, with Esage achieving a partial win in…

Topic updates

Get email updates and stay ahead of the latest threats to the security landscape, thought leadership and research.
Subscribe today