Friday, May 20, 2016

Technical Debt, Episode 1

One of the projects I'm working on for Mozilla is our Content Sandboxing. We've been using sandboxing for a while to protect some plugins like Flash, as well as media plugins, but now that Firefox can render webpages in a separate process, we can apply restrictions to what those "Web Content" processes can do, too. Those processes are the part of Firefox that is essentially exposed to the internet, and hence to potentially dangerous webpages.

Although we go to great lengths to make this impossible, there is always a chance that a bug in Firefox would allow an attacker to exploit and take over a Web Content process. But by using features provided by the operating system, we can prevent them from taking over the rest of the computing device by disallowing many ways to interact with it, for example by stopping them from starting new programs or reading or writing specific files.

This feature has been enabled on Firefox Nightly builds for a while, at least on Windows and Mac OS X. Due to the diversity of the ecosystem, it's taken a bit longer for Linux, but we are now ready to flip that switch too.

The initial version on Linux will block very, very little. It's our goal to get Firefox working and shipping with this first and foremost, while we iterate rapidly and hammer down the hatches as we go, shipping a gradual stream of improvements to our users.

One of the first things to hammer down is filesystem access. If an attacker is free to write to any file on the filesystem, he can quickly take over the system. Similarly, if he can read any file, it's easy to leak out confidential information to an attacking webpage. We're currently figuring out the list of files and locations the Web Content process needs to access (e.g. system font directories) and which ones it definitely shouldn't (your passwords database).

And that's where this story about technical debt really starts.

While tracing filesystem access, we noticed at some point that the Web Content process accesses /etc/passwd. Although on most modern Unix systems this file doesn't actually contain any (hashed) passwords, it still typically contains the complete real name of the users on the system, so it's definitely not something that we'd want to leave accessible to an attacker.

My first thought was that something was trying to enumerate valid users on the system, because that would've been a good reason to try to read /etc/passwd.

Tracing the system call to its origin revealed another caller, though. libfreebl, a part of NSS (Network Security Services) was reading it during its initialization. Specifically, we traced it to this array in the source. Reading on what it is used for is, eh, quite eyebrow-raising in the modern security age.

The NSS random number generator seeds itself by attempting to read /dev/urandom (good), ignoring whether that fails or not (not so good), and then continuing by reading and hashing the password file into the random number generator as additional entropy. The same code then goes on to read in several temporary directories (and I do mean directories, not the files inside them) and perform the same procedure.

Should all of this have failed, it will make a last ditch effort to fork/exec "netstat -ni" and hash the output of that. Note that the usage of fork here is especially "amusing" from the sandboxing perspective, as it's the one thing you'll absolutely never want to allow.

Now, almost none of this has ever been a *good* idea, but in its defense NSS is old and caters to many exotic and ancient configurations. The discussion about /dev/urandom reliability was raised in 2002, and I'd wager the relevant Linux code has seen a few changes since. I'm sure that 15 years ago, this might've been a defensible decision to make. Apparently one could even argue that some unnamed Oracle product running on Windows 2000 was a defensible use case to keep this code in 2009.

Nevertheless, it's technical debt. Debt that hurt on the release of Firefox 3.5, when it caused Firefox startup to take over 2 minutes on some people's systems.

It's not that people didn't notice this idea was problematic:
I'm fully tired of this particular trail of tears. There's no good reason to waste users' time at startup pretending to scrape entropy off the filesystem.
-- Brendan Eich, July 2009
RNG_SystemInfoForRNG - which tries to make entropy appear out of the air.
-- Ryan Sleevi, April 2014
Though sandboxing was clearly not considered much of a use case in 2006:
Only a subset of particularly messed-up applications suffer from the use of fork.
-- Well meaning contributor, September 2006
Nevertheless, I'm - still - looking at this code in the year of our Lord 2016 and wondering if it shouldn't all just be replaced by a single getrandom() call.

If your system doesn't have getrandom(), well maybe there's a solution for that too.



Don't agree? Can we then at least agree that if your /dev/urandom isn't secure, it's your problem, not ours?

4 comments:

  1. For sure, this ancient support crap is ridiculous. How about a parallel build for dinosaurs, and one for the rest of the mankind?

    ReplyDelete
  2. Here you have to remember that distributions work against users.

    In particular for xscreensaver, the author received email from outdated software that was used by in particular debian. So he added the notification.

    What was the next result? Debian started to patch-modify that warning away, rather than allowing users a simple way to upgrade.

    So those distributions (not all of them) are like a prison. And their users are their prison workers.

    ReplyDelete
  3. First of all, changing everything to a single call to getrandom() or read from /dev/urandom is absolutely the way to go right now.

    That said, the reading of the password file is somewhat defensible. If the randomness pool is implemented properly, then as long as the amount of entropy dropped into it exceeds a reasonable threshold then it's cryptographically impossible for an attacker to figure out what the data mixed in was. This is the whole point of a properly designed randomness pool. Private keying material is just about the only stuff which is supposed to have the property of not being guessable to anyone outside the system, with the counterintuitive result that whatever's in stored passwords and ssh keys are probably the only things which should be mixed into the pool.

    Likewise mixing in the current time may prevent a real attack - if there's a real source of entropy getting mixed in somewhere but it's the same across reboots, mixing in time will prevent replay attacks. Properly designed entropy pools will handle this properly.

    Of course, it may be that there's only a single user on the system and their password is guessable. Or it may be that it's a modern system where password info isn't actually in the password file. And if there's an ssh key then it may be that that key was generated using the same broken random number generator and contains very little entropy itself. So mixing in that info doesn't always fix things. But it also may by the only thing keeping the system secure under others, so it isn't nearly as dunderheaded as it sounds. Mixing in random other files which don't contain keying material is just plain stupid though.

    That said, we're long past the point where all computers should come with a built-in source of hardware entropy and it's totally reasonable for anything which claims to be secure to simply refuse to boot if it isn't there, at which point all this other voodoo should be done away with entirely.

    ReplyDelete