Talos Vulnerability Report

TALOS-2023-1725

Weston Embedded uC-HTTP HTTP Server out-of-bounds write vulnerability

November 14, 2023
CVE Number

CVE-2023-24585

SUMMARY

An out-of-bounds write vulnerability exists in the HTTP Server functionality of Weston Embedded uC-HTTP v3.01.01. A specially crafted network packet can lead to memory corruption. An attacker can send a network request 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.

Weston Embedded uC-HTTP v3.01.01
Weston Embedded Cesium NET 3.07.01
Silicon Labs Gecko Platform 4.3.1.0

PRODUCT URLS

uC-HTTP - https://weston-embedded.com/micrium/overview Cesium NET - https://www.weston-embedded.com/cesium-cs-net Gecko Platform - https://www.silabs.com/developers/gecko-software-development-kit

CVSSv3 SCORE

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

CWE

CWE-119 - Improper Restriction of Operations within the Bounds of a Memory Buffer

DETAILS

The uC-HTTP server implementation is designed to be used on embedded systems that are running the µC/OS II or µC/OS III RTOS kernels. This HTTP server supports many features including persistent connections, form processing, chunked transfer encoding, HTTP header fields processing, HTTP query string processing and dynamic content.

This out-of-bounds write vulnerability occurs when parsing the method of an HTTP request and could lead to heap corruption. The µC/OS heap implementation stores a pointer to the next free chunk within the first 4 bytes of a free chunk. An attacker could corrupt this next free chunk pointer using this out of bounds write vulnerability and potentially control the address of the next heap allocation.

If the method string within the HTTP request is prepended with bytes with a value less than 0x21 (exclamation point in ascii) or greater than 0x7e (tilde in ascii), the connection instance variable p_conn->RxBufLenRem will be larger than the actual remaining length of the receive buffer because the connection instance variable p_conn->RxBufPtr has been advanced farther than the length variable p_conn->RxBufLenRem accounts for. This leads to an out-of-bounds write occurring elsewhere in the code when p_conn->RxBufLenRem is used as an index for p_conn->RxBufPtr and writes beyond the bounds of the original buffer.

Consider the code below. A local variable p_request_method_start is used to store a pointer to the location of the first character whose value is less than 0x21 (exclamation point in ascii) or greater than 0x7e [1]. Next, the local variable len is correctly calculated to account for bytes that may have been skipped. After locating the next space character in the buffer [3], the local variable len is set as the number of characters before a space was encountered [4]. The result of this calculation is that the len variable no longer accounts for the previously skipped characters. Later, the connection instance variable p_conn->RxBufLenRem is updated but does not account for the skipped characters [5]. The connection instance variable p_conn->RxBufPtr is advanced and does account for the skipped characters. Therefore, the buffer pointer used elsewhere p_conn->RxBufPtr and the length variable used elsewhere p_conn->RxBufLenRem are now mismatched.

static  void  HTTPsReq_MethodParse (HTTPs_INSTANCE  *p_instance,
                                HTTPs_CONN      *p_conn,
                                HTTPs_ERR       *p_err)
{
...
    len = p_conn->RxBufLenRem;
...
                                                                /* Move the start ptr to the first meanningful char.    */
    p_request_method_start = HTTP_StrGraphSrchFirst(p_conn->RxBufPtr, len);             /* [1] p_request_method_start advances to the 
                                                                                            first character between 0x21 - 0x7e*/
    if (p_request_method_start == DEF_NULL) {
    *p_err = HTTPs_ERR_REQ_FORMAT_INVALID;
        return;
    }
    len -= p_request_method_start - p_conn->RxBufPtr ;                                  /* [2] len is correctly calculated */
                                                                /* Find the end of method string.                       */
    p_request_method_end =  Str_Char_N(p_request_method_start, len, ASCII_CHAR_SPACE);  /* [3] */
    if (p_request_method_end == DEF_NULL) {
    *p_err = HTTPs_ERR_REQ_FORMAT_INVALID;
        return;
    }
    len = p_request_method_end - p_request_method_start;                                /* [4] This is the bug */
...
    p_conn->RxBufLenRem -= len;                                                         /* [5] */
    p_conn->RxBufPtr     = p_request_method_end;                                        /* [6] */
}

Later on, if the packet contains form data, the incorrect p_conn->RxBufLenRem will be used as the local variable len_str [1] when parsing key/value pairs by calling HTTPsReq_BodyFormAppKeyValBlkAdd [2], which then calls HTTPsReq_URL_EncodeStrParse with that same incorrect value len_str.

static  CPU_BOOLEAN  HTTPsReq_BodyFormAppParse (HTTPs_INSTANCE  *p_instance,
                                                HTTPs_CONN      *p_conn,
                                                HTTPs_ERR       *p_err)
{
...
    while (done != DEF_YES) {
                                                                /* ----------- VALIDATE CUR KEY/VAL PAIRS ------------- */
        p_key_next = Str_Char_N(p_key_name,                     /* Srch beginning of next key/val pairs.                */
                                p_conn->RxBufLenRem,
                                ASCII_CHAR_AMPERSAND);

        if (p_key_next == DEF_NULL) {                           /* If next key/val pairs not found ...                  */
                                                                /* ... determine if all data are received or next ...   */
                                                                /* ... key/val pairs are missing.                       */
            len_content_rxd = p_conn->ReqContentLenRxd
                            + p_conn->RxBufLenRem;

            if (len_content_rxd < p_conn->ReqContentLen) {      /* If data are missing ...                              */
            *p_err = HTTPs_ERR_REQ_MORE_DATA_REQUIRED;       /* ... receive more data.                               */
                goto exit;

            } else {                                            /* If all data received ...                             */
                len_str = p_conn->RxBufLenRem;                  /* [1] 
                                                                /* ... last key/val pairs to parse.                     */
            }

        } else {                                                /* Next key/val pairs found ...                         */
            len_str = (p_key_next - p_key_name);                /* ... parse key/val pairs.                             */
        }

                                                                /* Add key-Value block to list.                         */
        result = HTTPsReq_BodyFormAppKeyValBlkAdd(p_instance,
                                                p_conn,
                                                p_key_name,
                                                len_str,
                                                p_err);         /* [2] */
...
}

When this function attempts to null terminate the value portion of the key/value pair, the pointer p_str is within the receive buffer, but str_len indexes beyond the receive buffer, causing an out-of-bounds write [1].

static  CPU_BOOLEAN  HTTPsReq_URL_EncodeStrParse (HTTPs_INSTANCE  *p_instance,
                                                HTTPs_CONN      *p_conn,
                                                HTTPs_KEY_VAL   *p_key_val,
                                                CPU_BOOLEAN      from_query,
                                                CPU_CHAR        *p_str,
                                                CPU_SIZE_T       str_len)
{
...
                                                                /* Find separator "=".                                  */
    p_str_sep = Str_Char_N(p_str, str_len, ASCII_CHAR_EQUALS_SIGN);

    p_str[str_len] = ASCII_CHAR_NULL;                           /* [1] */
...
}

Crash Information

Program received signal SIGSEGV, Segmentation fault.
0x565674b8 in Str_Cmp_N (
    p1_str=0x560071dc <error: Cannot access memory at address 0x560071dc>, 
    p2_str=0x5656a07b "Log out", len_max=9) at uc-lib/lib_str.c:882
882	    while ((*p1_str_cmp      == *p2_str_cmp)            &&      /* Cmp strs until non-matching chars (see Note #3c) ... */
(gdb) i r
eax            0x560071dc          1442869724
ecx            0x0                 0
edx            0x5656a07b          1448517755
ebx            0x56575f64          1448566628
esp            0xffffd418          0xffffd418
ebp            0xffffd438          0xffffd438
esi            0xf7f91000          -134672384
edi            0xf7f91000          -134672384
eip            0x565674b8          0x565674b8 <Str_Cmp_N+172>
eflags         0x10202             [ IF RF ]
cs             0x23                35
ss             0x2b                43
ds             0x2b                43
es             0x2b                43
fs             0x0                 0
gs             0x63                99
k0             0x0                 0
k1             0x0                 0
k2             0x0                 0
k3             0x0                 0
k4             0x0                 0
k5             0x0                 0
k6             0x0                 0
k7             0x0                 0
(gdb) bt
#0  0x565674b8 in Str_Cmp_N (
    p1_str=0x560071dc <error: Cannot access memory at address 0x560071dc>, 
    p2_str=0x5656a07b "Log out", len_max=9) at uc-lib/lib_str.c:882
#1  0x56557869 in HTTPs_ReqRdySignalHook (p_instance=0x5657645c <Mem_Heap+28>, 
    p_conn=0x56576988 <Mem_Heap+1352>, p_hook_cfg=0x0, p_data=0x56586de4 <Mem_Heap+68004>)
    at app_basic_http-s_hooks.c:685
#2  0x565594b1 in HTTPsReq_RdySignal (p_instance=0x5657645c <Mem_Heap+28>, 
    p_conn=0x56576988 <Mem_Heap+1352>) at Server/Source/http-s_req.c:742
#3  0x5655cb0d in HTTPsConn_Process (p_instance=0x5657645c <Mem_Heap+28>)
    at Server/Source/http-s_conn.c:184
#4  0x5655ee9b in HTTPsTask_InstanceTaskHandler (p_instance=0x5657645c <Mem_Heap+28>)
    at Server/Source/http-s_task.c:814
#5  0x5655ec01 in HTTPsTask_InstanceTask (p_data=0x5657645c <Mem_Heap+28>)
    at Server/Source/http-s_task.c:653
#6  0x565669ac in KAL_TaskCreate (task_handle=..., 
    p_fnct=0x5655ebdc <HTTPsTask_InstanceTask>, p_task_arg=0x5657645c <Mem_Heap+28>, 
    prio=17 '\021', p_cfg=0x0, p_err=0xffffd5e0) at uc-shims/Source/kal-shim.c:59
#7  0x5655e920 in HTTPsTask_InstanceTaskCreate (p_instance=0x5657645c <Mem_Heap+28>, 
    p_err=0xffffd654) at Server/Source/http-s_task.c:331
#8  0x5655c2e0 in HTTPs_InstanceStart (p_instance=0x5657645c <Mem_Heap+28>, 
    p_err=0xffffd654) at Server/Source/http-s.c:812
#9  0x56557fc2 in main (argc=1, argv=0xffffd714) at server_app.c:105
TIMELINE

2023-02-28 - Vendor Disclosure
2023-06-23 - Vendor Patch Release
2023-11-14 - Public Release

Credit

Discovered by Kelly Leuschner of Cisco Talos.