Reverse Engineering My Air Filter (Part 1)

Background

I recently purchased a new air filter from costco, and its actually a pretty nice unit: IMG_20200416_161131~2.jpg

Only one problem. For some ungodly reason, its an internet of shit device. It has built in wifi and an android app that both kinda seriously sucks and provides functionality not readily accessible through the physical controls on the air filter. This is unacceptable for a number of reasons.

  1. My goddamn air filter is an internet connected device
  2. It has some kind of air quality sensor, and lets you view information from it within the app, but the information provided in the app has been massaged to the point of uselessness.
  3. I have no control over the software currently running on the device
  4. The device could potentially be monitoring my network and sending data back.
  5. I can not (at least in a way I have figured out yet) turn off the ozone generator without using the app. Moving the filter to a different spot and getting it back to the settings I want is an ordeal that requires fumbling through a shitty app and hoping it works.

In this series of posts, I will explore the (ongoing) process of reverse engineering my air filter, and hopefully figuring out how to pop a custom firmware on it.

Without Cracking It Open

First, I want to explore what we can learn without cracking it open.

Packet Capture

Thankfully, my router runs OpenWrt, so I can do a little bit of packet capture by looking up the air filter’s DHCP lease, and pulling one of these boys:

ssh root@router tcpdump -i wlan1 -U -s0 -w - 'not port 22' | sudo wireshark -k -i -

Filtering by the air filter’s IP (in this case, 10.0.0.170) in wireshark, then gives me the ability to inspect all of it’s traffic: Screenshot_20200416_164923.png

Findings

Curiously enough, the air filter seems to be making repeated dns querys for www.google.com at a set interval. Playing around with the air filter and leaving the packet capture running for long periods of time have lead me to a few conclusions:

  1. This dns query for www.google.com is the only dns query the device makes
  2. It never attempts to connect to the IP it receives in the response.
  3. The devices behavior does not change if I blackhole www.google.com in my dns server (change the dns server to respond with 0.0.0.0)
  4. The device does not make any DNS queries for that would correspond to the 34.193.163.206 IP that the device is communicating with
  5. Aside from the DNS probe, the device does not appear to communicate with any IPs other than this 34.193.163.206

Based on this information, my belief is that the device is using the DNS query as some sort of janky probe for internet connectivity, and that it doesn’t care what the response is, just that it gets one. I also believe the 34.193.163.206 IP to be hard coded into the device’s firmware (boo). This leads me to believe that this is probably an incredibly simple device, and my be using a bare microcontroller rather than the typical embedded linux.

On the bright side, the device is actually using TLSv1.2 for its communication, though this hinders my RE efforts.

34.193.163.206

This IP belongs to amazon, and the rdns record for it points to ec2-34-193-163-206.compute-1.amazonaws.com, which doesn’t give us much to go off of, other than the fact that data is flowing into an amazon EC2 instance.

Port Scanning

My next stop is to throw nmap with OS fingerprinting turned on at it:

$ sudo nmap 10.0.0.170 -O                                      
Starting Nmap 7.80 ( https://nmap.org ) at 2020-04-16 17:02 EDT
Nmap scan report for 10.0.0.170
Host is up (0.015s latency).
Not shown: 999 closed ports
PORT     STATE SERVICE
8088/tcp open  radan-http
MAC Address: 84:72:07:32:3F:50 (I&C Technology)
Device type: specialized|general purpose|VoIP phone
Running (JUST GUESSING): NodeMCU embedded (98%), lwIP 1.4.X (98%), Espressif embedded (97%), Philips embedded (91%), Cognex embedded (90%), Ocean Signal embedded (89%), Grandstream embedded (89%), Rigol Technologies embedded (89%)
OS CPE: cpe:/o:nodemcu:nodemcu cpe:/a:lwip_project:lwip cpe:/h:philips:hue_bridge cpe:/a:lwip_project:lwip:1.4 cpe:/h:grandstream:gxp1105 cpe:/h:rigol_technologies:dsg3060
Aggressive OS guesses: NodeMCU firmware (lwIP stack) (98%), Espressif esp8266 firmware (lwIP stack) (97%), ESPEasy OS (lwIP stack) (92%), Philips Hue Bridge (lwIP stack v1.4.0) (91%), Cognex DataMan 200 ID reader (lwIP TCP/IP stack) (90%), Ocean Signal E101V emergency beacon (FreeRTOS/lwIP) (89%), Grandstream GXP1105 VoIP phone (89%), lwIP 1.4.0 lightweight TCP/IP stack (89%), Rigol DSG3060 signal generator (89%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 1 hop

OS detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 11.85 seconds

This doesn’t give us too much to go off of, only one port open and nmap left making an absolute crapshoot at what OS is running on it.

Pulling up a connection to this service on 8088 in a web browser reveals two things, a service gated by http basic auth, and the headers from the response reveal that it is, at least, using lwIP (also this device has no concept of time):

HTTP/1.0 401 Authorization Required
WWW-Authenticate: Basic
Server: lwIP/1.4.1 (http://savannah.nongnu.org/projects/lwip)
Content-type: text/html
Expires: Fri, 10 Apr 2008 14:00:00 GMT
Pragma: no-cache

The standard username/password combos haven’t worked, and trying a list of 10 common usernames with 1000 common passwords also failed. I am currently in the process of running a longer brute force attempt, as this thing’s only rate limiting is how slow its CPU seems to be, but that’s going to take the rest of the day, and I have my doubts that it will work.

Today’s Conclusion

I strongly suspect that there is some commodity microcontroller running the show in there, most likely running NodeMCU, but unless I make a breakthrough with the http basic auth on that port 8088 service, I’ve hit a wall for how far I can go without cracking it open.

Join me next time as I crack it open!