Talos Vulnerability Report

TALOS-2023-1715

Milesight UR32L libzebra.so security_decrypt_password buffer overflow vulnerability

July 6, 2023
CVE Number

CVE-2023-24018

SUMMARY

A stack-based buffer overflow vulnerability exists in the libzebra.so.0.0.0 security_decrypt_password functionality of Milesight UR32L v32.3.0.5. A specially crafted HTTP request can lead to a buffer overflow. An authenticated attacker can send an HTTP 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.

Milesight UR32L v32.3.0.5

PRODUCT URLS

UR32L - https://www.milesight-iot.com/cellular/router/ur32l/

CVSSv3 SCORE

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

CWE

CWE-121 - Stack-based Buffer Overflow

DETAILS

The Milesight UR32L is an industrial cellular router. The router features include support for multiple VPNs, a router console shell, firewall and many others.

The Milesight router offers several functionalities through the /cgi endpoint. The “core” functionality we are considering is called yruo_usermanagement. In this “core” there is one function called “set”. This API is used to change user related settings (for example, changing the password of a specific user). Following the vtysh_ubus’s usermng_set function, responsible for managing the “set” functionality:

    void usermng_set(undefined4 param_1,undefined4 param_2,undefined4 param_3,undefined4 param_4,
                    undefined4 *param_5)

    {
    [... variable declaration ...]

    [... variable initialization ...]
    [...]
    value_obj = (void *)blob_memdup(tb_outer.values);
    data = (void *)blobmsg_data(value_obj);
    values_obj_n = blobmsg_data_len(value_obj);
    blobmsg_parse(usermng_set_value_policy,7,tb_inner,data,values_obj_n);
    [...]
    if (((tb_inner[4] != (blob_attr *)0x0) && (tb_inner[5] != (blob_attr *)0x0)) &&
     (tb_inner[6] != (blob_attr *)0x0)) {
        tmp_encrypted = blobmsg_get_string((char *)tb_inner[4]);                                        [1]
        security_decrypt_password(tmp_encrypted,old_password_decrypted,0x20);
        tmp_encrypted = blobmsg_get_string((char *)tb_inner[5]);                                        [2]
        security_decrypt_password(tmp_encrypted,new_password_decrypted,0x20);
        tmp_encrypted = blobmsg_get_string((char *)tb_inner[6]);                                        [3]
        security_decrypt_password(tmp_encrypted,confirm_password_decypted,0x20);
    }
    [...]
    }

The function uses the blobmsg structures for parsing the received data. Each entry in tb_inner represents a specific parameter in the request’s JSON data. Following an example of a JSON input that could be used for this API. Note that this request must be sent by an authenticated user.

{
    "id": 60,
    "execute": 10,
    "core": "yruo_usermanagement",
    "function": "set",
    "values": [
        {
            "base": "security",
            "index": "index",
            "value": {
                "old_password": "264ISdU4Pqdnyksk0N1ssQ==",
                "new_password": "nW+tKhD05KjqWkOYjjz/xg==",
                "confirm_password": "luDNLgAE+s9LzVRK0ed8/Ujh/E+mduZm5GBcH/UVt/c="
            }
        }
    ]
}

The code at [1] parses the old_password parameter, the code at [2] parses new_password and the code at [3] parses confirm_password. All three parameters are AES-encrypted Base64-encoded. Indeed, the three parameters are passed to the security_decrypt_password function that will Base64 decode and AES decrypt them.

Following the libzebra.so.0.0.0’s security_decrypt_password function:

void security_decrypt_password(byte *enc_string,char *dec_string,size_t out_len)

{
    [... variable declaration ...]

    [... variable initialization ...]
    encoded_len = strlen(encoded_string);
    b64_decoded = base64_decode(encoded_string,encoded_len,&b64_decoded_len);
    decrypt_len = ys_aes_decrypt(b64_decoded,b64_decoded_len,AES_key,AES_IV,decrypted_tmp);             [4]
    decrypted_tmp[decrypt_len] = 0;
    [...]
    strncpy(dec_string,(char *)decrypted_tmp,out_len);                                                  [5]
    [...]
}

The function takes three parameters: - enc_string the encoded input string - dec_string the output buffer, where the result will be written - out_len the len of the dec_string output buffer

This function will call the base64_decode function to Base64 decode the enc_string string. Then the ys_aes_decrypt function is called, at [4], and will decrypt the Base64 decoded string into the decrypted_tmp stack buffer. Eventually the strncpy at [5] is reached and, at most, out_len bytes are copied from decrypted_tmp to the dec_string buffer.

A stack-based buffer overflow exists during the execution of the ys_aes_decrypt function. Indeed, it will decrypt the arbitrarily long enc_string into the decrypted_tmp stack buffer that is fixed in length.

VENDOR RESPONSE

Since the maintainer of this software did not release a patch during the 90 day window specified in our policy, we have now decided to release the information regarding this vulnerability, to make users of the software aware of this problem. See Cisco’s Coordinated Vulnerability Disclosure Policy for more information: https://tools.cisco.com/security/center/resources/vendor_vulnerability_policy.html

TIMELINE

2023-02-14 - Initial Vendor Contact
2023-02-21 - Vendor Disclosure
2023-07-06 - Public Release

Credit

Discovered by Francesco Benvenuto of Cisco Talos.