Advisory: Cisco RV34X Series - Authentication Bypass and Remote Command Execution
TLDR
In early 2021, we reported a few security issues to Cisco related to their RV34X series of routers, two of which have been recently patched. The issues in question were an authentication bypass and system command injection, both in the web management interface. These can be chained together to achieve unauthenticated command execution. Cisco has released an advisory, and assigned CVE IDs as follows:- RV34X /upload Authorization Bypass Vulnerability (CVE-2021-1472)
- RV34X OS Command injection in Cookie string (CVE-2021-1473)
Affected vendor & product | Cisco Small Business RV Series Router (www.cisco.com) |
Vulnerable version | RV34X 1.0.3.20 & below, RV16X/RV26X 1.0.01.02 & below. |
Fixed version | RV34X series: 1.0.03.21. RV16X/RV26X: 1.0.01.03. |
CVE IDs | CVE-2021-1472, CVE-2021-1473 |
Impact | 5.3 (medium) CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N 8.8 (high) CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L |
Credit | T. Shiomitsu, IoT Inspector Research Lab |
RV34X/RV26X/RV16X /upload Authorization Bypass Vulnerability (CVE-2021-1472)
While Cisco has noted that this issue affects other devices, I'll only go over the specifics of how it affects the RV34X series here. On RV34X devices, the web management interface is served bynginx
on port 443. nginx
is configured (by files in /etc/nginx/
) so that requests made to the URIs /upload
, /form-file-upload
and /api/operations/ciscosb-file:form-file-upload
are all passed to a CGI binary called upload.cgi
. Depending on which URI is requested, the behavior of upload.cgi
is slightly different.
In firmware revisions earlier than 1.0.3.20, there was no real attempt to restrict access to these upload.cgi
-related endpoints. In fact, a set of command injection issues from late 2020 affecting the RV34X series were initially disclosed as post-authentication issues but later revised to reflect the fact that these could be exploited pre-authentication (after a very charitable and publicly-uncredited researcher - cough cough - tipped off the Cisco PSIRT). These were tracked by the ZDI as ZDI-20-1100 and ZDI-20-1101, and you can see the Cisco advisory here. While the ZDI advisories have not been updated and still show the initial lower CVSS rating – the Cisco advisory and CVSS scores have been updated to reflect the pre-authentication nature of the bugs.
In 1.0.03.20, an authentication check was implemented. This was written into nginx
configuration, which you can see here:
The attempt here appears to be to check that some Authorization
header is set, and/or that a file exists in the /tmp/websession/token/
folder with the same name as the request sessionid
cookie. Then a user is assumed to be authorized.
Unfortunately, there’s a fatal flaw in this fix. The logic is such that any non-null Authorization
header would set $deny
to “0”. So, sending literally any valid-looking Authorization
header as part of a request to /upload
will bypass the authorization check.
RV34X OS Command injection in Cookie string (CVE-2021-1473)
Once we have bypassed authentication, it’s then possible to interact directly with the/upload
endpoint. Requests made to this endpoint are passed directly to the upload.cgi
binary by the nginx
uwsgi
CGI configuration.
Within the main()
function in upload.cgi
, the HTTP_COOKIE
environmental variable is read, and the value from the sessionid
cookie is extracted using a simple series of strtok_r
and strstr
. This specific sessionid
-reading logic is notable because, due to the strtok_r
call, it’s not possible to use “;” characters in any injection, as it will prematurely terminate the injection string. In pseudocode, it looks like this:
if (HTTP_COOKIE != (char *)0x0) { StrBufSetStr(cookie,HTTP_COOKIE); cookie = StrBufToStr(cookie); cookie = strtok_r(cookie, ";", &saveptr); while (cookie != 0x0) { cookie = strstr(cookie, "sessionid="); if (cookie != 0x0) { sessionid_cookie_value = pathparam_ + 10; } } }Because our HTTP request is made to the
/upload
URI, the main()
function in upload.cgi
calls a function at 000124a4
, which I’ve named handle_upload()
. This function takes a pointer to the sessionid
cookie value as its first argument.
void handle_upload(char *sessionId, char *destination, char *option, char *pathparam, char *fileparam, char *cert_name, char *cert_type, char *password)
It also takes several other arguments, each of which are populated by the multipart request parsing that takes place in the main()
function. The names I’ve given these arguments roughly align with the names of the parameters that this multipart ingesting logic looks for.
Depending on what string is passed as theWithinpathparam
parameter, slightly different code paths will be taken, which means that slightly different checks must be bypassed to be able to reach the vulnerable code. In this example, I am using a request with thepathparam
set to “Configuration”, so the pseudocode I'm showing reflects this.
handle_upload()
, a curl
command is constructed with a call to sprintf
, the resulting buffer of which is then passed directly to popen
:
ret = strcmp(pathparam, "Configuration"); if (ret == 0) { config_json = upload_Configuration_json(destination,fileparam); if (config_json != 0) { post_data = json_object_to_json_string(config_json); sprintf(command_buf, "curl %s --cookie \'sessionid=%s\' -X POST -H \'Content-Type: application/json\' -d\'%s\' ", jsonrpc_cgi, sessionId , post_data); debug("curl_cmd=%s",command_buf); __stream = popen(command_buf, "r"); if (__stream != (FILE *)0x0) { [...snip...] }The
sessionid
cookie value that we have passed in our request is passed directly into this sprintf()
call. With a crafted sessionid
value, we would therefore be able to inject arbitrary commands into this command buffer. This will run the command with the privileges of the upload.cgi
binary which, in this case, is www-data
.
Key Takeaways
Logic bugs can be quite easy to introduce, and sometimes tricky to identify. Authentication can be difficult to implement well, especially when multiple authorization methods might be accepted. As higher-end embedded devices start to use more common server software components (for purposes they were not necessarily intended for), there are often more layers of complexity introduced - thicker web servers requiring more precise configuration, CGI binaries, middleware gluing things together. Each layer introduces opportunities for mis-configuration, which could lead to security issues.Timeline
2021-01-02: Initial disclosure made to Cisco PSIRT. 2021-01-07: Confirmation of receipt of disclosure from Cisco PSIRT. 2021-01-27: Confirmation that issue is valid from Cisco PSIRT. 2021-02-12: Update from Cisco PSIRT. 2021-03-23: We contact Cisco PSIRT for timeline update and CVE IDs. 2021-03-23: Cisco PSIRT respond giving us timeline and CVE IDs. 2021-04-07: Cisco release advisory.
About Onekey
ONEKEY is the leading European specialist in Product Cybersecurity & Compliance Management and part of the investment portfolio of PricewaterhouseCoopers Germany (PwC). The unique combination of an automated Product Cybersecurity & Compliance Platform (PCCP) with expert knowledge and consulting services provides fast and comprehensive analysis, support, and management to improve product cybersecurity and compliance from product purchasing, design, development, production to end-of-life.
CONTACT:
Sara Fortmann
Marketing Manager
sara.fortmann@onekey.com
euromarcom public relations GmbH
+49 611 973 150
team@euromarcom.de
RELATED RESEARCH ARTICLES
Security Advisory: Unauthenticated Command Injection in Mitel IP Phones
Discover critical vulnerabilities in Mitel SIP phones that allow unauthenticated command injection. Learn how outdated input parsing can expose your devices and why it's essential to scan firmware for security risks. Protect your network with our in-depth analysis and expert takeaways.
Ready to automate your Product Cybersecurity & Compliance?
Make cybersecurity and compliance efficient and effective with ONEKEY.