Thursday, October 3, 2019

Cheap, Hackable IOT Light Bulbs (or, Philips Bulbs Have No Security)

Cheap, Hackable IOT Light Bulbs (or, Philips Bulbs Have So Security)
I was looking for a multi-color light bulbs, and most of what I found was kind of pricey, required bluetooth, or needed a crappy phone app to work. 

My three year old daughter has been waking us up around 5AM recently, and I wanted a simple way for her to know if she should stay in her room or get up for the day.  A color-changing bulb which I could schedule with simple Linux cron jobs would be ideal. 

With cron I could schedule the bulb to turn green when it's ok for my daughter to get up.  I could also set the brightness to the lowest level so it wouldn't wake her up if she was still asleep.

I didn't want to keep a dubiously insecure phone app just for this reason.  I also didn't want to buy a sleep training clock just for this purpose.

I found these inexpensive ($14.97) Philips 60w wifi LED bulbs at Home Depot and picked one up. These Internet of Things (IOT) devices seem to use the popular ESP series of wifi-enabled IOT SOCs, based on the hostname the device reports to DHCP.

After provisioning the bulb with the Wiz phone app, I did some snooping on the traffic between the phone app and the bulb.  Before going further, I should first explain that the Wiz app bulb setup required absolutely no authentication with the bulb you are setting up.  The only requirement is that the bulb be power-cycled so it enters discovery mode.

Anyone in wifi range could turn your bulb off and on, and set it up in whatever way they like.  If you're using the light with a timer or daylight detector, your neighbor could wait until it turns on in the evening and reconfigure your front porch light, for example.

Sniffing for traffic

Using tcpdump on my dd-WRT router, I found the traffic was mainly on UDP port 38899:
tcpdump -i br0 udp -vv -A       0x0000:  4500 00d3 00d1 0000 ff11 5593 2c14 0648  E.........U.,..H 
Most of it looked something like this:

       0x0010:  2c14 0646 c004 97f4 00bf 5be0 7b22 6d65  ,..F......[.{"me
       0x0020:  7468 6f64 223a 2273 796e 6350 696c 6f74  thod":"syncPilot
       0x0030:  222c 2269 6422 3a31 3033 2c22 656e 7622  ","id":103,"env"
       0x0040:  3a22 7072 6f22 2c22 7061 7261 6d73 223a  :"pro","params":
       0x0050:  7b22 6d61 6322 3a22 6138 6262 xxxx xxxx  {"mac":"0xdeadbe
       0x0060:  xxxx 3338 222c 2272 7373 6922 3a2d 3735  eeef","rssi":-75
       0x0070:  2c22 636e 7822 3a22 3031 3031 222c 2273  ,"cnx":"0101","s
       0x0080:  7263 223a 2275 6470 222c 2273 7461 7465  rc":"udp","state
       0x0090:  223a 7472 7565 2c22 7363 656e 6549 6422  ":true,"sceneId"
       0x00a0:  3a30 2c22 7222 3a32 3535 2c22 6722 3a30  :0,"r":255,"g":0
       0x00b0:  2c22 6222 3a31 342c 2263 223a 302c 2277  ,"b":14,"c":0,"w
       0x00c0:  223a 302c 2264 696d 6d69 6e67 223a 3130  ":0,"dimming":10
       0x00d0:  307d 7d                                  0}}

All the traffic was unencrypted and in plain text! Given the terrible state of IOT device security, I half expected some poor encryption or authentication scheme, but not a complete lack of security.  There was also no authentication scheme, such as a one-time hash that changes for each transaction. 

Examining the traffic, it was obvious what the light settings were right off the bat. "state" was the on/off switch, and "r", "g" and "b" were the light color RGB values. "dimming" was the light intensity. Wow, this was too easy. I wrote up a quick bash script to send the json payload with the values I wanted through netcat:

echo '{"method":"setPilot","id":527,"env":"pro","params":{"mac":"0x:de:ad:be:ee:ef","rssi":-75,"cnx":"0501","src":"udp","state"
:true,"sceneId":0,"r":0,"g":255,"b":4,"c":0,"w":0,"dimming":100}}' | nc -u 38899

The bulb returned a successful result and the light turned on with the color green.  Going further, I wondered if I could trick the Wiz app into changing its known state for a given bulb.  The app relies on once per 5 second heartbeat UDP packets from the bulb to know the power state and color the bulb is currently in.  It turns out this is trivially easy by just sending json to the app's IP address on port 38900:

echo '{"method":"syncPilot","id":126,"env":"pro","params":{"mac":"0xdeadbeef","rssi":-76,"cnx":"0C01","src":"hb","mqttCd":-2,"state":true,"scene

The Wiz app quickly registered the bulb's state change to purple.

Given how easy these were to work with, I picked up a few more.  Using cron, I could easily schedule the bulb to turn on and off and change its color, and I could delete the sketchy Wiz app from my phone.  These bulbs were exactly what I wanted.

Don't get me wrong, I care about security.  However, I keep the bulbs on a separate VLAN with no internet access and I block their MAC addresses from the internet at my firewall.  I also block internet access to the Wiz phone app, and I only installed it on an old phone I no longer use.

When first provisioned with the phone app, the bulbs appear to fetch a firmware update.  It's possible that by the time you read this the vendor will have updated the firmware to eliminate the security holes.  I doubt it will happen, but if you want to keep the old, vulnerable firmware you might want to block internet access on your provisioning subnet.

Phoning Home

These bulbs try to "phone home" to a host called "".  Taolight seems to be the firmware maker for these Philips-branded bulbs. I haven't fully analyzed the TCP traffic, but it seems the bulbs need to register with the outside network to enable things like Alexa communication (which I don't need).

The bulb also does regular DNS lookups on the host ""  I assume it's sending data to this host about its state and looking for new commands from services like Alexa.

21:39:56.662791 IP (tos 0x0, ttl 255, id 624, offset 0, flags [none], proto UDP (17), length 60) > [udp sum ok] 0+ A? (32)

A strategy to avoid phoning home is to add these hosts to your local DNS server's host lookup section (if using dnsmasq, for instance) and set them to resolve as  For this to work you'd need to intercept DNS queries.

While I find the bulbs very useful for my purposes and I hope to be able to keep buying them, I also think Philips should do the responsible thing and address these wide-open security vulnerabilities.  At the very least the packaging should include a warning that the device can easily be taken over by a novice hacker.  It is brazen that a company would release an internet-connected product with absolutely no effort at security.

For more information on protecting yourself against untrusted devices, see my other article on IOT Device Security.

Update 11/4/2019

I've submitted a CVE request for this product to hopefully get an official bulletin about these vulnerabilities.

This image is from the company's website.  The site tries hard to make it appear as if these products are secure.  The most comical part is the bit about each bulb having its own unique password.

That may well be true, but passwords do fuck-all good if there's no authentication, as is the case here.  I'm willing to bet these passwords are a simple hash based on the bulb's MAC address.

The device contains an Espressif IOT Module ESP WROO M02D, for which the manual is located here.

During the manual Wiz app discovery and configuration process the device creates a wifi access point with the name WiZConfig_abcd where the last 4 characters are the last 4 bytes of the main wifi MAC address.  This AP appears with a different MAC than the device uses when it connects to an access point.  You can put the light into manual pairing mode by turning it on and off 5 times.

I'm attempting to capture traffic between the app and the light bulb to see how the setup API works.  The WiZConfig access point assigns an address like to a connecting device, and gives to the bulb itself.  A scan of the bulb shows port 80 is open, but I have not been able to find any endpoints there yet.  It's possible the bulb accepts UDP traffic as well, but I haven't verified this.

Update 11/25/2019 - CVE Created

When I initially found these vulnerabilities in early October, I immediately contacted the vendor at their publicly listed security email address (available on their website).  I waited several weeks and, upon getting no response, I submitted a CVE request.

The CVE has been published here.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.