TCL LinkHub Mesh Wifi confsrv ucloud_add_node OS command injection vulnerability

# Talos Vulnerability Report

### TALOS-2022-1458

## TCL LinkHub Mesh Wifi confsrv ucloud_add_node OS command injection vulnerability

##### August 1, 2022

##### CVE Number



An os command injection vulnerability exists in the confsrv ucloud_add_node functionality of TCL LinkHub Mesh Wi-Fi MS1G_00_01.00_14. A specially-crafted network packet can lead to arbitrary command execution. An attacker can send a malicious packet to trigger this vulnerability.


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

TCL LinkHub Mesh Wifi MS1G_00_01.00_14


LinkHub Mesh Wifi –

##### CVSSv3 SCORE

9.6 – CVSS:3.0/AV:A/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

##### CWE

CWE-78 – Improper Neutralization of Special Elements used in an OS Command (‘OS Command Injection’)


The LinkHub Mesh Wi-Fi system is a node-based mesh system designed for Wi-Fi deployments across large homes. These nodes include most features standard in current Wi-Fi solutions and allow for easy expansion of the system by adding nodes. The mesh is managed solely by a phone application, and the routers have no web-based management console.

The LinkHub Mesh system uses protobuffers to communicate both internally on the device as well as externally with the controlling phone application. These protobuffers can be sent to port 9003 while on the Wi-Fi provided by the LinkHub Mesh in order to issue commands, much like the phone application would. Once the protobuffer is received, it is routed internally starting from the `ucloud` binary and is dispatched to the appropriate handler.

In this case, the handler is `confsrv`, which handles many message types. In this case we are interested in `MxpManageList`.

message MxpManage {
required string serialNum = 1; [1]
required int32 opt = 2;
message MxpManageList {
repeated MxpManage mxp = 1; //This is not optional, so it must be resolved by hand to compile to .proto //This is not optional, so it must be resolved by hand to compile to .proto
optional uint64 timestamp = 2;

Using [1] we have control over the `serialNum` in the packet. The parsing of the data within the protobuffer is `ucloud_add_node`.

00428478 int32_t ucloud_add_node(int32_t arg1, int32_t arg2, int32_t arg3)
00428498 arg_0 = arg1
004284a4 int32_t $a3
004284a4 arg_c = $a3
004284a8 int32_t var_12c = 0
004284ac int32_t var_130 = 0
004284cc void var_128
004284cc memset(&var_128, 0, 0x100)
004284d8 int32_t var_28 = 0
004284dc int32_t var_24 = 0
004284e0 int32_t var_20 = 0
004284e4 int32_t var_1c = 0
004284e8 int32_t var_18 = 0
004284ec int32_t var_14 = 0
004284f0 int32_t var_10 = 0
004284f4 int32_t var_c = 0
004284fc int32_t $v0_1
004284fc if (arg2 == 0) {
00428524 _td_snprintf(3, “api/map_manage.c”, 0x737, ” in is null ! n”, 0x4ae4b0)
00428530 $v0_1 = 0xffffffff
00428530 } else {
00428558 struct MxpManageList* $v0_3 = mxp_manage_list__unpack(0, arg3, arg2)
0042856c if ($v0_3 == 0) {
00428594 _td_snprintf(3, “api/map_manage.c”, 0x73d, ” unpack failed ! n”, 0x4ae4b0)
004285a0 $v0_1 = 0xffffffff
004285a0 } else {
004286b0 for (uint32_t var_130_1 = 0; var_130_1 umxp_manage_count; var_130_1 = var_130_1 + 1) {
004285d0 if (confctl_module_debug_en(module_id: 9) != 0) {
00428618 printf(“x1b[1;32m[%s][%d] : x1b[0mx1b…”, “ucloud_add_node”, 0x743, *(*($v0_3->p_mxp + (var_130_1 p_mxp + (var_130_1 > /proc/mesh/authorized”, *(*($v0_3->p_mxp + (var_130_1 is_timestamp_present != 0) {
004286f0 sprintf(&var_28, “%llu”, $v0_3->timestamp.d, $v0_3->timestamp:4.d, 0x4ae4b0)
00428714 SetValue(name: “sys.cfg.stamp”, input_buffer: &var_28)
00428708 }
00428728 CommitCfm()
00428744 mxp_manage_list__free_unpacked($v0_3, 0)
00428750 $v0_1 = 0
00428750 }
00428750 }
00428764 return $v0_1

At [2] the `serialNum` is used directly in `doSystemCmd`.

000209b0 int32_t doSystemCmd(int32_t arg1, int32_t arg2)
000209d0 arg_4 = arg2
000209d4 int32_t $a2
000209d4 arg_8 = $a2
000209d8 int32_t $a3
000209d8 arg_c = $a3
000209fc void var_408
000209fc memset(&var_408, 0, 0x400)
00020a30 log_debug_print(“doSystemCmd”, &data_1b8d, 0, 0x80, 0x55500) {“function entry!”}
00020a64 vsnprintf(&var_408, 0x400, arg1, &arg_4)
00020a80 int32_t $v0_1 = system(&var_408)
00020ab8 log_debug_print(“doSystemCmd”, &data_1b93, 0, 0x80, 0x55510) {“function exit!”}
00020ad8 return $v0_1

With a quick look at `doSystemCmd` we can see that no special escaping is happening here and thus this is a simple command injection using `serialNum` directly.


2022-04-27 – Vendor Disclosure
2022-08-01 – Public Release

##### Credit

Discovered by Carl Hurd of Cisco Talos.

* * *

Vulnerability Reports Next Report


Previous Report

TALOS-2022-1457Read More

Back to Main

Subscribe for the latest news:
Generated by Feedzy
%d bloggers like this: