Site icon API Security Blog

Milesight UR32L vtysh_ubus _get_fw_logs OS command injection vulnerability

# Talos Vulnerability Report

### TALOS-2023-1712

## Milesight UR32L vtysh_ubus _get_fw_logs OS command injection vulnerability

##### July 6, 2023

##### CVE Number

CVE-2023-22299

##### SUMMARY

An OS command injection vulnerability exists in the vtysh_ubus _get_fw_logs functionality of Milesight UR32L v32.3.0.5. A specially crafted network request can lead to command execution. 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.

Milesight UR32L v32.3.0.5

##### PRODUCT URLS

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-78 – Improper Neutralization of Special Elements used in an OS Command (‘OS Command Injection’)

##### 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_debug_firewall`. In this “core” there is a function called “get”. The function that manages this functionality is the `vtysh_ubus`’s `fw_logs_get` function:

void fw_logs_get(undefined4 param_1,undefined4 param_2,undefined4 param_3,undefined4 param_4,
undefined4 *data)

{
[… variable declaration …]

[… variable initialization …]
json_msg_output(“!! yruo_fw_logs.get params”,data);
blob_buf_init(b,0);
data_len = __bswapsi2(*data);
blobmsg_parse(fw_logs_get_policy,2,tb,data + 1,(data_len & 0xffffff) – 4); [1]
if (tb[0] != (blob_attr *)0x0) {
base_value_ptr = (char *)blobmsg_get_string(tb[0]);
strncpy(tb_base_value,base_value_ptr,0x20);
system_ptr = (system_base_struct *)get_handler_bybase(&debug_bases,1,tb_base_value); [2]
if (system_ptr != (system_base_struct *)0x0) {
switch_to_enable_node();
_get_fw_logs = (code *)system_ptr->get_func;
if ((_get_fw_logs != (code *)0x0) &&
(_get_fw_logs = (code *)(*_get_fw_logs)(tb,”get”), _get_fw_logs == (code *)0x0)) [3]
[…]
[…]
}
}
}
[…]
}

The data are transmitted through `blobmsg` structures. The two variable that are parsed into the `tb` array, at `[1]`, are: – `base`: this value must be equal to “firewall_log”, otherwise no functionality will be performed. The value of this data will be parsed in the `tb[0]` variable. – `command`: this value will be used as argument of the `iptables` shell command. The value of this data will be parsed in the `tb[1]` variable.

At `[2]` the `base` value will be used to get a struct handler that in the code is called `system_ptr`. Because there is only, in this case, one valid value for `base`, we know the struct fetched has as value of the `system_ptr->get_func` the `_get_fw_logs` function pointer. At `[3]` the `_get_fw_logs` function will be called:

void _get_fw_logs(blob_attr **tb, char* type)

{
[… variable declaration …]

[… variable initialization …]
= (undefined4 *)zcalloc(1,8);
if (two_word != (undefined4 *)0x0) {
command = tb[1]; [4]
[…]
if ((command != (blob_attr *)0x0) &&
(command_string = (char *)blobmsg_get_string(command), *command_string != ”)) {
dup_command = zstrdup(1,command_string);
[…]
command_string = (char *)(dup_command + -1);
do {
command_string = command_string + 1;
command_string_cursor = *command_string;
if (command_string_cursor == ”) {
snprintf(iptables_command,0x100,”iptables -w %s”,dup_command); [5]
zlog_debug(“exec fw command(%s)n”,iptables_command);
__stream = popen(iptables_command,”r”); [6]
[…]
}
[…]
}
} while (command_string_cursor != ‘&’ && command_string_cursor != ‘;’);
[…]
}
}
[…]
} This function takes as first argument the `fw_logs_get`’s `tb` variable pointer. Then, at `[4]`, `tb[1]` is used to fetch the `command` argument sent in the `/cgi` API. The `command` variable is used, at `[5]`, to compose the `iptables -w ` string. This is then used at `[6]` as the argument for the `popen` function, effectively executing the `iptables -w ` shell command.

The payload for the `/cgi` API to execute the “get” function in the “yruo_debug_firewall” core would look likes this:

{
“id”:60,
“execute”:10,
“core”:”yruo_debug_firewall”,
“function”:”get”,
“values”:[
{
“base”:”firewall_log”,
“command”:”-S”,
}
]
}

This would execute the `iptables -W -S` command listing all the iptables rules.

Because no exhaustive checks are performed on the `command` parameters until it reaches the `popen` function, this leads to an OS command injection vulnerability.

##### 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.

* * *

Vulnerability Reports Next Report

TALOS-2023-1713

Previous Report

TALOS-2023-1706Read More

Exit mobile version