
Une collection de vulnérabilités et d'exploits contre les GCS modernes
Vecteurs d'attaque, vulnérabilités et exploits de la station de contrôle au sol vers le drone
Les flottes de drones d'aujourd'hui, c'est un opérateur pilotant des dizaines ou des centaines de drones depuis une seule station au sol. Cela fait de la station de contrôle au sol une cible précieuse pour les adversaires. C'est là que se trouve généralement le pilote, elle stocke souvent les données de mission, et c'est un vecteur de choix pour le mouvement latéral à travers les réseaux UxS et les autres drones d'une flotte.
La plupart des recherches sur la sécurité des drones se sont concentrées sur le ciblage du drone. La recherche Infected Drone adopte une approche alternative et met en évidence comment un seul drone compromis peut attaquer les stations au sol qui s'y connectent. Comme la plupart des logiciels de contrôle au sol font confiance à tout ce que le drone leur envoie, il y a un manque d'authentification, de validation et de nettoyage, ce qui permet aux données d'un drone compromis d'entraîner des opérations CRUD sur fichiers, une exécution de code ou un plantage sur la machine de l'opérateur.
Ce dépôt documente des vulnérabilités dans les logiciels de station de contrôle au sol et fournit du code de preuve de concept fonctionnel pour celles-ci. Il est publié à des fins éducatives uniquement et pour que les opérateurs comprennent leur exposition et que les mainteneurs puissent reproduire et corriger ces problèmes.
N'exécutez les PoC que contre des systèmes que vous possédez ou pour lesquels vous avez une autorisation écrite de test. Chacun d'eux est écrit pour un banc d'essai : les charges utiles sont des marqueurs bénins, et rien ici n'est packagé pour être utilisé contre l'aéronef ou la station au sol de quelqu'un d'autre. Utiliser ce matériel contre des systèmes que vous ne contrôlez pas est probablement illégal où que vous soyez.
La section Reproduction de chaque découverte indique ce dont elle a besoin et ce qu'elle fait. Lisez-la avant d'exécuter quoi que ce soit.
Légende :
La classe de livraison est ce que l'attaquant doit faire sur la liaison, et elle détermine quels vecteurs fonctionnent. Les découvertes Push nécessitent uniquement qu'une trame arrive à la GCS, donc tout vecteur capable d'injection suffit. Les découvertes Handshake et request/response nécessitent que l'attaquant soit, ou contrôle entièrement, le pair conversationnel, ce qui favorise un périphérique sur le bus, un companion compromis, la chaîne d'approvisionnement, ou un MITM complet.
La colonne Fix renvoie à la pull request amont lorsqu'une a été soumise. Dix des quinze découvertes sont livrées avec un correctif déposé contre le dépôt du fournisseur lui-même.
La matrice évalue sept colonnes par découverte : les cinq vecteurs ci-dessous, plus les deux modes de radio SiK (télémétrie RF), qui font l'objet d'une mention spécifique ci-dessous compte tenu de la nuance injection-vs-MITM.
Contrôleur de vol infecté, connexion série, ou chaîne d'approvisionnement. Un attaquant local ou un contrôleur de vol malveillant est physiquement connecté à l'hôte GCS ou à la radio. Le véhicule ou le firmware est quelque chose que l'opérateur n'a pas construit : une unité de démonstration, une location, un aéronef saisi, ou un appareil d'occasion, dont le firmware est implanté pour émettre du MAVLink hostile dès qu'une GCS se connecte. La GCS propre de l'opérateur est la victime ; le « véhicule » était hostile avant même d'être mis sous tension. S'applique à chaque découverte, et c'est la façon la plus propre de livrer des exploits de handshake au moment de la connexion. Cela s'étend aux analystes forensiques qui peuvent se connecter directement à un véhicule infecté ou en extraire des données. Ces artefacts, s'ils ne sont pas correctement traités, pourraient infecter ou se propager à l'ordinateur et au réseau de l'analyste.
Périphérique MAVLink malveillant / contrefait sur le bus propre du véhicule. Une caméra tierce, un cardan, un ADSB-in, un télémètre, ou tout appareil qui parle MAVLink et est lui-même l'attaquant. C'est un participant légitime sur la liaison qui émet des trames hostiles. Une « caméra intelligente » contrefaite annonçant des messages empoisonnés fait exactement ce que fait une vraie, juste avec des valeurs hostiles.
Ordinateur companion compromis à bord (Raspberry Pi / Jetson exécutant mavlink-router / MAVProxy). Une fois compromis, il devient le point de terminaison du véhicule, avec un accès bidirectionnel complet à la liaison et une visibilité sur son état en direct. Il peut répondre à n'importe quel handshake et émettre n'importe quelle trame push, ce qui le rend viable pour chaque découverte de cet ensemble.
Bridge de télémétrie WiFi / UDP (ESP8266 / ESP32 « wifi telemetry »). Quiconque sur le point d'accès ou le LAN peut injecter des données MAVLink. Cela réduit le coût des vecteurs d'injection à presque zéro et, pour un attaquant qui peut aussi intercepter (ARP/AP MITM), permet aussi un contrôle total du handshake.
Relais TCP / cloud (SITL, mavlink-router TCP, mavp2p, GCS cloud 4G/LTE tels que les services UAV-cloud commerciaux). Un MITM au niveau du relais, ou quiconque peut atteindre le port TCP exposé, peut réécrire le flux. Les liaisons cloud/4G élargissent énormément le rayon d'impact géographique et manquent fréquemment d'authentification mutuelle.
La voie de télémétrie RF réelle dominante pour les appareils ArduPilot/PX4 de loisir et prosumer est une paire de radios SiK, un bridge série transparent qui ne parse ni ne valide le MAVLink, n'offrant donc à la GCS aucune protection contre un contenu hostile. Un module SiK malveillant rejoint ou ponte une liaison existante à l'aide de sikw00f. Deux modes d'attaque, avec une fiabilité très différente :
STATUSTEXT, PARAM_VALUE,
CAMERA_INFORMATION, etc.), sans réponse nécessaire. Peu fiable pour les protocoles
de handshake (MAVFTP, téléchargement de param/log) puisque l'injecteur doit gagner une course
au temps d'antenne et correspondre à une session/séquence qu'il ne contrôle pas.Remarque : le chiffrement SiK (AES-128, là où il est pris en charge) est désactivé par défaut avec une clé partagée statique.
| ID | Produit | Sév | CWE | Classe de sink | Classe de livraison | FC infecté / chaîne d'approvisionnement ? | Périphérique malveillant sur le bus ? | Companion compromis ? | Injection WiFi/UDP ? | MITM TCP/cloud ? | Injection SiKW00F ? | SiK full-MITM ? | Fix |
|---|
| QGC-01 | QGroundControl | 🟠 HIGH | 22/73/170 | CAMERA_INFORMATION → écriture par traversée de chemin (écriture zero-click à la connexion) | push (CAMERA_INFORMATION) + contenu via cam_definition_uri | ✅ | ✅ le plus naturel (caméra) | ✅ | ✅ | ✅ | ✅ le déclencheur est un push ; champs de nom en ligne | ✅ | PR #15083 |
| QGC-02 | QGroundControl | 🔴 CRITICAL | 22/73/494/918 | COMPONENT_METADATA uri → écriture de fichier arbitraire → RCE zero-click à la connexion | request/response (récupération d'URI via IP) | ✅ | ✅ émet une uri de métadonnées | ✅ | ✅ pour le déclencheur | ✅ | ⚠️ déclencheur push oui ; la récupération se fait via IP | ✅ | PR #15084 |
| QGC-03 | QGroundControl | 🟠 HIGH | 22/770 | Listing FTP → écriture par traversée + remplissage disque par offset | handshake (listing/téléchargement MAVFTP) | ✅ | ✅ le pair répond au FTP | ✅ | ⚠️ injection oui, propre handshake plus difficile | ✅ | ⚠️ session/seq soumis à course | ✅ le bridge sert le listing | PR #15085 |
| QGC-04 | QGroundControl | 🟡 MED | 191/125 | Parseur DataFlash .bin lecture OOB + underflow | handshake (téléchargement de log d'un .bin malveillant) | ✅ | ✅ le pair sert le DataFlash | ✅ | ⚠️ | ✅ | ⚠️ doit servir les chunks de log | ✅ le bridge alimente le .bin | PR #15086 |
| MP-01 | Mission Planner | 🟠 HIGH | 22→94 | Traversée MAVFTP → RCE via plugin-loader | handshake (req/ack MAVFTP → RCE) | ✅ | ✅ le pair répond au listing | ✅ contrôle total de la liaison | ⚠️ injection facile, mais il faut posséder le handshake | ✅ réécriture au relais | ⚠️ doit gagner la course + correspondre session/seq | ✅ le bridge possède la conversation FTP | PR #3774 |
| MP-02 | Mission Planner | 🟠 HIGH | 94/78 | gst:// → gst_parse_launch (lecture/écriture de fichier + exfiltration) | push (VIDEO_STREAM_INFORMATION → gst) | ✅ | ✅ la caméra annonce l'uri | ✅ | ✅ datagramme UDP | ✅ | ✅ trame de flux one-shot | ✅ trivialement | PR #3775 |
| MP-03 | Mission Planner | 🟠 HIGH | 74/601 | Balisage STATUSTEXT → Process.Start | push (STATUSTEXT → ShellExecute) | ✅ | ✅ n'importe quel composant émet | ✅ | ✅ | ✅ | ✅ texte fire-and-forget | ✅ | PR #3776 |
| MP-04 | Mission Planner | 🟡 MED | 248/20/1050 | RALLY_TOTAL/FENCE_TOTAL → int.Parse non validé + O(n²) sur le thread UI (zero-click à la connexion) | push (PARAM_VALUE, auto post-connexion) | ✅ | ✅ émet PARAM_VALUE | ✅ | ✅ | ✅ | ⚠️ doit faire atterrir le param dans le dict de MP | ✅ | PR #3777 |
| MAVSDK-01 | MAVSDK | 🟡 MED | 918/749 | SSRF curl de métadonnées de composant + file:// (pas de liste blanche de protocole) | request/response (récupération de COMPONENT_METADATA.uri via IP) | ✅ | ✅ émet une uri de métadonnées | ✅ | ✅ pour le déclencheur | ✅ | ⚠️ déclencheur push oui ; récupération via IP | ✅ | aucun |
| MAVSDK-02 | MAVSDK | 🟠 HIGH | 22/73/918 | SSRF de cam-definition + traversée mftp:// → destruction zero-click d'un fichier hors du bac à sable | push (CAMERA_INFORMATION.cam_definition_uri, auto) | ✅ | ✅ le plus naturel (caméra) | ✅ | ✅ | ✅ | ✅ push auto-consommé | ✅ | PR #3073 |
| MAVSDK-03 | MAVSDK | 🟠 HIGH | 409/400/770/459 | cam-definition .xz ou COMPONENT_METADATA → décompression non bornée → épuisement persistant du disque | push (CAMERA_INFORMATION.cam_definition_uri, auto) — un simple HEARTBEAT le déclenche | ✅ | ✅ le plus naturel (caméra) | ✅ | ✅ | ✅ | ✅ push auto-consommé ; ~50 s de temps d'antenne à 57,6 kbps par 2 GiB | ✅ | PR #3074 |
| MAVPROXY-01 | MAVProxy | 🟠 HIGH | 502 | pickle.loads asterix via UDP → RCE | canal latéral IP (UDP, PAS la liaison RF MAVLink) | ❌ pas sur la liaison MAVLink | ❌ socket UDP séparé | ⚠️ seulement s'il peut atteindre :45454 | ✅ UDP vers host:45454 | ❌ socket UDP propre, pas le relais | ❌ pas sur la liaison RF/MAVLink | ❌ pas sur la liaison RF/MAVLink | corrigé en amont par #1728, non publié |
| MAVROS-01 | mavros | 🟡 MED | 345/770 | PARAM_VALUE → /parameter_events global falsifié + map non plafonnée | push/stream (injecter PARAM_VALUE, sans handshake) | ✅ | ✅ émet PARAM_VALUE | ✅ | ✅ flood UDP | ✅ | ✅ flood de PARAM_VALUE | ✅ | aucun |
| MAVROS-02 | mavros | 🟠 HIGH | 125/617/248 | write-ack FTP → std::advance non borné → divulgation du tas + abandon du processus | handshake (empoisonner la réponse FTP → crash) | ✅ | ✅ le pair envoie un mauvais FTP | ✅ | ⚠️ | ✅ | ⚠️ doit faire atterrir une réponse malformée | ✅ le bridge injecte une mauvaise réponse | aucun |
| DRONEKIT-01 | DroneKit | ⚪ INFO | 20 | passation de frontière de confiance (param_id / STATUSTEXT) | push (télémétrie → callbacks de l'app ; passation by-design) | ✅ | ✅ n'importe quel composant | ✅ | ✅ | ✅ | ✅ n'importe quelle trame push atteint le callback | ✅ | n/a |