Uncovering OpenWRT Remote Code Execution (CVE-2020-7982)
Introduction: Fuzzing OpenWRT
For ForAllSecure, I’ve been focusing on finding bugs in OpenWRT using their Mayhem software. My research on OpenWRT has been a combination of writing custom harnesses, running binaries of the box without recompilation, and manual inspection of code.
I found this vulnerability initially by chance when I was preparing a Mayhem task for opkg.
Mayhem can serve data either from a file or from a network socket.
opkg downloads packages from downloads.openwrt.org, so my plan was to let this domain name point to 127.0.0.1 from which Mayhem is serving.
To test if opkg would indeed download packages from a custom network connection, I set up a local web server and created a file consisting of random bytes. When I ran opkg to install a package, it retrieved the file as I had intended, and then threw a segmentation fault.
I didn’t understand why an invalid package would cause this error. After all, the package shouldn’t be processed if the SHA256 hash was incorrect.
My initial hunch was that opkg would download the package, unpack it to a temporary directory, and only then verify the SHA256 hash before definitively installing it to the system. I suspected that the unpacker couldn’t deal with malformed data, like the file with random bytes served from my web server.
Further inspection showed that the SHA256 hash wasn’t checked at all, which is the basis of the vulnerability at hand.
I was right about the unpacker being buggy, though; malformed data would lead to a variety of memory violations.
Once I confirmed that opkg would attempt to unpack and install any package it downloads, I was able to recreate the findings with Mayhem with just a slight modification to opkg.
I set up a Mayhem task for opkg install attr (attr is a small OpenWRT package), and implicitly, Mayhem was able to find the remote code execution bug, by detecting the memory bugs in the package unpacker. If OpenWRT’s SHA256 verification had worked as intended, opkg would simply discard the package and not process it, and no segmentation faults would transpire.
Mayhem is capable of fuzzing binaries without recompilation or instrumentation. Coming from a workflow that involves writing many custom harnesses for software libraries (which Mayhem also supports), this has been a delightful experience and it has allowed me to set up targets for dozens of OpenWRT applications in just weeks, and more vulnerability disclosures are forthcoming.
In the following sections, I’ll dive deeper into how I identified the vulnerability.
OpenWRT
OpenWRT is a free, Linux-based operating system geared towards use in embedded devices in general and network routers in particular. By all accounts it is installed on millions of devices across the world.
The OpenWRT Package Manager
To install or update software on an OpenWRT system such as an OpenWRT web server, a utility called opgk is used. Its functionality and purpose are comparable to apt on Debian-based systems.
opkg retrieves the lists of package available for installation from downloads.openwrt.org over an unencrypted HTTP connection.
The package lists are digitally signed. This ensures that before the package file is processed, it is verified to come from the OpenWRT maintainers, and discarded if verification fails.
A typical entry in Packages looks like this:
The SHA256sum field is there to ensure that a downloaded package is not corrupted or compromised. The expected SHA256 hash is implicitly guaranteed to come from the OpenWRT maintainers, because the package list that embeds it, is itself verified with a valid signature.
In theory this means that through the use of signatures nor the package list, nor a package archive can be tampered even though the transport channel (HTTP) is by itself insecure.
Some discussion about this way of reasoning can be found here.
Fuzz Testing is a Proven Technique for Uncovering Zero-Days.
See other zero-days Mayhem, a ForAllSecure fuzz testing technology, has found.
Learn More Request Demo
The Bug
When the user installs a package by running opkg install <package>, opkg starts by parsing the package lists.
The parser traverses each package entry and performs different actions for each type of field. Once it comes across the SHA256sum field, it will call pkg_set_sha256:
But because checksum_hex2bin was not able to decode the SHA256sum field, the code from line 1418 onwards is simply bypassed.
It looks like the bug was introduced in February 2017, almost three years ago: https://git.openwrt.org/?p=project/opkg-lede.git;a=blobdiff;f=libopkg/file_util.c;h=155d73b52be1ac81d88ebfd851c50c98ede6f012;hp=912b147ad306766f6275e93a3b9860de81b29242;hb=54cc7e3bd1f79569022aa9fc3d0e748c81e3bcd8;hpb=9396bd4a4c84bde6b55ac3c47c90b4804e51adaf
Exploitation
For exploitation it is required that the attacker serves (compromised) packages from a web server.
The attacker must either be in a position to intercept and replace communication between the device and downloads.openwrt.org, or control the DNS server used by the device to make downloads.openwrt.org point to a web server controlled by the attacker.
Attacks on a local network using packet spoofing or ARP cache poisoning might be possible, but this has not been tested.
The sole constraint to reckon with is that the file size of compromised package must match the Size field in the package list.
Doing this is trivial:
- Create a package that is smaller than the original
- Compute the size difference between the original package and the compromised package
- Append this amount of zero bytes to the end of the compromised package
The following proof-of-concept demonstrates how exploitation may be achieved:
If we assume that the web server IP is 192.168.2.10, running following commands on an OpenWRT system:
echo "192.168.2.10 downloads.openwrt.org" >>/etc/hosts; opkg update && opkg install attr && attr
would print ‘pwned :)’ before the fixes were implemented.
The modification to /etc/hosts is required to emulate a man-in-the-middle (or compromised DNS) situation.
Remediation
As a stopgap solution, OpenWRT removed the space in the SHA256sum from the package list shortly after I reported the bug.
This helped mitigate the risk to users somewhat; users who updated their package lists following this change were no longer vulnerable, as subsequent installs would set out from a well-formed list that would not sidestep the hash verification.
However, this is not an adequate long-term solution because an attacker can simply provide an older package list that was signed by the OpenWRT maintainers.
The bug in checksum_hex2bin was fixed in this commit and integrated in OpenWRT versions 18.06.7 and 19.07.1, both released on February 1st 2020.
My recommendation is to upgrade OpenWRT versions to 18.06.7 or 19.07.1.
Notes
Back in 2016, Jann Horn of Google Project Zero found a bug with a comparable impact in Debian’s apt package manager.
Last year, another such flaw was discovered by Max Justicz.
Add Mayhem to Your DevSecOps for Free.
Get a full-featured 30 day free trial.