2018-09-19 12:40 | Lasse Trolle Borup

Linksys Velop and some bonus CGI scripts

In the spring, we decided to have a look at the state of security in routers for home users, and our last post documented our experiences with the Asus Lyra Mini. We also chose to look at the Linksys Velop routers, as they were prominently featured on Linksys’ product page. The Velop router proved to be our easiest target, as the bug was found within the first hour of downloading the firmware. The bug is quite serious, as it can be exploited by an external attacker to grant full code execution on the router. In this post I will go through how the bug was found and how it can be exploited.

Velop
The Linksys Velop router.

After purchasing the router, I downloaded the newest version of the firmware from Linksys’ support page. My newly purchased device was running on version 1.0 hardware, and another firmware version is available for the version 2.0 hardware.

A lot of these embedded devices are running Linux. Analyzing the firmware often boils down to extracting one or more file systems from the firmware image, mounting them, and analyzing the executables, scripts, and configuration files. Many helpful tools exist for the process, including binwalk which looks for signatures in an image, and can extract a lot of common data types.

Running binwalk on the firmware image gave me the following output:

Velop

This looked very standard. As seen in the screenshot, an ext4 file system could be found in the binary image. Binwalk can automatically carve and process a number of data types from a stream automatically by adding the -e option. To extract the ext4 file system, I ran the following command:

binwalk -e -o 0x800000 -n 1 FW_WHW03_1.1.2.187020_prod.img

The added options beside -e limits binwalk to extracting the ext4 file system, as it would otherwise create a mess of individually carved files from the image. After running the command, binwalk had supplied us with the contents of the file system:

Velop

After getting access to the files, I started looking for low hanging fruits. In embedded devices, low hanging fruits often come in the form of poorly written CGI scripts supporting the web interface.

The first scripts I zoomed in on were called zbtest.cgi and zbtest2.cgi and can be found in the directory /www/cgi-bin/. I did not see any references to the scripts when clicking through the regular web interfaces, so a black-box audit would likely have missed these scripts. Both scripts implement a function called ShellExecute() which is a wrapper for the Lua function popen(), with some added output handling. Since both scripts contain multiple lines where input supplied by the user is fed directly to ShellExecute(), and both scripts are reachable without authentication, this presents a very clean command injection. To test, I ran the following command, and confirmed that the router rebooted:

wget 'http://192.168.1.1/cgi-bin/zbtest.cgi?cmd=level&nodeid=1+2+0+1&level=;/sbin/reboot'

As the injected command is executed as root, any user on the local network can take full control over the router by exploiting this vulnerability. However, this is not the only attack vector. If a user on the network browses to a web page controlled by an attacker, the vulnerability can be triggered via a simple image tag, like in the html below:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<body>
<img src="http://192.168.1.1/cgi-bin/zbtest.cgi?cmd=level&nodeid=1+2+0+1&level=;/sbin/reboot">
</body>
</html>

While parsing the page, the browser will attempt to load the image by making a HTTP request, thereby triggering the vulnerability in the Velop router on the browsing user’s network. The implication is that even a security-aware user with a fully patched browser can compromise their own network by simply visiting a web page, clicking a link, or reading a mail.

The bug was reported to Belkin, and a patched image is now available on the product's support page.

In a later post, my coworker Mads will demonstrate how to leverage this bug to run your own code on the device and how to create persistence across reboots and updates.