Interfaces.d to RCE

Several months ago, I was having a poke at the Mozilla WebThings IoT gateway. The gateway essentially allows a user to host their own IoT cloud from a device (such as a Raspberry Pi) on their local network. It creates a tunnel to a personal subdomain of mozilla-iot.org for managing a user’s devices from the internet. I was—and am—fairly excited about the idea, especially the privacy aspects of hosting all your data yourself. I wanted to get a sense of the security controls and see if I could catch any cool bugs before the gateway matured and entered more widespread use.

After flashing the gateway image to my Raspberry Pi’s SD card and setting it up, I started exploring the web interface through my trusty proxy and testing some basic things. I quickly noticed an issue: the settings endpoints could be used without authentication. Anyone could change the configuration of anyone else’s gateway from the internet. In order to get a feel for severity, I had to see what these settings could do. In particular, I started looking for anywhere the settings might use user input in an unsafe way that could allow me to get code injection on the device and compromise it.

The underlying functionality for these endpoints is abstracted into platform-specific code (that is, separate code for Raspberry Pi, OpenWRT, and other platforms supported by the gateway). I focused on the Raspberry Pi code since that was what I was using. Most of these functions employed regexes to validate input, successfully thwarting any code injection attempts. 

I noticed one endpoint that lacked input validation: the one for setting the network interface configuration. In the platform-specific code, the functionality was implemented by writing the (unvalidated) user input into the /etc/interfaces.d/eth0 file and then restarting the network service. I had an interfaces.d injection. It didn’t feel like I had gained much, though, since these are configuration files. I assumed the worst I would be able to do would be to muck with the network settings. Still, I looked at the man page to find out how to do the aforementioned mucking and saw this bit:

IFACE OPTIONS
The following “command” options are available for every family and method. Each of these options can be given multiple times in a single stanza, in which case the commands are executed in the order in which they appear in the stanza. (You can ensure a command never fails by suffixing them with “|| true”.)

pre-up command
Run command before bringing the interface up. If this command fails then ifup aborts, refraining from marking the interface as configured, prints an error message, and exits with status 0. This behavior may change in the future.

Interfaces.d injection is not just data injection—it’s code injection too! By inserting one of these event hooks into the file, it’s possible to get command execution. Since these scripts must run as root in order to be able to manipulate the network settings, this leads to a complete compromise of the affected gateway. The following proof of concept request proved this by creating a file `/tmp/poc` with root ownership:

PUT /settings/network/lan HTTP/1.1
Host: affected-gateway-name.mozilla-iot.org
Accept: application/json
Content-Type: application/json
Content-Length: 148
Connection: close

{
 "mode":"static",
 "options":{
  "ipaddr":"192.168.0.250",
  "netmask":"255.255.255.0",
  "gateway":"192.168.0.1\n    pre-up touch /tmp/poc"
 }
}

This led to the following entry in /etc/interfaces.d/eth0:

auto eth0
iface eth0 inet static
    address 192.168.0.250
    netmask 255.255.255.0
    gateway 192.168.0.1
    pre-up touch /tmp/poc

Using such a request (with a more malicious payload), an attacker could have remotely compromised any WebThings gateway, knowing only the gateway’s subdomain. Since these gateways live on internal home networks, they would make a great pivot point to launch an attack against any vulnerable IoT devices on the network—the point of the gateway is to manage IoT devices, so there are bound to be some. Additionally the attacker could go marauding through any open file shares, attempt to intercept unencrypted traffic, exploit outdated computers, and generally cause all manner of hijinks.

I reported the issue to Mozilla on August 14 2019. A fix was implemented and automatically applied to all affected devices on August 15 2019, which is incredible! Great job Mozilla (and particularly the WebThings team) for having systems in place to receive and triage bug reports, fix them, and rapidly apply the fixes over the air.