Security
Headlines
HeadlinesLatestCVEs

Headline

CVE-2023-22653: TALOS-2023-1714 || Cisco Talos Intelligence Group

An OS command injection vulnerability exists in the vtysh_ubus tcpdump_start_cb functionality of Milesight UR32L v32.3.0.5. A specially crafted HTTP request can lead to command execution. An attacker can send an HTTP request to trigger this vulnerability.

CVE
#vulnerability#web#cisco#intel

SUMMARY

An OS command injection vulnerability exists in the vtysh_ubus tcpdump_start_cb functionality of Milesight UR32L v32.3.0.5. A specially crafted HTTP request can lead to command execution. An 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-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_tools. In this “core” there is one function called “tcpdump_start”. This API is used to execute the command “tcpdump” using the provided data, following the vtysh_ubus’s tcpdump_start_cb function, responsible for managing the “tcpdump_start” functionality:

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

{
    [... variable declaration ...]

    [... variable initialization ...]
    [...]
    if ((param_advanced == (blob_attr *)0x0) ||
         (advanced_value = (char *)blobmsg_get_string(param_advanced), *advanced == '\0')) {
        [...]
        interface_value = (char *)blobmsg_get_string(param_interface]);
        [...]
        strncpy(interface_ptr,interface_value,0x80);
        snprintf(last_param.interface,0x80,"%s",interface_ptr);
        is_equal = strcmp(interface_ptr,"Any");
        if (is_equal == 0) {
          interface_ptr = "any";
          interface_ptr_dup = zstrdup(1,interface_ptr);
        }
        else {
          interface_ptr_dup = if_name_display2ori(interface_ptr);
          [...]
        }
        snprintf(tcpdump_options_string,0x100,"-i %s",interface_ptr_dup);
        [...]
        if (0 < (int)port) {
            last_param.port = port;
            len = strlen(tcpdump_options_string);
            snprintf(tcpdump_options_string + len,0x100 - len," port %d",port);                       [1]
        }
        [...]
        if (param_ip != (blob_attr *)0x0) {
            ip_value = (char *)blobmsg_get_string(param_ip);
            strncpy(host_string,ip_value,0x80);
            if (*host_string != 0) {
                snprintf(last_param.host,0x80,"%s",host_string);
                len = strlen(tcpdump_options_string);
                if ((int)port < 0) {
                  temp = "";
                }
                else {
                  temp = " and";
                }
                snprintf(tcpdump_options_string + len,0x100 - len,"%s host %s",temp,host_string);       [2]
            }
        }
        [...]
  }
  else {
    strncpy(advanced_param_buff,advanced_value,0x100);
    strtok(advanced_param_buff,";");
    strtok(advanced_param_buff,"|");
    temp = advanced_param_buff._0_4_ & 0xff;
    if (temp != 0) {
      snprintf(last_param.advance,0x100,"%s",advanced_param_buff);
      snprintf(tcpdump_options_string,0x100," %s",advanced_param_buff);                                 [3]
      [...]
    }
  }
  [... populate the dest_location variable with the destination of the recorded pcap ...]
  [...]
  len = strlen(tcpdump_options_string);
  snprintf(tcpdump_options_string + len,0x100 - len," -w %s",dest_location);
  snprintf(shell_command,0x200,"%s %s \"%s\" \"%s\" \"%s\" 2>&1 &","/usr/sbin/webtools.sh","tcpdump"
           ,tcpdump_options_string,dest_location,"/tmp/webtcpdump.lock");                               [4]
  [...]
  system(shell_command);                                                                                [5]
  [...]
}

This function parses, at most, four possible params: “interface”, “ip”, “port” and “advanced”. If the “advanced” param is present and not empty, the other three are ignored. Otherwise, if the “advanced” param is not present or its value is empty, then the function will parse the “interface”, “ip” and “port”.

Eventually the code at [4] is reached and the string ‘/usr/sbin/webtools.sh tcpdump “<tcpdump_options_string>” “dest_location” “/tmp/webtcpdump.lock” 2>&1 &’ is composed. The tcpdump_options_string is composed using the provided parameters. For instance, at [1] is appended the ' port <port>’ string, and at [2] the ' host <host>’ or ' and host <host>’ is appended, based on the presence of the “port” parameter. Otherwise, if the “advanced” parameter is present, neither [1] or [2] are reached, but instead the code at [3] is executed.

Following are two example of commands that will result in the same composed string:

{
    "id":60,
    "execute":1,
    "core":"yruo_tools",
    "function":"tcpdump_start",
    "values":[
        {
            "interface":"Any",
            "ip":"192.168.0.100",
            "port":12345,
            "advanced":""
        }
    ]
} The above command uses the "interface", "ip" and "port" params.

{
    "id":60,
    "execute":1,
    "core":"yruo_tools",
    "function":"tcpdump_start",
    "values":[
        {
            "interface":"",
            "ip":"",
            "port":"",
            "advanced":"-i any port 12345 and host 192.168.0.100"
        }
    ]
} The above command uses only the "advanced" param.

Eventually the string composed at [4] will be used as argument of the system function at [5]. No particular checks are performed against the parameters provided, which can lead to an OS command injection.

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

Discovered by Francesco Benvenuto of Cisco Talos.

Related news

The many vulnerabilities Talos discovered in SOHO and industrial wireless routers post-VPNFilter

Given the privileged position these devices occupy on the networks they serve, they are prime targets for attackers, so their security posture is of paramount importance.

Taking over Milesight UR32L routers behind a VPN: 22 vulnerabilities and a full chain

In all, Cisco Talos is releasing 22 security advisories today, nine of which have a CVSS score greater than 8, associated with 69 CVEs.

CVE: Latest News

CVE-2023-50976: Transactions API Authorization by oleiman · Pull Request #14969 · redpanda-data/redpanda
CVE-2023-6905
CVE-2023-6903
CVE-2023-6904
CVE-2023-3907