• Aucun résultat trouvé

1.3 Besoins en sécurité

1.3.7 Modélisation et vérication

if (x != y) 2 { 3 killcard(); 4 } 5 else 6 { 7 z = 1; 8 }

Cependant, cette forme de code défensive ne sécurise pas contre les scénarii d'attaque 2 et 3. Elle ne fonctionne pas non plus contre des sauts de multiples instructions consécutives. Par exemple, ne pas exécuter l'instruction d'appel à la fonction killcard() et le saut à la ligne 10

peut avantager l'attaquant si celui-ci cherche à exécuter le contenu de la branche else alors que la condition est fausse (x != y). Cette défense ne protège pas non plus contre les doubles attaques qui permettent, par exemple, le saut du contenu d'une branche (ligne 8) et le saut d'une instruction de branchement (ligne 10), ce qui, dans certains cas, peut être équivalent au saut de multiples instructions successives. Les doubles sauts sont cependant plus ecaces dans l'hypothèse d'une modication de code qui avantage l'attaquant suivi du saut de la contre-mesure associée.

La seule défense possible qui prend en compte les scénarii 1, 2 et 3 (sans pour autant prendre en compte les doubles sauts et certains sauts d'un grand nombre d'instructions successives) est de doubler la condition (si possible avec une implémentation diérente de la première) et de vérier la cohérence entre ces deux conditions. Cependant, cette méthode double au minimum le temps d'exécution ainsi que la mémoire nécessaire pour implémenter la condition.

Ce travail de dénition du gain pour l'attaquant, d'analyse des eets des fautes sur le com-portement du code et de lien entre l'eet et le gain devrait être fait sur l'ensemble des formes de code du système et une version défensive de cette forme de code devrait être utilisée lors d'une opération sensible. Dans le chapitre 5, nous proposons une méthode exhaustive an de tester dynamiquement les comportements possibles du code en injectant exhaustivement des attaques au niveau C mais aussi au niveau assembleur. Cette méthode permet d'évaluer la résistance des contre-mesures implémentées en réduisant au maximum le travail de dénition et d'analyse.

1.3.7 Modélisation et vérication

Dans un premier temps, an de pouvoir proposer des méthodes cherchant à garantir la sécurité de ces systèmes embarqués, nous proposons de formaliser des propriétés de sécurité exprimant les garanties de sécurité souhaitées. Dans un second temps, an de pouvoir cerner l'ensemble des problématiques liées à la sécurité embarquée, il convient d'établir un modèle des

capacités de l'attaquant, c'est-à-dire, sa capacité à modier le comportement fonctionnel du code par le biais d'une attaque physique. Grâce à ces éléments, nous proposerons des outils permettant l'analyse du code et la vérication de la sécurité de celui-ci.

Modélisation de la sécurité : une approche fonctionnelle

Une approche possible pour modéliser la sécurité est l'approche fonctionnelle. Les probléma-tiques de sécurité sont abordées à partir des fonctionnalités que doit accomplir le programme. L'avantage d'une telle approche est qu'elle reste générique par rapport au code et s'adapte à un environnement industriel. Un logiciel a toujours un cahier des charges décrivant ses fonctionna-lités. Une fois les tâches à accomplir par le programme connues, on peut facilement en déduire le gain pour un attaquant. Par exemple, si une tâche est d'authentier un utilisateur s'il présente un bon PIN, le gain pour l'attaquant est de s'authentier avec un mauvais PIN. De plus, on peut aisément, en lisant le cahier des charges, isoler les fonctionnalités sensibles sur lesquelles se concentrer et éliminer celles dont la sécurité est moins importante. Par conséquent, une re-présentation de la sécurité à ce niveau est adaptée à un contexte industriel par son caractère générique. En eet, on arrivera toujours à exprimer un besoin de sécurité à partir du cahier des charges du logiciel.

Finalité fonctionnelle Soit une transaction regroupant plusieurs commandes. Chaque com-mande peut être vue comme une fonctionnalité disposant d'une nalité. Cette nalité est géné-ralement présente dans la réponse renvoyée par la carte mais aussi dans les changements opérés au sein de la carte par l'exécution de la commande. L'ensemble de ces commandes mène à la nalité globale qui est l'aboutissement de la transaction représentant la tâche fonctionnelle glo-bale. En faisant l'hypothèse que la dernière réponse de la carte reète l'exécution d'un ensemble d'opérations internes relatives à la transaction, on peut en vériant cette réponse déduire que ces opérations internes se sont bien eectuées. Cette hypothèse est à la base des tests en boîte noires. Cette hypothèse revient aussi à dire que l'attaquant ne peut pas à la fois réaliser une attaque et perturber le mécanisme de gestion d'erreur et aussi qu'une gestion d'erreur est im-plémentée pour chaque opération critique au sein de la carte. La réponse nale à l'échelle d'une transaction, reète le déroulement d'opérations issues de commandes précédentes car dans une transaction (par exemple emv) ces commandes sont liées par des informations partagées. Pour notre modélisation nous prendrons une perspective à l'échelle d'une commande. Les propriétés de sécurité peuvent être établies sur des commandes ou à l'échelle d'une transaction.

Perspective et vision allégée Si on analyse le ot de contrôle du code source, on peut distinguer :

 des opérations ou calculs ;

 des appels de fonctions regroupant un ou plusieurs opérations et calculs. Ces fonctions peuvent être vues comme des boites avec des entrées et sorties ;

 des conditions.

Le listing 1.8illustre ce niveau d'abstraction en ne faisant apparaître que les deux derniers éléments mentionnés ci-dessus. Ainsi, une fois que le terminal a envoyé une requête à la carte, le code présent dans celle-ci s'exécute de manière autonome sans autre intervention de la part du terminal. Les seules informations modiant les décisions lors de l'évaluation des conditions

Listing 1.8 Requête schématique

1

# Terminal --envoi d’une requête--> Carte

2 # -in-> 3 F() 4 { 5 if (toto == titi) 6 { 7 f(); 8 } 9 } 10 #

<-out-du code se trouvent dans la requête d'origine. En gardant une représentation faisant seulement apparaître les éléments du graphe d'appels et les éléments du ot de contrôle, nous avons abs-trait le code présent qui s'exécute séquentiellement pour ne garder que les points de changement possibles dans la logique du programme. Ces points de décision sont clés dans la logique de fonc-tionnement du programme et représentent des points d'attaques particulièrement intéressants pour un attaquant.

Ces visions de l'exécution fonctionnelle permettent focaliser uniquement sur un sous ensemble du graphe d'appels total, i.e., les séquences d'appels se terminant par une nalité fonctionnelle. Les transitions à l'intérieur de ces séquences (conditions, appels) sont des points dans le code potentiellement vulnérables à une attaque physique car ce sont des points clés au niveau fonc-tionnel.

D'un point de vue de la sécurité, on peut considérer qu'une vérication, par exemple doubler une opération comme dans le listing1.9, déclenchera en cas de non respect une réponse sécuritaire et n'amènera jamais à appeler la fonction permettant de continuer dans le graphe d'appels vers la nalité. En eet, doubler une opération n'apporte rien dans le ot fonctionnel du code mais est uniquement présent pour protéger contre des attaques physiques.

Dans l'exemple 1.9, après une vérication de l'exécution correcte de calcul_crypto(), le ot du code retournera sur le chemin fonctionnel standard et eectuera une transition normale hors de la fonction F(). Par contre, si la condition if (res1 == res2) n'est pas vériée, le ot d'exécution standard est interrompu et bascule sur l'exécution du code d'une réponse sécuritaire. Cette perspective nous montre qu'il est important de considérer un point de vue particulier lorsqu'on s'intéresse à la sécurité fonctionnelle et que seule une partie du code sera pertinente dans la modélisation.

Expression du gain pour l'attaquant

Le gain pour l'attaquant est lié à l'utilisation faite de la carte à puce. Une carte à puce peut, par exemple, être utilisée dans le cadre d'une authentication entre plusieurs partis. Dans ce scénario, un attaquant dispose de plusieurs moyens pour exploiter les possibilités d'une at-taque physique. Nous décrivons dans la suite deux types d'exploitation suivant que l'atat-taque compromet des données ou modie le comportement de la carte.

Si on considère qu'une attaque permet d'obtenir des informations ou des données conden-tielles permettant d'authentier les partis (typiquement des clés d'authentication), le but de l'attaquant est de se faire passer pour un des partis an de se placer comme destinataire d'un

Listing 1.9 Exemple simple d'une fonction avec doublement sécuritaire qui n'a pas d'impact sur la nalité fonctionnelle

1

// on arrive fonctionnellement ici

2 // appel à F 3 4 void F(void) 5 { 6 res1 = calcul_crypto(); 7 8

// de peur d’être attaqué on double le calcul

9 res2 = doublement_calcul_crypto(); 10 11 if (res1 == res2) 12 { 13

// transition sur le chemin fonctionnel

14 continuer_fonctionnellement_vers_finalite(); 15 } 16 else 17 { 18 // non transition 19 killcard(); 20 } 21 }

transfert nancier et intercepter le montant transféré. Dans la pratique, l'attaquant fabrique des clones de la carte contenant les données condentielles obtenues malicieusement. Dans le cas d'un vol de carte, l'attaquant cherche en priorité à obtenir le code PIN du possesseur de la carte ce qui lui permettra de retirer de l'argent.

Si on considère qu'une attaque physique est capable de modier le comportement fonctionnel de la carte, le but de l'attaquant est de générer de l'argent (en ciblant les compteurs de montant), de monter en privilège dans des scénarii qui ne le demandent pas (en ciblant des variables d'état), de faire du rejeu de commande (en ciblant des variables de machines d'état), d'empêcher le verrouillage sécuritaire de la carte (en empêchant la mise à jour de variables d'état sécuritaire). Dans la pratique, l'attaquant a besoin de l'assistance d'un commerçant complice an de réussir son attaque dont le gain monétaire n'est pas immédiatement visible.

Cependant modier le comportement du code peut amener à une exploitation indirecte dans un contexte diérent. Par exemple, le rejeu autorise la génération de traces exploitables dans des attaques par canaux cachés an de retrouver des clés secrètes. On retombe dans un des cas mentionnés ci-dessus où l'attaquant cherche à se faire passer pour l'un des partis. Le rejeu inconditionnel d'une commande ouvre aussi la possibilité d'épuiser exhaustivement les combinaisons d'entrée et ainsi découvrir un secret.

On remarque ainsi qu'il existe de nombreux vecteurs d'attaques qui dépendent à la fois du contexte d'utilisation de la carte et de son implémentation fonctionnelle.

An d'assurer une complétude au niveau de la sécurité fonctionnelle et se prémunir contre un maximum de vecteurs d'attaque possibles, il convient de réaliser un travail de réexion préliminaire sur les besoins fonctionnels et l'implémentation de ceux-ci dans une optique de sécurité.