CVE-2022-26346
A denial of service vulnerability exists in the ucloud_del_node functionality of TCL LinkHub Mesh Wi-Fi MS1G_00_01.00_14. A specially-crafted network packet can lead to denial of service. An attacker can send packets 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 - https://www.tcl.com/us/en/products/connected-home/linkhub/linkhub-mesh-wifi-system-3-pack
9.6 - CVSS:3.0/AV:A/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
CWE-284 - Improper Access Control
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, or wired network, 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;
optional uint64 timestamp = 2;
}
Using [1] we have control over serialNum
in the packet. The parsing of the data in the protobuf is done in ucloud_del_node
.
00428a98 int32_t ucloud_del_node(int32_t arg1, int32_t arg2, int32_t arg3)
00428ab8 arg_0 = arg1
00428ac4 int32_t $a3
00428ac4 arg_c = $a3
00428ac8 int32_t var_440 = 0
00428acc int32_t var_444 = 0
00428aec void group_sn
00428aec memset(&group_sn, 0, 0x100)
00428af8 int32_t var_448 = 0
00428afc int32_t sn = 0
00428b00 int32_t var_338 = 0
00428b04 int32_t var_334 = 0
00428b08 int32_t var_330 = 0
00428b0c int32_t var_32c = 0
00428b10 int32_t var_328 = 0
00428b14 int32_t var_324 = 0
00428b18 int32_t var_320 = 0
00428b38 void var_31c
00428b38 memset(&var_31c, 0, 0x100)
00428b60 void var_21c
00428b60 memset(&var_21c, 0, 0x210)
00428b70 int32_t $v0_1
00428b70 if (arg2 == 0) {
00428b98 _td_snprintf(3, "api/map_manage.c", 0x7a1, " in is null ! \n", 0x4ae4b0)
00428ba4 $v0_1 = 0xffffffff
00428ba4 } else {
00428bc8 GetValue(name: "sys.mesh.groupsn", output_buffer: &group_sn)
00428bec GetValue(name: "serial.number", output_buffer: &sn)
00428c14 struct MxpManageList* pkt = mxp_manage_list__unpack(0, arg3, arg2)
00428c28 if (pkt == 0) {
00428c50 _td_snprintf(3, "api/map_manage.c", 0x7a9, " unpack failed ! \n", 0x4ae4b0)
00428c5c $v0_1 = 0xffffffff
00428c5c } else {
00428c78 init_node_opt_hash_table(&var_21c)
00428c94 get_node_opt_hash_table(&var_21c)
00428ca0 int32_t loop_idx = 0
00428f40 while (true) {
00428f40 if (loop_idx u>= pkt->mxp_manage_count) {
00428f50 if (pkt->is_timestamp_present != 0) {
00428f80 sprintf(&var_31c, "%llu", pkt->timestamp.d, pkt->timestamp:4.d, 0x4ae4b0)
00428fa4 SetValue(name: "sys.cfg.stamp", input_buffer: &var_31c)
00428f98 }
00428fc0 mxp_manage_list__free_unpacked(pkt, 0)
00428fdc save_all_mesh_node_opt(&var_21c)
00428ff8 free_the_hash_table(&var_21c)
0042902c printf("[%s][%d][kg] groupsn = %s\n", "ucloud_del_node", 0x7d0, &group_sn, 0x4ae4b0)
00429050 SetValue(name: "sys.mesh.groupsn", input_buffer: &group_sn)
00429064 CommitCfm()
00429070 $v0_1 = 0
00429070 break
00429070 }
00428cd8 upload_one_node_basic_info(serial_number: *(*(pkt->p_mxp + (loop_idx << 2)) + 0xc), 2)
00428d24 if (strncmp(&sn, *(*(pkt->p_mxp + (loop_idx << 2)) + 0xc), 0x20) == 0) { [2]
00428d4c printf("[%s][%d][luminais] mpp is deleteā¦", "ucloud_del_node", 0x7b5)
00428d68 monitor_stop("cmdsrv")
00428d84 monitor_stop("mesh_status_check")
00428da0 monitor_stop("pann")
00428dbc monitor_stop("netctrl")
00428dd8 mxp_manage_list__free_unpacked(pkt, 0)
00428df4 free_the_hash_table(&var_21c)
00428e08 systool_handle_restore_zero()
00428e14 $v0_1 = 0
00428e18 break
00428e18 }
00428e60 printf("[%s][%d][kg] del sn = %s\n", "ucloud_del_node", 0x7c1, *(*(pkt->p_mxp + (loop_idx << 2)) + 0xc), 0x4ae4b0)
00428e94 client_node_del(*(*(pkt->p_mxp + (loop_idx << 2)) + 0xc)) [3]
00428ed8 str_list_del_item(&group_sn, &data_480a1c, *(*(pkt->p_mxp + (loop_idx << 2)) + 0xc))
00428f18 update_node_opt_to_hash_table(&var_21c, 2, *(*(pkt->p_mxp + (loop_idx << 2)) + 0xc))
00428f2c loop_idx = loop_idx + 1
00428f28 }
00428f28 }
00428f28 }
00429084 return $v0_1
At [2] a check is done to see if the serialNum
provided is of the current device. If it is, a factory reset will effectively occur, reverting all the network configurations. [3] represents if the serialNum
provided is not the receiving base station. In this case the message is passed along the mesh to find the base station that is trying to be deleted, at which point the matching base station will perform a factory reset. This protobuf message does not require any authentication.
2022-03-29 - Vendor Disclosure
2022-08-01 - Public Release
Discovered by Carl Hurd of Cisco Talos.