SMM
Prochainement à BlackHat aura lieu une conf abordant le thème du SMM (System Management Mode) ce fameux mode du CPU qui a fait tant parlé de lui depuis que Loic Duflot a présenté une conf à CanSecWest 2k6. Au point même qu’on en retrouve une colonne sur SecurityFocus et un article dans le dernier Phrack, bref tout cela a provoqué un bon petit buzz tout comme celui de Kris Kaspersky sur sa future conf de HITB sur l’exploitation de bugs dans les CPU en remote, miam ! Sur le papier le SMM à l’air vraiment cool, ca déchire sa race et ca poutre des loutres comme on dit nous les jeunes cependant je vais vous expliquer pourquoi je suis sceptique à propos de la conf de Sherri Sparks et Shawn Embleton à BlackHat.
Tout d’abord le SMM c’est quoi ? Le SMM est un mode existant sur les CPU x86 qui vient s’ajouter à ceux déjà existant, qui à la particularité d’être exécuté sans que l’OS en soit conscient, contrairement aux autres ou l’OS peut contrôler l’activation ou l’arrêt. C’est justement tout l’intérêt du SMM, pourvoir agir sur la mémoire et les périphériques sans que l’OS le sache, lorsque le CPU en SMM toutes les interruptions, exceptions et même les NMIs sont masquées à noter que le SMM ne peut pas être réentrant ce qui signifie donc que les autres SMIs sont masquées aussi. Le SMM est utilisé par des routines critiques comme celle qui est appelée lorsque le CPU chauffe trop pour l’éteindre ou bien pour gérer les erreurs sur le bus processeur, le fameux FSB.
Lorsque le CPU passe en SMM il se retrouve en real-mode, le bit 0 du cr0 (PE, Protection Enable) est à 0. Le real-mode exécute du code 16 bits en ayant une gestion de la segmentation et des exceptions réalisée de façon plus basique. Le calcul de l’adresse virtuelle (qui est en fait l’adresse physique car il n’y à pas de pagination) est effectué en décalant le segment selector courant (CS, DS, ES ou SS) de 4 bits puis en l’ajoutant à l’offset, ce qui au final donne une adresse sur 20 bits, soit un espace d’adressage allant de 0h à 10FFEFh (1Mo+64Ko). A noter que le support des segments FS et GS ainsi que la possibilité d’exécuter des instructions 32 bits est arrivé plus tard, pour plus d’info vous pouvez lire le « Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 3A: System Programming Guide« .
Pour les exceptions on retrouve aussi une table des interruptions sauf que celle-ci porte le nom d’IVT (Interrupt Vector Table). C’est une table de 256 entrées dont les champs font 4 bytes, 2 pour l’offset du handler et 2 bytes pour le nouveau segment selector CS.
C’est donc dans ce contexte que s’exécute le SMM. Le passage du processeur en SMM se fait à travers une SMI (System Management Interrupt), ce n’est pas une interruption comme les autres dans le sens ou elle n’est pas gérée par un handler de l’IDT, c’est un signal envoyé au CPU sur le pin in SMI# depuis le bus APIC. Remarquez que le lorsque le CPU est en SMM il notifie le northbrige avec le pin SMIACT# ceci aura de l’importance pour la suite. Enfin, la sortie du SMM se fait en appelant l’instruction RSM, celle ci va juste restaurer le contexte du CPU qui a été sauvegardé en mémoire lors du passage en SMM.
Le code et des données SMM sont stocké dans une zone mémoire spéciale appelée la SMRAM, cette zone mémoire de 64 Ko commençant par défaut à l’adresse physique 0×30000 (SMBASE) est uniquement accessible lorsque le CPU est en SMM. Lors du passage en SMM le CPU sauvegarde son contexte dans la zone allant de [SMBASE+0xFE00] jusqu’à [SMBASE+0xFFFF] il va ensuite exécuter les instructions situées à partir de [SMBASE+0x8000] le reste de la SMRAM est libre et dépend de votre BIOS car c’est lui qui initialise cette zone mémoire lors du démarrage. En temps normal, la SMRAM pointe vers la mémoire vidéo.
Il est possible de modifier la SMBASE en changeant le registre SMBASE (offset SMBASE+0x7EF8) qui est sauvegardé lors du passage en SMM, le retour avec l’instruction RSM mettra à jour ce registre interne du CPU et les prochaines SMIs prendront en compte la nouvelle SMBASE. D’après ce que j’ai pu voir sur les systèmes actuels la SMRAM est relogée en 0xA0000. Il est même possible d’accéder à un espace mémoire de 4Go depuis le SMM avec l’aide d’un préfixe sur les instructions et adresses, le Operand-size override prefix (0×66) et le Address-size override prefix (0×67).
Finit l’introduction, passons aux choses sérieuses. Un attaquant aurait ainsi 2 raisons d’utiliser le SMM :
- Avoir son code placé dans une zone mémoire inaccessible en temps normal par les HIPS et qui récemment encore était inconnu du public.
- Travailler avec un niveau de privilège élevé sur la machine en ayant avec un accès total au CPU, à la mémoire et aux périphériques permettant de contourner toutes les protections mises en place par l’OS.
C’est donc sur ces points qu’insistent les conférences de Loic Dufloc et très certainement celle de BlackHat aussi. En fait le vrai problème si situe au niveau de l’accès à cette fameuse mémoire SMRAM, si un attaquant veut exécuter du code en il va devoir y écrire mais comme je l’ai dit précdemment la SMRAM est uniquement accessible lorsque le CPU est en SMM, donc pour le moment est bloqué. Serait-ce la fin de notre voyage ? Pas forcément, il est quand même possible de taper dans la SMRAM, cependant pour cela il faut descendre un peu plus bas sur la carte mère en allant jouer avec le nortbridge.
Le northbridge ce chipset faisant le lien entre votre CPU, votre bus PCI Express (ou AGP), votre SDRAM et le southbridge, contrôle en effet l’accès à la SMRAM à travers la notification par le pin out du CPU SMIACT#. En temps normal les adresses physiques de 0xA0000 à 0xBFFFF sont des memory mapped I/O gérées par le bus PCI. Sur l’image suivante on voit que ces adresses sont routées vers le chipset Intel Q965/Q963 qui est mon northbridge.
Hop on récupère datasheet du chipset sur le site de Intel et on passe en mode RTFM. A l’aide de la doc et des papiers sur le sujet on apprend qu’il existe un registre dans le northbridge qui sert à contrôler l’accès à la SMRAM, le SMRAMC (System Management RAM Control). Pour accéder au registre de quelconque périphérique sur un bus PCI on doit connaître l’indice du bus, l’indice du device, l’indice de la fonction du device et l’index du registre à lire/écrire, dans mon cas les devices accessibles depuis le northbridge peuvent être représenté par le schéma suivant :
Pour pouvoir accéder à la configuration de ces devices le mécanisme est assez simple : Il existe 2 registres d’I/O, le CONF_ADDR (4 bytes en 0xCF8)et le CONF_DATA (4 bytes en 0xCFC). Le premier sert à choisir le bus, le device, la fonction et le registre du device à accéder, le second sert à effectuer l’opération d’I/O. On définir ces registres à l’aide des instructions IN et OUT. Sachant que le SMRAMC est sur le bus 0, device 0, function 0, registre 0x9D, on définit le CONF_ADDR de la façon suivante :
#define MAKE_CONF_ADDR(Reg, Fct, Dev, Bus, Enable) (((Reg)&0xfc) | (((Fct)&7)<<8) | (((Dev)&31)<<11) | ((Bus&0xff)<<16) | ((Enable)<<31)) #define SMRAM 0x9D ConfReg=MAKE_CONF_ADDR(SMRAM&0xFFFC, 0, 0, 0, 1); // On aligne notre I/O sur une adress multiple de 4
Problème, l’I/O ne fonctionne pas en user-land. Faire une I/O demande que le CPL (Current Privilege Level) soit inférieur ou egal à l’IOPL (I/O Privilege level, bits 12 et 13 de l’EFlags), comme par hasard on a par défautl un IOPL à 0 et un CPL de 3 en user-land. Une solution simple serait de coder un driver pour les I/O mais la flemme. Après quelques recherches sur le net j’ai trouvé une solution plus élégante : L’api native ZwSetInformationProcess permet avec l’InformationClass ProcessUserModeIOPL (16) de modifier l’IOPL du thread courant. On code un sample et là bim ! Echec ! La fonction nous renvoie le numéro d’erreur 0xC0000061 (STATUS_PRIVILEGE_NOT_HELD), en effet comme le spécifie l’article de Michael Wookey il faut que le token du process possède le privilège SeTcbPrivilege. Celui-ci peut être attribuer en allant dans Panneau de configuration -> Outils d’administration -> Stratégie de sécurité locale -> Stratégies locales -> Attribution des droits utilisateurs et en ajoutant votre utilisateur dans la stratégie « Agir en tant que partie du système d’exploitation » (après avoir ajouter votre user il faut vous relancer votre session). Voilà le bout de code :
BOOL EnablePrivilege(PTCHAR Privilege) { BOOL rc=FALSE; HANDLE hToken; LUID luid; TOKEN_PRIVILEGES tokenPrivilege; // // Open the current process' token. // rc=OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken); if(rc) { rc=LookupPrivilegeValue(NULL, Privilege, &luid); if(rc) { tokenPrivilege.PrivilegeCount = 1; tokenPrivilege.Privileges[0].Luid = luid; tokenPrivilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // // Assign the given privilege. // rc=AdjustTokenPrivileges( hToken, FALSE, &tokenPrivilege, sizeof(tokenPrivilege), NULL, NULL); } } if(hToken) CloseHandle(hToken); return rc; } BOOL EnableUserModeHardwareIO() { NTSTATUS Status; ULONG IOPL=3; // // Enable SeTcbPrivilege // if(!EnablePrivilege(SE_TCB_NAME)) return FALSE; // // Grant user mode hardware IO access. // Status=ZwSetInformationProcess( GetCurrentProcess(), ProcessUserModeIOPL, &IOPL, sizeof(IOPL)); if(!NT_SUCCESS(Status)) { printf("Error with ZwSetInformationProcess : 0x%x\n", Status); return FALSE; } return TRUE; }
Dorénavant on sait comment faire des I/O depuis le user-land, c’est beaucoup plus smooth que de coder un driver. Revenons donc à ce registre SMRAMC, ce qui nous intéresse c’est la valeur des bits D_OPEN et D_LCK, ceux qui contrôlent l’accès à la SMRAM. Par défaut le D_OPEN est à 0, signifiant que tout accès à une adresse comprise entre 0xA0000 et 0xBFFFF sera renvoyé vers la mémoire vidéo, par contre lorsque ce bit est à 1, le northbridge reroute les accès vers la SMRAM, c’est ce qui se passe lorsque le CPU est en SMM. Pour info voici à quoi ressemble le registre SMRAMC :
La question de-la-mort-fatale-atomique-qui-tue-avec-des-chocapicz est : Peut-on modifier ce bit librement pour accéder à la SMRAM sans être en SMM ?
La réponse est oui ! On peut avoir accès à la SMRAM ! Une fois le droit d’écriture obtenu il est possible d’ajouter notre propre handler en [SMBASE+0x8000] et hop le tour est joué ! Evidemment il faut prendre quelques précautions en écrivant un handler de SMI mais ca se fait. Je ne vais rentrer dans les détails de ce qu’il est possible de faire en SMM, vous avez les papers de phrack et de Duflot pour ça, nan je vais plutôt commencer à pleurer. En effet après avoir codé un tool qui dump le registre SMRAMC j’obtiens les résultats suivants :
Windows SMRAMC and ESMRAMC registers dumper Only works on Intel northbridge chipsets By Ivanlef0u BE M4D! Reading VID-Vendor Identification Register Writing CONF_ADDR register with 0x80000000 CONF_DATA register 0x8086 Reading SMRAM and ESMRAMC register Writing CONF_ADDR register with 0x8000009c CONF_DATA register 0xb81a Dumping SMRAM register ... RESERVED0_BIT : 0 D_OPEN_BIT : 0 D_CLS_BIT : 0 D_LCK_BIT : 1 G_SMRAME_BIT : 1 C_BASE_SEG2_BIT : 0 C_BASE_SEG1_BIT : 1 C_BASE_SEG0_BIT : 0 Writing CONF_ADDR register with 0x8000009c CONF_DATA register 0xb81a Dumping ESMRAMC register ... T_EN_BIT : 0 TSEG_SIZE_BIT0 : 0 TSEG_SIZE_BIT1 : 0 SM_L2_BIT : 1 SM_L1_BIT : 1 SM_CACHE_BIT : 1 E_SMERR_BIT : 0 H_SMRAME_BIT : 1
Regardez bien le bit D_LCK, il est à 1 ! Il verrouille donc le bit D_OPEN qui ne peut plus être modifié par l’utilisateur … YOU FAILED ! Ce qui est drôle c’est que tous les chipsets que j’ai pu tester avaient ce bit de lock à 1, m’empêchant ainsi de progresser dans mon ambition de conquête du monde. Juste un mot sur le registre ESMRAMC (Extended System Management RAM Control), pour faire court c’est une extension du registre SMRAMC, il permet de gérer le caching de la SMRAM ou bien un remapping de la SMRAM dans les adresses hautes.
Pour le moment on est au point mort, pas moyen de dumper cette foutue SMRAM. Loic Duflot propose dans sa thèse de contourner le bit D_OPEN en utilisant une feature du chipset AGP, l’ouverture graphique ou en anglais « aperture« , disponible sur les chipsets plus anciens. L’aperture permet au chipset AGP de faire croire à la carte graphique que le FrameBuffer est une zone contigue dans la mémoire physique alors qu’en réalité elle ne l’est pas, pour cela le chipset AGP agit comme un TLB (Translation Lookaside Buffer) mais au niveau des adresses physiques en les remappant vers d’autres adresses physiques. L’attaque pour contourner le bit D_LCK consiste en gros à crée une table de translation représentant la fonction identité, c’est à dire qui mappe chaque page sur elle-même, puis, comme si de rien n’était faire une écriture ou une lecture dans la SMRAM. Le chipset croyant qu’on tente un accès dans la mémoire vidéo va translater les adresses en fonction des TLB du chipset, en sachant que l’aperture est prioritaire sur la SMRAM la notre translation s’effectue sans problème nous laissant ainsi libre accès à la SMRAM. Dans la réalité cette technique est un peu plus compliqué que ça, d’ailleurs je pense que je n’ai pas tout parfaitement compris mais l’idée est là :p Au passage je fais de la pub pour les posts d’un pote qui à fait un travail intéressant sur le PCI et bootvid.dll.
Alors, de mon coté sur ma carte-mère je n’ai pas d’AGP, j’ai du PCI-Express, en lisant la doc on apprend que le mécanisme d’aperture n’existe plus avec le PCI-Express. Par contre le chipset Q963/Q965 possède un Integrated Graphics Device (IGD) une sorte de carte graphique de base qu’on peut configurer à travers le bus 0, device 2, function 0 (voir le schéma du bus PCI plus haut). Cet IGD offre la possibilité de mettre en place une Graphics Translation Table (GTT), un mécanisme d’aperture, W0ot ! Il ne reste plus qu’a faire le même type d’attaque qu’avec l’aperture AGP et c’est gagné. Sauf que la doc dit :
3.8.7 SMM Access Through GTT TLB (Intel 82Q965, 82Q963, 82G965 GMCH Only)
Accesses through GTT TLB address translation to enabled SMM DRAM space are no
allowed. Writes will be routed to memory address 000C_0000h with byte enables d
asserted and reads will be routed to memory address 000C_0000h. If a GTT TLB
translated address hits enabled SMM DRAM space, an error is recorded.PCI Express and DMI Interface originated accesses are never allowed to access SM
space directly or through the GTT TLB address translation. If a GTT TLB translated
address hits enabled SMM DRAM space, an error is recorded.PCI Express and DMI Interface write accesses through GMADR range will be snoop
Assesses to GMADR linear range are supported. PCI Express and DMI interface tile
and tileX writes to GMADR are not supported. If, when translated, the resulting
physical address is to enabled SMM DRAM space, the request will be remapped to
address 000C_0000h with de-asserted byte enables.PCI Express and DMI Interface read accesses to the GMADR range are not support
therefore, will have no address translation concerns. PCI Express and DMI interfac
reads to GMADR will be remapped to address 000C_0000h. The read will complete
with UR (unsupported request) completion status.
Pan ! Dans les dents ! L’accès à la SMRAM avec la GTT est vérifié, YOU FAILED AGAIN ! Y’en a marre … j’abandonne.
Après toutes ces tentatives je me demande comment actuellement avec les derniers chipsets il est possible d’accéder à la SMRAM sans être en SMM. C’est pourquoi je suis si sceptique sur la prochaine conf de Sherri Sparks et Shawn Embleton parce que le bit D_LCK est à 1 sur tous les chipsets récents et que les accès à la SMRAM sont sévèrement contrôlés. On peut très bien supposer qu’ils aient trouvé une technique géniale qui permet d’accéder à la SMRAM même si ces protections sont activées et là c’est de la balle mais j’avoue en douter fortement. Je dirais plus qu’ils ont fait leurs expériences sur des b0x débridées leurs offrant un accès sans problèmes à la SMRAM. Au final on se retrouvait avec une technique de furtivité de code impressionnante mais qui marche uniquement sur des machines datant de la guerre des druides. Bref wait and see …
En attendant voici le code qui permet de dumper les registres SMRAC et ESMRAMC :
http://ivanlef0u.fr/repo/smram.rar
Si quelqu’un connaît une technique permettant d’accéder à la SMRAM, je suis très intéressé et prêt à payer en chocapicz !!!
Réf :
http://www.rcollins.org/ddj/Jan97/Jan97.html
6 comments juillet 21st, 2008