Talos Vulnerability Report

TALOS-2023-1697

Milesight UR32L uhttpd login buffer overflow vulnerability

July 6, 2023
CVE Number

CVE-2023-23902

SUMMARY

A buffer overflow vulnerability exists in the uhttpd login functionality of Milesight UR32L v32.3.0.5. A specially crafted network request can lead to remote code 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 - https://www.milesight-iot.com/cellular/router/ur32l/

CVSSv3 SCORE

9.8 - CVSS:3.1/AV:N/AC:L/PR:N/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 router provides an administrative web page to manage its various settings. This web page requires the client to login. The password is AES encrypted and then Base64 encoded before being sent to the server. The uhttpd’s decrypt_string function is the one responsible for decrypting the received AES-encrypted, Base64-encoded password in the login process. Following the decrypt_string function:

void decrypt_string(char *b64_encrypted_password,char *decrypted_password,size_t size_decrypted_password)

{
  [...]
  uchar stack_decrypted_string [72];

  [... init the AES_key variable]
  [... init the AES_IV variable]
  [... calculate the __size variable value ...]
  base64_decoded_string_start = (uchar *)malloc(__size);
  [...]
  memset(base64_decoded_string_start,0,__size);
  processed_len = 0;
  base64_decode_string_cursor = base64_decoded_string_start;
  do {
    // this check allow to base64 decode the string before decrypting it
    if ((password_len_ - padding) <= processed_len) {
      *base64_decode_string_cursor = '\0';
      ctx = EVP_CIPHER_CTX_new();
      [... check error ...]
      cipher_type = EVP_aes_128_cbc();
      processed_len = EVP_DecryptInit_ex(ctx,cipher_type,(ENGINE *)0x0,AES_key,AES_IV);
      [... check error ...]
      processed_len =
           EVP_DecryptUpdate(ctx,stack_decrypted_string,&output_len,base64_decoded_string_start,
                             (int)(base64_decode_string_cursor +
                                  (-1 - (int)base64_decoded_string_start)));                             [1]
      [... check error ...]
      processed_len = output_len;
      iVar3 = EVP_DecryptFinal_ex(ctx,stack_decrypted_string + output_len,&output_len);
      [... check error ...]
      processed_len = processed_len + output_len;
      EVP_CIPHER_CTX_free(ctx);
      stack_decrypted_string[processed_len] = '\0';
      EVP_cleanup();
      ERR_free_strings();
      free(base64_decoded_string_start);
      strncpy(decrypted_password,(char *)stack_decrypted_string,size_decrypted_password);                [2]
      return;
    }
    [...]
    [... base64 decode ...]                                                                              [3]
  } while( true );
}

This function has three parameters: the b64_encrypted_password parameter is the AES-encrypted and Base64-encoded password string that will be Base64 decoded and then AES decrypted; decrypted_password is the destination buffer where the decoded and decrypted password will be copied; size_decrypted_password is the size of the decrypted_password buffer.

At [3] the b64_encrypted_password string is Base64 decoded and then, at [1], the decoded value is AES decrypted into stack_decrypted_string, a 72-byte long stack buffer. Eventually, at [2], size_decrypted_password bytes will be copied from the stack_decrypted_string buffer into the decrypted_password buffer.

In the decrypted_password only size_decrypted_password bytes will be copied, which will prevent any type of buffer overflow in the decrypted_password buffer. At [1] the OpenSSL’s EVP_DecryptUpdate function will perform the decryption of the Base64 decoded user-controlled data into the stack_decrypted_string stack buffer. Because the stack_decrypted_string stack buffer is fixed in size and the decrypted string, provided by a user, can be greater in length than the stack buffer, this can lead to a buffer overflow in the stack_decrypted_string buffer.

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.