CVE-2023-23547
A directory traversal vulnerability exists in the luci2-io file-export mib functionality of Milesight UR32L v32.3.0.5. A specially crafted network request can lead to arbitrary file read. An attacker can send a network 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/
6.5 - CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
CWE-22 - Improper Limitation of a Pathname to a Restricted Directory (‘Path Traversal’)
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 router offers many CGI endpoints, one of which is cgi-bin/file-export
. This CGI endpoint allows for the various configuration files to be exported, based on parameters provided. This API is managed by the luci2-io
’s file-export
function.
The file-export
function parses the parameters provided, checks if the session provided is valid and then returns, if the parameters are valid, the content of the requested configuration file. Following the file-export
function:
void file-export(void)
{
[... variable declaration ...]
[...]
key_value_array[0].key = "sessionid";
key_value_array[0].value = (char *)0x0;
key_value_array[1].key = "type";
key_value_array[1].value = (char *)0x0;
key_value_array[2].key = "file";
key_value_array[2].value = (char *)0x0;
[...]
is_ok = parse_data_and_url_decode(key_value_array,3); [1]
if ((is_ok == 0) ||
(is_ok = check_permission(key_value_array[0].value,"export","read"),
file_variable = key_value_array[2].value, type_variable = key_value_array[1].value,
is_ok == 0)) {
type_variable = "Export permission denied";
}
else {
cgi_struct_cur = cgi_helper_structure;
struct_idx = 0;
do {
iVar2 = strcmp(type_variable,cgi_struct_cur->type);
if (iVar2 == 0) {
if (cgi_struct_cur->file == (char *)0x0) {
snprintf(EXPORT_FILEPATH,0x80,"%s%s",cgi_helper_structure[struct_idx].filepath/dirpath,
file_variable); [2]
cgi_helper_structure[struct_idx].filepath/dirpath = EXPORT_FILEPATH; [3]
}
else {
[...]
}
iVar2 = ::pipe((int *)pipe_obj);
if (iVar2 == 0) {
__pid = fork();
if (__pid != -1) {
if (__pid == 0) {
iVar2 = -1;
close((int)pipe_obj[0]);
cgi_helper_structure[struct_idx].pipe_write_side = pipe_obj[1];
(*(code *)cgi_helper_structure[struct_idx].read_file_func)
(cgi_helper_structure + struct_idx); [4]
close((int)pipe_obj[1]);
}
[...]
}
[...]
}
[...]
}
struct_idx = struct_idx + 1;
cgi_struct_cur = cgi_struct_cur + 1;
} while (struct_idx != 0x61);
type_variable = "Export : incorrect file type";
}
[...]
}
The function uses a static array with the different configuration info. Following the relevant portion of an element of this array:
char * type = "mib"
char * file = 0
char * filepath/dirpath = "/usr/share/snmp/mibs"
char * filename = "mib.txt"
char * respone_content_type = "text/plain"
code * read_file_func = read_file # function at 0x13a30
[...]
The relevant request’s parameters are: type
and file
. The function will iterate over all the elements of the static array and will perform an action when the type
field of the considered element is equal to the type
parameter provided. Above it is shown the mib
element.
The function parses, at [1]
, the parameters provided. The function then iterates over all the elements in the static array as soon as the type
matches the provided one. If the file
field is not empty in the considered element, the file
parameter is used at [2]
to compose the pathname of the desired file using the filepath/dirpath
field of the specified element. For instance, using the element shown above, at [2]
, the composed string will have the following form: /usr/share/snmp/mibs/<file_param>
. Then the field filepath/dirpath
is modified to be equal to the just-composed string.
In the case of the mib
type, at [4]
, the read_file
function will be called, and the string composed at [2]
will be used as pathname. Because the composed string at [2]
is not sanitized, a directory traversal vulnerability exists in the file-export
function, which can lead to arbitrary file read.
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.