CVE-2023-27380
An OS command injection vulnerability exists in the admin.cgi USSD_send functionality of peplink Surf SOHO HW1 v6.3.5 (in QEMU). A specially crafted HTTP request can lead to command execution. An attacker can make an authenticated 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.
Peplink Surf SOHO HW1 v6.3.5 (in QEMU)
Surf SOHO HW1 - https://www.peplink.com/products/soho-series-surf/
7.2 - CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H
CWE-78 - Improper Neutralization of Special Elements used in an OS Command (‘OS Command Injection’)
The Surf series of SOHO routers is marketed as an entry-level router for use at home. It provides networking via USB cellular modems, ethernet and Wi-Fi. The device can host a VPN and supports Wi-Fi meshing.
The device hosts a web interface for administrative configuration. An OS command injection vulnerability exists in the handling of requests destined for the /cgi-bin/MANGA/admin.cgi
endpoint that are intended to interact with the GSM module. This endpoint is accessible only after successfully authenticating as a user with write privileges on the device. The HTTP POST request must have a parameter, section
, whose value is set to USSD_send
in order to reach the vulnerable code. This vulnerability only impacts systems with a functioning GSM module.
The vulnerable function is located in the file admin.cgi
at offset 0x4b124c
in firmware version 6.3.5, and we refer to it as USSD_send
. An annotated decompilation of the function is included for reference.
int USSD_send(taglist_t* taglist) {
char cmd[0x400] = {0};
FILE* stream;
int conn_id = strtol(cgi_safe_param("conn_id"), 0, 10);
int sim_id = strtol(cgi_safe_param("sim_id"), 0, 10);
char* ussd_code = cgi_safe_param("ussd_code"); // [1] Extract ussd_code parameter into ussd_code variable
char* status;
if (is_wan_connection_available(taglist, conn_id) == 0) // [2] Device must have functioning GSM module
{
status = "Selected WAN connection is not available at this moment.";
xml_error(&status);
return 1;
} else {
snprintf(&cmd, 0x400, "mdstatus -W%d -Ci", conn_id);
stream = popen(&cmd, "r");
... // [3] Perform some data extraction on response
snprintf(&cmd, 0x400, "mdstatus -W%d -u"%d,%s"", conn_id, sim_id, ussd_code); // [4] Craft the command using unchecked, attacker-controlled ussd_code parameter
stream = popen(&cmd, "r"); // [5] Execute the command with root privileges
... // [6] Perform some data extraction and business logic on response
}
return 0;
}
Observe at [1]
that a pointer to the HTTP POST param ussd_code
is placed into the ussd_code
stack variable. If the network status check at [2]
is passed, the command is crafted at [4]
by directly injecting the attacker-controlled value. Finally, at [5]
, the command is executed with root privileges. A properly formatted request can escape the intended command and execute arbitrary commands.
2023-06-26 - Initial Vendor Contact
2023-06-27 - Vendor Disclosure
2023-10-11 - Public Release
Discovered by Matt Wiseman of Cisco Talos.