 Ces deux chercheurs de l'université de Roux ont beaucoup travaillé ensemble sur les vulnérabilités de très bas niveau et les défenses à très bas niveau. Foyac hier chaleureusement Benjamin et Philippe. Merci pour l'intro, merci d'être venu. Aujourd'hui nous présentons notre recherche sur le microcode. C'est un travail conjoint avec nos collègues et nos superviseurs de la Roux Université de Burrum. Je vais commencer par un petit disclaimer. Les détails techniques dans cette progression sont assez spécifiques au processeur NDEK10 toute dernière version et beaucoup des découvertes que nous avons pu faire ici viennent de reverse engineering, d'ingénierie inversée. Donc si c'est quelque chose que vous voulez faire à la maison, faites-le à vos propres risques puisque ça peut déclencher plein de comportements non voulu dans le processeur. Pour commencer je vais donner une idée générale de ce qu'est un microcode. Je vais faire un peu une supervue de l'architecture en X-26. On va se poser la question de est-ce qu'on peut haquer quelque chose là-dedans et ensuite il y aura une petite démo. D'abord, qu'est-ce que le microcode? Là on va un CPU. On peut imaginer que le microcode c'est comme un ordinateur dans votre processeur qui peut faire beaucoup des instructions très complexes. On verra comment on peut gérer ça. On commence avec le travail qui est déjà fait avec ça. Il y a quelques patents d'AMD qui font un petit overview des détails. Après il y a cette web type architecte où on peut voir un type en détails sur le placement spécifique des composants sur le dais. Après il y a un blog post anonyme. On peut voir pour le premier fois que le microcode peut être mis à jour. C'était l'idée. Nous avons pris l'idée qu'on peut faire la recherche sur ce sujet. Après il y a un paper avec le titre Security Analysis of X86 Processor Microcode qui donne un petit overview sur ce sujet. C'est découvert d'un chercheur Arrigo qui a probablement eu des informations en interne parce qu'il sait énormément de choses sur les microcodes. On va parler de microcodes. Pourquoi c'est utilisé un microcode? La première chose c'est pour des codes d'instruction. La deuxième chose c'est pour corriger des bugs dans les CPU qui ont déjà été vendus et qu'on ne peut pas corriger du côté hardware. Une troisième chose c'est pour la gestion des exceptions. Par exemple une division par zéro. Dans ce genre de cas le processeur doit détecter ce cas et il doit passer l'exception et certaines informations au système d'exploitation. Ça s'est géré dans le microcode. Le microcode est également utilisé pour la gestion de l'alimentation et ça peut aussi être utilisé par les fabricants pour des fonctionnalités plus complexes dans le processeur comme par exemple Intel SGX. Par exemple on peut décoder les instructions avec le microcode. Les instructions sur la plateforme X86 est plutôt complexe. Par exemple une instruction qui est entaille de 1 byte il y a des instructions de plusieurs bytes. Normalement avec le 1er byte on peut déjà voir le taille d'instruction. Mais il y a des instructions comme même. Il peut y avoir par exemple un prefix qui peut délayer le début d'instruction. Il peut également y avoir plusieurs préfixes et X86 contient énormément d'extensions qui ont été ajoutées au fil du temps et qui peuvent former des instructions vraiment très complexes. Par exemple des instructions ici qu'on peut voir pour opérer sur des nombres virgules flottantes qui ont de nombreuses opérantes qui sont difficiles à déchiffrer. A cause de cette complexité il faut un véritable petit ordinateur dans le processeur pour pouvoir décoder ça. Et maintenant on verra comment ça se déroule, le décodage. Par exemple on a une instruction pop qui prend en valeur du stack, du pile et ça se décoder de plusieurs instructions en X86. On les voit dans les trois, à droite. On commence avec une instruction qui télécharge en valeur dans une registre temporaire. Après c'est stocké, la valeur est stockée dans EBX et le stack pointer est incrementé. Comme vous l'avez entendu, le microcode peut également être utilisé pour mettre à jour les processeurs après qu'ils aient été vendus. Ça c'est quelque chose qui a probablement été mis en place suite au fameux problème sur l'intel-pensium dans les années 90 où certaines opérations à virgule flottantes produisaient des résultats qui étaient légèrement incorrectes. Ça a causé énormément de problèmes et l'intel a dû payer énormément d'argent pour remplacer les processeurs dans la nature. Donc l'intel et AMD veulent tous les deux éviter d'avoir ce genre de problèmes dans le futur et on fait en sorte que leur processeur puisse être mis à jour. Par exemple récemment il y a eu ce bug sur les processeurs Intel Capilleg dans certaines conditions sous l'utilisation d'hyper thread. Ça pouvait amener à des comportements instables et ce bug a été résolu avec une update du microcode. Chez AMD il y a aussi des bugs similaires. Il y a eu le bug Phenom en 2008 dans lequel les entrées de la TLB ne pouvaient pas être mises en cache dans certaines circonstances et ça a aussi été résolu par des mises à jour du microcode. Plus récemment, cette année, il y a eu un autre problème sur les processeurs AMD et les processeurs AMD Ryzen qui a aussi été résolu via les mises à jour du microcode. Maintenant on regarde les architectures du microcode comment c'est embarqué dans le système. Enfin, quand on récupère la instruction de même voir, c'est décodé, c'est mis dans les schedulers et mis dans une pipeline là tu as un lieu en multiplicateur et des autres parties du processeur où on peut exécuter une opération en parallèle. On va maintenant passer dans le décodeur pour regarder un peu plus en détail ce qui s'y passe. Il y a un buffer qui garde l'instruction en elle-même et plusieurs petits décodeurs dedans. Il y a des décodeurs courts qui traduisent une instruction avec 86 simples en une seule opération et qui ensuite les combines ensemble pour les mettre dans le scheduler. Il y a aussi des décodeurs longs qui sont capables de traduire des instructions avec 86 plus complexes en plusieurs micro-ops. Et le plus intéressant des décodeurs, c'est le décodeur de vecteur, le décodeur vectoriel. Celui-ci contient la machine virtuelle qui exécute le micro-code et les instructions décodées par le vecteur des codeurs, le décodeur vectoriel, déclenchent un petit programme en micro-code et ce programme peut générer un nombre arbitraire de micro-ops qui vont être ensuite inclus dans le pipeline et exécutés. On vient d'entendre que, même dans le processeur, il y a des micro-programmes qui sont exécutés et du coup il faut les stocker quelque part. Il y a dans la puce du processeur une ROM, la ROM de micro-code qui contient le micro-code et les micro-programmes. Durant le runtime, le parti de apte peut être mis dans le RAM du micro-code. Le micro-code utilise également de la RAM pour diverses choses. Il y a aussi des registres, notamment un compteur de programme pour savoir où on est dans le micro-code et plus important, ce qu'on appelle les registres de match, les registres de comparaison. Ces registres permettent de mettre en place des breakpoints qu'on peut mettre à des adresses données dans le micro-code. Quand cet adresse est exécuté, le contrôle est redirigé vers la RAM micro-code ou d'autres micro-codes est stocké. C'est un mécanisme très important pour les mises à jour du micro-code qui donne le contrôle de ce qui se passe dans le CPU et qui peuvent changer le comportement de ce code, par exemple nettoyer des entrées de certaines instructions pour éviter des bugs. Comment est-ce qu'on met à jour un micro-code ? Déjà, il faut être en mode kernel. On charge la mise à jour dans la RAM. On écrit à une certaine adresse, l'adresse virtuelle à laquelle se trouve le micro-code. Ces adresses ne sont pas persistantes. Lors d'un reboot, ce sera nettoyé. Écrire à cette adresse déclenche la copie du code du micro-code dans la RAM du micro-code. Le format de fichier de la mise à jour du micro-code, vous pouvez le voir ici. Il y a divers champs, notamment d'information sur l'idée du patch, un checksum, les registres de comparaison qui contiennent les breakpoints, après ça, le micro-code en lui-même. Le micro-code est organisé en triad, comme on dit. Une triad contient 3 micro-hops et un mode séquence. Les micro-hops sont des simples instructions du micro-code qui exécutent du code. Le mode séquence, SequenceWare dans l'anglais, est utilisé pour la gestion du flow de contrôle. Maintenant, on veut répondre à la question plus intéressante. Est-ce qu'on peut le haquer ? On a appris que les CPU, on peut le mettre à jour. On peut en plus mettre à jour les pilotes dans les new scanners. Et en plus, on peut aussi mettre à jour les micro-codes. Les micro-codes, on peut les mettre à jour avec des familles qui sont distribuées dans l'Internet. Il y a des indices qui nous laissent penser qu'il n'y a pas de cryptographie forte qui protège ces mises à jour du micro-code. Voici un dump hexadecimal d'une mise à jour de micro-code. Et si on regarde juste comme ça, on peut voir qu'il y a des valeurs qui se répètent beaucoup. Si on met certaines de ces valeurs en rouge, on peut voir qu'il y a des patterns qui se répètent. On voit beaucoup de 0 et de F, ça c'est aussi un indice. Ce qu'on a appris, c'est que le CPU acceptera des mises à jour modifiées si le checksum est correct. Donc oui, c'est à câble. A ce point-là, on savait qu'il faut juste gérer beaucoup des micro-codes updates dans une manière automatisée pour forcer un comportement du CPU et voir comment un update influence le comportement du CPU. On a fait la implementation des certaines nœuds. On fait tourner sur le processeur notre propre OS. Sur lequel on a notre OS, ça tourne sur des CPU AMD. On a connecté ces ordinateurs à un Raspberry Pi qui utilise le GPIO, l'entressante hygiénérique, pour communiquer avec nos machines. On a un système pour recette les nœuds sur lesquels on fait nos tests parce que quand on fait des choses assez intrusives dans le processeur, ils ont tendance à se bloquer très souvent. Donc voilà à quoi ça ressemblait au tout début dans notre maison. Et voilà à quoi ça ressemblait un peu plus tard à l'université. Et maintenant, on a des outils qui nous permettent de tester ça de façon automatique. Et ça devient intéressant. On a pu les utiliser pour générer des distributions ou des cartes thermiques. Alors c'est peut-être un petit peu incorrect si je dis que c'est une heat map ou une carte thermique. Ce que nous on veut, c'est la correspondance du microcode vers les instructions X86. Les instructions X86 sont implémentées en microcode et ce microcode est stocké à certaines adresses dans la ROM. Donc cette correspondance, cette heat map, elle sert à nous dire quelle zone dans la ROM implemente quelle instruction X86. On a généré ces correspondances en instrumentant les instructions avec nos registres de breakpoint et en exécutant répétitivement les instructions X86 correspondantes. Donc une fois qu'on a eu cette carte thermique, cette correspondance, on pouvait de façon fiable exécuter nos propres bits en tant que microcode. Il suffisait pour nous de mettre le breakpoint à une adresse qu'on connaît et ensuite exécuter l'instruction X86 correspondante. Une fois qu'on fait ça, le flow de contrôle est rétirigé sur notre propre microcode dans la mise à jour et là on peut mettre les bits qu'on veut et le CPU lier à sa carte du microcode et l'exécutera. Comme le jeu d'instruction du microcode n'est pas documenté, on a dû faire de l'ingénierie inversée, du reverse engineering puisque le CPU est vraiment une black box ici, il n'y a pas de désassembleur ou de compilateur ou même de documentation. Heureusement on a un oracle, c'est le CPU lui-même. On peut donner les entrées qu'on veut au CPU et voir les sorties. Donc ça nous permettait de deviner les structures, l'encodage et la sémantique des opérations. Donc on peut regarder un peu plus en état à quoi ressemble cet oracle. En entrée on donne des instructions X86 et un état initial qui contient des valeurs pour les registres X86 et on lui donne l'update de microcode qu'on a généré avec nos propres match registers des registres de correspondance et nos propres micro-ops qui contiennent du coup l'update du microcode avec les bits qui vont réellement être exécutés par le CPU. Après on a une sortie qui est l'état dans lequel se trouve le processeur. Après l'exécution de nos bits de microcode très souvent quand le CPU va exécuter des bits aléatoires de microcode, ça va cracher le CPU et dans certains cas on ne verra aucune différence entre l'entrée et la sortie. Après quelques semaines de brute force on a pu trouver une séquence de bits qui ne crachait pas le CPU et qui donnait un état d'entrée et de sortie différent et ça c'était notre première découverte et la marche qui nous a permis d'accéder à des analyses plus complexes. On a commencé avec tous les bits par bits ici on a changé les bits à la droite et l'output a changé et après on l'a fait plusieurs fois et on a vu l'input et l'output on a réalisé cette instruction qui fait un addition sur le registre EIX dans le microcode après on a utilisé ces résultats pour changer les valeurs dans le chambre du valeur immédiate si on change les autres bits par exemple les bits qui sont plus à gauche dans le gauche dans les bits string on sait déjà l'output change le valeur dans le registre EIX ça change différemment après plusieurs attentes on a réalisé que ça c'était une instruction XOR sur le registre EIX ce qu'on a pu trouver à partir de ça c'est que l'opération est encodée sur un certain nombre de bits et ça c'était une façon pour nous de commencer nos expériences et d'automatiser plus de tests pour changer le code parfois un peu plus aléatoire le but étant de trouver des résultats sortis intéressants pour nous et parfois de trouver différents états de sortie c'est intéressant quand le CPU ne crache pas parfois on avait des choses qui avaient l'air d'être juste du bruit qui n'avaient pas forcément de sens dans la sortie et après beaucoup de travail vous pouvez probablement pas le lire mais c'est pas grave on est arrivé à ça ça donne une liste assez exhaustive d'opérations des médias, parfois des registres parfois propagent les flags de résultats parfois non etc notre truc qu'on voulait entrer c'est la logique à base de ROM2 donc une chose qu'on a pu faire c'était regarder ce qu'il y avait dans les zones de ROM directement sur le circuit on a regardé pour ça dans les registres de breakpoint en mettant des adresses qu'on connaissait pour récupérer le contrôle on exige ensuite une instruction x86 pour récupérer le contrôle et ensuite on pouvait écrire du microcode dans notre mise à jour qui nous permettait de repasser dans le microcode exactement à l'adresse qu'on voulait analyser en ajoutant un à cette adresse on remettait un autre breakpoint qui correspond à cette adresse qui nous renvoyait à nouveau dans la RAM là on avait encore des informations de microcode et ça nous permettait de récupérer les résultats et c'est comme ça qu'on pouvait comparer les entrées et les sorties mais cette fois-ci c'était après avoir exécuté non pas notre microcode aléatoire mais après avoir exécuté le code qui était dans la ROM du code qui venait déjà du fabricant ça a plusieurs avantages ou parfois des avantages premièrement le microcode peut parfois changer des étapes en CPU qu'on ne peut pas observer et également il y a parfois une triade dans la ROM du microcode donc il n'exécutera pas forcément l'adresse suivante mais parfois il y aura un saut et on ira ailleurs dans la ROM du microcode et donc dans ce cas-là on perdait le contrôle de l'exécution mais bon c'est pas forcément grave parce que c'est aussi un signal qu'on apprend donc on a décidé de procéder avec cette méthode après dans notre projet on a décidé de lire la ROM directement du die physiquement on a décapté le processeur et on a pris une image un photo de processeur en verre on peut voir les areas de microcode ça prie pas mal des areas sur le processeur si on zoom un peu plus on peut voir les pits et les patterns et si on zoom même plus on peut voir les points qui sont plutôt mis à gauche ou mis à droite ça veut dire qu'ils sont connectés soit à ground à la masse soit à VCC ça veut dire que chaque chambre représente soit en 0 soit en 1 on peut à cette point utiliser OCR pour traitre le plus part des bits mais pour les gens qui sont plutôt dans les côtés software c'est un peu tranch ça veut dire pour nous c'était plutôt un challenge plutôt difficile pour récapituler les résultats de notre reverse engineering la prochaine chose qu'on a eu c'était CITMAP cette correspondance entre les instructions x86 et le microcode on a trouvé à partir de là 29 microhops logique, arithmétique et pour charger des données on pouvait écrire dans le programme canteur en x86 et on pouvait sauter on a aussi compris à quoi servait le mode séquence, le sequence word il peut servir à sauter à la prochaine triade il peut indiquer que la séquence est complète c'est à dire que le décodage de cette instruction x86 est terminé et que du coup la prochaine instruction x86 doit être exécutée et le mode séquence peut aussi être utilisé pour sauter inconditionnellement dans le microcode on a aussi trouvé ce qu'on appelle le substation engine la logique de substitution qui peut être utilisé pour de manière automatique placer des opérandes d'une instruction x86 dans une instruction du microcode donc par exemple si vous avez l'opérande eax dans une instruction x86 ça sera eax l'instruction de l'opérande en microcode également ça simplifie beaucoup l'écriture d'instruction x86 dans microcode alors on veut aussi augmenter les instructions x86 jusque là on pouvait uniquement remplacer la logique des instructions x86 en mettant un breakpoint vers notre point d'entrée et en écrivant notre propre microcode à ce endroit là étendre la logique existante tout en préservant la sémantique de l'instruction et on peut faire ça soit en sautant à nouveau vers la rome existante ou alors pour des instructions simples en x86 on peut simplement les émuler en microcode et on a quelques exemples ici donc enfin quand on a reverse engineer le microcode du plateforme x86 on a commencé à emplémenter notre propre microcode le premier programme qu'on a écrit c'est une instrumentation qui faire le compte le numéro des instructions qui sont exécutés après on a écrit une façon pour faire un hook dans les microcodes instructions et on a implémenté une attaque qui marche remonte on a besoin d'une CPU qui a le vector implémenté en microcode si le ordinateur a accès à une web site le vector et à partir du navigateur on peut déclencher l'exécution de l'instruction x86 vectorielle qui déclenchera notre microcode on a deux versions de ça on a aussi deux Trojans deux Trojans pour le microcode ce qui est très difficile à détecter on introduit une attaque à canaux parallèles en tant qu'on sent dans les implémentations des courbes elliptiques et le deuxième permet d'injecter des erreurs dans les cryptographiques maintenant on peut regarder à quoi ressemble un microprogramme un microprogramme nous est donné dans notre propre langage d'assambleurs qu'on a développé pour pouvoir développer des microprogrammes de façon simple ça nous évitait d'avoir à écrire des longues chaînes de bits à la main il y a une instruction de soustraction qu'on utilise pour comparer on peut voir qu'il y a notre registre TLD qui est comparé à la valeur d'EAX après ça on a un saut conditionnel on veut d'abord considérer le cas où on ne saute pas donc c'est ici donc là c'est juste une perne instruction on crée une division d'entier et on peut voir que la toute dernière instruction ici saute en arrière pour revenir dans la mémoire du microcode donc ce qu'on voit ici c'est qu'on fait la même chose qu'une division d'entier et à la fin on saute dans la Rome à nouveau pour exécuter le code de la division à proprement parler maintenant si le code dans EAX et dans TLD sont égaux dans ce cas on ajoute un au programme counter et ensuite on écrit cette valeur dans le programme counter x86 ça ce que ça veut dire c'est que ce vecteur de microcode compare EAX à une valeur magique et si cette valeur magique est trouvée le compteur de programme d'x86 sera incrementé de 1 ce qui signifie que toutes les instructions x86 après seront exécutées sans être alignées en mémoire ça c'est une primitive très utile qui peut être utilisé pour par exemple nos exploit qui ciblent javascript et pour toutes sortes de bugs maintenant c'est l'heure de la démo et je passe la scène à Benjamin j'ai un IPC avec moi c'est avec un CPU qu'on peut exploiter avec notre programme le microcode aidé dans les CPU on est mis à faire un update c'est juste un microcode mector on cherche les valeurs pour faire un trigger les valeurs qui vont faire de déclencher notre microcode et voilà où se passe la magie on lit ce qui est écrit dans le buffer en mémoire et la raison pour laquelle c'est important vous allez le voir dans une minute quand je vous montrerai l'exploitation lui-même là on lit ce qui est en mémoire et on le place dans un registre temporaire et ensuite on fait un peu d'arithmétique sur cette valeur et ensuite on implemente la sémantique d'un décalage d'un décalage binaire qui était un opcode x86 ça c'est parce que si la valeur de déclenchement c'était pas celle que vous voulez caturer, vous voulez pas perturber l'exécution des programmes c'est possible que d'autres programmes sur votre système utilisent cette instruction du coup c'est important de préserver la sémantique originale si vous voulez pas déclencher votre bac dehors à ce niveau là on sait si on doit ou non déclencher le comportement et on peut voir ici qu'on peut ajouter conditionnellement des valeurs dans un registre ou modifier la valeur dans les registres d'une autre façon la prochaine chose qu'on fait si on met eax à la valeur 11 et ça c'est la valeur de syscall sous Linux je n'ai pas capté quelle syscall ensuite on met d'autres registres à des valeurs et ça ça va être les arguments qu'on contrôle le syscall était 7uid et à la fin on écrit dans le programme compteur une valeur par le javascript en lui-même donc dans le javascript on peut décider où est-ce qu'on veut aller et ça c'est tout conditionnel, ça ne s'exécute que si l'entrée de l'instruction match notre consent magique donc pour vous montrer ce que ça donne en pratique ça c'est notre module en WebAssembly on peut voir qu'ici j'ai notre payload qui contient ici des instructions x86 non alignées je déclenche une interruption dans le kernel et ça ça fait à un syscall x86 et la valeur qu'on lit on la voit ici tout simplement ce qu'on dit ici c'est voilà, utilise ça donc ça c'est un Firefox pas modifié, c'est Firefox que je n'ai pas ajouté de code dedans du tout c'est une version que j'étais chargé de l'internet et quand j'exécute mon javascript comme j'ai attaché un debugger on va se retrouver ici dans une seconde il faut attendre un petit peu parce que mon IPC est vraiment très très lent surtout quand on debug Firefox et voilà donc là je vais avancer pas à pas dans mes instructions jusqu'à arriver sur le hook que je veux mettre en place et maintenant on est dans notre module WebAssembly que je vous ai montré avant donc ça c'est du code qui est assemblé par Firefox pendant le runtime donc Firefox peut choisir quel code doit être émis en contrôlant les hooks dans le module WebAssembly et comme vous pouvez voir ici on exécute notre opcode shift right décalage à droite moi j'ai pris bien soin de mettre ma constante ici dans le registre rx et ici ça ça va déclencher notre microcode j'ai débit va perdre le contrôle dans une seconde je vais tricher un petit peu et ici je sais qu'on va avancer dans 6 octaves plus loin donc j'ajoute 6 et je presque continue et maintenant on est désaligné dans nos instructions vous pouvez voir qu'on a exécuté toutes les choses qu'on avait codées dans le microcode pour préparer les registres d'argument et si vous comparez donc ça c'est la sortie de notre code WebAssembly moi j'ai dit merci d'émettre cet opcode et ensuite un jump à 5 et ensuite exécuter ces opcodes et ça c'est les opcodes qu'on a donc si on continue j'espère que ça va marcher et bon ça va marcher bien sûr laissez moi l'exécuter hors de gdb ça charge et on y est notre exploit marche et donc comme j'ai dit déjà on peut implémenter des backdoors cryptographiques alors comme il faut rebooter pour revenir à un état sonar je vais vous montrer un autre exemple d'abord donc là on a un truc qui vient de faire des signatures vous voyez que la signature est vérifiée avec succès puisque tout marche bien là je vais rapidement charger ma backdoor et charger notre carnel modifié puisque on a besoin que le carnel ajoute notre oblet et pendant que ça reboute je vais vous montrer une autre démo j'ai ici cette console elle va lancer un script sur un Raspberry Pi qui est quelque part dans notre assemblée dans le CCL et ça c'est connecté à une de nos machines sur laquelle on fait nos tests ça ça va exécuter le microcode qu'on lui dit d'exécuter donc d'abord je vais vous montrer ce qui se passe si je lui dis ne fais rien dans le microcode donc on fait juste ça et on lui dit exécuter et le registre qu'il faut prêter attention c'est obp là pour l'instant c'est 0 parce que je n'ai rien fait pour le modifier et maintenant je vais dire ok fais quelque chose et là on peut voir que quelque chose a changé donc ça c'est le microcode que j'ai uploadé sur le cpu et tout ce que ça fait c'est que ça push la prochaine instruction x86 qui doit être exécuté et elle la stock sur la stack et ensuite saut à une adresse que j'ai prédéfinie et cette adresse elle est jusqu'ici et là on récupère le contrôle en x86 sans avoir à écrire un programme un programme de plus compliqué en microcode donc ça c'est aussi quelque chose qu'on peut faire on peut introduire d'autres hooks de microcode et là on peut voir que mon laptop a fini de redémarrer donc je peux relancer vnc une seconde là on est à nouveau sur notre IPC et là on a démarré avec une update différente celle-ci ne déclencherait pas la backdoor qu'on a mis dans Firefox mais à la place je vais vous montrer la démo de crypto que vous avez vu avant et cette fois-ci il dit signature invalid puisque moi j'ai introduit une erreur pendant la vérification de la crypto à cause de ma backdoor dans les courbes électiques et grâce à ça vous pouvez faire des attaques cryptographiques et en utilisant ça on peut reconstruire les clés et tout ça c'est fait entièrement en microcode je n'ai pas eu à modifier les binaire qui tournent sur la machine donc ça ça nous amène aux questions de sécurité qui se mettent en place donc ça nous amène aux questions de sécurité qui se posent bien sûr premièrement on peut mettre n'importe quel mise à jour moi j'en ai modifié en live je l'ai mis sur le cpu et il a accepté on peut bien sûr faire des backdoors et on peut pas vraiment reçoure ce problème il faudrait changer le matériel alors on peut faire un fixe un petit peu dégueulasse on peut juste détruire le mécanisme de mise à jour mais bon c'est pas trop mal parce que si un attaquant est capable de faire ça c'est quelqu'un qui a déjà le contrôle pour modifier votre bios donc c'est probablement pas un problème pour vous et en conclusion le microcode ça peut être réversé ça peut être modifié, si vous voulez en parler si vous voulez essayer ça avec vous vous devez nous voir l'évole 0 du CCL à l'Assemblée Binary Security on a aussi un con de github avec des échantillons que vous pouvez essayer vous pouvez tout essayer c'est sur github il y a même un utilitaire pour mettre à jour votre propre cpu mais fadiaf ça peut quand même casser votre cpu on a réussi à casser les notes plusieurs fois mais on a réussi à aller reset à chaque fois donc faites attention on a le temps pour poser les questions on a aussi un signal angel qui me dit qu'il y a des questions dans les internet en attendant les questions vous pouvez les poser sur rc au signal angel en anglais ou en allemand sur rc les questions est-ce que c'est possible de microcode maveillon est-ce que c'est possible de briquer le cpu alors nous on n'a pas briqué, on n'a pas cassé de cpu, à part ce qu'on a détruit pour les mettre dans le microscope électron c'est peut-être possible, on n'a pas réussi encore, mais de ce qu'on a vu du design on a analysé, ça n'a pas l'air possible à moins qu'il y ait des fonctionnalités spéciales pour ça dans le microcode donc c'est peut-être possible mais il est probable que ça ne soit pas le cas est-ce que c'est possible de faire un fixe pour la performance avec microcode alors on y a pensé par exemple pour l'instrumentation binaire il y a différentes choses qu'on peut faire pour émuler du code, comme par exemple je n'avais pas compris ou on peut modifier du code de façon statique donc toutes ces approches marchent probablement mais soit elles sont lentes, soit elles sont incomplètes, donc le microcode peut être une façon assez performante et complète pour faire de l'instrumentation dans le code, ce sera intéressant d'avoir un framework d'instrumentation qui utilise le microcode pour ça le problème du microcode c'est qu'à l'heure actuelle c'est relativement limité puisque dans la plupart des CPU modernes c'est quelque chose de complètement fermé que ce soit chez Intel ou AMD mais peut-être que les fabricants de CPU vont ouvrir un petit peu cette chose et que ça pourra être utilisé pour améliorer la performance des frameworks d'instrumentation mais je ne pense pas que ce soit une implementation très rapide par rapport à l'implémentation proprement en X86 Est-ce que vous avez regardé dans les plus vieux Intel code appletters ? Ils semblent, ils sont cryptés mais probablement une strong cryptographie peut-être un stream cipher ou un block cipher parce que j'ai déjà réussi à patcher un microcode update mais je n'ai pas réussi à contrôler les comptes tenus donc on n'a pas vraiment regardé nous du côté des mises à jour Intel mais c'est une très très bonne question on devrait probablement discuter donc passez-nous voir à l'assemblée est-ce que c'est correct de dire que le microcode c'est une architecture risque ? ou est-ce qu'on y trouve des instructions qu'on ne trouverait pas typiquement sur une architecture typique risque ? donc la question est est-ce que le jeu d'instruction du microcode c'est un jeu d'instruction plutôt réduit, risque alors les instructions dans le microcode sont généralement très simples une fontilité intéressante par contre c'est qu'il y a 3 modes d'opérant on peut avoir 3 registres on peut avoir un registre de destination de registres de source ce qui n'est pas possible en x86 comme vous le savez probablement tous mais c'est possible au niveau du microcode c'est très intéressant comment c'est le microcode entry point pour une instruction donnée ? très bonne question donc on vous a montré les hit maps qu'on a généré en mettant les breakpoints sur toutes les adresses de la ROM du microcode, une par une et ensuite en essayant toutes les instructions x86, une par une et de toute évidence, quelquefois on aurait plusieurs hits pour une instruction donnée puisque une adresse ne suffit pas forcément pour une instruction mais on peut mettre 8 breakpoints à la fois avec nos 8 registres de breakpoints du coup on mettait 8 breakpoints à des adresses différentes et on regardait lequel était déclenché en premier et donc ça nous permettait étape par étape pour débuguer pour savoir quelle était l'adresse exécutée pour implémenter une instruction donnée donc ça nous a permis de trouver les points d'entrée dans le code vous avez dit que c'est microcode update il ne semble pas persistance en reboot est-ce que ça veut dire que si un vendor fait un update du microcode, c'est un BIOS patch qui change le microcode dans chaque boot app alors oui donc les vendeurs de processeurs ne publient pas les mises à chaud ils les donnent aux fabricants de cartes-mères et généralement ces fabricants de cartes-mères mettent les updates du microcode dans une update du BIOS et du coup le BIOS est exécuté à chaque démarrage et peut appliquer ce correctif généralement en chaque démarrage sous les nukes il existe également un package pour le kernel, un module pour le kernel qui au démarrage fait des mises à jour du microcode donc selon votre OS ça peut venir soit du BIOS soit de votre OS je voulais savoir vous avez dit que ça devrait être possible d'utiliser un microcode implémenté par soi-même comment vous pensez c'est dur de implémenter ça dans le libre boot par exemple est-ce qu'on serait faisable d'intégrer ça dans le libre boot pour s'assurer que le code est exécuté à chaque boot de façon très fiable oui c'est quelque chose qui est plutôt simple à faire mais pour notre recherche avec Nohitmap on avait une compréhension suffisante de où serait le microcode puisqu'à partir du moment où on mettait des breakpoints à un endroit on ne peut plus faire des mises à jour du microcode donc ça demanderait pas mal de travail de faire ça comme ça mais ce serait par contre très simple d'avoir juste une mise à jour qui est exécutée par libre boot et pendant le boot de désactiver le système de mise à jour est-ce que c'est possible d'utiliser cette fonctionnement est-ce qu'on peut utiliser ce système pour externe importe qu'à l'information d'RCPU notamment les informations qui sont dans une secure enclave la réponse probablement pas en tout cas pas sur AMD mais on peut pas donner de réponse définitive à ça puisque les CPUs sur lesquels on a pu faire nos tests n'ont pas de secure enclave donc forcément on ne sait pas ce qu'on peut vous dire c'est que le microcode qui est exécuté a les mêmes permissions que le code qui l'a déclenché donc si c'est du code exécuté dans la MMU par exemple on y aura les permissions du code de la MMU vous avez dit que vous avez 29 microhubs est-ce que vous pensez qu'il y a des instructions de microcode que vous n'avez pas encore trouvé ou est-ce que vous pensez que vous avez toutes trouvé il est très probable qu'il y en ait qu'on n'a pas trouvé il y a certaines régions dans les instructions qu'on ne comprend pas encore et si on les cible ou si on les modifie ça crache le processeur ou ça a des résultats de telle sorte qu'on ne sait pas quelles opérations sont exécutées et par exemple on peut pas observer tous les registres, il y a des registres internes au CPU qu'on ne peut pas observer dans notre code qui modifie les états internes du CPU par exemple qui active ou désactive certaines fonctionnalités ou certaines méthodes rapides ou lentes pour certaines fonctionnalités ça c'est des choses que nous on ne peut pas voir et du coup à cause de ça on ne sait pas qu'est-ce que les instructions font ou si le bitstream qu'on a mis pour nos tests est correct du coup il y a certainement beaucoup d'instruction qu'on ne connaît pas Est-ce que vous avez des manières dans le cas que ma cocotte est bague d'or de détecter tout ça ? Alors pendant notre recherche on n'a pas vraiment regardé dans cette direction là pour le moment mais on a eu plusieurs discussions sur ce sujet c'est une très bonne question si vous utilisez le microcode pour instrumenter une instruction x86 vous ajoutez un légère overhead sur le timing et si vous mesurez précisément le temps d'exécution de l'instruction vous pouvez peut-être détecter que cette instruction exécute plus que ce qu'elle devrait faire donc on peut probablement détecter la plupart de ces opérations mais le microcode ne fait pas que ça comme on a vu vous pourriez aussi cacher d'autres choses dans le microcode puisque le microcode contrôle d'autres forgètes du cpu comme par exemple la régulation thermique et on pourra mettre un trojan là-dedans Comme vous pouvez implémenter les instructions est-ce que c'est possible à implémenter en such action c'est tout différent ? Oui on pourrait dans les autres publications que j'ai liés le talk de Mariko il fait quelque chose comme ça donc oui c'est possible on n'a pas assez avancé nous dans notre recherche pour faire quelque chose comme ça mais on pense que lui a des informations en interne d'autres J'ai deux questions, les premières est-ce que vous trouvez une manière pour déclencher le co-processor des x86 donc vous voulez dire la fpu floating point unit ou les autres extensions alors on a regardé un petit peu de ce côté là mais on n'a pas regardé ce côté là mais ce qu'on sait des brevets il doit y avoir une façon de le faire Deuxième question Il y a quelques noms Quand vous utilisez le decoder court de ce que j'ai compris les instructions ne passent pas par le cache de micro-hop du coup est-ce que ça sauve des choses ? La réponse, donc le decoder court traduit des instructions x86 simple on sait qu'il est on sait qu'il est élan possible d'instrumenter des instructions qui ne sont pas implémentées en micro-code on sait que c'est possible, on n'a pas encore trouvé comment Vous avez déjà dit ça ? Est-ce que vous avez contacté Intel ? Est-ce que vous avez en feedback des Intel ? Alors non, on n'a pas parlé à Intel on a contacté AMD Tout ce travail, on l'a publié sur Usenex et plus de 90 jours avant la publication on l'a envoyé les résultats de notre recherche on leur a demandé si ils avaient des choses à nous dire ils n'ont pas beaucoup communiqué avec nous probablement parce qu'ils ont déjà une mise à jour des versions plus récentes de leur processeur en probablement une cryptographie plus sécurisée donc ils n'étaient probablement pas très intéressés par ce qu'on avait à dire donc voilà Ok, merci beaucoup à nos présentateurs c'était tout ce que vous avez toujours voulu savoir sur le micro-code X86 mais que vous aviez peur de demander présenté par Benjamin Colenda et Philippe Kopp