# Advisory: Cisco RV34X Series - Privilege Escalation in vpnTimer
May 5, 2021
|In [Research](https://www.iot-inspector.com/blog/category/research/ "View all
posts in Research")
|By [research@iot-inspector.com](https://www.iot-
inspector.com/blog/author/researchiot-inspector-com/)
## TL;DR
A few weeks ago, [we published an advisory on the Cisco RV series
routers](https://www.iot-inspector.com/blog/advisory-cisco-
rv34x-authentication-bypass-remote-command-execution/), where we outlined the
root cause for authentication bypass and remote command execution issues.
This week, [Cisco has released an advisory for another bug we
reported](https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-
sa-rv-34x-privesc-GLN8ZAQE) around the same time: A privilege escalation
issue, which could be used in combination with the other two issues to run
arbitrary code with root privileges on affected RV34X devices. As embedded
devices often run everything with root privileges, it's relatively uncommon
that we have the opportunity to find privilege escalation bugs, so this is a
particularly interesting case. In this post, we'll do a quick root-cause
analysis of this bug we found.
![Cisco RV340](https://images.seebug.org/1620972449474-w331s)(C) Cisco
| Affected vendor & product | Cisco Small Business RV Series Router ([www.cisco.com](https://www.cisco.com/)) |
| ------------------------- | ------------------------------------------------------------ |
| Vulnerable version | RV34X 1.0.3.20 & below, |
| Fixed version | RV34X series: [1.0.03.21](https://software.cisco.com/download/home/286287791/type/282465789/release/1.0.03.21). |
| CVE IDs | CVE-2021-1520 |
| Impact | 6.7 (medium) [CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H](https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?vector=AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H&version=3.1) |
| Credit | T. Shiomitsu, IoT Inspector Research Lab |
## RV34X Privilege Escalation in vpnTimer (CVE-2021-1520)
It's not often that one gets to report a privilege escalation in the embedded
security world. So often, everything is running as root, that opportunities
for privilege escalation are few and far between. However, on the RV34X
series, the `nginx` server runs as the `www-data` user. This isn't
particularly useful for an attacker and, as such, a privilege escalation
exploit is required in order to run code as root.
Luckily, we identified a vulnerable service called `vpnTimer`, which runs as
`root`, and exposes a socket on the local loopback interface (on UDP/9999).
This service receives data on this socket and passes it insecurely to a system
command string. As such, if an attacker can run lower-privileged code on an
RV34X device, they would be able to then send a crafted UDP packet to
127.0.0.1:9999, which will run an arbitrary command with root privileges.
`vpnTimer` is simply a Perl script, which waits for connections on UDP/9999.
`process_timer()` is the main function of this script, and runs in a loop as
follows (with some extraneous code snipped):
```
sub process_timer() {
while(1) {
@sockets_ready = $select->can_read(1);
if (! scalar(@sockets_ready)) {
[...snip...]
} else {
#print("$cur_min : $cur_sec\n");
foreach $socket_new (@sockets_ready) {
if (! recv($socket_new, $message, 1024, 0)) {
print "Error reading from socket: $!\n";
} else {
my $temp=substr($message,1,);
[...snip...]
if (index(substr($message,0,1),"+") == 0){
my $isTVPNC=`uci get strongswan.$temp`;
chomp $isTVPNC;
if ($isTVPNC eq "client"){
system("tvpnc_timer $temp &");
} else {
my $interval=`uci get strongswan.$temp.keep_alive_interval`;
chomp $interval;
$conn_time{$temp}=$interval;
addtimer($temp,$interval,1);
}
```
Highlighted here are the key components. The `$message` is read from the
socket. `$temp` is initialized to contain the contents of `$message` without
the first character (removed with this call to `substr`).
The first character of `$message` is then checked, to see if it is a "+"
character. If it is, the value of `$temp` is passed to a statement within two
separate backtick strings. In Perl, statements within backticks are executed
as a system shell command. Since the `vpnTimer` service is running as root,
the command will be run as root.
As such, sending a packet to UDP/127.0.0.1:9999 with the content "+;touch
/tmp/test;" will result in command `uci get strongwan.;touch /tmp/test;` being
run, and the file `/tmp/test` being written to the filesystem by the `root`
user.
## How Long Was This Bug There?
We thought it might be interesting to see if we could quickly figure out how
long this bug has been affecting the Cisco RV34X devices. Often, our
assumption might be that a script with a 2015-dated copyright string at the
top has not been thought about or actively changed since 2015. However,
assumptions are always good to challenge.
With our API, it's relatively simple to script a set of GraphQL queries, and
spit out a list of file hashes for all version of `vpnTimer` for all available
RV34X firmware images:
![vpnTimer hashes over time.](https://images.seebug.org/1620972450789-w331s)
We can very easily see that between firmware versions 1.0.01.20 (released on
the 19th October 2018) and 1.0.02.16 (released 1st January 2019), the
`vpnTimer` script was actually slightly edited.
The changes were not significant, mainly adding a `signal_handler()` function,
and fixing a couple of typos:
![vpnTimer diff](https://images.seebug.org/1620972453156-w331s)
New function and logging lines added.
![vpnTimer diff](https://images.seebug.org/1620972454740-w331s)
A typo being fixed.
But it does illustrate that this script was actively developed in the past
couple of years, and that a bunch of small extra functionalities were added.
This script may not have necessarily been thought about deeply, but it was
certainly not a forgotten relic. This info does also show that this bug in the
`vpnTimer` script has been present since at least the first firmware update
package of the RV34X series (February 2017).
## **Key Takeaways**
When implementing different privilege levels, it's always important to follow
the principle of least privilege. Does the component need root privileges, or
is it possible to run it with a more limited set? In a well-hardened system,
an attacker would be hard-pressed to come across a component running as root,
which does not absolutely require root privileges. In more complex systems,
you may even consider implementing SELinux (well) to provide an even more
granular set of permissions to each process.
Despite all this, since so many embedded devices run everything with root
privileges, it's still heartening to see when privilege separation is at least
attempted in a device. Thanks for the challenge, Cisco!
Unavailable Comments