mrkeyoor.com_
Sat 05 Sept 18:33 UTC
Tech7 min read

A Tampered GNU strip Backdoored 3,790 NixOS Binaries in a Test

Researchers carried a self-replicating implant through a NixOS bootstrap. Clean source and functional tests missed it; matching rebuilds could repeat it.

A research team got a malicious payload into 3,790 of 3,791 command-line executables in a NixOS graphical installer build by tampering with one small utility at the start. The installer still built, and its functional tests still passed. The uncomfortable result in the team's paper is that rebuilding everything from clean source did not remove the implant. For developers who treat a successful clean build as evidence of trustworthy binaries, that is the part worth sitting with.

The five researchers did not uncover an attack on NixOS in the wild. Their work is a controlled proof of concept, submitted in July 2026, under a strict threat model: an attacker may replace one executable in the distribution's binary bootstrap seed but may not alter package recipes or source code. Within those limits, the experiment carried a self-replicating payload all the way into the final build environment after the original bad seed had dropped out of its recorded dependencies.

The utility with unusually broad reach

GNU strip, part of binutils, normally removes symbols and other information from object files and executables. Nixpkgs calls it late in a package build, after source has been compiled and installed. The Nixpkgs manual says the default fixup phase strips debug information from libraries and executables; on Linux, the same phase also runs patchelf on ELF files. This gives strip write access to binaries from many languages without asking it to understand their source.

Nixpkgs needs prebuilt tools to begin building the tools that will eventually replace them. At the revision used by the researchers, the x86-64 bootstrap definition pointed to an archive and a BusyBox binary, each pinned by a SHA-256 hash in the Nixpkgs source. The paper describes the unpacked seed as roughly 20 programs, including a compiler, binutils, the C library and basic Unix tools. Later stages rebuild these pieces until the standard environment no longer depends on the seed at runtime.

That replacement process creates the route the payload needs. The seed's compromised strip processes a freshly compiled, clean copy of strip during the fixup phase. It inserts its own payload into that successor. The newly infected copy then processes the next generation. The authors call this relationship the successor edge: an older build tool writes to the newer copy that will take over the same job. Clean source enters every round, yet the malicious behavior crosses between binaries after compilation.

How the implant survives a clean rebuild

The prototype works on finished ELF files, the executable format used by Linux. Its injector appends payload bytes, changes a spare PT_NOTE program header into an executable PT_LOAD segment, and redirects the ELF entry point so the added code runs first. It also appends a replacement section table that describes the new .payload section. These changes preserve the original program bytes, according to the paper's binary-level design, so the host program can continue at its former entry point after the payload runs.

Running before the usual _start routine imposes tight limits. The payload saves the register state supplied by the kernel, aligns the stack, calls the kernel directly without relying on the host's libraries, then restores state before handing control back. During the experiment it checks NIX_BUILD_TOP and stays silent inside the build sandbox. Outside that environment, an infected executable prints a fixed marker before continuing normally. The paper explains that the marker stands in for an arbitrary action the same code position could perform with the user's permissions.

Propagation and execution use separate checks. Every eligible program receives the entry-point payload, but the injector activates only when the running program's basename is strip. Other infected programs display the marker and then start as usual. Because the payload works below language runtimes, the test set reached software written in C, C++, Python, Rust, Go and Lua. A compiler-bound implant would not have the same language-independent path.

One miss among 3,791 command-line programs

The team tested nixpkgs revision fef9403a3e4d on x86-64 Linux, where the bootstrap used GNU binutils 2.44. It built a graphical NixOS installer representing about 2,000 packages. The resulting closure, meaning the outputs required by the build, held 1,199 package outputs and 3,799 user-invokable ELF executables across 6.16 GB.

Of those executables, 3,791 could be invoked from the command line. The infection marker appeared in 3,790. Firefox 147.0.3 was the sole miss because its package configuration disabled stripping. The reported examples include bash, git, sudo, curl, Python 3.13 and tools built with Rust and Go. The count measures the reach of this prototype inside that one installer closure, rather than all NixOS packages or every kind of Linux binary.

The NixOS functional suite also passed. According to the evaluation, its virtual machines exercised installation and graphical applications while infected copies of GNOME Shell, Mutter, Nautilus and GNOME Control Center were present. Passing those tests showed that the injected marker did not disrupt the workflows under test. It did not establish that the binaries matched clean source.

That distinction explains why ordinary feedback can look reassuring. Build logs can show successful compilation from reviewed code. Tests can confirm expected product behavior. Dependency records can show that the original seed is gone. In this experiment all of those statements remained true while almost every command-line binary ran added code, as the end-to-end results document.

Why matching rebuilds can repeat the problem

Reproducible builds ask independent builders to produce bit-for-bit identical artifacts from the same declared inputs. The method is useful for finding unexplained differences between source and release binaries. Here, however, a rebuild that reuses the same compromised seed deterministically recreates the implant. Two identical results can therefore agree on the wrong bytes. The researchers' analysis says the build is reproducible because the hidden transformation itself is reproducible.

Diverse double-compiling has a related blind spot when its diversity stops at the compiler. Rebuilding a compiler with an independent compiler can expose a compiler that secretly reproduces its own backdoor. The malicious component in this experiment acts later, when both outputs pass through the same strip. The paper argues that the compromised utility sits on both sides of the comparison, so the extra compiler does not remove the common point of trust.

The pinned hash in the bootstrap definition still serves an important integrity purpose: it lets Nix verify that the downloaded archive is the exact object named by the recipe. It cannot decide whether maintainers chose a benign object in the first place. The experiment's threat model includes compromise of the system that produces or publishes a seed, an authorized update to a bad seed, or manipulation of the process used to approve one. A checksum preserves that decision faithfully.

The boundaries of the demonstration

The current injector assumes little-endian ELF on x86-64, a spare PT_NOTE header and an executable layout it knows how to rewrite. Shared libraries, other architectures, static position-independent executables without an interpreter, and files lacking a suitable note segment are outside the tested implementation. The limitations section also says the exact attack may not transfer to another distribution because bootstrap graphs and default post-processing differ.

Several clues are intentionally conspicuous. The proof of concept prints its marker, names its new section .payload, checks the strip basename and tries to spread broadly so its reach can be measured. A real implant could stay dormant except when propagating into another strip, then wait for a chosen program, the authors note. Their experiment establishes a mechanism and its potential reach. It does not establish that anyone has deployed such an implant.

strip is also not the only possible carrier. Reviewing close to 40 utilities called across nine default build phases, the researchers found that patchelf met the same three conditions: it is present early, rewrites binaries and later processes its own successor. They describe install and cp as plausible carriers because build systems often use them to place newly built programs, though that path depends on each package's Makefile. The candidate analysis shifts the audit question from which compiler is trusted to which programs can rewrite their replacements.

Full-source bootstrapping narrows the answer by reducing the opaque binary seed, but the source path has to cover post-build utilities too. Rebuilding only the compiler leaves a tampered strip available to edit the clean compiler output afterward. The paper's comparison with bootstrappable-build work points toward a stricter target: establish source-to-binary correspondence for every tool that can modify an executable on its way into a release, including the tools used to rebuild those modifiers.

The next useful evidence will be replication against newer nixpkgs revisions, tests on other distributions and architectures, and build designs that break the successor edge or verify post-processing in an independently trusted environment. The published experiment makes each of those questions concrete. Until they are answered, its measured claim should remain narrow: one altered bootstrap utility was enough to survive clean source, matching rebuilds and functional tests in a real NixOS build graph.

We reviewed this

  1. paper — our honest review
  2. nixpkgs — our honest review
  3. register — our honest review

Sources

  1. Trusting-Trust Attack against an Entire Linux Distribution through Binary Manipulation
  2. Nixpkgs Reference Manual: The fixup phase
  3. Nixpkgs x86-64 bootstrap-tools definition at revision fef9403a3e4d