QNAP QSA-22-15 : PhotoStation - Application Privileges Checking Bypass

CVE CVE-2021-44057 QNAP QSA-22-15

QTS vQTS 4.4.3.1444 PhotoStation 6.0.19

Timeline (DD/MM/YYYY)

CVE-2021-44057 - Application Privileges Checking Bypass

A Simple user with no privileges can access a few applications by bypassing the privilege checking system. The behavior occurs at least on Video Station and Photo Station, but other applications may be impacted.

The following exploitation show how to bypass privilege checking for the VideoStation application.

Preparation

We need to create a user without any privileges to exploit the vulnerability.

image-20220130181828547

Exploitation

First, we can try to connect on Video Station with the “dummy user

image-20220130182126516

Here, the privilege checking denies access.

Then, we can try to get a valid “NAS_SID” for this user by connecting to the QTS dashboard.

image-20220130182428605

Once connected, we got a valid “NAS_SID” and can try to access Video Station by using this token.

image-20220130182543791

The application access is still denied.

During the authentication process with this kind of token. A request is done on the “/cgi-bin/authLogin.cgi” endpoint. Some parameters are added to this request :

//File: /api/user.php
CHECK_CSRF();
$auth_by = 'qts';
$baseURL = "$protocol://127.0.0.1:".$port."/cgi-bin/authLogin.cgi?sid=".$NAS_SID;
$loginURL = $baseURL."&service=101&remote_ip=".$SESSION->ip;

The variable “$NAS_SID” is defined by using this code :

if (isset($_COOKIE['NAS_SID']) || isset($_COOKIE['QTS_SSID']) || isset($_COOKIE['QTS_SSL_SSID'])) {
	if (empty($_SERVER['HTTPS'])) {
		$NAS_SID = $_COOKIE['QTS_SSID'] ?  $_COOKIE['QTS_SSID'] : $_COOKIE['NAS_SID'];
	} else { //https connection
		$NAS_SID = $_COOKIE['QTS_SSL_SSID'] ?  $_COOKIE['QTS_SSL_SSID'] : $_COOKIE['NAS_SID'];
	}
} else {
	$NAS_SID = '';
}

The function “simplexml_load_file” load an XML document at the provided URL. The “NAS_SID” is the first parameter and can be used to comment on all next parameters. The privilege checking parameter is the last.

Thus, to bypass the security, we can use the character “#” at the end of our “NAS_ID” token. The URL will be the following:

http://127.0.0.1:8080/cgi-bin/authLogin.cgi?sid=4b4eedkl#&service=103&remote_ip=&check_privilege=VIDEO_STATION

All parameters after the “#” will be ignored and the final authentication URL is :

http://127.0.0.1:8080/cgi-bin/authLogin.cgi?sid=4b4eedkl

image-20220130183541479

We can now use the Video Station application as usual.

image-20220130183634840

Remediation

To fix this issue, the function “rawurlencode” can be used to sanitize the input while using in concatenation with the URL. The fix should be applied to each location where “$NAS_SID” is concatenated to an URL.

//File: api/libs/inc_common.php
if(!$IS_LOGIN && empty($SID) && !empty($NAS_SID)){
			//try to validate with NAS SID
			$port = exec('/bin/cat /var/lock/._thttpd_.port');
			$baseURL = "http://127.0.0.1:".$port."/cgi-bin/authLogin.cgi?sid=".rawurlencode($NAS_SID)."&service=103&remote_ip=".getClientIP();
			if($_SESSION['NASVARS']['application_privilege'] == 'TRUE')
				$loginURL = $baseURL."&check_privilege=VIDEO_STATION";

			$xml = simplexml_load_file($loginURL);
// File: api/libs/user.php
}else if(!empty($NAS_SID)) {

					CHECK_CSRF();

					$auth_by = 'qts';
					$baseURL = "$protocol://127.0.0.1:".$port."/cgi-bin/authLogin.cgi?sid=".rawurlencode($NAS_SID);
					$loginURL = $baseURL."&service=103&remote_ip=".getClientIP();
				}else{
					$auth_by = 'nobody';
					$loginURL = "";
				}