Replacing exploit-ridden firmware with a Linux kernel [pdf]
schd.ws
schd.ws
With the WikiLeaks release of the vault7 material, the security of the UEFI (Unified Extensible Firmware Interface) firmware used in most PCs and laptops is once again a concern. UEFI is a proprietary and closed-source operating system, with a codebase almost as large as the Linux kernel, that runs when the system is powered on and continues to run after it boots the OS (hence its designation as a “Ring -2 hypervisor"). It is a great place to hide exploits since it never stops running, and these exploits are undetectable by kernels and programs.
Our answer to this is NERF (Non-Extensible Reduced Firmware), an open source software system developed at Google to replace almost all of UEFI firmware with a tiny Linux kernel and initramfs. The initramfs file system contains an init and command line utilities from the u-root project (http://u-root.tk/), which are written in the Go language.
Does anyone know how they were able to compare the size of the codebase given that UEFI is proprietary/closed-source?
Programming languages and styles can vary in verbosity a lot. For example, if UEFI is written in some form of assembler code, then you quickly get a lot of lines of code while the binary doesn't grow particularly. On the other hand, high-level programming languages like Java, or even moreso functional and logic programming languages like Haskell and Prolog respectively, can generate huge binaries out of even just a few lines of code.
Linux also lacks the most basic level of the firmware (which is most likely handled Coreboot? I'm not fully clear on what NERF is overall). A much smaller proportion of Linux codebase would be running on a given device, as compared to the EDK-II, though. Who knows. /kernel and /arch/x86 together make about 440k SLOC.
Funnily enough, this is actually the second time a "Linux BIOS" has been tried. The first time was called LinuxBIOS (later renamed to Coreboot), and the idea was quite literally to put a kernel on ROM and have as little firmware as possible to get to a booted system.
Nice project though, and sad that it's needed in the first place. Oh well.
https://sthbrx.github.io/blog/2016/05/13/tell-me-about-petit...
This seems logical, if you are going to run linux, why reimplment all the drivers in UEFI.
So now AMD has just lost a big chuck of potential Google business, and who knows what kind of other potential contracts, because it continues to ignore this issue instead of actually showing some leadership on it.
Maybe it's time for AMD to actually differentiate from Intel on this issue?
However, these fixes are generally not offered to the public even when they already exist (again, see: Intel HAP).
The rules of the game are different when you're one of the Super 7 instead of just some random pleb. And increasingly those guys are no longer interested in leaving this threat vector open, which says something.
Do you have any links or documentation you could share regarding these exceptions? Thanks.
>> So now AMD has just lost a big chuck of potential Google business...
> Devil's advocate: if large customers care, AMD can easily give them a "special" microcode image with it disabled...
> However, these fixes are generally not offered to the public even when they already exist (again, see: Intel HAP)...
Google could take the "don't be evil" motto one step further: they could require CPU vendor give them publicly available microcode that disables these "features." Basically, they could use their market clout to put the PC market in the right direction.
There's nothing weird in caring about security.
Data in tech companies and banks is worth hundreds of millions. Attackers are therefore willing to pay millions for a ME 0-day.
I've implemented a number of system tools in Go and it is pretty well suited to this sort of job IMHO. One particular case that I've struggled with however, is when I've needed to modify some characteristic of my running thread using an OS mechanism. For instance, say I have a number of go-routines running and then I want to make sure a filesystem is unmounted from all mount namespaces the kernel has. My only pure-Go option right now, is to execute another go program (or re-execute the same program with some arguments) that will enter the namespace via syscall, do the work in that namespace and then quit. If you have a bunch of namespaces this seems wasteful.
This is because you don't have complete control over what Go does with the OS-thread you are working in. Yes, you can lock your go-routine to a particular OS thread but you can't stop Go from using that somewhat special and potentially more privileged thread, to create other OS threads to service go-routines. Thereby potentially sprinkling your go-routines with different capabilities and/or namespaces.
I ended up using cgo, which was a shame. Perhaps someone knows some neat trick to work around this?
In snapd (which is implemented in mostly go) we have this problem a lot. The real issue is that certain system calls fail if more than one thread exists in the calling process. One of those is setns(2), as is documented in the manual page.
What I ended up doing is to use a small C preamble that parses command line arguments, figures out where to go and uses setns before the go code even begins initializing.
This solved the particular case we were working on but in my opinion golang's opinionated approach to threading is not suitable for writing many system tools in it.
My wishlist item for golang 2.x is a build mode where threading is 100% under developer control but this seems to be at odds with the design for non-blocking IO.
Looking at the man-page briefly it looks like the restriction you are talking about applies only to user namespaces.
Interesting to know that there are even more situations to be considered.
Why Linux? You already have Linux for OS, why the same on firmware?
Why not Minix? ( Used in Intel Me Already )
Why not OpenBSD? ( Very Secure )
Why not NetBSD? ( Extremely Portable )
Why not FreeBSD?
Why not Coreboot?
Why not the OpenSource UEFI implmentation?
Why not switch back to simple and easy BIOS?
The Linux they're flashing to the firmware ROM is a custom minimal build. Using that, they can then boot a standard distro kernel. And depending on your threat model, you might not need to update the firmware kernel as often as the distro kernel, as it's only used for booting. Note that this is for servers; for an embedded system you might as well boot directly to the final kernel.
> Why not Minix? ( Used in Intel Me Already ) Why not OpenBSD? ( Very Secure ) Why not NetBSD? ( Extremely Portable ) Why not FreeBSD?
A few guesses:
- Linux is the one most familiar to the developers of this. And to the Google sysadmins, presumably.
- Compatibility: If the distro kernel resides on, say, an XFS filesystem on an MD-RAID device, the firmware boot kernel needs drivers for that.
- It uses kexec which is a quick and easy way to boot a Linux kernel from another Linux kernel. If you want to boot from another OS you'd have to figure out something else.
> Why not Coreboot?
Coreboot is preferable, but since the low-level firmware on modern x86 server platforms is tightly controlled with hardware signing keys, no data sheets released etc., coreboot hasn't been able to run on an Intel x86 server platform for the past decade. This project (NERF) is a pragmatic compromise, by replacing only the upper parts of the firmware stack and removing/disabling the ME as much as possible.
> Why not the OpenSource UEFI implmentation?
It's designed-by-committee crap which is far less battle tested than Linux.
> Why not switch back to simple and easy BIOS?
It's less evil than UEFI sure, but I guess it's unfortunately only a question of time until it's removed from PC firmwares (Apple already did it many years ago, AFAIU). Besides, AFAIK switching to BIOS mode does nothing to disable the ME.
And if you're going to do something from scratch, you might as well use something sane like coreboot.
As far as the bit I know, Legacy Boot, an option on (most/all) current PCs that simulates BIOS and which many people confuse with disabling UEFI, is actually a mode of UEFI. For more detail, look up Compatibility Support Module, and the difference between Class 2 and Class 3 UEFI.
I think Linus' opinion remains relevant over a decade later:
http://yarchive.net/comp/linux/efi.html
https://plus.google.com/+LinusTorvalds/posts/QLe3tSmtSM4
I'd actually love to see a "replace UEFI with regular BIOS" project ;-)
No. Though I think there is some work to allow people to build a coreboot + upper layers of UEFI (presumably using the open source tianocore UEFI implementation) combination in order to boot operating systems that require UEFI. Just like it's possible to build a coreboot + seabios combination in order to boot OS's that require BIOS services (such as DOS).
> nonetheless, the idea of putting a full Linux kernel in the firmware (if I'm reading the article correctly), presumably just to boot another one in the actual OS, despite the openness, sounds like it would increase complexity even more.
UEFI is very complex. I.e. it contains a TCP/IP stack (v4 & v6), and whatnot. The Linux IP stack is certainly a lot more battle tested than the UEFI one. And further, the idea is to allow the owner of the hardware to update the boot Linux kernel and not be beholden to the whims of the Intel & the HW manufacturer, who may not have the owners interest as a primary concern.
For better or worse. Most regular BIOSes contains IPv4 for PXE anyway.
UEFI just allows you to do PXE a little more cleanly directly from firmware without needing a disk to bootstrap the process with other stuff like iPXE.
Indeed. But that's UDP only, not TCP which is a lot more complex. UEFI contains TCP as well (IIRC recent versions of UEFI support boot over HTTP, similar to iPXE).
> UEFI just allows you to do PXE a little more cleanly directly from firmware without needing a disk to bootstrap the process with other stuff like iPXE.
That's correct. OTOH when your firmware + bootloader stuff requires a TCP/IP stack, HTTP, support for booting from all kinds of software RAID setups, filesystem support in order to be able to find and load the kernel, and whatnot, replacing all of that with a minimal Linux kernel + userspace doesn't sound so crazy anymore.
Can you still buy machines with non-UEFI BIOS from mainstream manufacturers?
It is an interesting wrinkle to ponder leaving such an embedded kernel running in place of UEFI or other SMM hypervisors. Should there be a new syscall layer between the two kernels, or are current SMM traps, ACPI, and UEFI constructs really the right way to do it, even if the same open source community is in charge of both layers?
However, a general practice has been that the firmware loaders would be replaced less often and stick with "known good" versions, while the booted kernel could be updated more often. But in this new and threatening world, the firmware kernel can be "known bad", so you need a way to push out updates frequently. You also need a fairly good fail-safe technique to recover when the new update isn't so good after all. And you need to avoid solving this problem with yet another firmware layer which lets you choose between your firmware copies, but itself can be compromised and cannot be easily patched...
IE: Innovation Engine
There's also the PCU Package/Power Control Unit that no one talks about.
If you're up to that stuff, I think they'd be happy to have your help. Otherwise maybe you should wait and hope they'll figure out how to flash it from Linux, and make it robust enough that there's a relatively low risk of bricking your system.
> The problem
> ● Linux no longer controls the x86 platform
> ● Between Linux and the hardware are at least 2 ½ kernel
Please release it for general public, so that everyone has the choice (for Intel+AMD CPUs). > code you don't know about: Kernel -3, Minix 3
Wtf "Minix 3" running invisible behind the scenehttp://blog.ptsecurity.com/2017/04/intel-me-way-of-static-an...
> UEFI is a proprietary and closed-source operating system, with a codebase almost as large as the Linux kernel...
And this kernel build is a trimmed-down one, removing functionality that isn't necessary to initialize the hardware and boot the full kernel.