web-dev-qa-db-fra.com

Pourquoi mon programme de test de shellcode x86 est-il défectueux?

J'essaie d'apprendre à créer un shellcode, mon objectif est d'appeler execve(). J'ai écrit le code dans Assembly et cela fonctionne parfaitement, il n'y a pas de méthodes d'adressage nul ou absolu. Le code fonctionne bien, mais quand je copie les opcodes dans un programme c et le teste pour voir s'il fonctionne, il renvoie un défaut de segmentation. Quel est le problème?

ASM:

section .text     
global _start
_start:
    jmp xxx

xx:
    mov al,11
    pop ebx
    xor ecx,ecx
    xor edx,edx
    int 0x80
    mov al,1
    xor bl,bl
    int 0x80

section .data
xxx:
    call xx
path db "/bin/sh"

CODE C:

char shellcode[]={"\xb0\x0b\x5b\x31\xc9\x31\xd2\xcd\x80\xb0\x01\x30\xdb\xcd\x80"};

int main(){
    void (*ptr) (void) = &shellcode;
    ptr();
    return 0;
}
7
tropz

Je vois plusieurs problèmes avec votre shellcode. Commençons par déboguer votre code. J'ai compilé le code C contenant votre shellcode, l'exécuter avec gdb et étape jusqu'au premier appel système (int 0x80)

[----------------------------------registers-----------------------------------]
EAX: 0x5655700b --> 0xde3050f7 
EBX: 0x5655550c (<main+35>: mov    eax,0x0)
ECX: 0x0 
EDX: 0x0 
ESI: 0xf7f9fe24 --> 0x1d6d2c 
EDI: 0xf7f9fe24 --> 0x1d6d2c 
EBP: 0xffffd9d8 --> 0x0 
ESP: 0xffffd9c0 --> 0xf7fe5ae0 (<_dl_fini>: Push   ebp)
EIP: 0x5655701f --> 0x1b080cd
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x5655701a <shellcode+2>:    pop    ebx
   0x5655701b <shellcode+3>:    xor    ecx,ecx
   0x5655701d <shellcode+5>:    xor    edx,edx
=> 0x5655701f <shellcode+7>:    int    0x80
   0x56557021 <shellcode+9>:    mov    al,0x1
   0x56557023 <shellcode+11>:   xor    bl,bl
   0x56557025 <shellcode+13>:   int    0x80
   0x56557027 <shellcode+15>:   add    BYTE PTR [eax],al
[------------------------------------stack-------------------------------------]
0000| 0xffffd9c0 --> 0xf7fe5ae0 (<_dl_fini>:    Push   ebp)
0004| 0xffffd9c4 --> 0x0 
0008| 0xffffd9c8 (")UUV\030pUV$\376\371\367$\376\371\367")
0012| 0xffffd9cc --> 0x56557018 --> 0x315b0bb0 
0016| 0xffffd9d0 --> 0xf7f9fe24 --> 0x1d6d2c 
0020| 0xffffd9d4 --> 0xf7f9fe24 --> 0x1d6d2c 
0024| 0xffffd9d8 --> 0x0 
0028| 0xffffd9dc --> 0xf7de3141 (<__libc_start_main+241>:   add    esp,0x10)
[------------------------------------------------------------------------------]

Ici, nous pouvons voir quelques problèmes:

  • Le registre EAX n'est pas défini sur 0xb. Cela est dû au fait que votre shellcode n'efface pas les valeurs dans le registre et qu'il définit simplement l'octet inférieur avec l'instruction mov al, 0xb
  • Le registre EBX doit pointer vers le char* avec le fichier que vous essayez d'exécuter (généralement c'est "/bin/sh"), au lieu de cela, il pointe vers une position de mémoire aléatoire, dans ce cas dans la fonction main.
  • Le registre ECX doit pointer vers le tableau de char* qui indique la commande complète que vous souhaitez exécuter. Dans la plupart des shellcodes que j'ai vus c'est juste ["/bin/sh", 0], mais vous souhaiterez peut-être utiliser quelque chose de différent, comme ["/path/to/binary","-argument1",..., 0], dans ce cas, vous devez créer ce tableau en mémoire. Dans votre shellcode ECX est défini sur 0x0
  • Le registre EDX représente l'environnement pour l'exécution du binaire. Vous pouvez jeter un oeil à la page manuel execve (3) pour comprendre un peu plus comment il est utilisé, mais pour notre objectif ici, il est correct d'avoir une valeur NULL dedans

Maintenant, comment pouvons-nous y remédier? Eh bien, tout d'abord, nous devrons pointer EBX vers une chaîne "/ bin/sh\0" dans une partie mémoire à laquelle nous pouvons accéder, par exemple. la pile. Et nous devons le faire dans notre shellcode. Nous pouvons le faire avec le gadget suivant:

xor eax, eax //Clear the eax register so we have a null byte to end our string
Push eax
Push "n/sh" //The string needs to be written "backwards"
Push "//bi" //The double "/" is to avoid null bytes in our shellcode
mov ebx, esp //esp is pointing to "//bin/sh\0", so we need to move that pointer to ebx

Ensuite, nous devons pointer ECX vers un tableau de char* tel que ["/bin/sh", 0]. Nous en avons déjà une partie dans EBX, donc en continuant notre shellcode nous pouvons faire ce qui suit:

xor eax, eax 
Push eax
Push "n/sh"
Push "//bi"
mov ebx, esp
Push eax // Remember it's still 0 from our previous xor eax, eax
Push ebx // Push it so ESP points to EBX
mov ecx, esp // move ESP to ECX, the result is ECX -> EBX -> "//bin/sh\0"

Enfin, nous devons définir le registre AL sur 0xb et faites l'appel système. Notre shellcode final devrait donc ressembler à ceci:

section .text
global _start
_start:
    jmp trampoline

shellcode:
    xor eax, eax
    Push eax
    Push "n/sh"
    Push "//bi"
    mov ebx, esp
    Push eax
    Push ebx
    mov ecx, esp
    mov al,11
    int 0x80

section .data
trampoline:
    call shellcode

Nous pouvons le compiler nasm nasm -o shellcode.bin -f elf32 -O0 shellcode.nasm, extrayez les opcodes et mettez-les dans le code C pour le tester:

char shellcode[] = "\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";
int main(){
    (*(void(*)())shellcode)();
    return 0;
}

Compilez-le avec votre compilateur préféré, j'utilise gcc gcc -o shellcode.o -fno-stack-protector -z execstack -m32 shellcode.c. Et lancez-le:

$ ./shellcode.o 
sh-4.4$ 
20
Mr. E