QNAP QSA-22-14 : VideoStation - Plusieurs vulnérabilités
Timeline (DD/MM/YYYY)
- 31/01/2022 : Vulnérabilité envoyée à l’équipe de sécurité de QNAP
- 16/02/2022 : QNAP confirme la réception du rapport et assigne les CVE
- 23/02/2021 : Correction disponible avec VideoStation 5.5.9 / 5.3.13 / 5.1.8
- 06/05/2022 : Bulletin de sécurité disponible sur le site de QNAP
CVE-2021-44055 - Fuite du nom de fichier et de l’identifiant des vidéos.
Les utilisateurs non authentifiés peuvent récupérer les noms de fichiers des films.
La fonction “get_imdb_info” peut être utilisée sans authentification et permettre à un attaquant de trouver un “mediaId” valide et de récupérer le nom de la vidéo.
Cet id est généré avec cette fonction et peut être facilement énuméré :
public static function hash($num, $len = 6) {
$ceil = bcpow(62, $len);
$primes = array_keys(self::$golden_primes);
$prime = $primes[$len];
$dec = bcmod(bcmul($num, $prime), $ceil);
$hash = self::base62($dec);
return str_pad($hash, $len, "0", STR_PAD_LEFT);
}
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
Préparation
Nous avons besoin d’un utilisateur sans aucun privilège pour exploiter cette vulnérabilité.
Exploitation
Tout d’abord, nous vérifions que notre utilisateur n’a pas les droits d’accéder à l’application
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.
Une fois connecté, nous avons un cookie “NAS_SID” valide et nous pouvons essayer d’accéder à l’application.
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
Nous pouvons alors utiliser l’application.
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 = "";
}