Keypoints
- Attackers committed malicious files (test data and a build helper) to the XZ repository and served tainted source archives from xz.tukaani.org, introducing the backdoor into official releases xz-5.6.0 and xz-5.6.1.
- A malicious build-to-host.m4 executed during package builds extracted an embedded shell script from bad-3-corrupt_lzma2.xz, which in turn extracted a hidden object-file payload from good-large_compressed.lzma.
- The payload reconstruction used layered head/tail/tr pipelines, a custom RC4-like decryption, and XZ decompression to produce liblzma_la-crc64-fast.o which was linked into liblzma during compilation.
- The build-time source modification changed a call from __get_cpuid to _get_cpuid so the compiled library would call the injected object’s symbol instead of the GLIBC intrinsic.
- The binary backdoor uses IFUNC mechanics and GOT manipulation to load stealthily at runtime, then patches the dynamic linker’s dl_audit/symbind callback to hook libcrypto functions in /usr/bin/sshd.
- Execution is gated by environment checks and a kill switch string; the backdoor resolves many symbols via a trie-based resolver and only proceeds when its criteria are met.
- Kaspersky assigned detection names (HEUR:Trojan.Script.XZ, Trojan.Shell.XZ, MEM:Trojan.Linux.XZ) and provided YARA/signature artefacts and hashes for affected libraries and embedded files.
MITRE Techniques
- [T1195] Supply Chain Compromise – Malicious code and files were introduced into the upstream XZ source and releases, allowing downstream packaging to ship compromised libraries (‘the backdoored library was introduced at two levels’).
- [T1105] Ingress Tool Transfer – Attackers served a tainted build helper from a controlled URL used by distributions: ‘This URL is used by most distributions, and, when downloaded, it comes with a file named build-to-host.m4 that contains malicious code.’
- [T1059] Command and Scripting Interpreter – Build-time execution of shell scripts was used to extract and execute payload stages: ‘the decompressed data contains a shell script that will be executed later using /bin/bash.’
- [T1027] Obfuscated Files or Information – Payloads were hidden and deobfuscated via pipelines and simple transforms: ‘tr command applies a basic substitution to the output to deobfuscate it’ and ‘a custom RC4-like algorithm is used to decrypt the binary data.’
- [T1574] Hijack Execution Flow – At runtime the backdoor patches the dynamic linker’s audit interface to intercept symbol resolution and redirect targets: ‘opts to perform a runtime patch on the already registered (default) interfaces loaded in memory, thereby hijacking the symbol-resolving routine.’
- [T1055] Process Injection – The backdoor modifies in-process pointers (GOT) and swaps cpuid pointers to call its code, enabling hooks into targeted functions: ‘The backdoor then swaps the pointers to the main malware function, and calls it as if it were calling cpuid.’ (used to later hook RSA_public_decrypt and other libcrypto functions)
Indicators of Compromise
- [File name] build/extraction artifacts – bad-3-corrupt_lzma2.xz, good-large_compressed.lzma
- [Library versions] backdoored source releases – xz-5.6.0, xz-5.6.1
- [Hashes] known compromised libraries – 4f0cf1d2a2d44b75079b3ea5ed28fe54 (liblzma.so.5.6.0), 53d82bb511b71a5d4794cf2d8a2072c1 (liblzma.so.5.6.1)
- [Object file] injected backdoor object – liblzma_la-crc64-fast.o (listed with artefact id 212ffa0b24bb7d749532425a46764433)
- [Process] targeted runtime process – /usr/bin/sshd (backdoor checks process name and aborts if not sshd)
- [Detections / signatures] vendor detections and YARA – HEUR:Trojan.Script.XZ, Trojan.Shell.XZ, MEM:Trojan.Linux.XZ, and provided YARA rule liblzma_get_cpuid_function
<li/[URL] distribution source – xz.tukaani.org (tainted source archives and build-to-host.m4)
The technical infection chain began with malicious commits that added two test files and a build helper; when distributions downloaded source archives from the attacker-controlled xz.tukaani.org, the build-to-host.m4 executed a deobfuscation pipeline that fixed and decompressed bad-3-corrupt_lzma2.xz to run a shell stage. That shell stage used head/tail/tr pipelines to extract a second embedded blob from good-large_compressed.lzma, applied a custom RC4-like decryption, decompressed an inner LZMA stream, stripped header bytes and wrote the result as liblzma_la-crc64-fast.o so it would be linked into liblzma during compilation.
During source modification the build scripts also altered crc_x86_clmul.h to replace a call to __get_cpuid with _get_cpuid so the compiled library would resolve the attacker-provided symbol in the injected object file. At runtime the object leverages IFUNC-based selection (lzma_crc32/lzma_crc64) and, after an initialization counter, locates the GOT and swaps cpuid pointers so subsequent IFUNC resolutions invoke the backdoor code instead of the GLIBC intrinsic.
Once active the backdoor performs environment checks (process name must be /usr/bin/sshd and absence of a kill-switch variable), resolves many symbols via a trie-based resolver, and patches the dynamic linker’s audit interface (dl_audit/symbind64) to obtain addresses for target functions. Those hooks allow it to intercept and monitor libcrypto functions (RSA_public_decrypt, EVP_PKEY_set1_RSA, RSA_get0_key) within sshd, enabling stealthy observation or manipulation of SSH sessions.
Read more: https://securelist.com/xz-backdoor-story-part-1/112354/