threatintel
actor tracker
Named attack · kill-chain walkthrough

XZ Utils Backdoor

A three-year social-engineering operation to backdoor sshd on every Linux distro

Jia Tan persona (unattributed; likely state-sponsored)Late 2021 – March 29, 2024 (multi-year run-up; detection by Andres Freund)Unattributed

No public attribution has been made by any government agency. The sophistication of the social engineering, the patience of the multi-year operation, and the surgical precision of the technical payload are widely assessed as indicative of a well-resourced state actor. The identity behind the 'Jia Tan' persona remains unknown.

An unknown actor operating as 'Jia Tan' (GitHub: JiaT75) spent roughly two years cultivating trust in the xz-utils open-source project before inserting a cryptographically authenticated backdoor into the release tarballs of versions 5.6.0 and 5.6.1. Disguised as test fixture files, the payload was extracted at build time by a malicious Bash script in m4/build-to-host.m4, which injected a rogue object file into liblzma. Because Debian and RPM-family distributions patch OpenSSH to link libsystemd — which in turn links liblzma — every sshd process on affected rolling/testing distributions loaded the compromised library. The backdoor used IFUNC resolver hooking to intercept OpenSSL's RSA_public_decrypt function, enabling the attacker to authenticate to any targeted host using a private Ed448 key never made public. The attack was discovered by Andres Freund on March 28–29, 2024, while investigating anomalous SSH latency during unrelated PostgreSQL benchmarking.

scene 00 / 08
2021-10-292022 Apr–Jun2022-10 – 2023-072024-02-23PHASE 01 · PERSONAJia TanJiaT75jiat0218@gmail.compersona — unknown actorfirst patch to xz-develT1195.002 · T1585.001PHASE 02 · PRESSURESock-puppet campaignJigar Kumar"add new maintainer now"Dennis Ens"progress is stalled"Hans JansenT1585.001 · T1566.004PHASE 03 · ACCESSCo-maintainer escalation2022-10-28: Tukaani org added2022-12-30: direct commit access2023-03-18: first solo releasexz 5.4.22023-07-07: oss-fuzz IFUNCT1078 · T1195.002PHASE 04 · PAYLOAD STAGINGObfuscated payloadFeb 23–24, 2024tarballs only — not in git treebad-3-corrupt_lzma2.xzStage 0–1 bash via tr ciphergood-large_compressed.lzmaStage 2 binary via AWK RC4T1027 · T1027.001PHASE 05 · BUILD-TIME INJECTIONm4/build-to-host.m4 fires at package buildxz 5.6.0 (2024-02-24) · xz 5.6.1 (2024-03-09)extracts → liblzma_la-crc64-fast.o → linked into liblzma.sox86-64 · GCC · GNU ld · Debian or RPM build env onlyT1195.002 · T1059.004 — Unix Shell executionPHASE 06 · LOAD CHAINsshdopenssh-server+ systemd-notify patchlinkslibsystemdsystemd libraryDebian / RPM distroslinksliblzma.sobackdoored buildT1574.006 · T1547Arch / GentooNixOS: safe —no sshd linkPHASE 07 · CAPABILITY · CVSS 10.0 CRITICALIFUNC resolver hooks RSA_public_decryptinspects RSA modulus N from incoming SSH clientChaCha20 + Ed448 signature → command 0–3 decodedauth bypass · shell exec · UID/GID exec — only attacker key worksT1556 · T1574 · T1059.004 — magic-key RCE inside sshdPHASE 08 · DISCOVERY & RESPONSEAndres Freund2024-03-28 — ~500 ms SSH latency+ Valgrind errors in liblzmaMicrosoft / PostgreSQL teamCVE-2024-3094 publishedEmergency rollbackDebian sid/testing · Fedora 40/41βopenSUSE Tumbleweed · Kali rollingdowngraded to 5.4.x within hoursCVE-2024-3094 · CVSS 10.0 CRITICALCVE-2024-3094CVSS 10.0 CRITICAL — supply-chain backdoor
  1. Phase 01 · Initial Access — Persona EstablishmentTA0001

    JiaT75 spends a year making genuinely useful contributions to build trust

    • On October 29, 2021 the 'Jia Tan' persona submitted its first patch to the xz-devel mailing list — a modest, correct fix that attracted no suspicion.
    • Over the following months JiaT75 continued contributing helpful patches, building a verifiable public record of competent, cooperative maintenance work.
    • Lasse Collin, the sole maintainer of xz-utils since 2009, was known to be working under significant personal stress and limited bandwidth — a condition the attacker researched and exploited.
    • Jia Tan's first commit was merged on February 7, 2022, using the email address jiat0218@gmail.com — establishing a foothold in the version-control history.
  2. Phase 02 · Social Engineering — Sock-Puppet Pressure CampaignTA0001

    Fake personas 'Jigar Kumar' and 'Dennis Ens' flood the mailing list demanding a new co-maintainer

    • Between April and June 2022, personas using the names 'Jigar Kumar' and 'Dennis Ens' posted repeated complaints to the xz-devel list, accusing Lasse Collin of neglecting the project and demanding faster releases.
    • The campaign explicitly called for Jia Tan to be given a larger role: 'Progress will not happen until there is a new maintainer' — a coordinated nudge designed to manufacture social proof.
    • Lasse Collin publicly acknowledged 'long-term mental health issues' limiting his capacity in May 2022, adding legitimacy to the pressure and making the handover appear voluntary and reasonable.
    • A third persona, 'Hans Jansen,' appeared in June 2022 with similar pressure messaging, then resurfaced in March 2024 to file a Debian bug requesting expedited adoption of xz 5.6.1 — the backdoored version.
    • On May 19, 2022 Lasse Collin hinted publicly that Jia Tan 'has helped me off-list … he might have a bigger role in the future,' showing the pressure campaign was working.
  3. Phase 03 · Privilege Escalation — Co-Maintainer AccessTA0004

    Jia Tan gains commit rights and full co-maintainer status over 14 months of patience

    • October 28, 2022: Jia Tan was added to the Tukaani GitHub organization, granting them triage and review permissions.
    • December 30, 2022: Jia Tan received direct commit access to the xz repository — a critical escalation enabling future code injection.
    • January 11, 2023: Lasse Collin released what would become his final version (v5.4.1), effectively ceding day-to-day control.
    • March 18, 2023: Jia Tan cut the first release under their own authority (v5.4.2), establishing operational control over the official release pipeline.
    • June 2023: Jia Tan submitted code introducing GNU indirect function (IFUNC) support — a prerequisite for the backdoor's runtime hooking mechanism — and simultaneously disabled IFUNC fuzzing in oss-fuzz on July 7, 2023, citing 'incompatibility with address sanitizer,' preventing automated detection of the future payload.
  4. Phase 04 · Defense Evasion — Obfuscated Test-Fixture PayloadTA0005

    Malicious binary data lands in the repository disguised as benign test fixture files

    • The backdoor payload was split across two files that appeared to be ordinary compressed test data: tests/files/bad-3-corrupt_lzma2.xz and tests/files/good-large_compressed.lzma — both committed to the repository by Jia Tan in February 2024.
    • The files existed only in the release tarballs distributed via tukaani.org, not in the GitHub source tree — meaning standard source-code audits of the git repository would not reveal them.
    • bad-3-corrupt_lzma2.xz contained Stage 1 bash code obscured by a character-translation map (tab↔space, hyphen↔underscore); applying the inverse transform with tr restored a valid xz stream.
    • good-large_compressed.lzma contained the Stage 2 binary payload: a pre-compiled object file (liblzma_la-crc64-fast.o) encrypted with an AWK-implemented RC4 variant (using modular addition rather than XOR), extracted by carving alternating 1024-byte and 2048-byte blocks.
    • Jia Tan disabled oss-fuzz IFUNC coverage on July 7, 2023 to prevent the fuzzer from exercising the code paths the backdoor would later hook.
  5. Phase 05 · Execution — Build-Time Payload InjectionTA0002

    m4/build-to-host.m4 silently unpacks the payload and links the rogue object into liblzma during package build

    • The malicious logic lived in m4/build-to-host.m4, a GNU autotools macro file included during the configure phase — a standard part of every source build, executed with the developer's or packager's privileges.
    • Stage 0 used tr to 'uncorrupt' bad-3-corrupt_lzma2.xz, revealing a Stage 1 bash script. Stage 1 carved encrypted binary chunks from good-large_compressed.lzma using alternating head invocations, then applied a substitution cipher before final decompression.
    • Stage 2 decrypted the pre-compiled object file liblzma_la-crc64-fast.o using an AWK-based RC4 variant and injected it into the build system's object list, causing it to be linked directly into the finished liblzma.so.
    • The injection only activated on x86-64 Linux builds using GCC and the GNU linker when the package was being built in a Debian or RPM environment — narrowing the blast radius to exactly the distributions the attacker wanted to compromise.
    • xz 5.6.0 shipped on 2024-02-24. A refinement (fixing a Valgrind-detectable anomaly) shipped as 5.6.1 on 2024-03-09.
  6. Phase 06 · Persistence — IFUNC Hook in sshd via libsystemd → liblzmaTA0003

    The rogue liblzma loads inside every sshd process on affected distros through the libsystemd dependency chain

    • Debian and RPM-family distributions patch OpenSSH to use systemd socket activation, causing the openssh-server package to link libsystemd. libsystemd in turn links liblzma — loading the compromised library into every sshd process at startup.
    • Arch Linux, Gentoo, NixOS, and other distributions that do not apply the systemd-notify OpenSSH patch do not link libsystemd from sshd, and are therefore not exploitable via this vector even with the backdoored xz installed.
    • The injected object file registered a GNU IFUNC resolver that replaced the legitimate implementation of a crc64 function, then at runtime redirected OpenSSL's RSA_public_decrypt to the attacker's hook code — surviving across normal process lifetime with no additional dropper.
    • Because the hook lived in a shared library loaded at process start, there was no process injection, no unusual child process, and no network callback to a C2 — making the implant exceptionally clean and difficult to detect with conventional EDR telemetry.
  7. Phase 07 · Credential Access / Command Execution — RSA Hook RCETA0006

    The attacker authenticates with a private Ed448 key and executes arbitrary commands inside the sshd process

    • The IFUNC hook intercepted OpenSSL's RSA_public_decrypt function, giving the backdoor visibility into every incoming SSH public-key authentication attempt before OpenSSH's own verification logic ran.
    • For each connection the hook inspected the RSA public-key modulus (the 'N' value) supplied by the connecting client. The first 16 bytes of N were used to derive a command number (0–3), selecting operations including: authentication bypass, remote command execution, and UID/GID-specified command execution.
    • The backdoor decrypted the final 240 bytes of the modulus with ChaCha20 using a hardcoded key embedded in the payload, then validated a signature using an Ed448 elliptic-curve public key — ensuring only the operator possessing the corresponding private key could issue valid commands.
    • Invalid payloads (normal SSH connections) fell through transparently to OpenSSH's standard authentication path, producing no detectable error and making targeted exploitation nearly invisible in logs.
    • The full set of attacker capabilities was never exercised in the wild — the backdoor was caught before large-scale exploitation began.
  8. Phase 08 · Discovery — Caught by Accident, Not DesignTA0007

    Andres Freund notices ~500 ms SSH slowness and Valgrind errors during unrelated PostgreSQL benchmarking

    • On March 28, 2024 Andres Freund (Microsoft, PostgreSQL core team) was benchmarking PostgreSQL on a Debian sid machine when he noticed SSH logins consuming anomalous CPU and taking 500–800 ms longer than expected.
    • Running Valgrind on sshd revealed unexpected memory access errors originating inside liblzma — a library that should have had no involvement in SSH authentication.
    • Freund traced the anomaly through the libsystemd dependency to the xz-utils package, found that the release tarball differed from the GitHub source tree in the m4/build-to-host.m4 file, and reverse-engineered the injection chain.
    • He privately disclosed his findings to distributors and the oss-security mailing list on March 29, 2024. Affected distributions (Debian sid/testing, Fedora 40/41 beta, openSUSE Tumbleweed, Kali rolling) issued emergency downgrades within hours.
    • The discovery was serendipitous — no automated security tooling, fuzzer, or CI pipeline caught the backdoor. The attacker had carefully disabled the one fuzzer (oss-fuzz IFUNC coverage) most likely to trigger the relevant code path.
Diamond Model

Caltagirone / Pendergast / Betz 2013 — four-vertex attribution framework.

Adversary
  • Jia Tan persona (unattributed; likely state-sponsored)
Capability
  • T1195.002
  • T1585.001
  • T1566.004
  • T1078
  • T1027
  • +1 more
Infrastructure
Victim
  • See narrative above
Primary sources