CVE-2023-24018
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.
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
UR32L - https://www.milesight-iot.com/cellular/router/ur32l/
8.8 - CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
CWE-121 - Stack-based Buffer Overflow
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.
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
2023-02-14 - Initial Vendor Contact
2023-02-21 - Vendor Disclosure
2023-07-06 - Public Release
Discovered by Francesco Benvenuto of Cisco Talos.