Par Gabriel Campana (Sogeti/ESEC) : Sécuriser un programme dans un système embarqué
Leur faible puissance de calcul empêche les systèmes embarqués d'intégrer les dispositifs de sécurité classiques. Une difficulté qui sera dépassée en prenant certaines précautions lors de la phase de développement.
La principale caractéristique d'un système embarqué est d'être dédié à une tâche bien précise tout en étant soumis à de fortes contraintes (taille, coût, puissance, sûreté...). En conséquence, ses ressources sont généralement très limitées, et les mécanismes de sécurité diffèrent des systèmes classiques.
Une contrainte de faible puissance de calcul empêche l'intégration de dispositifs de sécurité présents sur les systèmes "classiques".
Par exemple, le mécanisme de mémoire virtuelle est absent sur de nombreux systèmes possédant ce type de contrainte : un accès à une adresse mémoire invalide dans un programme quelconque impactera le système dans son ensemble, le faisant immédiatement redémarrer dans la plupart des cas. Les développeurs doivent donc se focaliser particulièrement sur les problèmes de gestion de la mémoire, pouvant donner lieu à des attaques classiques de type dépassement de tampons.
Plus la taille du code composant le système est importante, plus la probabilité qu'une vulnérabilité soit présente est élevée. Ainsi, tout logiciel ou dépendance non nécessaire ne devrait pas être inclus dans le système : la taille du code doit être limitée au strict minimum, tout comme le nombre de services en écoute afin de réduire la surface d'attaque. Par ailleurs, les logiciels ne doivent pas être utilisés hors de leur contexte original. Une pile TCP/IP destinée originellement à un système embarqué dont l'usage est réservé à un réseau privé sûr n'a pas sa place dans un routeur.
La randomisation des adresses de piles et de tas rend le développement d'exploits plus complexe
Les services utilisés pendant la phase de développement doivent être supprimés lors de la mise en production. L'accès à ce type de services, comme un débuggueur distant par exemple, permettrait facilement à un attaquant de prendre le contrôle du système, sans même avoir à chercher une vulnérabilité.
De même, des binaires usuels comme bash ou ls n'ont pas forcément leur place sur un système de production, leur absence rendant la tâche de l'attaquant plus difficile.
Des mesures limitant la progression d'un attaquant peuvent être considérées lorsque la puissance de calcul n'est pas une des contraintes du système. Ainsi, une application tournant avec les droits d'un utilisateur sans privilège diminue l'importance d'une vulnérabilité. De même, des protections empêchant des accès en écriture et en exécution à une même page mémoire, ou la randomisation des adresses de piles et de tas rendent le développement d'exploits beaucoup plus complexe.
Une conception modulaire du système permettra d'isoler chaque fonctionnalité des autres, et offrira au système une sécurité supérieure à celle de son composant le plus faible. Un mécanisme de watchdog peut être implémenté pour vérifier à intervalle régulier le fonctionnement correct de certains programmes, ou encore l'intégrité de certaines zones mémoire.
Pour résumer, le système et l'ensemble des programmes devront être conçus en intégrant des concepts de sécurité dès le départ. L'éventuelle découverte de vulnérabilités devrait être, si possible, mitigée par des mécanismes de protection rendant le développement d'exploits complexe.
Enfin, tout programme non indispensable devra être supprimé.
Tribune réalisée par Gabriel Campana.
Gabriel Campanaest Ingénieur R&D au sein de Sogeti/ESEC