In this blogpost, we take a closer look at our research regarding CVE-2022-47758: a critical vulnerability impacting a very large number of Internet of Things smart devices. We could leverage this vulnerability in the lamp's firmware for unauthenticated remote code execution on the entire device with the highest privileges and hence abuse it for information gathering (and for haunting someone in their own house). Additionally, we could pivot to the management devices using a vulnerability in the smart lamps' desktop management software (CVE-2022-46640). To make matters more interesting: the vulnerable traffic flowed through an encrypted outbound connection which means that it typically isn't blocked by a firewall. This blogpost serves as a cautionary tale for both vendors and consumers, highlighting the importance of IoT security. Join us as we dive into the technical details and lessons learned from our research.
- Introduction to smart lighting
- Proof of Concept exploit
- Analyzing the smart device firmware
- Creating a Proof of Concept exploit
Introduction to smart lighting
IoT smart lighting bring convenience and style to your home lighting. These lamps can be controlled through a variety of devices, including smartphones and computers, allowing you to change the color, brightness, and even schedule when they turn on and off. This allows you to customize your lighting to fit your mood and needs, making your home a more comfortable and inviting place. The integration of these lamps into the internet of things (IoT) allows for even greater control and automation, streamlining your home lighting experience and making it easier than ever to create the perfect ambiance.
However, as with any connected device, there are security implications to consider. A vulnerability in one of these smart lamps could potentially give an attacker access to your home network and other connected devices... It is important to follow best practices for securing your home network to reduce the risk of a security breach. Examples of these best practices are keeping your devices' software up-to-date and perhaps even keeping the smart devices on a seperate sub-network to avoid privacy concerns.
Proof of Concept exploit
The goal of our proof of concept (PoC) exploit is proving that we can remotely execute code on our own smart lamps. For the PoC exploit we're redirecting local traffic to the vendors MQTT(S) broker to our own machine via malicious DNS records. In practice, an attacker could perform this redirect by committing either a rogue DHCP server attack, hacking a router, hacking a DNS server, et cetera. Once we have control over the MQTT traffic, we send a debugging command to a debugging endpoint on our smart lamp. Finally, we activate a persistent OpenSSH server in order to easily access the lamp.
We use the following methodology in this blogpost:
- *.acme.org - the vendor domain names
- mqtt.acme.org - the vendor MQTT broker domain name
- 192.168.128.0/24 - our controlled network environment
- 192.168.128.10 - our attacker machine
- 192.168.128.20 - our vulnerability smart device
In order to spoof DNS we need to set up a rogue DHCP server. The Dynamic Host Configuration Protocol (DHCP) is primarily used by network administrators to set the private ip addreses of devices on the network dynamically. However, DHCP packets also have a few more interesting parameters: domain name servers IP addresses, hostnames, and even gateway IP addresses. In order to MitM MQTT traffic to
mqtt.acme.org, we are setting the domain name of the smart lamp by creating a malicious DHCP offer - using our rogue DHCP server - which sets the domain name server to
isc-dhcp-server on our Linux install and configuring it to run maliciously on our local network environment (
192.168.128.0/24). We want to make the smart lamp use our own DNS resolver over at
192.168.128.10. The configuration we use is as following:
In order to change the IP address to which
mqtt.acme.org points, we need to setup our own DNS resolver by installing
bind9 and setting a custom DNS record for the zone
mqtt.acme.org which points to our own MQTT broker:
Setting up a malicious MQTT broker
Since our traffic to
mqtt.acme.org now points to our own IP address (
192.168.128.10), we can eavesdrop the traffic. However, in order to interact with this traffic, we need to set an MQTT broker up on
192.168.128.10. We do this so we can publish to a custom debugging MQTT channel devoted to debugging (custom made by Acme). By publishing on this MQTT channel, we can execute commands. It's important that the server listens on port 443, has TLS encryption and allows anonymous logins. Hence, if the smart lamp tries to connect to
mqtts://email@example.com:443 it should succeed. We configured it by using the following configuration:
As you might have noticed, we are dealing with MQTTS. Like HTTPS, the S in MQTTS stands for Secure. In order to make such a protocol secure, we need to create TLS certifications so we can encrypt the MQTT trafifc coming from our own MQTT broker. We can create such TLS certifications by running the following command:
Performing the exploit
Now we have our infrastructure set up, we need to reboot the lamp such that it will trigger a DHCP discover request as part of the Discover Offer Request Accept (DORA) sequence. The next part of the DORA sequence would be 'Offer', where the server offers a new IP address (and our domain name server IP address) to our smart lamp. That offer will set the lamps DNS records of
We can confirm that the vulnerable smart lamp is using our own MQTT broker by inspecting the local traffic using Wireshark on
192.168.128.10. After the victim device has connected to our server, we want to activate an OpenSSH server. In order to do this, we create the
/acme/ssh_enabled file which enables persistent SSH access after the device reboots. We could probably do it without rebooting, be it would be a lot more unnecessary effort. After that, we stop the debugging of the touch command, and instead debug
passwd -d root which deletes the password for the root user. This is convenient, because the default password is unknown and this way we can set the password without a TTY. Additionally the SSH server allows passwordless logins. In order to pull it off, we execute the following commands using
mosquitto_pub (publishes messages to the Mosquitto broker):
Once we started the OpenSSH server on the smart lamp, we can log into our smart lamp by simply executing
$ ssh firstname.lastname@example.org
email@example.com:~ $ uname -a
Linux AcmeProduct-MAC 4.14.195 #0 Sun Sep 6 16:19:39 2020 mips GNU/Linux
Analyzing the smart device firmware
Since we have access to the firmware, we can analyze the firmware by extracting it using Binwalk - a tool for analyzing and extracting firmware. By running it with the
--extract) parameter, we can extract the firmware partitions. In our case, we can see that we have 3 partitions: a bootloader, a kernel, and an OpenWRT install (interestingly enough).
Enumerating the OpenWRT installation
The output of Binexp is a SquashFS filesystem instance which got carved out of the extracted partition. SquashFS performs heavy compressions and hence it probably was used by the smart lamp developers because it saves storage costs. Since SquashFS doesn't have different layers such as OverlayFS, we do not have any hassle regarding fixing the FS.
One of the first things we did was verifying with what OS we were working and checking which users existed on the device. After we established that the lamp was running OpenWRT - a router OS interestingly enough - and we couldn't find any custom users in
/etc/passwd, we decided to look into the next interesting directory:
We started searching in
/acme_config/ for interesting keywords such as
grep -iRPe '(ssh)|(mqtt)|(ftp)|(api)' to find possible exposed services as an attack surface. As we researched the binaries containing the specified keywords, we found out that a particular binary called
ColorCC.bin contained the entire smart lamp API accessible via HTTP (built using the OpenAPI C++ SDK). We tried searching for memory corruption bugs for easy RCE but could not find any. Next, a binary called
cloud_daemon caught our attention because it contained an MQTT client...
Investigating the MQTT handler
In order to grasp the internal logic of the
cloud_daemon, we can open it in Ghidra. Ghidra is a software reverse engineering suite developed by the National Security Agency (NSA). We can use Ghidra to decompile Assembly instructions (the raw instructions that go into the CPU) into normal C, which is relatively readable by code monkeys like us.
We can see that
libcloudpipe.so) is called in
main(), which registers several callback functions:
cloud_pipe_start(..., ..., register_channels, on_disconnect_cb, on_tick_cb, ...). The function
register_channels is a wrapper for registering handlers for the MQTT channels discussed above.
The most interesting handler function sounds like
debug, which handles messages on the channel
/acme/device/serialno/exec/server. This function handles debug requests: it can execute a binary (debug a process) based on the MQTT requests parameters, or kill the process (stop the debugging). In order to start debugging a binary, we can publish the following the the server exec channel:
debug /bin/echo "Hello World!", of which "Hello World!" should be nicely returned in an MQTT message on the channel
/acme/device/serialno/exec/client. When we want to execute another binary or generally stop debugging, we can simply issue a
So far, I hope that the following part of the MQTT payload in the PoC exploit makes sense:
Investigating the communication protocol
Now we have a primitive for our exploit: a debugging endpoint which could be abused if we could send messages on the
/acme/device/serialno/exec/server channel of the MQTT broker. Mind you, it would cause CHAOS if this MQTT broker could be hacked to allow an attacker to send messages to all devices connected to the MQTT broker. Since we don't want to try to hack the vendor since it would be cybercrime, we aren't going to test the official MQTT broker, so we tried to find ways to MitM the traffic going to
mqtt.acme.org, however we couldn't succeed since it used TLS... But - we asked ourselves - what if the TLS configuration was insecure? E.g. an insecure version?
In order to find the TLS configuration, we dug into the functions that were called to setup the MQTT client:
cloud_pipe_start. By running a simple
grep -iRe 'cloud_pipe_subscribe' query again, we can see that our function is originating from
An interesting part of the
cloud_pipe_start() function is the subsystem where a TLS network connection gets initiated by
ConnectNetwork() and the MQTTClient gets initiated by
MQTTClient(). We can find the TLS configuration in
ConnectNetwork() and I quickly identified the used TLS library as mbedtls. Whilst searching for documentation of the used functions in the mbedtls library, I found out that the parameter
MBEDTLS_SSL_VERIFY_NONE gets passed to the configuration function
mbedtls_ssl_conf_authmode. This means that TLS certifications are not validated...
We have the final piece.
Creating a Proof of Concept exploit
The primitives in our exploit are complete: we have a dangerous debugging endpoint listening to a server which can be eavesdropped. Now it's a matter of performing a Man-in-the-Middle (MitM) attack on the MQTT broker and creating a payload to send.
We have plenty of options to MitM network traffic when the TLS certifications aren't verified, but our favorite approach is using a rogue DHCP server to serve fake DNS records. We picked the
isc-dhcp-server DHCP service because it works on Linux and because it's very customizable. We're using option
domain-name-server to set the DNS server to
192.168.128.10 on the smart lamp. This means that if the lamp requests
mqtt.acme.org, it will be resolved by our own DNS resolver over at
bind9 as a DNS resolver in order to create fake DNS zones/records. We created a basic type A (IPv4) DNS record for
mqtt.acme.org which redirects to our own MQTT broker
192.168.128.10. Usually these kind of attacks are prevented by verifying the TLS certifications of the broker as a client, but the smart lamp did not perform those verification checks.
For the final serice we needed an MQTT broker, for which we chose
mosquitto. We didn't configure it at all and just made sure that it was possible to publish and subscribe to any MQTT channels. However, we had to make sure that our service was running on port 443 (which is typically used for HTTPS), that it supported TLS, and that anonymous logins were allowed (anonymous login means that any username/password is allowed to login).
Now we have our entire infrastructure up and running, we need to send the payload commands to our own MQTT broker. We can easily use the
mosquitto_pub utility for this to publish our own messages to specific channels. Additionally, we can use the
mosquitto_sub utility for subscribing to other channels so that we can receive stdout from the smart lamp. In order to easily get our very own OpenSSH server we need to create a file called
/acme_config/ssh_enabled and reboot. However,
root is the only user with a default shell (
/bin/ash) but we don't know its password.
We can overwrite the root password using
passwd -d which resets the password to be empty, and the OpenSSH will gladly accept that. This means that we can essentially start an OpenSSH server using
touch /acme_config/ssh_enabled && passwd -d && reboot. However, in practice our commands get executed using
execv(char* filepath, char** argv). This means that we need to execute the commands seperately with the full path. Hence, our payload is as follows:
$ mosquitto_pub -L mqtts://127.0.0.1:443/acme/device/serialno/exec/server -m "debug /bin/touch /acme/ssh_enabled" --insecure --cafile /etc/mosquitto/certs/ca.crt
$ mosquitto_pub -L mqtts://127.0.0.1:443/acme/device/serialno/exec/server -m "stop" --insecure --cafile /etc/mosquitto/certs/ca.crt
$ mosquitto_pub -L mqtts://127.0.0.1:443/acme/device/serialno/exec/server -m "debug /bin/passwd -d root" --insecure --cafile /etc/mosquitto/certs/ca.crt
$ mosquitto_pub -L mqtts://127.0.0.1:443/acme/device/serialno/exec/server -m "stop" --insecure --cafile /etc/mosquitto/certs/ca.crt
$ mosquitto_pub -L mqtts://127.0.0.1:443/acme/device/serialno/exec/server -m "debug /sbin/reboot" --insecure --cafile /etc/mosquitto/certs/ca.crt
When we execute this, we start the OpenSSH server and we can log in as root:
$ ssh firstname.lastname@example.org
As we have discovered in this article, a critical vulnerability was found in many, many IoT smart lighting devices, allowing attackers to gain control over the entire device and access sensitive information. This serves as a reminder of the importance of IoT security for both vendors and consumers.
As consumers, we can follow these best practices to enhance the security of our home network:
- Keep devices' software up-to-date to prevent vulnerabilities from being exploited.
- Keep smart devices on a separate sub-network to reduce privacy concerns.
- Use long passwords (even pass-sentences) and two-factor authentication where possible.
- Disable unused or unnecessary services and ports on devices.
As developers, we can implement the following best practices to ensure the security of our IoT devices:
- Conduct thorough security assessments and penetration testing to identify and fix vulnerabilities before deploying devices.
- Implement encryption and authentication mechanisms to secure data transmitted between the device and the server.
- Use secure coding practices and avoid insecure software libraries.
- Regularly update and patch devices to fix security vulnerabilities (and do it fast :-) ).
By following these best practices, we can reduce the risk of security breaches and ensure the safety and security of our connected devices and home networks.
Furthermore, the vulnerabilities in said smart lamps were patched by the vendor in early January 2023, about a month after coordinated vulnerability disclosure. The vendor gave us explicit permission to publish this blogpost - under the agreement we wouldn't mention the vendors name nor product name - and gave us permission to publish
We hope this blogpost has been as interesting to read for you as it was for us to write, and thank you for taking the time to read this blogpost.
Notselwyn, March 2023