Keynote SSTIC 2009 – Injection de code dans une javacard

Présentation : Jean-Louis  LANET , Julien  IGUCHI-CARTIGNY

Les Javacards embarquent une VM Java. Dans quelle mesure est-il possible decontourner un usage normal des applications sur ces cartes à puce ?

  •  Le système utilise du typage fort, le bytecode est vérifié, il est donc a priori plus difficile d’accéder à des espaces mémoire arbitraires.
  •  Les applications sont isolées entre elles, par le « firewall« , qui contrôle les droits d’accès d’une application à l’autre. Malgré cela, il s’avère que seules les instances sont filtrées, pas les classes elles-mêmes (et leurs méthodes statiques) !
  • Enfin, le chargement d’un applet ne peut s’effectuer que par authentification préalable de l’application.

Ce qui est intéressant, c’est que la vérification du bytecode ne s’effectue pas sur la carte elle même (dans la majorité des cas). Il est alors possible de modifier le code après cette vérification, puis le charger dans la carte.
Par exemple, pour remplacer un appel effectué en cas de code PIN invalide (appllication standard) l’attaque se réalisera en 3 etapes.

1. Modifier le CAP file (bytecode vérifié): En manipulant la stack Java, on est en mesure de modifier une methode de notre application, pour lui faire retourner l’adresse mémoire de l’argument qu’on lui passe.
Cette méthode est utilisée pour récupérer l’adresse du tableau qui contient du code à exécuter, ainsi que l’adresse de l’instance du « trojan ».

2. Modifier le code lui même (JVM): Le linker JAVA va modifier les adresses utilisées dans le code, notamment pour les référencer. Ce comportement empêche la
spécification d’une adresse mémoire arbitraire.
Il faut donc modifier le « reference location » du fichier compilé, retirer la translation d’adresse de cette référence.
Ainsi, en y plaçant une adresse arbitraire, celle-ci ne sera pas remplacée, et la modification de la mémoire de la javacard sera possible, c’est gagné.

3. Remplacer le « invoke » de la méthode à effacer par un invoke vers l’adresse du tableau comprenant le bytecode qui ne fera rien (sorte de shellcode en somme), utilisant putstatic avec l’adresse du tableau, tant qu’il s’agit d’une structure méthode valide.

Avec tout ça, il suffit de parcourir la mémoire a la recherche du pattern a noper, remplacer l’invoke par notre invoke « nop », qui ne fait rien.

Cependant, toutes les cartes ne sont pas compatibles, notamment si elles contiennent un vérificateur de bytecode intégré, les cartes se « suicident ».
D’autres méthodes permettent de parcourir la mémoire, comme le transtypage (marche plutôt bien, mais basé sur un défaut de la VM)

Les contremesures existent, des vérifications du bytecode à l’exécution on été implémentées pour tester, et fonctionnent très bien, ça n’agrandit pas trop le binaire, mais le temps d’exécution est rallongé.
Reste la possibilité de ne l’activer que sur certaines méthode sensibles.

La prochaine étape de cette étude : exécuter du code natif (sortir de la JVM) pour par exemple, dumper la ROM.