Software Supply Chain Security
Abstract
A modern program is mostly other people’s code: libraries pulled from public registries, compilers, build servers, update channels and signing keys, each trusted because the one before it was. An attacker who compromises any link reaches every user downstream at once, with a signed and legitimate-looking update. Ken Thompson described the extreme case in 1984, a compiler that inserts a backdoor into the programs it compiles. The real attacks came later and were mostly simpler: a tampered CVS mirror of the Linux kernel in 2003, a poisoned CCleaner download in 2017 that reached 2.27 million machines, a volunteer-maintained npm package handed to a stranger in 2018, and the SolarWinds Orion build server in 2019 and 2020, whose updates were installed by about 18,000 customers. The defences that followed, reproducible builds, software bills of materials, signed provenance and the EU’s Cyber Resilience Act, all try to answer one question: where did this binary actually come from?
Trusting Trust
In his 1984 Turing Award lecture, Reflections on Trusting Trust, Ken Thompson described a C compiler modified to recognise two programs. When it compiled the Unix login command it inserted a backdoor accepting a secret password. When it compiled a C compiler, it inserted both of these modifications into the new compiler. Once the modified compiler binary existed, the source code could be cleaned, and every future compiler built from clean source would still carry the trap. “You can’t trust code that you did not totally create yourself,” Thompson concluded. “(Especially code from companies that employ people like me.)” The idea was not his; he credited “an unknown Air Force document,” which was Paul Karger and Roger Schell’s 1974 security evaluation of Multics (see Paul Karger: Mr. High Assurance).
Thompson’s attack lived in the toolchain. Its lesson, that inspecting source code proves nothing about a binary produced by tools nobody inspected, became the founding problem of supply chain security. For the next three decades it was mostly a thought experiment.
The Mirror
The first well-documented attempt on a major open-source project came in November 2003. Linux kernel development then ran on BitKeeper, a proprietary version control system, and a CVS mirror was kept for developers who refused to use it. Larry McVoy, BitKeeper’s author, noticed that a change to the CVS copy had no matching record in the BitKeeper master. It added two lines to the wait4 system call that looked like an error check:
if ((options == (__WCLONE|__WALL)) && (current->uid = 0))
retval = -EINVAL;The second comparison has one equals sign where two belong. It does not test whether the caller is root; it makes the caller root. A program that called wait4 with that unusual combination of flags would gain superuser rights. Someone had broken into the CVS server and planted it. Because the change existed only in the mirror, it was caught before it reached the real kernel tree. The attacker was never identified.
Poisoned Updates
For criminal and state attackers the update channel was more attractive than source code. A software vendor’s update is downloaded automatically, signed with the vendor’s key and trusted by antivirus programs. Between 15 August and 12 September 2017 the official download of CCleaner version 5.33, a Windows clean-up utility published by Piriform, which Avast had just bought, contained a backdoor. The installer carried Piriform’s valid digital signature. 2.27 million users downloaded it. The first stage reported each machine’s name, IP address and installed software to a server and could install a second stage. Cisco’s Talos group found it.
Three months earlier the same method had been used as a weapon. On 27 June 2017 an update to M.E.Doc, accounting software that Ukrainian companies needed to file taxes, delivered NotPetya, a wiper disguised as ransomware that then spread worldwide on its own and caused an estimated $10 billion in damage (see The History of Cyberwar).
The Volunteer Who Handed Over
Open-source package registries added a new link to the chain: maintainers nobody had vetted. In 2018 Dominic Tarr, the author of event-stream, a small Node.js library installed millions of times a week, no longer used it himself. A user calling himself right9ctrl offered to take over, and Tarr gave him publishing rights. right9ctrl then released version 3.3.6, which added a new dependency, flatmap-stream. Its encrypted payload did nothing in almost every program that included it. It activated only when built into Copay, a bitcoin wallet app from BitPay, where it tried to steal the private keys of wallets holding large balances. A computer science student, Ayrton Sparling, reported it on GitHub on 20 November 2018, more than two months after release. BitPay said Copay versions 5.0.2 to 5.1.0 were affected.
Tarr explained himself in the GitHub thread two days later: “he emailed me and said he wanted to maintain the module, so I gave it to him. I don’t get any thing from maintaining this module, and I don’t even use it anymore, and havn’t for years.” The same pattern, a tired volunteer and a helpful stranger, recurred in 2024 with xz Utils (see Volunteer-Maintained Infrastructure).
In February 2021 the security researcher Alex Birsan showed that no maintainer needed to be fooled at all. Companies used internal packages with names like internal-auth and installed them with the same tools that fetched public packages. If a public registry held a package with the same name and a higher version number, many build systems took the public one. Birsan uploaded harmless packages under the names of internal packages he had found mentioned in public code, and they ran inside more than 35 organisations, including Apple, Microsoft and PayPal. He called the technique dependency confusion and collected more than $130,000 in bug bounties for it.
SolarWinds
The attack that made the subject a matter of national policy went through a build server. SolarWinds of Austin, Texas sold Orion, network-monitoring software used by about 33,000 customers, many of them government agencies, with administrator access to the networks it watched. No later than September 2019, intruders established themselves in SolarWinds’s software development environment. In October they made a harmless test change to Orion. From around February 2020 (its build timestamp reads 20 February) they ran an implant that CrowdStrike later named SUNSPOT on the build server. It checked every second whether Visual Studio’s MSBuild was compiling Orion, and if so, swapped one source file, InventoryManager.cs, for a version containing a backdoor, SUNBURST, then restored the original once the build finished so the developers’ own source tree never showed the change. The compiled Orion was signed with SolarWinds’s certificate, as always, and released in versions 2019.4 to 2020.2.1 between March and June 2020.
About 18,000 customers installed a compromised version. SUNBURST stayed dormant for up to two weeks after installation, then contacted its servers with traffic disguised as Orion’s own Improvement Program protocol; the attackers chose a small fraction of the infected networks for further intrusion. The victims included the Treasury, Commerce, Justice and Energy departments, the courts’ document system and the security company FireEye, which on 8 December 2020 announced that its own penetration-testing tools had been stolen. Investigating its own breach, FireEye traced the intrusion to Orion and published the finding on 13 December, the day the breaches at Treasury and Commerce were confirmed. The Cybersecurity and Infrastructure Security Agency issued an emergency directive the same day ordering federal agencies to disconnect SolarWinds products. The U.S. government attributed the operation to Russia’s foreign intelligence service, the SVR, on 15 April 2021.
The SolarWinds attack was Thompson’s scenario with a build server in place of a compiler. Anyone auditing Orion’s source code would have found nothing, because the backdoor existed only for the minutes in which the code was compiled.
The Chain Gets Longer
The incidents of 2021 to 2023 showed how many links there were. From 31 January 2021 an attacker modified Codecov’s Bash Uploader, a script that thousands of companies downloaded and ran inside their build pipelines to report test coverage, so that it sent the build’s environment variables, often full of credentials, to an outside server. A customer noticed on 1 April that the script’s checksum no longer matched the one published on GitHub. On 2 July 2021 the ransomware group REvil used the remote-management product Kaseya VSA to encrypt about 1,000 companies through roughly 60 IT service providers that used it (see The Ransomware Epidemic).
In March 2023 the desktop app of 3CX, a business phone system, turned out to have been trojanised in its build. Mandiant traced the intrusion to a 3CX employee who had installed X_TRADER, discontinued trading software whose installer had itself been tampered with on its maker’s website. It was the first case Mandiant had seen of one software supply chain attack leading to another. Mandiant attributed both to a group linked to North Korea.
Countermeasures
The defences fall into three groups, each aimed at a different link.
Reproducible builds answer Thompson. If any two people building the same source with the same tools get bit-for-bit identical binaries, a binary can be checked by rebuilding it independently, and a compromised build server, like SolarWinds’s, produces a binary that does not match. Bitcoin’s developers built the first practical tool for it, Gitian, in 2011, so that users could trust a downloaded wallet. Mike Perry of the Tor Project made the Tor Browser build reproducibly in 2013, and that July Jérémy Bobbio (Lunar) organised a discussion at DebConf13 that started the effort to make Debian reproducible. It took years: compilers embed timestamps, file orders and build paths, and every one of those had to be removed. The answer to Thompson’s compiler problem specifically had been published in 2005 by David A. Wheeler, whose “diverse double-compiling” builds a compiler with a second, independent compiler and compares the results.
Software bills of materials answer the question of what is inside. An SBOM is a machine-readable list of every component in a product and its version, so that when a flaw such as Log4Shell is published, an operator can find every affected system at once instead of asking every vendor. U.S. Executive Order 14028 of 12 May 2021, written in response to SolarWinds, ordered security guidance for software sold to the federal government that includes giving the purchaser an SBOM for each product; the National Telecommunications and Information Administration published the minimum elements on 12 July 2021 (see Secure by Design).
Signed provenance answers the question of where a binary came from. Google introduced SLSA, a graded set of requirements for build integrity, on 16 June 2021, and the Linux Foundation’s Sigstore project, announced on 9 March 2021 with Red Hat, Google and Purdue University as founding members, made it practical for open-source projects to sign releases without managing long-lived private keys.
The European Union went further and wrote supply chain duties into product law. The Cyber Resilience Act, Regulation (EU) 2024/2847, entered into force in December 2024. Manufacturers of products with digital elements must report actively exploited vulnerabilities from September 2026 and meet the full security requirements, including documentation of the components they use, from 11 December 2027. After protests from open-source developers, volunteers who do not commercialise their code were left out of scope, and foundations that support commercially used open-source projects were given a lighter category of “open-source software steward.”
Dead End: Trust the Signature
For most of the period the industry’s answer to tampering was code signing: a vendor signs its binaries, the operating system checks the signature, and anything with a valid signature from a known vendor is trusted. The 2017 CCleaner installer, the 2020 Orion updates and the 3CX desktop app were all signed with their makers’ genuine certificates. The signatures were correct. They proved that the binary came from the vendor’s build system, and the build system was where the attacker was.
Signing protected the last few metres of the chain, from the vendor’s server to the customer, and said nothing about what had happened inside the vendor. The responses after 2020 move the check earlier: reproducible builds compare a binary against an independent rebuild, and provenance attestations record which source and which build produced it, so that a signature says more than that the vendor’s key was used.
📚 Sources
- Thompson, Ken: “Reflections on Trusting Trust,” Communications of the ACM 27(8), August 1984 (ACM Digital Library)
- Karger, Paul A. and Roger R. Schell: “Thirty Years Later: Lessons from the Multics Security Evaluation,” ACSAC 2002 (the 1974 evaluation, ESD-TR-74-193, as Thompson’s “unknown Air Force document”)
- Felten, Ed: “The Linux Backdoor Attempt of 2003,” Freedom to Tinker, 9 October 2013 (the
wait4change, McVoy’s discovery, CVS mirror) - Cluley, Graham: “CCleaner, distributed by anti-virus firm Avast, contained malicious backdoor,” September 2017 (Avast’s purchase of Piriform two months earlier; 5.33 from 15 August, clean 5.34 on 12 September)
- “CCleaner hacked to distribute malware,” The Hacker News, September 2017 (version 5.33, 2.27 million downloads, valid signature)
- “Hackers compromised free CCleaner software, experts say,” Reuters via ABS-CBN, 19 September 2017 (download window 15 August to 12 September, Talos)
- “Popular Node.js event-stream module compromised to steal bitcoins,” The Hacker News, November 2018 (right9ctrl, flatmap-stream, Copay, Sparling’s discovery)
- Trend Micro: “Hacker Infects Node.js Package to Steal from Bitcoin Wallets” (Copay versions 5.0.2 to 5.1.0)
- Snyk: “A post-mortem of the malicious event-stream backdoor” (3.3.6 on 9 September 2018; payload only for wallets above 100 BTC or 1,000 BCH)
- “Researcher hacks over 35 tech firms in novel supply chain attack,” BleepingComputer, February 2021 (Birsan, Apple, Microsoft, PayPal, more than $130,000 in bounties)
- 2020 United States federal government data breach, Wikipedia (timeline from September 2019, Orion versions, 33,000 Orion customers and 18,000 downloads, FireEye 8 and 13 December 2020, CISA directive, SVR attribution)
- CrowdStrike: “SUNSPOT: An Implant in the Build Process,” January 2021 (MSBuild monitoring, InventoryManager.cs replacement)
- FireEye/Mandiant: “Highly Evasive Attacker Leverages SolarWinds Supply Chain to Compromise Multiple Global Victims With SUNBURST Backdoor,” 13 December 2020 (dormancy up to two weeks, OIP protocol masquerade)
- event-stream issue #116, “I don’t know what to say,” GitHub, opened 20 November 2018 (Tarr’s comment of 22 November)
- Codecov: “Bash Uploader Security Update” (alterations from 31 January 2021; customer’s shasum check on 1 April;
envoutput sent to a third-party server) - CNCF TAG Security: “Compromise of Codecov Bash Uploader tool”
- Supply chain attack, Wikipedia (Kaseya VSA, 2 July 2021; M.E.Doc)
- Kaseya VSA ransomware attack, Wikipedia (REvil, over 1,000 companies, about 60 managed service providers)
- 2017 cyberattacks on Ukraine, Wikipedia (M.E.Doc update, 27 June 2017; Bossert’s estimate of over $10 billion)
- Krebs, Brian: “3CX Breach Was a Double Supply Chain Compromise,” Krebs on Security, 20 April 2023
- Mandiant: “3CX Software Supply Chain Compromise Initiated by a Prior Software Supply Chain Compromise” (X_TRADER installer on the Trading Technologies website, UNC4736)
- White House fact sheet, 15 April 2021: “Imposing Costs for Harmful Foreign Activities by the Russian Government” (SVR named for SolarWinds)
- Reproducible Builds: History (Gitian 2011, Tor Browser 2013, DebConf13 discussion, systematic Debian rebuilds from 2014)
- Reproducible Builds: “Reproducible Builds mourns the passing of Lunar,” November 2024 (Jérémy Bobbio started the effort in 2013)
- Wheeler, David A.: “Fully Countering Trusting Trust through Diverse Double-Compiling”
- Executive Order 14028, “Improving the Nation’s Cybersecurity,” 12 May 2021, Federal Register (Sec. 4(e)(vii), SBOM for each product)
- NTIA: “The Minimum Elements for a Software Bill of Materials,” 12 July 2021 (under Executive Order 14028)
- Google Security Blog: “Introducing SLSA, an End-to-End Framework for Supply Chain Integrity,” 16 June 2021
- Linux Foundation: “Linux Foundation Announces Free sigstore Signing Service to Confirm Origin and Authenticity of Software,” 9 March 2021
- “Kubernetes standardises on Sigstore,” The Stack (Sigstore first released March 2021 by Red Hat, Google and Purdue; SLSA introduced by Google June 2021)
- Regulation (EU) 2024/2847 (Cyber Resilience Act), EUR-Lex (Annex I Part II: identify and document components, including a software bill of materials)
- Simmons & Simmons: “The EU Cyber Resilience Act” (Regulation (EU) 2024/2847, phased obligations, 11 December 2027)
- “The EU Cyber Resilience Act’s Obligations: What Does It Mean for Open Source Software?,” JD Supra (open-source stewards)