Vulnérabilité RCE dans qBittorrent : 14 ans d’erreurs de vérification des certificats SSL ignorées
(sharpsec.run)- Le DownloadManager de qBittorrent a ignoré les erreurs de vérification des certificats SSL pendant environ 14 ans et 6 mois, du 6 avril 2010 au 12 octobre 2024, exposant plusieurs flux de téléchargement à une surface d’attaque en cas d’interception réseau
- Le problème a reçu l’identifiant CVE-2024-51774 et son exploitation nécessite un accès MITM ou un spoofing DNS
- Les parcours utilisant DownloadManager, comme la recherche, le téléchargement de
.torrent, les flux RSS ou le téléchargement de favicons, étaient affectés, et pouvaient accepter des certificats expirés ou auto-signés - Sous Windows, le prompt d’installation de Python et le flux RSS de vérification des mises à jour peuvent mener au téléchargement d’exécutables ou à la manipulation de liens de mise à jour si la réponse à des URL codées en dur est modifiée
- Il est plus sûr pour les utilisateurs de télécharger manuellement v5.0.1 directement dans le navigateur pour effectuer la mise à niveau, plutôt que d’utiliser le prompt de mise à jour intégré à l’application
Contournement de la vérification des certificats pendant plus de 14 ans
- La classe DownloadManager de qBittorrent ignore toutes les erreurs de vérification des certificats SSL depuis le commit
9824d86du 6 avril 2010 - Le comportement par défaut a été modifié dans le commit
3d9e971du 12 octobre 2024 pour vérifier correctement les certificats - Le premier correctif publié est qBittorrent v5.0.1, sorti 2 jours avant la rédaction de l’article
- Le problème est référencé sous CVE-2024-51774
- Les conditions d’exploitation sont un accès MITM ou un spoofing DNS
Une large surface d’attaque créée par DownloadManager
- Comme DownloadManager est utilisé dans de nombreux cas, la recherche, le téléchargement de
.torrent, les flux RSS et le téléchargement de favicons sont concernés - Ces parcours pouvaient accepter des certificats expirés, des certificats auto-signés, ou les deux
- Les principaux vecteurs d’impact se répartissent entre l’installation de Python sous Windows, les mises à jour logicielles, les flux RSS et la surface d’attaque liée à la décompression
Téléchargement d’exécutable via le flux d’installation de Python sous Windows
- Sous Windows, si une version suffisamment récente de Python n’est pas installée, qBittorrent affiche une fenêtre proposant d’installer ou de mettre à jour Python pour utiliser les plugins de recherche
- Si l’utilisateur clique sur Yes, sélectionné par défaut, ou appuie sur Entrée, le programme télécharge l’installateur Python depuis une URL
python.orgcodée en dur - Après le téléchargement, qBittorrent renomme le fichier en
.exe, l’exécute, puis supprime le fichier temporaire une fois l’opération terminée - Ce comportement existe depuis juin 2015 jusqu’à aujourd’hui et affecte les versions de
v3.2.1àv5.0.0 - Sur les autres variantes d’OS, si Python est absent ou pas assez récent, le widget de recherche est désactivé, et ce comportement ne semble pas reproductible
- L’exécutable peut par exemple être enregistré dans un chemin comme
C:\Users\_user_\AppData\Local\Temp\is-G61QK.tmp - Cela peut être lié au comportement de
QProcess, mais deux threads du processus exécutable peuvent être créés, un seul se terminant tandis que l’autre reste en état de veille
Manipulation d’URL via le RSS de vérification des mises à jour
- Les versions Windows ou Linux installées de qBittorrent effectuent par défaut une vérification des mises à jour au lancement si ce n’est pas un fichier
appImage - La vérification télécharge un document XML depuis une URL RSS Fosshub codée en dur et parse les informations de publication du programme
- Si le XML contient une version supérieure à celle en cours d’exécution, le programme extrait l’URL de mise à jour puis demande à l’utilisateur de la visiter, sans filtrage ni vérification supplémentaires
- Si l’utilisateur accepte le prompt, l’URL est ouverte dans le navigateur par défaut
- L’utilisateur s’attend alors à recevoir un fichier
.exedans un contexte de confiance, puisque le lien est fourni par le logiciel - Si un attaquant redirige vers un site de partage de fichiers comme mediafire, l’utilisateur peut télécharger un exécutable contrôlé par l’attaquant en croyant effectuer une mise à jour
- Comme qBittorrent est open source, il est facile d’ajouter des fonctions dérobées à la dernière version puis de la recompiler
- Les développeurs fournissent bien des clés pour vérifier les signatures des binaires, mais la protection n’est effective que si l’utilisateur effectue réellement cette vérification et respecte un échec de vérification
Injection dans les flux RSS et les liens
- Tous les flux RSS analysés par l’application passent par DownloadManager et peuvent donc être détournés
- Les URL visitées par la victime peuvent être observées et listées, et les URL RSS ont par nature tendance à être statiques et durables
- L’élément
linkde chaque entrée est parsé directement et peut être téléchargé si l’utilisateur double-clique dessus - Même sans modification MITM, une URL arbitraire insérée dans un flux RSS auquel l’utilisateur est abonné, par son auteur ou via une compromission du flux, peut être ouverte d’un simple double-clic
- CVE-2019-13640 est une vulnérabilité qui permettait l’exécution de commandes à distance via des métacaractères shell dans le nom du torrent ou les paramètres actuels du tracker, et un attaquant MITM pouvant injecter des données malveillantes dans un flux RSS augmente le risque combiné
Base de données GeoIP, téléchargement et surface d’attaque liée à la décompression
- Au lancement, qBittorrent télécharge automatiquement par défaut une base de données binaire MaxMind GeoIP avec l’extension
.gzdepuis une URL codée en dur, puis la décompresse - S’il existait une vulnérabilité dans la décompression zlib, un attaquant pourrait viser cette surface avec un fichier arbitraire allant jusqu’à 64 Mo
- L’exemple CVE-2022-37434 est un buffer overflow critique noté CVSS 9.8 en 2022, mais il ne s’applique pas ici car la fonction
inflateGetHeader()n’est pas appelée - Le code de parsing de la base binaire MaxMind est bien protégé en 2024, mais cela n’a pas toujours été le cas et une surface d’attaque supplémentaire a pu exister auparavant
- Dans un commit de la fonction
gzip::decompress(), le buffer cible est passé d’une allocation statique sur la pile à une allocation dynamique sur le tas, avec une vérification avant écriture, ce qui ne le rendait pas exploitable
Scénarios d’attaque automatisables
- L’URL du fichier d’installation de Python et celle du flux RSS de mise à jour sont toutes deux codées en dur, ce qui permet à un script malveillant en environnement MITM d’énumérer les versions vulnérables et de les attaquer
- L’URL du flux RSS n’a aucune raison d’être visitée en dehors de l’exécution de qBittorrent, ce qui permet de l’utiliser comme empreinte logicielle
- En l’absence de vérification des certificats, l’attaquant peut usurper le serveur cible sans déploiement complexe de type Man-On-The-Side comme QUANTUM
- Comme les URL sont codées en dur, il est possible d’intercepter sélectivement uniquement les requêtes non vérifiées de qBittorrent, sans alerter la victime via des échecs de connexion sécurisée dans le navigateur ou d’autres applications
- En combinant l’option
-sde mitmproxy avec un script Python, il est possible d’automatiser les actions suivantes- Remplacer le
.exede Python par un exécutable arbitraire : RCE en un seul clic - Remplacer automatiquement l’URL du RSS de mise à jour qBittorrent : détournement du navigateur / RCE, avec un niveau d’interaction utilisateur modéré
- Remplacer tout ou partie des liens dans la visionneuse RSS de qBittorrent : RCE jusqu’en 2019, détournement de téléchargement
- Remplacer le
Mise à niveau et contournements
- Il faut passer à qBittorrent v5.0.1
- Il est recommandé d’effectuer la mise à niveau non pas via le prompt intégré à l’application, mais via un téléchargement manuel direct dans le navigateur
- En alternative, il est possible d’utiliser des clients comme Deluge ou Transmission, non affectés par cette vulnérabilité
- Il est difficile de considérer les attaques nécessitant un MITM comme purement théoriques, au vu de l’histoire récente et de cas concrets dans certains pays
- Le mainteneur du dépôt a bien été contacté, mais aucune réponse n’a été obtenue quant à l’intention de publier un avis de sécurité GitHub
1 commentaires
Avis sur Hacker News
La classe DownloadManager de qBittorrent a ignoré toutes les erreurs de validation de certificat SSL sur toutes les plateformes pendant 14 ans et 6 mois, depuis le commit 9824d86 du 6 avril 2010
Ça paraît assez grave.
Ce qui frappe, c’est que ce n’était pas un bug mais une « fonctionnalité »
void downloadThread::ignoreSslErrors(QNetworkReply* reply,QList errors) {
// Ignore all SSL errors
reply->ignoreSslErrors(errors);
}
https://github.com/qbittorrent/qBittorrent/commit/9824d86a3c...
Sans vouloir minimiser cette faille, compter sur la seule combinaison d’un certificat TLS valide et d’un nom de domaine comme unique ligne de défense sur un chemin permettant une exécution de code à distance me semble proche de la catastrophe
Au minimum, si une application télécharge puis exécute un artefact depuis Internet, elle devrait être épinglée à une version précise et vérifier le hash du fichier téléchargé avant l’exécution.
Donc cela veut dire que les mises à jour automatiques sont impossibles ?
À mon avis, le minimum, c’est une vérification de signature
Si le logiciel est mis à jour assez souvent, il y aura aussi suffisamment d’occasions de faire une rotation de clés.
Sous Windows, on peut utiliser un certificat de signature de code dans l’outil de build et laisser le système d’exploitation vérifier le binaire téléchargé
En revanche, il faut absolument utiliser un serveur d’horodatage pour la signature de code, sinon tout casse une fois le certificat expiré.
Ici, à cause de la nature potentielle d’une attaque de l’homme du milieu, j’ai l’impression qu’un contrôle de hash est difficile
Si l’attaquant peut voir et modifier le contenu de la requête, alors la vérification du hash ne sert plus guère qu’à confirmer l’intégrité.
De manière générale, la façon dont les applications desktop envoient des requêtes d’auto-update ou de vérification vers un domaine précis ne semble pas recevoir assez d’attention du point de vue de la sécurité
Il y a aussi des scénarios où l’auteur d’origine cesse de renouveler le domaine et où celui-ci est récupéré de manière hostile, et je n’ai toujours pas trouvé de bonne solution.
Ce serait surprenant de pouvoir savoir combien de personnes ont réellement été touchées par ce problème sur les quelque 15 dernières années
Je me demande à quel point la validation SSL était importante pour un attaquant capable de se fondre dans la foule, même dans les zones un peu douteuses d’Internet
Si tant de choses « fonctionnent tout simplement », c’est parce que personne n’y prête attention, et maintenant que ce problème a été mis en lumière, la situation ne peut que se dégrader pour ceux qui ne font pas de mise à jour automatique.
Ce code servait à télécharger Python depuis python.org, et même si l’exploitation était possible, elle devait être assez ciblée et supposait probablement déjà un certain niveau d’accès à la cible
Pour l’exploiter autrement, il aurait fallu quelque chose comme une prise de contrôle du domaine python.org, ce que tout le monde aurait probablement remarqué.
Si quelqu’un a été touché, il s’agissait probablement d’une attaque ciblée.
Une bonne partie de l’article me semble exagérée ; il y a bien un problème, mais parler de « qBittorrent exécution de code à distance » est techniquement exact tout en étant inutilement alarmiste.
Obtenir de vraies données semble quasiment impossible, mais ce serait amusant à voir.
Si vous avez déjà regardé le code de qBittorrent, vous savez que c’était vraiment un code affreux
Des fonctions sans commentaires s’étalaient sur plusieurs pages, avec un interlignage serré, et l’ensemble ressemblait à des notes de prison d’un patient psychotique injustement interné
Même dans la petite portion que j’ai vue, quelques pages compactées, il n’y avait absolument aucun contrôle des valeurs de retour.
Si un certain champ recevait la valeur correcte sur 3 lettres au lieu de la valeur correcte habituelle sur 2 lettres, le programme plantait au bout d’environ une minute, de mémoire
Il m’est arrivé deux fois d’être payé pour programmer en C++ il y a une vingtaine d’années, et après un message du mainteneur du genre « allez voir par vous-même », je l’ai lancé dans un débogueur
J’ai remonté jusqu’au point où l’interface lisait à la fois la mauvaise et la bonne valeur, puis en suivant cette valeur, je me suis retrouvé avec un -1 propagé un peu partout, tandis que le programme continuait tranquillement pendant un bon moment.
Au final, le chemin exécuté avec la mauvaise valeur a fini par planter dans une fonction assez éloignée, avec un message d’erreur rappelant les propos d’un psychotique injustement interné
Ensuite, l’un des vrais développeurs de qBittorrent l’a corrigé dans la version suivante, mais bref, c’était ce genre de code.
Il est simplement écrit « BUGFIX: Don't ignore SSL errors (sledgehammer999) »
https://www.qbittorrent.org/news
Personnellement, je pense qu’il devrait y avoir un avis de sécurité.
Même avec une vérification correcte des certificats, télécharger puis exécuter un exécutable distant est, par définition, une faille d’exécution de code à distance
Syncthing fonctionne aussi comme ça ; il vérifie probablement les certificats, mais les mises à jour automatiques sans intervention sont, sur le plan logique, difficiles à distinguer d’un RAT/trojan.
Le terme de faille ne s’applique que lorsqu’un tiers peut l’exploiter
Comme il faut de toute façon faire confiance à l’éditeur du logiciel, l’auto-update en soi n’est pas une faille d’exécution de code à distance.
Même si ce n’était pas la cause directe de cette faille, des applications de ce type, qui communiquent avec un grand nombre de nœuds potentiellement malveillants, profiteraient sans doute énormément de la sûreté mémoire
Or, les implémentations actuelles semblent toutes écrites en C++
L’article aborde aussi ce type de risque de faille au point 4.
Même Deluge, pourtant écrit en Python, dépend de
libtorrenten C++Je ne sais pas s’il existe un fork moderne de l’ancien client Azureus basé sur Java
De nos jours, beaucoup de clients BitTorrent séparent l’interface graphique et le processus démon qui gère réellement les torrents, donc faire le démon en Java et le connecter à une GUI native pourrait offrir un bon équilibre entre sécurité, performances et expérience utilisateur
Même en cherchant vite fait, on trouve quelques bibliothèques BitTorrent pures en Go
Il y a rqbit, écrit en Rust et sans dépendance à
libtorrent: https://github.com/ikatson/rqbitPlutôt que de chercher moi-même pour lancer la discussion, je vais poser une question de paresseux : par où commencerait-on pour créer une alternative à
libtorrent?Je serais aussi curieux de savoir s’il y a eu des tentatives ou des réussites, et s’il existe des clients réellement fonctionnels qui utilisent une autre implémentation
Surtout vers la fin de la liste, ça paraît un peu exagéré
Le point 1, « chargeur d’exécutable malveillant avec fonctionnalité furtive », revient à dire que le client télécharge Python depuis python.org en HTTPS
Ce n’est pas bien, et le fait que ce soit codé en dur sur la version 3.12.4 est aussi problématique, mais je ne vois pas de voie d’exploitation claire qui ne nécessiterait ni attaque de l’homme du milieu ni interaction de l’utilisateur
Le point 2, « détournement de navigateur + téléchargement d’exécutable », revient à dire que le client télécharge un fichier RSS en HTTPS et demande éventuellement à l’utilisateur s’il faut ouvrir l’URL contenue dans ce fichier
C’est encore moins risqué que le point 1, et même si on mène une attaque de l’homme du milieu contre l’utilisateur et qu’on le fait cliquer sur « update », au final tout ce qu’on peut lui montrer, c’est une page web
Le point 3, « injection d’URL arbitraire dans un flux RSS », donne l’impression que le chercheur a mal compris le comportement attendu d’un client RSS
Le point 4, « surface d’attaque de bibliothèque de décompression », signifie que si l’on pouvait trouver une vulnérabilité dans zlib, on pourrait faire bien pire qu’attaquer un client torrent
La décompression d’entrée est fondamentalement supposée être une opération sûre
On pense tout de suite à quelque chose comme un serveur HTTP prenant en charge la compression de flux
Exact
Je ne veux pas être trop négatif, mais on dirait que certains “chercheurs” en sécurité s’accrochent même aux possibilités les plus improbables pour grappiller un peu de notoriété
J’aurais eu plus de respect si c’avait été documenté honnêtement, mais la manière employée ici fait surtout lever les yeux au ciel
S’ils n’avaient pas ignoré les erreurs de certificat, ces points auraient été mineurs
Le vrai problème étant l’ignorance des erreurs SSL, tous les téléchargements HTTPS ont en pratique été rétrogradés au niveau de téléchargements HTTP, et l’attaque reste possible même sans homme du milieu
Autrement dit, l’absence de validation SSL a donné aux évaluateurs un faux sentiment de sécurité en leur faisant croire que les URL HTTPS étaient sûres
D’accord
Appeler ça une « vulnérabilité d’exécution de code à distance » est absurdement exagéré
La prochaine étape, ce sera quoi : affirmer qu’un navigateur web a une « vulnérabilité d’exécution de code à distance » parce qu’il permet de télécharger puis d’exécuter des programmes depuis des sites arbitraires, potentiellement sûrs ou non ?
Ou bien republier la découverte qu’en vivant dans un régime autoritaire, l’État peut faire de mauvaises choses ?
qBittorrent est un client 1000 fois plus performant que les autres options citées dans l’article, donc c’est choquant de voir un niveau aussi faible dans la qualité de ces problèmes
Ce qui apporte les performances, c’est libtorrent-rasterbar (libtorrent.org)
Pour compiler et lancer la dernière version, https://github.com/userdocs/qbittorrent-nox-static est un script auxiliaire correct qui construit un binaire statique avec Docker
Je voulais faire tourner la 5.0.0 avec
libtorrent1.2, et ce script a été de très loin la solution la plus simple