CVE-2024-3094 was a deliberate backdoor planted in xz Utils 5.6.0 and 5.6.1 after a multi-year campaign to gain maintainer trust. A build step present only in the release tarball, not git, injected a payload from disguised test files into liblzma, which sshd loads via libsystemd on some distros. Andres Freund found it on 2024-03-29 through an sshd performance anomaly, before it reached stable releases. The lessons are reproducible builds, building from reviewed source, and maintainer health.
- The malicious code was in the release tarballs, not the git repository, so reviewing the repo would not have found it.
- A deliberate, key-controlled backdoor, not an open flaw: only the author could trigger it, and it is not in CISA KEV.
- It reached rolling and pre-release channels (Fedora Rawhide, Debian testing/unstable, openSUSE Tumbleweed, Kali) but no stable RHEL, Debian, or Ubuntu release.
- It was caught by luck: one engineer chasing an sshd slowdown, days before wide rollout.
- The controls that defeat it are building from reviewed source, diffing tarball against repository, reproducible builds with provenance, and healthier maintainer staffing.
- Separate confirmed facts from attribution: 'Jia Tan' is a pseudonym and no first-party source names the actor.
Incident at a glance
- Dates
- to
- Category
- Supply-chain compromise
- Organizations
- Tukaani Project (XZ Utils)
- Fedora Project
- Debian
- openSUSE
- Kali Linux
- Impact
- Release tarballs of 5.6.0 and 5.6.1 built a backdoor into liblzma that let the holder of a private key run code through sshd on some Linux distributions. It reached rolling and pre-release channels, not stable releases.
- How it was found
- Andres Freund traced slow, CPU-heavy SSH logins on Debian unstable to liblzma and reported the backdoor to oss-security.
Timeline
- The Jia Tan persona sends a first patch to the xz mailing list.
- Jia Tan's first commit lands in xz.
- After two months of public pressure for a new maintainer, Lasse Collin calls Jia Tan practically a co-maintainer.
- Jia Tan tags a release, 5.4.2, for the first time.
- Test files that hide the payload are committed.
- xz Utils 5.6.0 is released with the backdoor.
- xz Utils 5.6.1 is released with the backdoor.
- Andres Freund reports the backdoor; CISA publishes an alert and Debian reverts to 5.4.5 code.
- The project releases a cleaned-up 5.6.2.
| Setting | Value | Note | Source |
|---|---|---|---|
| Backdoored releases | xz / liblzma 5.6.0 and 5.6.1 | CWE-506, CVSS 10.0. 5.6.0 released 2024-02-24, 5.6.1 on 2024-03-09. | CVE-2024-3094 (Red Hat) |
| Safe action | Downgrade to 5.4.6, or use a reviewed 5.6.2+ | The project published a cleaned-up 5.6.2 on 2024-05-29. | CISA alert; tukaani.org |
| Exploit preconditions (as analyzed) | glibc + x86-64 + gcc/GNU ld + deb/rpm build + sshd linked to liblzma via libsystemd | Outside these, a host may carry the library yet not be exploitable via sshd. | thesamesam xz backdoor FAQ |
| CISA KEV status | Not listed; no confirmed in-the-wild exploitation | Caught before reaching stable distributions. | CISA KEV catalog |
| Debian remediation | 5.6.1+really5.4.5-1 (reverted to 5.4.5) | Compromised packages were in testing, unstable, and experimental only. | Debian DSA-5649-1 |
| Discovered and disclosed | 2024-03-29 by Andres Freund | Traced from an sshd latency and CPU anomaly in liblzma. | oss-security |
- Inventory every host and container image for xz and liblzma 5.6.0 or 5.6.1, checking both the binary and the shared library, and move affected systems to 5.4.6 or a reviewed 5.6.2+.
- Prefer building dependencies from the version-controlled source; when you must use a release tarball, pin and verify the hash you recorded when you reviewed it.
- On Linux, check whether sshd links liblzma (through libsystemd) to decide whether the exploit precondition even applies to a given host.
- Record which upstream commit each artifact was built from and verify a pinned source hash, so a tarball that differs from the repository is rejected.
- Where feasible, confirm a reproducible rebuild of the dependency from tracked source matches the distributed artifact byte for byte.
- Applies to
- XZ Utils / liblzma Backdoored 5.6.0 and 5.6.1; safe 5.4.x and reviewed 5.6.2+
- OpenSSH as patched by distributions At risk only when a distribution's systemd notification patch links it to libsystemd and so to liblzma; OpenSSH 9.8 added native notification that makes the patch unnecessary
- Affected channels Fedora Rawhide and 40 pre-release, Debian testing/unstable, openSUSE Tumbleweed, Kali; no stable RHEL, Debian, or Ubuntu release
Open-source dependencies built from release tarballs and maintained by small teams. A release tarball can differ from the tagged source in the repository.
- Part of
- Workflow: Respond to a vulnerability. Collection: case studies
Learning goals and prerequisites
What you will learn
- Explain how build-time injection hid the payload from the git repository and reached sshd through libsystemd.
- Separate confirmed facts from attribution and inference in the maintainer-takeover timeline.
- Determine whether a given host carries a backdoored xz and whether it meets the exploitation conditions.
- Apply the controls that defeat this class of attack: reviewed-source builds, tarball-versus-repo diffing, reproducible builds with provenance, and maintainer-health practices.
Before you start
Maintainers, release engineers, and AppSec and platform teams who build or consume open-source dependencies
- Understand how a C project is built from a release tarball versus from a version-control checkout.
- Know what dynamic linking is and how to list a binary's shared-library dependencies.
- For the code: a shell with
xzandshasum; both scripts run offline.
How a tarball-only trigger reached sshd
- Payload hidden in gitTest files committed on 2024-02-23,
bad-3-corrupt_lzma2.xzandgood-large_compressed.lzma, carry the encoded payload. Nothing tracked in git activates it. - Trigger only in the tarballThe 5.6.0 and 5.6.1 release tarballs add a modified
build-to-host.m4that is not in the repository. - Build injects the objectDuring
./configureand the build, on x86-64 Linux with glibc, GCC and GNU ld inside a deb or rpm build, the script decodes a prebuilt object and links it into liblzma. - sshd loads liblzmaA distribution patch links sshd to libsystemd, which depends on liblzma. During dynamic linking the backdoor's IFUNC resolvers run and redirect a function sshd uses for RSA authentication.
- Only the key holder can trigger itA client presenting data signed with the attacker's private key gets remote code execution through sshd; every other client sees normal behavior.
On 2024-03-29 Andres Freund reported that the xz compression utilities, specifically liblzma, had been backdoored in versions 5.6.0 (2024-02-24) and 5.6.1 (2024-03-09). The payload modified liblzma so that a process linking it, in practice OpenSSH's sshd on some Linux distributions, could be subverted by whoever held a secret key. The identifier is CVE-2024-3094, scored 10.0 with the weakness class CWE-506, Embedded Malicious Code.
The remarkable part is where the activation lived. The attack was carried in the release tarballs that maintainers and distributions actually build from. A build step present only in the tarball, not in the version-controlled source, extracted a payload hidden inside files that posed as compression test fixtures and injected it into the library during compilation. Reviewing the repository's human-readable source would not have revealed that activation step, because it was never committed to git.
It was caught before it reached any stable release. CVE-2024-3094 is not in CISA's Known Exploited Vulnerabilities catalog, and no in-the-wild exploitation of the backdoor has been confirmed. This study separates what is established, who did what and when, from what remains attribution and inference, which is considerable.
Autotools projects ship a generated tarball that contains files not tracked in version control, such as configure and bundled m4 macros. The attacker exploited that gap. The tarballs for 5.6.0 and 5.6.1 carried a modified build-to-host.m4 whose extra line was absent from the upstream source and was not used by xz in git. During ./configure it kicked off an obfuscated chain of ordinary build tools.
That chain reached into the test corpus. The repository legitimately contained binary .xz and .lzma files described as good and corrupt inputs for the decompressor's tests. Two of them, bad-3-corrupt_lzma2.xz and good-large_compressed.lzma, actually held the encoded payload. The build decoded and decompressed it into a prebuilt object file and linked that object into liblzma, so the shipped shared library differed from what the published source implied.
The lesson is structural, not clever: if you build from a tarball you have not compared against the tagged source, you are trusting an artifact that no code review ever covered. Binary test fixtures are a convenient hiding place precisely because reviewers skip them.
At runtime the injected code took control through GNU indirect functions (IFUNC), a glibc feature that picks an implementation at load time. A CRC IFUNC resolver had been added to xz in 2023; the backdoor replaced those resolvers so its code ran during dynamic linking. From there it installed a hook in the linker's symbol-resolution path and waited for the main program to resolve a specific OpenSSH function used in RSA authentication, then redirected it to attacker code.
OpenSSH does not use liblzma. The link exists because Debian, Fedora, and other distributions patch sshd to send systemd readiness notifications, which pulls in libsystemd, which depends on lzma. According to the community analysis, the payload activated only under a narrow set of conditions: glibc with IFUNC, an x86-64 linux-gnu target, a build driven by gcc and GNU ld inside a Debian or RPM packaging context, and the running process being sshd. Filippo Valsorda's analysis describes the effect as an authenticated-looking bypass: a client presenting the right key has attacker-supplied data passed to a command execution path, giving remote code execution, and the analysis notes it left no normal log trail.
Because the trigger was a private key the attacker held, this was a deliberate, access-controlled backdoor rather than a flaw anyone could fire. That distinction matters for the response: the risk was control of the backdoor by its author, not a race against the whole internet as with Log4Shell.
The backdoored releases reached fast-moving channels but, by luck of timing, no mainline stable release. Whether a host that carried the library was actually exploitable depended on the sshd-and-glibc conditions above, so 'carried the package' and 'was exploitable' are different questions.
| Channel | Shipped 5.6.0/5.6.1? | Exploitable via sshd? |
|---|---|---|
| Fedora Rawhide; Fedora 40 pre-release | Yes | Yes, under the stated conditions |
| Debian testing / unstable / experimental | Yes | Yes, under the stated conditions |
| openSUSE Tumbleweed / MicroOS | Yes | Yes, under the stated conditions |
| Kali Linux (late March 2024) | Yes | Yes, under the stated conditions |
| Arch Linux | Yes (the library) | No: Arch sshd does not link liblzma |
| Debian stable, all RHEL, Ubuntu releases | No | No |
Freund was investigating why ssh logins on Debian unstable were slow and using unusual CPU, with Valgrind errors in liblzma. An sshd invocation took about half a second longer than it should, and a login's real time nearly tripled. Chasing that performance anomaly, he found the backdoor and alerted the Debian and openwall security lists before posting publicly.
It is worth being honest that this was a near miss. The backdoor was discovered by a careful engineer noticing a side effect, days before the backdoored library would have flowed into widely used stable distributions. Had the author tuned the performance or had no one looked, it could have shipped broadly. A detection story that depends on one person's curiosity is not a control; the lessons below are about the controls that would have caught it by design.
The immediate response was narrow and clear: identify hosts and images carrying xz or liblzma 5.6.0 or 5.6.1, and move off them. CISA advised downgrading to an uncompromised release such as 5.4.6; the project later published a reviewed 5.6.2 as a cleanup. Debian reverted its package to the 5.4.5 code, versioned 5.6.1+really5.4.5-1.
Scan binaries and shared objects, not just one of them, because a tool and its library can diverge. The script below reports the version and flags the two bad releases; pair it with your package manager's audit and your container-image inventory.
#!/usr/bin/env bash
# Report the xz/liblzma version and flag the two backdoored releases.
# Run on every host; pair it with your package manager's own audit.
set -u
ver="$(xz --version 2>/dev/null | sed -n 's/^xz (XZ Utils) //p' | head -1)"
case "$ver" in
5.6.0|5.6.1)
echo "AFFECTED: xz $ver is backdoored (CVE-2024-3094)."
echo "Downgrade to 5.4.6, or install a reviewed 5.6.2 or later."
exit 2 ;;
"") echo "xz not found on PATH"; exit 0 ;;
*) echo "OK: xz $ver is not a known-backdoored release (5.6.0, 5.6.1)"; exit 0 ;;
esac
# On Debian/Red Hat also check the library package, since a binary can be
# patched while the shared object is not:
# dpkg-query -W -f='${Version}\n' liblzma5 # look for 5.6.0/5.6.1
# rpm -q --qf '%{VERSION}\n' xz-libsThe SSDF column names NIST SP 800-218 version 1.1 practices and tasks; SLSA refers to version 1.2. The first rows apply to anyone who packages or builds open-source code, such as distributions; the last to the organizations that depend on it.
| Control | Who | SSDF practice or standard | Guide |
|---|---|---|---|
| Build from the tagged repository, or prove the release tarball matches it | Packagers and distributions | SSDF PW.4.4 | /domains/supply-chain/builds/reproducibility-and-hermeticity/ |
| Reproducible builds checked by an independent rebuild | Maintainers and distributions | SSDF PO.3.2 (reproducible builds), PS.2.1; SLSA Build track | /domains/supply-chain/builds/reproducibility-and-hermeticity/ |
| Signed provenance naming the source commit, verified before an artifact is used | Maintainers and consumers | SSDF PS.3.2; SLSA Build L2 and L3 | /domains/supply-chain/provenance/read-and-verify-slsa/ |
| Review build-system, macro and test-fixture changes as carefully as code; two people for releases | Maintainers | SSDF PW.7.2, PS.1.1 | /domains/supply-chain/ci-cd/osps-baseline-for-maintainers/ |
| Track maintainer health and changes of control in critical dependencies, and fund them | Consumers | SSDF PW.4.1 | /domains/supply-chain/dependencies/project-health-and-intake/ |
| An inventory that finds a named library version on every host and image within hours | Consumers | SSDF PS.3.2, RV.1.1 | /domains/supply-chain/sbom/generate-release-inventory/ |
Two controls defeat this class of attack. First, build from the version-controlled source rather than a convenience tarball, or confirm that every tracked file in the tarball matches the repository. Second, and more powerful, use reproducible and hermetic builds (/domains/supply-chain/builds/reproducibility-and-hermeticity/): if independent rebuilds from the tracked source, with pinned toolchain and imported macros, produce identical bytes, a payload injected only at package time cannot hide. A plain tarball-versus-repository diff helps but is not sufficient on its own, because the generated and imported files it cannot vet are exactly where the xz activation lived.
Provenance ties the two together. Signed build provenance (for example SLSA, /domains/supply-chain/provenance/read-and-verify-slsa/) that records which source commit produced an artifact lets a consumer reject a binary that did not come from the reviewed tree. None of this requires trusting the maintainer's goodwill, which is the point.
The human lesson is maintainer health and succession. A critical library depended on one unpaid volunteer, and social pressure was used to accelerate handing authority to an unknown contributor. Fund and staff critical projects, require more than one trusted maintainer for releases, be wary of pressure campaigns that push for faster handovers, and review a new maintainer's build and release changes, not only their feature code. event-stream's 2018 takeover (/domains/supply-chain/dependencies/case-study-event-stream/) followed the same pattern more crudely: a tired maintainer handed publishing rights to a stranger.
Start with the baseline control: pin the exact source artifact you reviewed and verify its hash on every build, so a silently swapped or re-tagged release is rejected. The script below does that. Be clear about its limit, because this is the subtle part of the xz story: a pinned hash proves you got the same bytes, not that those bytes are clean, and a file-by-file diff of an autotools tarball against its git tree is dominated by legitimately regenerated files such as po/*.po, ChangeLog, and configure. The malicious activation hid among exactly those generated and imported files, so a diff would not have flagged it.
The control that does close the gap is a reproducible build: regenerate the distributable from the tracked source with a pinned toolchain and imported macros, and compare bytes. Record which upstream commit each artifact came from, and on Linux check what your sshd actually links (ldd "$(command -v sshd)" | grep -E 'liblzma|libsystemd') so you know whether the exposure condition even applies to a given host.
#!/usr/bin/env bash
# Pin the exact source artifact you reviewed, and reject anything that does not
# match it on later builds. Record the hash once, from a trusted download, then
# verify on every build and in CI.
set -euo pipefail
artifact="$1"; expected="$2" # e.g. xz-5.4.6.tar.gz <sha256 you recorded>
actual="$(shasum -a 256 "$artifact" | cut -d' ' -f1)"
if [ "$actual" = "$expected" ]; then
echo "OK: $artifact matches the reviewed hash"
else
echo "MISMATCH for $artifact"
echo " expected: $expected"
echo " actual: $actual"
exit 1
fi
# A pinned hash stops a silently swapped or re-tagged artifact. It does NOT
# vouch for the contents: a diff of an autotools tarball against git is
# dominated by legitimately generated files (po/*.po, ChangeLog, configure),
# which is where the xz activation hid. Only a reproducible build, which
# regenerates those files from pinned sources and compares bytes, closes that.The public record does not establish who 'Jia Tan' is, or whether the persona was one person, a group or a government; attributions in the press rest on inference, not first-party evidence. That the accounts pressing for a new maintainer were sockpuppets is Russ Cox's reasonable inference from mailing-list records, not a proven fact. The activation conditions and the remote-code-execution behavior come from community reverse engineering, such as the thesamesam FAQ and Filippo Valsorda's analysis, not from any statement by the author. No in-the-wild exploitation has been confirmed, and it is not known whether further payloads were planned for later releases.
Guided practice
You run a mixed fleet and a CI builder. Decide, for each item, whether it carries a backdoored xz and whether it is exploitable via sshd, then state the action. Facts are given; treat them as true.
Supplied scenario material
H1: Debian 12 stable, xz 5.4.1, OpenSSH patched for systemd. x86-64, glibc.
H2: Fedora Rawhide image, liblzma 5.6.0, sshd present and linked to libsystemd. x86-64, glibc.
H3: Arch Linux, xz 5.6.1 installed, sshd present. x86-64, glibc.
H4: Alpine 3.19 container, xz 5.6.1 (musl libc), sshd present.
C1: CI image that builds a C project from the xz-5.6.0 release tarball downloaded over HTTPS, pinned by version only.1. Clear H1
Decide H1's status.
Expected result and reasoning
Not affected: 5.4.1 predates the backdoor, which exists only in 5.6.0 and 5.6.1. No action beyond normal patching; stable Debian never shipped a backdoored xz.
2. Treat H2 as compromised
Decide H2's exposure and action.
Expected result and reasoning
Carries a backdoored liblzma and meets the conditions (glibc, x86-64, sshd linked via libsystemd), so treat it as exploitable and potentially compromised. Rebuild or roll back to 5.4.x, rotate exposed keys and secrets, and investigate.
3. Scope H3 precisely
Decide whether Arch's 5.6.1 is exploitable via sshd.
Expected result and reasoning
It carries the backdoored library, but Arch's sshd does not link liblzma, so the sshd path does not apply. Still replace the library with a fixed version, because other consumers of liblzma and future linkage remain a concern.
4. Judge H4
Decide the Alpine container's status.
Expected result and reasoning
The backdoor relies on glibc IFUNC behavior and a deb/rpm build context, so a musl-based Alpine build does not meet the activation conditions. Replace the package anyway for hygiene, but do not treat it as an exploitable sshd host on the stated facts.
5. Catch C1 at the source
Explain why C1 is dangerous despite 'building from source' and how to stop it.
Expected result and reasoning
The 5.6.0 release tarball contains the injected build step that is absent from git, so building from it produces a backdoored library even though the version looks normal. Build from the tagged repository or a reproducible build rather than the convenience tarball, pin and verify a source hash, and prefer a reviewed 5.6.2+ or 5.4.x.
The activation conditions are from community analysis, not an exhaustive proof; absence of a condition lowers but does not formally eliminate risk. No in-the-wild exploitation is confirmed, and carrying the package is not evidence that a host was compromised.
Save a draft on this device to resume later. Automatic saving begins after you choose Save. Drafts are never sent by this application; use synthetic data.
No draft saved on this device yet.
Sources & further reading
The standards, specifications and documentation behind the guidance above. Each is the evidence for a claim on this page; consult it for detail beyond the defaults and versions listed here.
- oss-security: backdoor in upstream xz/liblzma (Andres Freund, 2024-03-29)
- NVD: CVE-2024-3094 detail (CVSS 10.0, CWE-506)
- Red Hat: Urgent security alert for Fedora 40 and Fedora Rawhide users. 29 March 2024.
- CISA: Reported supply chain compromise affecting XZ Utils (CVE-2024-3094). 29 March 2024.
- Tukaani project: XZ Utils backdoor (first-party statement). Last updated 17 January 2025.
- Russ Cox: Timeline of the xz open source attack. 1 April 2024, updated 3 April 2024.
- Debian Security Advisory DSA-5649-1: xz-utils security update. 29 March 2024.
- thesamesam: FAQ on the xz-utils backdoor (CVE-2024-3094)
A multi-year maintainer takeover
xz was effectively a one-maintainer project. Russ Cox's annotated timeline, built from mailing-list and commit records, shows a persona using the name Jia Tan (JiaT75) sending a first patch on 2021-10-29 and landing a first commit on 2022-02-07. Through 2022, that account took on more of the work, and the original maintainer, Lasse Collin, wrote on 2022-06-29 that Jia Tan was 'practically a co-maintainer already.'
That shift was accompanied by pressure. In April to June 2022, accounts using the names Jigar Kumar and Dennis Ens pushed, on the public list, for faster releases and a new maintainer. Cox notes that many of these addresses appear nowhere else and 'seem likely' to have been sockpuppets supporting the takeover; that is a reasonable inference, not a proven fact. By late 2022 Jia Tan had commit access, tagged the first release (5.4.2) in March 2023, and over 2023 landed groundwork, including the IFUNC CRC change and an oss-fuzz configuration edit, that the backdoor later used.
The endgame was fast. The backdoor was committed on 2024-02-23 and 5.6.0 released the next day; 5.6.1 followed on 2024-03-09. The same persona and an associated account then filed bug reports asking Debian and Ubuntu to ship the new version. A crucial caveat: 'Jia Tan' is a pseudonym. The patience and tradecraft led many to suspect a well-resourced actor, but no public, first-party evidence attributes the campaign to a specific person or government. Treat any named attribution as unconfirmed.