Enabling the hidden Wi-Fi radio on the Philips Hue Bridge 2.0: Adventures with 802.11n, ZigBee 802.15.4 and OpenWrt

The Philips Hue Bridge bridges ZigBee 802.15.4 from compatible light bulbs to your wired Ethernet network. But it also happens to contain a built-in yet disabled 802.11n/g Wi-Fi radio.

Is it possible to enable Wi-Fi on bridge, obviating the need for a pesky Ethernet cable? In this article I will attempt to find out, with limited success.

Long story short, the Wi-Fi interface is fully functional, configurable within the native Linux-based OpenWrt operating system, and can indeed either act as an access point or client connecting to your local home Wi-F network, but configuring the services running on the bridge to use the Wi-Fi interface instead of Ethernet is trickier.

Wiring up the serial port

Before we can do anything else, the bridge has to be rooted.

Remove the sticky feet, unscrew using Torx T10 screwdriver:

Once unscrewed the case comes apart easily:

Solder on a 1x6 header to the J6. Note there is another serial port on J1 described by wehooper4 on /r/hue: “Jailbreaking” the V2 hub, but J6 is the one we want as detailed in turmio’s notes and Philips Hue Bridge 2.0 — Getting Root (Rooting), Colin O’Flynn. O’Flynn’s research is the most thorough and the steps below are based on his work. Soldered J6 header:

There is also a 2x7 (J9) header, not placed in production but pins shown in FCC photos. But back to J6: the hole with a square footprint on the reverse, and an arrow on the converse, is ground. Skip two pins, then the next are tx and rx.

Conveniently enough, the pinout happens to be compatible with the SparkFun FTDI Basic Breakout 3.3V board connector, so I plugged it into the pins directly, with one exception: disconnect the 3.3V (power) pin. If power is provided, the devices won’t be damaged (or at least mine wasn’t), but the virtual serial port stopped responding and I had to reboot the host machine. The other pins can remain connected, just make sure GND is matched up with the ground pin indicated by the triangle on the board:

Fault injection to boot loader

As demonstrated in Philips Hue Bridge 2.0 — Getting Root (Rooting), short the point on the corner of the SU9 chip to ground during boot, then type:

ath> setenv bootdelay 3
ath> saveenv

this gives you three seconds to press a key during boot (“Hit any key to stop autoboot: 3”), so shorting out the flash is no longer needed.

Root password

At the same bootloader prompt:

ath> printenv security

Colin’s hash is:

$5$DbnQ4d5SrWGOaTzG$P6VtmkDbwO2/LvqajVT.9ZNszIIInWzHNK01g1ga3n4

where “$5” indicates SHA-256 crypt, designed by Ulrich Drepper in 2007. sha256crypt is supported by hashcat:

hashcat $ ./hashcat -m 7400 ‘$5$DbnQ4d5SrWGOaTzG$P6VtmkDbwO2/LvqajVT.9ZNszIIInWzHNK01g1ga3n4’
hashcat (v3.00–69-g804ee28) starting…
OpenCL Platform
=========================
- Device #1: Intel(R) Core(TM) i7–4790K CPU @ 4.00GHz, skipped
- Device #2: AMD Radeon R9 M295X Compute Engine, 1024/4096 MB allocatable, 32MCU
Hashes: 1 hashes; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1
Applicable Optimizers:
* Zero-Byte
* Single-Hash
* Single-Salt

Starting attack in stdin mode…
Session.Name…: hashcat
Status………: Running
Input.Mode…..: Pipe
Hash.Target….: $5$DbnQ4d5SrWGOaTzG$P6VtmkDbwO2/LvqajVT.9…
Hash.Type……: sha256crypt, SHA256(Unix)
Time.Started…: Sat Aug 13 22:10:07 2016 (9 secs)
Speed.Dev.#2…: 0 H/s (0.00ms)
Recovered……: 0/1 (0.00%) Digests, 0/1 (0.00%) Salts
Progress…….: 0
Rejected…….: 0

but I wasn’t patient enough to crack this hash, and it may not necessarily even be feasible if the passwords are of sufficiently high entropy (they are different per device, at least). Waited 47+ hours, nothing.

Instead, generate a new hash for a known password. Linux ‘mkpasswd’ tool does this, but I didn’t have a Linux machine handy. ‘openssl passwd’ seemed promising, but it only supported crypt, or the MD5-based BSD password algorithm 1 (identifiable with the $1$ prefix), not $5$ sha256crypt. mkpasswd v1.10 by Rob Levin also looked promising, but has similar limitations.

Connected to a Raspberry Pi machine running Raspbian, based on Debian, but mkpasswd is missing from Debian. What gives? No matter, it is easy enough to write a command-line wrapper for the crypt(3) function:

generating a sha256crypt hash for the password ‘toor’ (as an example to match Colin O’Flynn’s steps, but better to use something unique and secure, of course, and another random salt):

pi@raspberrypi:~ $ cc crypt.c -o crypt -lcrypt
pi@raspberrypi:~ $ ./crypt toor ‘$5$wbgtEC1iF’
$5$wbgtEC1iF$ugIfQUoE7SNg4mplDI/7xdfLC7jXoMAkupeMsm10hY9

At the ath> prompt on the bridge, setenv security to change, then saveenv and power cycle. Let it boot completely, press enter, then login as root and with your password (or ‘toor’ if set as above):

Success! We have a root shell.

Remote shell via SSH

You can enable telnetd, but ssh is more secure and running by default:

root@Philips-hue:~# nc localhost 22
SSH-2.0-dropbear_2014.63
cxQ#”lyPdiffie-hellman-group1-sha1,diffie-hellman-group14-sha1,kexguess2@matt.ucc.asn.aussh-rsa,ssh-dss=aes128-ctr,3des-ctr,aes256-ctr,aes128-cbc,3des-cbc,aes256-cbc=aes128-ctr,3des-ctr,aes256-ctr,aes128-cbc,3des-cbc,aes256-cbchmac-sha1,hmac-md5hmac-sha1,hmac-md5nonenone3
^C

yet ssh’ing in to ourselves as root with the set password fails:

root@Philips-hue:~# ssh localhost
root@localhost’s password:
root@localhost’s password:

/usr/sbin/ssh-factory-key looks useful:

root@Philips-hue:~# ssh-factory-key
error: FUNCTION not specified
ssh-factory-key [OPTIONS] FUNCTION
Bridge v2.0 ssh public key management utility
Any one of the following FUNCTIONs can be specified:
-h Prints this help
-l Lists trusted public keys that can be used for ssh access.
-r KEY_FILE_TO_REGISTER Register and trust the specified ssh public key. Use ‘-’ to read the public key from standard input.
-d KEY_TO_DEREGISTER De-register (dont trust) the specified public key.
-a Automatically restore keys from the U-Boot environment.
-s Skip reloading the firewall settings.

I tried to register an ed25519 key (which ought to be supported by dropbear 2014 according to the change log, added in 2013), but it said it wasn’t rsa. Use an id_rsa.pub file from ~/.ssh, it is accepted:

echo ‘ssh-rsa …’ | ssh-factory-key -r
registered: …
installed: firewall rule for ssh

It also sets up the firewall rule for us, neat. Next I shutdown the device with halt, unpowered, and put it back together. As long as the SSH key was configured correctly, we won’t be needing physical access anymore. Put it back on the network, power on, and ssh:

It works! Now the device is fully rooted, it can be customized and explored remotely at our leisure.

Exploring the hardware & software

This section has some miscellaneous reference notes which may be useful later, you can skip it. High-level references:

Chips

Wireless Microcontroller: Atmel SAM R21 (datasheet)
“The Atmel® | SMART SAM R21 is a series of low-power microcontrollers using the 32-bit ARM® Cortex®-M0+ processor and an integrated ultra-low power 2.4GHz ISM band transceiver.”, 21E18A = 256 KB flash, 32 KB SRAM

ZigBee RF Front-End: Skyworks SE2438T — 2.4 GHz ZigBee/Smart Energy Front-End Model

Memory: Winbond W9751G6KB — 8M x 4 banks x 16-bit DDR2 SDRAM

SoC/ISP: Qualcomm QCA4531-BL3A SoC/ISP, “Low-power Linux connectivity hub for Internet of Everything” Qualcomm Atheros

MIPS 24Kc processor clocked at 650 MHz. Hackerboards: Latest Atheros IoT SoCs include OpenWRT-friendly model, the QCA4531 SoC in this device:

Block diagram of the QCA4531 (from linuxgizmos)

ZigBee Channels

iOS app > Hue bridges > info, change ZigBee channel: 11, 15, 20, 25

Several spectrums:

  • channel 0 = 868.3 MHz
  • channel 1–10 = 902–928 MHz (2 MHz)
  • channel 11–26 = 2.4–2.835 GHz (5 MHz apart, 2 MHz bandwidth)

Hue only supports the 2.4 GHz spectrum, and only a few channels:

  • channel 11 = 2405 MHz
  • channel 15 = 2425 MHz
  • channel 20 = 2450 MHz
  • channel 25 = 2475 MHz

Each are 2 MHz, plenty narrow for HackRF (20 MHz). 2405–2425 = 20 MHz, could in theory receive ZigBee channels 11, 12, 13, 14, and 15 all at once in SDR and tune in software.

SDR?

From CGRAN: gr-ieee802–15–4, a ZigBee transceiver for GNU Radio. With the appropriate hardware, this could be used to interface with the Hue Bridge 2.0 over the air.

Connecting to the light bulbs by replacing the bridge with a completely software-defined ZigBee radio is an interesting possibility; a less ambitious project would be to capture encrypted ZigBee packets between the bridge and bulbs, and decrypt it using key material from the rooted bridge.

Firmware

iOS Hue app > About, shows model: BSB002, software version: 01033989

0103989 = 10.33.989, major.minor.build

http://fds.cpp.philips.com/firmware/BSB002/1033989/BSB002_01033989.product.RSA_prod_01.fw2

Known version URLs:

BSB002 header, 2048-bit public key near end; encrypted. TODO: decrypt

Enabling the Wi-Fi interface

Now that’s out of the way, here is where it get interesting. Poking around the system, notice there is a mysterious “wlan0” interface, lets try it:

root@Philips-hue:~# ifconfig wlan0 up
root@Philips-hue:~# iwinfo wlan0 scan

Shows a list of Wi-Fi networks in my area. Even with no external antenna, finds 33 networks. Whoa, Hue has Wi-Fi? Not an advertised feature, you are supposed to connect it over wired Ethernet. But it does pickup the 802.11bgn signals… can it join a network? Using iw:

root@Philips-hue:~# iw wlan0 connect -w xfinitywifi
wlan0 (phy #0): connected to 00:71:c2:98:c4:12

Connected successfully to this open network. However, I ran into trouble trying to connect to my home network, which has WPA2 encryption. iw supports WEP, but requires wpa_supplicant (or similar) for WPA2, and it isn’t installed by default on the Hue.

Configuring WPA2

OpenWrt — Configure Wi-Fi encryption recommends wpad-mini (WPA daemon, non-enterprise version), but it cannot be found:

root@Philips-hue:~# opkg install wpad-mini
Unknown package ‘wpad-mini’.
Collected errors:
* opkg_install_cmd: Cannot install package wpad-mini.

New in OpenWrt 10.03+, but attempting to install wpa-supplicant (in 7.06+) fails with the same error. Maybe the packages need updating?

root@Philips-hue:~# opkg update
Downloading
http://downloads.openwrt.org/snapshots/trunk/ar71xx/packages/Packages.gz.
wget: server returned error: HTTP/1.1 404 Not Found

Collected errors:
* opkg_download: Failed to download
http://downloads.openwrt.org/snapshots/trunk/ar71xx/packages/Packages.gz, wget returned 1.

There is a ar71xx directory on openwrt.org, but no packages directory in snapshots. Looks like the directory structure changed between OpenWrt versions, the last without the restructuring is Backfire 10.03.1.

But what version of OpenWrt is the Hue Bridge 2.0 running, anyways?

root@Philips-hue:~# cat /etc/openwrt_version
1.9
root@Philips-hue:~# cat /etc/openwrt_release
DISTRIB_ID=”QSDK.BSB002"
DISTRIB_RELEASE=”1.9"
DISTRIB_REVISION=”r40838"
DISTRIB_CODENAME=”bsb002"
DISTRIB_TARGET=”ar71xx/generic”
DISTRIB_DESCRIPTION=”QSDK.BSB002 BSB002 1.9"
DISTRIB_TAINTS=”no-all busybox override”

OpenWrt’s download page lists White Russian 0.9 then jumps to Kamikaze 7.06, but Wikipedia says ar71xx was added in Kamikaze 8.09, so what is 1.9? Custom build; another hint is dmesg shows ‘Linux kernel version 3.14.0’, between the OpenWrt Barrier Breaker 14.07 (Linux 3.10.49) and OpenWrt Chaos Calmer 15.05.1 (Linux 3.18.20) releases. The open source licenses page, served at the web server on the bridge’s IP address port 80, does not show the version of OpenWrt either, clearly it is a custom build.

Nonetheless, packages from OpenWrt Backfire can be installed. Edit /etc/opkg.conf and change this line:

src/gz bsb002 http://downloads.openwrt.org/snapshots/trunk/ar71xx/packages

to the working URL:

src/gz bsb002 http://downloads.openwrt.org/backfire/10.03.1/ar71xx/packages

(Other versions may work, not tested) Then update and install:

root@Philips-hue:/etc# opkg update
Downloading
http://downloads.openwrt.org/backfire/10.03.1/ar71xx/packages/Packages.gz.
Updated list of available packages in /var/opkg-lists/bsb002.
root@Philips-hue:/etc# opkg install wpa-supplicant
Installing wpa-supplicant (20111103–2) to root…
Downloading
http://downloads.openwrt.org/backfire/10.03.1/ar71xx/packages/wpa-supplicant_20111103-2_ar71xx.ipk.
Configuring wpa-supplicant.

There were some irrelevant warnings about kmod, but the package installed successfully. Test by running wpa_supplicant, it should show the help usage.

You may now also be able to other packages you want using OpenWrt’s opkg, thousands are available. But for now I only installed wpa_supplicant.

Follow the instructions on OpenWrt — Configure Wi-Fi encryption to configure with uci, or edit /etc/config/wireless, as documented here, which I found simpler. Remove the helpfully-commented “option disabled ‘1’” line, and change the network to “wlan” (which we’ll create in the next section). The mode defaults to “ap”, and you can run a Wi-Fi access point on your Hue Bridge if you would like, but to use it as a Wi-Fi client change the mode to “sta” (for station). The wireless config should look something like this:

config wifi-iface
 option device ‘radio0’
 option network ‘wlan’
 option mode ‘sta’
 option ssid ‘your wifi ssid here’
 option encryption ‘psk2’
 option key ‘your wifi WPA2 password here’

To apply the changes:

root@Philips-hue:~# uci commit wireless
root@Philips-hue:~# wifi

DHCP configuration

Once the Wi-Fi interface is associated with the AP, acquire an IP via DHCP:

root@Philips-hue:/etc/config# udhcpc -i wlan0
udhcpc (v1.19.4) started
Sending discover…
Sending discover…
Sending select for 192.168.0.166…
Lease of 192.168.0.166 obtained, lease time 43200
udhcpc: ifconfig wlan0 192.168.0.166 netmask 255.255.255.0
udhcpc: setting default routers: 192.168.0.1

It should take the address, try pinging:

~ $ ping 192.168.0.166
PING 192.168.0.166 (192.168.0.166): 56 data bytes
92 bytes from 192.168.0.166: Destination Port Unreachable
Vr HL TOS Len ID Flg off TTL Pro cks Src Dst
4 5 00 5400 1464 0 0000 40 01 0de0 192.168.0.1 192.168.0.166

The device responds (but with port unreachable, due to the firewall — see below. Note the Wi-Fi is responding, this is different than “Request timeout”).

To persist the network configuration, add to /etc/config/network:

config interface ‘wlan’
 option ifname ‘wlan0’
 option proto ‘dhcp’
 option hostname ‘Philips-hue-wifi’

Edit /etc/config/wireless ensuring it connects to the wlan interface (not lan):

config wifi-iface
 option device ‘radio0’
 option network ‘wlan’
 …

Restart the network configuration:

/etc/init.d/network restart

wlan0 should now automatically acquire a DHCP address. Reboot the device to confirm it automatically re-acquires the DHCP network configuration.

Moving services to Wi-Fi

The Wi-Fi interface is now functional, but we aren’t done yet. The various services running on the Hue Bridge assume a wired interface and will have to be reconfigured for wireless.

This section is incomplete as I haven’t yet completely figured out how to configure everything, or it may not be possible, but I’m putting out there what I have now in the hope that it will be helpful and inspire others to take it a step further.

If you get it to work better, I’d be very interested. Feel free to comment below or post a follow-up article. Hopefully these tips provide a good start.

Opening up the firewall

By default, as specified in /etc/config/firewall, the firewall will reject incoming packets.

It is possible to manually open the various ports (ssh, hap, http, ssdp, mdns), or replicate the lan configuration, but for now, I opened up everything for wlan by adding this section after config defaults in /etc/config/firewall:

config zone ‘wlan’
 option name ‘wlan’
 list network ‘wlan’
 option input ‘ACCEPT’

then apply changes with: /etc/firewall/firewall reload

Once it is all opened up, the Wi-Fi interface should respond to pings as normal (64 bytes from… instead of Destination Port Unreachable), and port scans show various ports open.

dropbear

Even though port 22 is open on the wlan interface, sometimes ssh would not connect over Wi-Fi, even though it was consistently reliable over Ethernet:

~ $ ssh -v whue

debug1: Enabling compatibility mode for protocol 2.0
debug1: Local version string SSH-2.0-OpenSSH_6.9
ssh_exchange_identification: read: Connection reset by peer

Testing with netcat reveals port 22 is open, but the server is not responding with the SSH banner. With -p 22, dropbear is supposed to listen on all interfaces, and it is, so why isn’t it responding? Unclear; I saw it work once, but not again. -p ipaddress:22 could be used to explicitly bind to one interface only, but for now I am only using SSH over Ethernet.

hk_mdns, hk_hap

The mdns service also binds to the wired Ethernet interface (eth1).

Edit /etc/config/hk_mdns, change this line to ‘wlan0’:
option netif ‘eth1’

then restart:
/etc/init.d/hap restart
/etc/init.d/mdns restart

The interface MAC addresses is advertised as in:

/var/homekit/mdns-config/_hap._tcp

From an OS X machine on the same local network, run:

dns-sd -q Philips-hue.local
dns-sd -B _hap._tcp

it should find a Philips hue instance, in both cases.

There is some commented-out code in /etc/rc.d/S84mdns:

#MDNS_NETIF=eth1
BASE_CONFIG=/etc/config/hk_mdns
MDNS_BINARY=hk_mdns
start_service() {
 echo “Starting hk_mdns service…”
 # start_logger
# TODO: Replace to config value ASAP
 # set_route $MDNS_NETIF

Why is it commented out? (at least it was in firmware 33370; updated to 33989, still there). set_route adds a multicast route:

set_route() {
 route add -net 224.0.0.0 netmask 224.0.0.0 dev $1
}

I didn’t need to make any changes for wlan0 to pickup the new route:

root@Philips-hue:~# route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default 192.168.0.1 0.0.0.0 UG 0 0 0 wlan0
192.168.0.0 * 255.255.255.0 U 0 0 0 eth1
192.168.0.0 * 255.255.255.0 U 0 0 0 wlan0
224.0.0.0 * 224.0.0.0 U 0 0 0 wlan0

After it is configured, you should see the new address in the Rdata field:

~ $ dns-sd -q Philips-hue.local
DATE: — -Mon 15 Aug 2016 — -
0:06:15.482 …STARTING…
Timestamp A/R Flags if Name Type Class Rdata
0:06:15.484 Add 2 4 Philips-hue.local. Addr IN 192.168.0.6
0:06:17.290 Rmv 1 4 Philips-hue.local. Addr IN 192.168.0.6
0:06:17.290 Add 2 4 Philips-hue.local. Addr IN 192.168.0.166

Relaunch the Hue app on your phone. Observe with tcpdump on the router, traffic is indeed flowing through the Wi-Fi interface of the Hue Bridge, not the wired Ethernet.

However, I reverted these changes for now, since although the traffic to control the Hue lights from the app was flowing over Wi-Fi, for some reason HomeKit became nonfunctional. Other changes may be required.

Unplugging the Ethernet

If you unplug the wired Ethernet connection, the two LEDs on the front turn off, signifying no network connection. This command:

ifconfig eth1 down

will as well, necessitating a reboot (since I couldn’t get SSH to connect consistently over the Wi-Fi interface, and had unplugged the serial connection, but fortunately power cycling gets it back to a good state).

How else is the Hue Bridge relying on Ethernet?

Changing the eth1 IP address, the Hue app continues to operate over the WiFi-connected bridge. Changing to a non-routable address, it doesn’t.

There are hotplug triggers in /etc/hotplug.d/iface/00-netstate, potentially relevant. /etc/init.d/led controls the LEDs, using /sys/class/leds in the filesystem, so something may be noticing the interface going down and drive the LEDs appropriately, as well as adjust other settings.

Not clear. Unfortunately, this is where my investigation ends, for now.

Conclusions

We have seen rooting the Hue Bridge 2.0 opens up a world of new possibilities, unlocking the potential of the OpenWrt software and the integrated hardware. The built-in Wi-Fi radio can be enabled and used with opkg packages, no hardware modifications required.

However, more research is needed to determine to what extent the Wi-Fi interface can replace Ethernet, if at all. Why did Philips choose to ship a SoC supporting Wi-Fi but instead require users to tediously stringan Ethernet cable to the Hue Bridge instead?

Perhaps there are unintended side-effects of enabling both Wi-Fi and ZigBee or other downsides which would be uncovered had this exercise proceeded further. Or maybe it is merely a matter of configuring the services correctly. Or could Philips intentionally mandate a wired connection for reliability? Your guess is as good as mine. Raising more questions than answered.

But what I do know is that Hue Bridge hacking is in its infancy. Now that it is easily rooted (thanks to Colin O’Flynn’s excellent Getting Root on Philips Hue Bridge 2.0 article published last month), exploration into the possibilities of modifying this neat little single-board computer has only just begun. I look forward to see what the community can come up with, and especially if anyone is able to completely solve the problem of replacing Ethernet w/ WiFi

Update 2016/08/16: Posted this article to /r/hue: https://www.reddit.com/r/Hue/comments/4xyaak/experiments_with_enabling_the_builtin_wifi_radio/ — @wehooper4 points out that the FCC filings show they tested Wi-Fi on the Hue Bridge. Specifically the “Test Report BGN” (where BGN = 802.11/b/g/n, the various Wi-Fi standards), page 5:

“Temporary RF connector provided”… the internal photos FCC document, last page, pictures this Wi-Fi antenna: