CVE-2023-34356
An OS command injection vulnerability exists in the data.cgi xfer_dns 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/data.cgi
endpoint, which are intended to interact with the DNS zone transfer operations. This endpoint is accessible only after successfully authenticating as a user with write privileges on the device. The HTTP POST request must have its parameter option
set to xfer_dns
and parameter step
set to view_domain
in order to reach the vulnerable code.
The vulnerable function is located in the file data.cgi
at offset 0x491814
in firmware version 6.3.5, and we refer to it as view_domain
. In order to reach it, the request first transits through a dispatcher function located at 0x491f14
, which we refer to as xfer_dns
. Annotated decompilations of the dispatcher function and vulnerable function are included for reference.
int xfer_dns()
{
char *xfer_session = cgi_safe_param("xfer_session");
// [1] This must be set to `view_domain` in order to be dispatched to the vulnerable function
char* step = cgi_safe_param("step");
if (xfer_session[0] == 0)
{
fprintf(*stdout, "<%s>%s</%s>", "error", "Missing session", "error");
return 0;
}
// [2] Observe that some parameters are subject to (a degree of) scrutiny and validation before use
if (contains_dangerous_shell_characters(xfer_session) == 0)
{
fprintf(*stdout, "<%s>%s</%s>", "error", "Unauthorized session", "error");
return 0;
}
// [3] Dispatch code follows
if (strcmp(step, "zone_transfer") == 0)
{
return zone_transfer(xfer_session);
}
if (strcmp(step, "status") == 0)
{
return status(xfer_session);
}
if (strcmp(step, "view_domain") == 0)
{
return view_domain(xfer_session, cgi_safe_param("domain"));
}
if (strcmp(step, "import") == 0)
{
return import("session");
}
fprintf(*stdout, "<%s>%s</%s>", "error", "Unknown step", "error");
}
The dispatch functionality is relatively straightforward and is included to show that at [2]
certain parameters are subject to a limited degree of scrutiny. The implementation of what we refer to as contains_dangerous_shell_characters
is limited to checking whether the parameter contains grave mark, double quote or dollar sign characters. At [3]
the dispatcher hands control off to the target function, which in this case is view_domain
.
int view_domain(char* xfer_session, char* domain)
{
char cmd[0x400] = {0};
// [4] Craft the command using the unchecked, attacker-controlled domain parameter
snprintf(&cmd, 0x400, "/usr/local/ilink/bin/dns_import view \"%s\" \"%s\" 2>/dev/null", xfer_session, domain);
// [5] Execute the command with root privileges
FILE* stream = popen(&cmd, "r");
... // The remainder of this function is responsible for parsing the output of dns_import and is unrelated to this vulnerability
}
}
Observe that when dispatched (at [3]
) the HTTP POST param domain
is passed as the domain
parameter to view_domain
. An OS command is crafted at [4]
by directly injecting the attacker-controlled domain
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.