Talos Vulnerability Report

TALOS-2023-1788

Yifan YF325 gwcfg_cgi_set_manage_post_data stack-based buffer overflow vulnerabilities

October 11, 2023
CVE Number

CVE-2023-35967,CVE-2023-35968

SUMMARY

Two heap-based buffer overflow vulnerabilities exist in the gwcfg_cgi_set_manage_post_data functionality of Yifan YF325 v1.0_20221108. A specially crafted network request can lead to a heap buffer overflow. An attacker can send a network request to trigger these vulnerabilities.

CONFIRMED VULNERABLE VERSIONS

The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.

Yifan YF325 v1.0_20221108

PRODUCT URLS

YF325 - https://yifanwireless.com/entry-level-wifi-router/yf325-series-gprs/3g/4g-wifi-router-with-sim-card-slot.html

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-190 - Integer Overflow or Wraparound

DETAILS

The Yifan YF325 is an industrial cellular router. This device is designed for M2M and IOT applications, allowing remote management, offering several VPN services and many other features.

The YF325 router provides a series of APIs. The API that manages the gwcfg.cgi/set* endpoints uses the httpd’s gwcfg_cgi_set_manage_post_data function to parse the POST data:

void gwcfg_cgi_set_manage_post_data(undefined4 URL,FILE **fd,int content_length)

{
  [...]

  APP_TRACE("do_set_cfg_post,url:%s,len:%d\n",URL);
  if (POST_DATA == (char *)0x0) {
    POST_DATA = (char *)malloc(content_length + 1);                                                             [1]
  }
  else {
    POST_DATA = (char *)realloc(POST_DATA,content_length + 1);                                                  [2]
  }
  if (POST_DATA != (char *)0x0) {
    read_len = wfread(POST_DATA,1,content_length,fd);                                                           [3]
    [...]
  }
  return;
}

The first thing checked is if the POST_DATA has already been initialized. Based on the answer, a malloc or a realloc is called to accommodate the actual request’s Content-Length.

The Content-Length is parsed in the manage_request function:

void manage_request(void)
{
  [... receive and parse the request until the start of the headers ...]
  [... peforms some checks and parse on the received data ... ]
  
  while( true ) {
    [... tmp_buff is a buffer just after current_header buffer ...]
    read_len = wfgets(current_header,(int)tmp_buff - (int)current_header,(char *)CLIENT_REQUEST_FD);
    if (((is_equal == 0) || (is_equal = strcmp(current_header,"\n"), is_equal == 0)) ||
        (is_equal = strcmp(current_header,"\r\n"), is_equal == 0)) break;
    is_equal = strncasecmp(current_header,"Authorization:",0xe);
    if (is_equal == 0) {
      [...]
    }
    else {
      is_equal = strncasecmp(current_header,"Referer:",8);
      if (is_equal == 0) {
        [...]
      }
      else {
        is_equal = strncasecmp(current_header,"Host:",5);
        if (is_equal == 0) {
          [...]
        }
        else {
          is_equal = strncasecmp(current_header,"Content-Length:",0xf);                                         [4]
          if (is_equal == 0) {
            content_length_value_ptr = current_header + 0xf;
            [...]
            content_length_empty_space = strspn(content_length_value_ptr," \t");
            content_length = content_length_value_ptr + content_length_empty_space;
            CONTENT_LENGTH = strtoul(content_length,(char **)0x0,0);
    [...]
  while( true ) {                                                                                               [5]
    URL_path_pattern = (code *)API_entry_cursor->URL_path_pattern;
    [... find the correct API entry based on the URL path pattern ...]
  }
  [...]
  post = 0;
  is_equal = strcasecmp(request_method,"post");
  if (is_equal == 0) {
    post = 1;
  }
  if ((code *)API_entry_cursor->manage_request_data != (code *)0x0) {
    (*(code *)API_entry_cursor->manage_request_data)
              (request_URL+1,CLIENT_REQUEST_FD,CONTENT_LENGTH,boundary);                                        [6]
  }
  [...]
}

The function will eventually parse, at [4], the Content-Length. Then, at [5], based on the URL path, the correct API entry struct to use is located. At [6], the manage_request_data function of the located API entry, if not null, is called. The manage_request_data struct field, for the gwcfg.cgi/set* API, corresponds to the gwcfg_cgi_set_manage_post_data function.

CVE-2023-35967 - malloc integer overflow

At [1] for the malloc function the requested size is content_length + 1. The + 1 is allegedly used to ensure there is enough space for the null terminator. Because the content_length value is never checked, an integer overflow vulnerability exists at [1] that can lead to a heap buffer overflow at [3].

CVE-2023-35968 - realloc integer overflow

At [2] for the realloc function the requested size is content_length + 1. The + 1 is allegedly used to ensure there is enough space for the null terminator. Because the content_length value is never checked, an integer overflow vulnerability exists at [2] that can lead to a heap buffer overflow at [3].

TIMELINE

2023-06-28 - Initial Vendor Contact
2023-07-06 - Vendor Disclosure
2023-10-11 - Public Release
2023-10-24 - Vendor Patch Release

Credit

Discovered by Francesco Benvenuto of Cisco Talos.