QNAP QSA-22-15 : PhotoStation - Contournement du contrôle des privilèges de l'application

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

QTS vQTS 4.4.3.1444 PhotoStation 6.0.19

Timeline (DD/MM/YYYY)

CVE-2021-44056 - Contournement du contrôle des privilèges de l’application

Un utilisateur sans privilèges d’accès peut accéder à plusieurs applications en contournant le système de vérification d’accès. Ce contournement est présent au moins sur les application VideoStation et PhotoStation

Cette exploitation est pour l’application VideoStation mais le principe est identique pour PhotoStation

Préparation

Nous avons besoin d’un utilisateur sans aucun privilège pour exploiter cette vulnérabilité.

image-20220130181828547

Exploitation

Tout d’abord, nous vérifions que notre utilisateur n’a pas les droits d’accéder à l’application

image-20220130182126516

Ici, nous constatons le refus d’accès par l’application

Ensuite, nous avons besoin d’un cookie “NAS_SID” valide. Pour cela, il faut se connecter sur QTS.

image-20220130182428605

Une fois connecté, nous avons un cookie “NAS_SID” valide et nous pouvons essayer d’accéder à l’application.

image-20220130182543791

L’application nous refuse toujours l’accès

Pendant le processus d’authentification avec ce cookie “NAS_SID”, une requête est effectuée sur l’URL interne “/cgi-bin/authLogin.cgi”. Certains paramètres sont ajouté dont un permettant d’activer la vérification des privilèges d’accès.

//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=".$NAS_SID."&service=103&remote_ip=".getClientIP();
            if($_SESSION['NASVARS']['application_privilege'] == 'TRUE')
                $loginURL = $baseURL."&check_privilege=VIDEO_STATION";

            $xml = simplexml_load_file($loginURL);

La variable “$NAS_SID” est définie ici :

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'];
    }
    //https connection
    else {
        $NAS_SID = $_COOKIE['QTS_SSL_SSID'] ?  $_COOKIE['QTS_SSL_SSID'] : $_COOKIE['NAS_SID'];
    }
}
else {
    $NAS_SID = '';
}

La fonction “simplexml_load_file” charge la réponse en XML de la requête. Le “NAS_SID” est le premier paramètre et peut être modifié pour commenter tous les autres paramètres. La vérification des privilèges étant le dernier, celui-ci sera ignoré.

Ainsi, pour contourner cette sécurité, il est possible d’utiliser le caractère “#” à la fin de notre cookie “NAS_ID”.

La requête interne sera la suivante :

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

Tous les paramètres après le caractère “#” seront ignorés et la requête d’authentification finale sera :

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

image-20220130183541479

Nous pouvons alors utiliser l’application.

image-20220130183634840

Correction

Pour corriger cette vulnérabilité, la fonction “rawurlencode” peut être utilisées pour filtrer l’entrée utilisateur concaténée à l’URL.

Cette correction doit être appliquée à chaque endroit ou cette valeur “$NAS_SID” est ajoutée à l’URL de connexion.

//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 = "";
                }