Lucene search

K
talosTalos IntelligenceTALOS-2024-1980
HistorySep 11, 2024 - 12:00 a.m.

Microsoft Windows 10 AllJoyn Router Service information disclosure vulnerability

2024-09-1100:00:00
Talos Intelligence
www.talosintelligence.com
8
microsoft windows 10
alljoyn router service
information exposure

CVSS3

7.5

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

NONE

Availability Impact

NONE

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

AI Score

7.9

Confidence

High

EPSS

0.001

Percentile

50.0%

Talos Vulnerability Report

TALOS-2024-1980

Microsoft Windows 10 AllJoyn Router Service information disclosure vulnerability

September 11, 2024
CVE Number

CVE-2024-38257

SUMMARY

An information disclosure vulnerability exists in the AllJoyn Router Service in Microsoft Windows 10 version 10.0.19041.4170 and prior. During the initiation of an ARDP session, the service can send a reset packet that includes information from the address space of the process. An attacker can send an unauthenticated packet to trigger this vulnerability.

CONFIRMED VULNERABLE VERSIONS

The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.

Microsoft Windows 10 MSAJApi.dll 10.0.19041.4170

PRODUCT URLS

Windows 10 - <https://www.microsoft.com/en-us/windows&gt;

CVSSv3 SCORE

5.3 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N

CWE

CWE-201 - Information Exposure Through Sent Data

DETAILS

Windows 10 is a pretty popular operating system.

AllJoyn is an open-source software framework that was designed to allow compatible devices in a heterogeneous network environment to perform discovery, communication, and collaboration regardless of the connection, product type, or brand. It was originally promoted by the AllSeen Alliance to allow interoperability between the various IoT devices that were being developed at the time. The AllJoyn framework facilitates these capabilities by providing a D-Bus message bus to clients and servers which can then be used by devices to organize themselves into one cohesive system. Microsoft maintains an implementation of the AllJoyn framework as a runtime client and routing node service that was introduced with Windows 10. Additionally, Microsoft has added support for a device system bridge in order to allow interoperability with devices using protocols such as Z-Wave or BACnet as part of an AllJoyn network.

Microsoft’s implementation of the AllJoyn routing node resides within the “AllJoyn Router Service” which can be found with the name “AJRouter” on Windows 10. The “AJRouter” service is generally not started by default, but can be activated by accessing the “\PIPE\ProtectedPrefix\LocalService\MSAJPipe” named pipe. Once the service has been activated, it will bind to both TCP and a number of UDP ports. The TCP ports provide authenticated services for devices participating within the AllJoyn network. Similar services are provided using UDP, but with the addition of another protocol being used as the transport for devices to communicate over. This protocol is referred to as ARDP, or the AllJoyn Reliable Datagram Protocol.

The ARDP protocol aims to provide a messaging interface that allows for packet fragmentation and fragment reordering. Establishing a connection with the ARDP protocol requires a device to perform a handshake that is not dissimilar from the handshake used by the TCP protocol. In the handshake, the first packet sent by a participant is used to synchronize the sequence and acknowledgement numbers for the protocol, which will then be acknowledged by the receiver. Upon being acknowledged, the participant and receiver can then freely exchange packets which will be assembled before extracting the AllJoyn messages being transported. Upon completion or failure, the connection will be reset by transmitting a final packet. During the process of resetting a connection that is being authenticated, the AllJoyn framework will fail to initialize the buffer being sent to the device initiating the connection. This results in leaking 0x24 bytes from the stack of the process to the original sender.

The following code shows the main loop of the thread that is used to handle all traffic related to the UDP protocol. Upon the incoming socket being signalled, the method at [1] will be used to enter the main function that is responsible for handling the ARDP protocol, ARDP_Run.

alljoyn_core/router/UDPTransport.cc:8391-8774
void* UDPTransport::Run(void* arg)
{
    QCC_UNUSED(arg);
...
    for (;;) {
        IncrementPerfCounter(PERF_COUNTER_UDP_TRANSPORT_RUN_OUTER_LOOP);

        for (vector&lt;Event*&gt;::iterator i = signaledEvents.begin(); i != signaledEvents.end(); ++i) {
...
            m_ardpLock.Lock(MUTEX_CONTEXT);
            if (socketReady) {
                ardpStatus = ARDP_Run(m_handle, (*i)-&gt;GetFD(), readReady, writeReady, socketIsAccepting, &ms);
...
            }
            m_ardpLock.Unlock(MUTEX_CONTEXT);
...
        }
    }
    return (void*) status;
}

The ARDP_Run function first allocates a 64k buffer onto the stack. This is intended to contain the entirety of the datagram being fed into the ARDP protocol. At [2], the datagram is read from the socket into this buffer with the resulting size being written to the nbytes variable. Once the packet has been read, two 16-bit integers will be extracted from the header of the packet at [3]. These integers represent the sender and receiver that the packet is associated with. During the initiation of the ARDP handshake, the local connection number is set to 0, as the connection has not been initialized yet. Afterwards, at [4], the InitconnRecord function will be used to initialize the necessary information used for tracking the connection if there are enough slots available. When the function returns, the newly constructed connection will then be appended to the end of a linked list before being accepted at [5].

alljoyn_core/router/ArdpProtocol.cc:3428-3538
QStatus ARDP_Run(ArdpHandle* handle, qcc::SocketFd sock, bool sockRead, bool sockWrite, bool sockAccepts, uint32_t* ms)
{
    const size_t bufferSize = 65536;
    uint32_t buf32[bufferSize &gt;&gt; 2];
    uint8_t* buf = reinterpret_cast&lt;uint8_t*&gt;(buf32);
    qcc::IPAddress address;
    uint16_t port = 0;
    size_t nbytes;
    QStatus status = ER_OK;
...
    if (sockRead) {
        while ((status = qcc::RecvFrom(sock, address, port, buf, bufferSize, nbytes)) == ER_OK) {       // [2] Read entire datagram from socket
...

            if (nbytes &gt; 0 && nbytes &lt; 65536) {
                uint16_t local, foreign;
                ProtocolDemux(buf, nbytes, &local, &foreign);                                           // [3] Extract the local and foreign connection numbers
                if (local == 0) {
                    if (sockAccepts && handle-&gt;accepting && handle-&gt;cb.AcceptCb) {
                        if (!IsDuplicateConnRequest(handle, foreign, address)) {
                            ArdpConnRecord* conn = NewConnRecord();
                            status = InitConnRecord(handle, conn, sock, address, port, foreign);        // [4] Initialize a connection record
                            if (status == ER_OK) {
                                EnList(handle-&gt;conns.bwd, (ListNode*)conn);
                                status = Accept(handle, conn, buf, nbytes);                             // [5] Accept the connection
                            }
...
                        }
...
                    }
                    if (status != ER_OK) {
                        QCC_LogError(status, ("Failed to accept incoming connection request from %s (ARDP port %u)", address.ToString().c_str(), foreign));
                        SendRst(handle, sock, address, port, local, foreign);                           // [11] Reset the ARDP connection
                    }
...
                }
...
        }
    }
...
    return status;
}

The following code shows the implementation of the Accept function. At [6], the function will verify that both the flags and version within the ARDP header are as expected. Afterwards, at [7], the Receive function will be called. At [8], both the header length and data length will be validated before entering the main function containing the state machine for the ARDP protocol at [9]. If any of these tests fail, a QStatus error will be returned to the caller, ARDP_Run.

alljoyn_core/router/ArdpProtocol.cc:3395-3414
QStatus Accept(ArdpHandle* handle, ArdpConnRecord* conn, uint8_t* rxbuf, uint16_t len)
{
...
    if (!(rxbuf[FLAGS_OFFSET] & ARDP_FLAG_SYN) || (rxbuf[FLAGS_OFFSET] & ARDP_FLAG_RST)) {              // [6] Verify the flags in the header
        return ER_ARDP_INVALID_CONNECTION;
    }

    if ((rxbuf[FLAGS_OFFSET] & ARDP_VERSION_BITS) != ARDP_FLAG_VER) {                                   // [6] Verify the version in the header
        QCC_DbgHLPrintf(("Accept(): Unsupported protocol version 0x%x",
                         rxbuf[FLAGS_OFFSET] & ARDP_VERSION_BITS));
        return ER_ARDP_VERSION_NOT_SUPPORTED;
    }
...
    return Receive(handle, conn, rxbuf, len);                                                           // [7] Execute the function for processing the received packet
}
\
alljoyn_core/router/ArdpProtocol.cc:3307-3393
static QStatus Receive(ArdpHandle* handle, ArdpConnRecord* conn, uint8_t* rxbuf, uint16_t len)
{
...
    hdrSz = (seg.FLG & ARDP_FLAG_SYN) ? ARDP_SYN_HEADER_SIZE : ARDP_FIXED_HEADER_LEN;                   // [8] Check sizes in header

    /* Perform length validation checks */
    if (((seg.HLEN * 2) &lt; hdrSz) || (len &lt; hdrSz) || (seg.DLEN + (seg.HLEN * 2)) != len) {              // [8] Check sizes in header
...
        return ER_ARDP_INVALID_RESPONSE;
    }

...
    conn-&gt;state = LISTEN;                                                                               // [9] Initialize connection state as "LISTEN"
    conn-&gt;passive = true;

    ArdpMachine(handle, conn, &seg, rxbuf, len);                                                        // [9] Enter state machine
    return ER_OK;
}

Once the Accept functions returns, its result will be assigned to the status variable at [10]. Due to an error being encountered, the ARDP_Run function will need to reset the connection. After assigning a result to the status variable, the ARDP_Run function will check it against ER_OK to determine whether the connection was successfully accepted. If it wasn’t, the SendRst function at [11] will be used to transmit a reset packet back to the initiator of the connection.

alljoyn_core/router/ArdpProtocol.cc:3428-3538
QStatus ARDP_Run(ArdpHandle* handle, qcc::SocketFd sock, bool sockRead, bool sockWrite, bool sockAccepts, uint32_t* ms)
{
    const size_t bufferSize = 65536;
    uint32_t buf32[bufferSize &gt;&gt; 2];
    uint8_t* buf = reinterpret_cast&lt;uint8_t*&gt;(buf32);
...
                if (local == 0) {
                    if (sockAccepts && handle-&gt;accepting && handle-&gt;cb.AcceptCb) {
                        if (!IsDuplicateConnRequest(handle, foreign, address)) {
                            ArdpConnRecord* conn = NewConnRecord();
                            status = InitConnRecord(handle, conn, sock, address, port, foreign);
                            if (status == ER_OK) {
                                EnList(handle-&gt;conns.bwd, (ListNode*)conn);
                                status = Accept(handle, conn, buf, nbytes);                             // [10] Accept the connection
                            }
...
                        }
...
                    }
                    if (status != ER_OK) {
                        QCC_LogError(status, ("Failed to accept incoming connection request from %s (ARDP port %u)", address.ToString().c_str(), foreign));
                        SendRst(handle, sock, address, port, local, foreign);                           // [11] Reset the ARDP connection
                    }
...
                }
...
    return status;
}

The following code is the implementation of the SendRst function and is responsible for sending a reset packet on behalf of the ARDP_Run function. This function contains a number of issues and is directly related to the vulnerability described by this document. In order to send a reset packet, the SendRst functions allocates 3 variables, h as an ArdpHeader, buf32 as a 9-element array of uint32_t, and txbuf which is used as a pointer into buf32. At [12], the buf32 array is allocated on the stack using the total size of the ARDP header (36). Afterwards, at [13], the txbuf pointer is assigned the address of buf32 with the intention that it is used to set the necessary fields within the reset packet that is to be sent. Once txbuf has been assigned, the memset at [14] is used to initialize buf32 using the correct size. After buf32 has been initialized, at [15] the txbuf pointer is used to assign the “flags”, “source”, and “destination” fields inside the space allocated for the buf32 array. It is at this point that both the txbuf and buf32 variables are not used anymore. At [16], the h variable is then written to the socket. Due to both the buf32 and txbuf variables being unused before going out of scope, the memset at [14] and the assignments at [15] are optimized out by the compiler leaving only the two calls to htons in case they have any side effects. When the h variable is written to the socket, it has not yet been written to resulting in leaking 36 bytes from the stack to the initiator of the connection.

alljoyn_core/router/ArdpProtocol.cc:2002-2036
static QStatus SendRst(ArdpHandle* handle, qcc::SocketFd sock, qcc::IPAddress ipAddr, uint16_t ipPort, uint16_t local, uint16_t foreign)
{
    QCC_DbgTrace(("SendRst(handle=%p, sock=%d., ipAddr=\"%s\", ipPort=%d., local=%d., foreign=%d.)",
                  handle, sock, ipAddr.ToString().c_str(), ipPort, local, foreign));

    ArdpHeader h;
    uint32_t buf32[ARDP_FIXED_HEADER_LEN &gt;&gt; 2];                                 // [12] Allocate 9 uint32_t on stack for packet
    uint8_t* txbuf = reinterpret_cast&lt;uint8_t*&gt;(buf32);                         // [13] Cast packet from a uint32_t pointer to a uint8_t pointer

    memset(buf32, 0, ARDP_FIXED_HEADER_LEN);                                    // [14] Attempt to initialize "buf32", despite being unused.

    *(txbuf + FLAGS_OFFSET) = ARDP_FLAG_RST | ARDP_FLAG_VER;                    // [15] Set the required fields (flags) for the RST packet
    *(txbuf + HLEN_OFFSET) = ARDP_FIXED_HEADER_LEN &gt;&gt; 1;                        // [15] Set the required fields (hlen) for the RST packet
    *reinterpret_cast&lt;uint16_t*&gt;(txbuf + SRC_OFFSET) = htons(local);            // [15] Set the required fields (src) for the RST packet
    *reinterpret_cast&lt;uint16_t*&gt;(txbuf + DST_OFFSET) = htons(foreign);          // [15] Set the required fields (dst) for the RST packet
...
    size_t sent;
    return qcc::SendTo(sock, ipAddr, ipPort, &h, ARDP_FIXED_HEADER_LEN, sent);  // [16] Send the packet to the socket using the wrong variable
}

The definition of the ArdpHeader structure has the following layout. Each of these fields, other than “reserve”, are uninitialized before being written to the socket.

alljoyn_core/router/ArdpProtocol.h:88-103
#pragma pack(push, 1)
typedef struct {
    uint8_t flags;      /**&lt; See Control flag definitions above */
    uint8_t hlen;       /**&lt; Length of the header in units of two octets (number of uint16_t) */
    uint16_t src;       /**&lt; Used to distinguish between multiple connections on the local side. */
    uint16_t dst;       /**&lt; Used to distinguish between multiple connections on the foreign side. */
    uint16_t dlen;      /**&lt; The length of the data in the current segment.  Does not include the header size. */
    uint32_t seq;       /**&lt; The sequence number of the current segment. */
    uint32_t ack;       /**&lt; The number of the segment that the sender of this segment last received correctly and in sequence. */
    uint32_t ttl;       /**&lt; Time-to-live.  Zero means forever. */
    uint32_t lcs;       /**&lt; Last "in-order" consumed segment. */
    uint32_t acknxt;    /**&lt; First unexpired segment, TTL accounting */
    uint32_t som;       /**&lt; Start sequence number for fragmented message */
    uint16_t fcnt;      /**&lt; Number of segments comprising fragmented message */
    uint16_t reserve;   /**&lt; Reserved for future use */
} ArdpHeader;

In the following we will demonstrate the vulnerability by observing the service behavior from within a debugger.

The AllJoyn Router Service is run by Microsoft Windows super-server daemon, svchost.exe. After the service has been started, it can be attached to with a debugger.

0:033&gt; lm m msajapi
Browse full module list
start    end        module name
50a40000 50cae000   MSAJApi    (deferred)             

The reset packet is sent by the ajn::SendRst function. The following commands set a breakpoint when the ajn:SendRst function is entered.

0:033&gt; bp msajapi!ajn::SendRst
0:033&gt; g

At this point, the proof-of-concept can be used which will result in the debugger interrupting execution at the entrypoint of the ajn::SendRst function.

Breakpoint 0 hit
eax=00000001 ebx=044f8b28 ecx=044f8b28 edx=00000f10 esi=05aef75a edi=05aef6ca
eip=50bc0e87 esp=05aef6b4 ebp=05aff780 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
MSAJApi!ajn::SendRst:
50bc0e87 8bff            mov     edi,edi

This function allocates 0x8c bytes for its frame. If we continue execution through the prologue of the function, a stack canary will be placed at the end of the frame before the prologue completes.

0:026&gt; g 180e8f+msajapi
eax=00000001 ebx=044f8b28 ecx=044f8b28 edx=00000f10 esi=05aef75a edi=05aef6ca
eip=50bc0e8f esp=05aef6b0 ebp=05aef6b0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
MSAJApi!ajn::SendRst+0x8:
50bc0e8f 81ec8c000000    sub     esp,8Ch

The following command continues execution until the prologue completes. At this point, the local variables for the function have been allocated.

0:026&gt; g 180ea3+msajapi
eax=a0bc30e4 ebx=044f8b28 ecx=044f8b28 edx=00000f10 esi=05aef75a edi=05aef6ca
eip=50bc0ea3 esp=05aef624 ebp=05aef6b0 iopl=0         nv up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000286
MSAJApi!ajn::SendRst+0x1c:
50bc0ea3 0fb7451c        movzx   eax,word ptr [ebp+1Ch]   ss:0023:05aef6cc=26e3

The buffer that will be sent has been allocated at -0x28(%ebp). The following commands dump out the contents of the buffer before it has been initialized. After dumping out the contents of the buffer, two hardware breakpoints are set to confirm that this buffer is not written to during execution.

0:026&gt; db @ebp-28 L0n36
05aef688  28 c0 3f 05 b1 3b 75 75-7f 3b 75 75 53 59 15 05  (.?..;uu.;uuSY..
05aef698  10 c1 a6 50 28 c0 3f 05-28 8b 4f 04 d0 1f 78 75  ...P(.?.(.O...xu
05aef6a8  27 7f 3a 75                                      '.:u

0:026&gt; ba w4 @$exp
0:026&gt; ba w4 @$exp+4

After the hardware breakpoints have been set, the following command resumes execution until encountering the first call to the htons function.

0:026&gt; g 180ffe+msajapi
eax=00000001 ebx=044f8b28 ecx=000007d4 edx=00000000 esi=00000000 edi=053fc028
eip=50bc0ffe esp=05aef614 ebp=05aef6b0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
MSAJApi!ajn::SendRst+0x177:
50bc0ffe ff157cc0c850    call    dword ptr [MSAJApi!_imp__htons (50c8c07c)] ds:0023:50c8c07c={WS2_32!htons (774356e0)}

Disassembling the instructions that follow the current instruction pointer shows that the results from both calls to htons are discarded. Despite these functions being called, their result is not written to anywhere inside the buffer.

0:026&gt; u .-4 L5
MSAJApi!ajn::SendRst+0x173:
50bc0ffa ff74241c        push    dword ptr [esp+1Ch]
50bc0ffe ff157cc0c850    call    dword ptr [MSAJApi!_imp__htons (50c8c07c)]
50bc1004 ff742410        push    dword ptr [esp+10h]
50bc1008 ff157cc0c850    call    dword ptr [MSAJApi!_imp__htons (50c8c07c)]
50bc100e 8d442454        lea     eax,[esp+54h]

After the results from both calls to htons have been discarded, we resume execution until we encounter the qcc::SendTo function. This function essentially writes some number of bytes from a pointer to a socket, resulting in a packet being transmitted from the application.

0:026&gt; g 181179+msajapi
eax=05aef688 ebx=044f8b28 ecx=00000f10 edx=05aef6b8 esi=00000000 edi=50b9e5b0
eip=50bc1179 esp=05aef600 ebp=05aef6b0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
MSAJApi!ajn::SendRst+0x2f2:
50bc1179 e8078ff0ff      call    MSAJApi!qcc::SendTo (50aca085)

The qcc::SendTo function takes 7 parameters using the __fastcall calling convention. The following annotated snippet describes each parameter and its purpose. Relevant to this vulnerability are the 3rd and 4th parameters, representing the buffer and length to be sent.

0:026&gt; ub . Lb
MSAJApi!ajn::SendRst+0x2d9:
50bc1160 8d442418        lea     eax,[esp+18h]
50bc1164 51              push    ecx                            ; flags
50bc1165 8b4c2418        mov     ecx,dword ptr [esp+18h]        ; socket
50bc1169 8d5508          lea     edx,[ebp+8]                    ;
50bc116c 50              push    eax                            ; number of bytes sent
50bc116d 6a24            push    24h                            ; length
50bc116f 8d44247c        lea     eax,[esp+7Ch]                  ; &buffer[0]
50bc1173 50              push    eax                            ; buffer
50bc1174 56              push    esi                            ; scope
50bc1175 ff742420        push    dword ptr [esp+20h]            ; remote port
50bc1179 e8078ff0ff      call    MSAJApi!qcc::SendTo (50aca085)

Examining the contents of the buffer being sent as a parameter shows that it retains the same contents from when it was allocated during the execution of the function prologue.

0:026&gt; db poi(@esp+4*2) L(dwo(@esp+4*3))
05aef688  28 c0 3f 05 b1 3b 75 75-7f 3b 75 75 53 59 15 05  (.?..;uu.;uuSY..
05aef698  10 c1 a6 50 28 c0 3f 05-28 8b 4f 04 d0 1f 78 75  ...P(.?.(.O...xu
05aef6a8  27 7f 3a 75                                      '.:u

If we step over the call to qcc::SendTo, the uninitialized contents of this buffer will be sent to the initiator of the ARDP session.

0:026&gt; p
eax=00000000 ebx=044f8b28 ecx=a512c6c0 edx=00000000 esi=00000000 edi=50b9e5b0
eip=50bc117e esp=05aef618 ebp=05aef6b0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
MSAJApi!ajn::SendRst+0x2f7:
50bc117e 8b8c2494000000  mov     ecx,dword ptr [esp+94h] ss:0023:05aef6ac=a0bc30e4

None of the hardware breakpoints that were set (after the function prologue has allocated space for the buffer) have been triggered. As the buffer has been untampered with, this will always result in leaking 0x24 bytes from the stack.

0:026&gt; g

Exploit Proof of Concept

The proof-of-concept requires Python3 and can be run remotely or locally. If run against localhost, the proof-of-concept will attempt to activate the service before triggering the vulnerability.

$ python poc.py3.zip $ADDRESS

The proof of concept connects to UDP port 9955 of a host and initiates an ARDP connection. In the packet that initiates the session, the version field is specified incorrectly in order to trigger the vulnerability. The following description shows the header of the packet being sent.

&lt;class ardp.ArdpHeader&gt; 'Header'
[0] &lt;instance c(pb(ardp.ArdpHeader._flags)) 'flags'&gt; {bits=8,partial=True} (0x01,8) :&gt; SYN
[1] &lt;instance be(pint.uint8_t) 'hlen'&gt; 0x0e (14)
[2] &lt;instance be(pint.uint16_t) 'src'&gt; 0xb4ae (46254)
[4] &lt;instance be(pint.uint16_t) 'dst'&gt; 0x0000 (0)
[6] &lt;instance be(pint.uint16_t) 'dlen'&gt; 0x00a0 (160)
[8] &lt;instance be(pint.uint32_t) 'seq'&gt; 0x04ccb123 (80523555)
[c] &lt;instance be(pint.uint32_t) 'ack'&gt; 0x5a0c3863 (1510750307)

The following description shows the header of a leaked ARDP packet. The first byte at offset 0 contains the flags.

&gt;&gt;&gt; packet['header']
&lt;class ardp.ArdpHeader&gt; 'Header'
[0] &lt;instance c(pb(ardp.ArdpHeader._flags)) 'flags'&gt; {bits=8,partial=True} (0x01,8) :&gt; SYN
[1] &lt;instance be(pint.uint8_t) 'hlen'&gt; 0x0e (14)
[2] &lt;instance be(pint.uint16_t) 'src'&gt; 0x2768 (10088)
[4] &lt;instance be(pint.uint16_t) 'dst'&gt; 0x0000 (0)
[6] &lt;instance be(pint.uint16_t) 'dlen'&gt; 0x00a0 (160)
[8] &lt;instance be(pint.uint32_t) 'seq'&gt; 0xde678615 (3731326485)
[c] &lt;instance be(pint.uint32_t) 'ack'&gt; 0x79b79195 (2042073493)

The error being triggered in this instance is due to the AllJoyn framework checking that the 2-bit version in the flags is always set to 1. In this case, it has been set to 0.

&gt;&gt;&gt; packet['header']['flags']
&lt;class c(pb(ardp.ArdpHeader._flags))&gt; 'flags' {bits=8,partial=True}
[0.0] &lt;instance c(pbinary.integer) 'VER'&gt; (0x0,2)
[0.4] &lt;instance c(pbinary.integer) 'unused'&gt; (0x0,1)
[0.6] &lt;instance c(pbinary.integer) 'NUL'&gt; (0x0,1)
[0.8] &lt;instance c(pbinary.integer) 'RST'&gt; (0x0,1)
[0.a] &lt;instance c(pbinary.integer) 'EACK'&gt; (0x0,1)
[0.c] &lt;instance c(pbinary.integer) 'ACK'&gt; (0x0,1)
[0.e] &lt;instance c(pbinary.integer) 'SYN'&gt; (0x1,1)

Despite only the version being set incorrectly in this proof-of-concept, any situation where the ARDP session may be reset triggers the vulnerability. This includes the data or header length being incorrect, the wrong flags being set, or the sequence and acknowledgement numbers being out-of-order after a session has been initiated.

VENDOR RESPONSE

https://msrc.microsoft.com/update-guide/vulnerability/CVE-2024-38257

TIMELINE

2024-05-06 - Vendor Disclosure
2024-09-10 - Vendor Patch Release
2024-09-11 - Public Release

Credit

Discovered by a member of Cisco Talos.


Vulnerability Reports Next Report

TALOS-2024-2008

Previous Report

TALOS-2024-1973

CVSS3

7.5

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

NONE

Availability Impact

NONE

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

AI Score

7.9

Confidence

High

EPSS

0.001

Percentile

50.0%