TLDR: a backdoored binary downloaded by any Hydra build can be used to backdoor every Hydra build, Ken Thompson-style. To do this: submit a “referesh the bootstrap-files” LGTM-merge PR and use the storepath-hash of the exploit as the hash
argument of the import <nix/fetchurl.nix>
call. Hydra won’t bother trying to download from tarballs.nixos.org
.
A friend of mine mentioned that one of their friends had submitted #332462 so I took a look at it (after it had been merged). Unlike the past bootstrap-files update PRs submitted by me and @trofi, this one didn’t have copypasta that the reviewer could use to reproduce the refresh process.
So I took a closer look.
It turned out that the URL in the fetchurl
invocation’s url was 404! How strange, I thought to myself. How is this possible? Thinking about how that could happen is how I discovered this exploit. Fortunately, in the case of #332462, the outpath which built the artifacts hadn’t yet been garbage-collected (but it will be) and I was able to download it and verify the hashes. So this PR does not carry an exploit.
Here’s how it could have carried an exploit: there is no automated check that the hash
field of import <nix/fetchurl.nix>
matches nix hash path /nix/store/xxx-stdenv-bootstrap-tools-${stdenv.hostPlatform.config}.drv
. If they don’t match, nobody will notice. In that situation, all we would know is that Hydra built the (potentially-exploited) busybox
binary by realising some FOD whose outputHash is sha256-R6nAiaIOg...
. But we wouldn’t know that the FOD was a fetch from http://tarballs.nixos.org/
– or even that it was a fetch at all. It could have been built by any other FOD in any version of nixpkgs at any point in the past.
In other words, any random github user who can sneak an exploit binary into any build for any package buried in the nether-regions of some sub-sub-sub-ecosystem like buildCrystalPackage
or harePackages
can then smuggle that exploited binary into the root of the bootstrap sequence and compromise every package.
The fact that nobody else noticed that the tarballs.nixos.org
URL was 404 proves that nobody is checking these hashes.
More details
How did we get here?
The current situation seems to be a side effect of the fact that we've automated the process for updating the bootstrap-files.I don’t know if frequent updates to the bootstrap-files was a good idea… we don’t have a full source bootstrap like Guix does, and never will since the nix interpreter isn’t written in nix.
So the bootstrap files are basically a big huge weak point in our security story.
Maybe we shouldn’t make it easy to poke that weak point. But even we should, doing so is not the kind of PR that should be “LGTM merged”.
Possible Mitigiations:
Require copypasta in bootstrap-files update PRs
@trofi and I always made sure to include copypasta in our bootstrap-files update PRs that the reviewer could execute for themselves to validate the data being copied into the expression.I think we need to require these shell commands in any bootstrap-files updates, and the person merging the PR needs to confirm that they executed those commands and checked the hashes.
When merging a bootstrap-files update, you must confirm that you ran the copypasta commands and checked the result, before merging the PR.
Restrict who can merge bootstrap-files updates
In the past, only @lovesegfault could upload to `tarballs.nixos.org`, so he was the only one who could take the final step which would allow CI to pass. Because of this, he was basically always the person who merged these PRs, because they were unmergeable until he took action.It looks like this upload process may have become automated somehow. That’s good