GNU/Linux >> Tutoriels Linux >  >> Linux

Linux Shellcode Bonjour, Monde !

Comme BSH l'a mentionné, votre shellcode ne contient pas les octets de message. Sauter au MESSAGE label et en appelant le GOBACK routine juste avant de définir le msg octet était une bonne décision car l'adresse de msg serait en haut de la pile en tant qu'adresse de retour qui pourrait être sautée à ecx , où l'adresse de msg est stockée.

Mais le vôtre et le code de BSH ont une légère limitation. Il contient NULL bytes ( \x00 ) qui serait considéré comme une fin de chaîne lorsqu'il serait déréférencé par le pointeur de fonction.

Il existe un moyen intelligent de contourner cela. Les valeurs que vous stockez dans eax, ebx and edx sont suffisamment petits pour être directement écrits dans les quartets inférieurs des registres respectifs en une seule fois en accédant à al, bl and dl respectivement. Le quartet supérieur peut contenir une valeur indésirable afin qu'il puisse être xoré.

b8 04 00 00 00 ------ mov $0x4,%eax


devient

b0 04          ------ mov $0x4,%al
31 c0          ------ xor    %eax,%eax


Contrairement au jeu d'instructions précédent, le nouveau jeu d'instructions ne contient aucun octet NULL.

Ainsi, le programme final ressemble à ceci :

global _start

section .text

_start:
jmp message

proc:
    xor eax, eax
    mov al, 0x04
    xor ebx, ebx
    mov bl, 0x01
    pop ecx
    xor edx, edx
    mov dl, 0x16
    int 0x80

    xor eax, eax
    mov al, 0x01
    xor ebx, ebx
    mov bl, 0x01   ; return 1
    int 0x80

message:
    call proc
    msg db " y0u sp34k 1337 ? "

section .data

Assemblage et liaison :

$ nasm -f elf hello.asm -o hello.o
$ ld -s -m elf_i386 hello.o -o hello
$ ./hello
 y0u sp34k 1337 ? $ 

Extrayez maintenant le shellcode du binaire hello :

$ for i in `objdump -d hello | tr '\t' ' ' | tr ' ' '\n' | egrep '^[0-9a-f]{2}$' ` ; do echo -n "\\x$i" ; done

sortie :

\xeb\x19\x31\xc0\xb0\x04\x31\xdb\xb3\x01\x59\x31\xd2\xb2\x12\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xb3\x01\xcd\x80\xe8\xe2\xff\xff\xff\x20\x79\x30\x75\x20\x73\x70\x33\x34\x6b\x20\x31\x33\x33\x37\x20\x3f\x20

Maintenant, nous pouvons avoir notre programme de pilote pour lancer le shellcode.

#include <stdio.h>

char shellcode[] = "\xeb\x19\x31\xc0\xb0\x04\x31\xdb"
                   "\xb3\x01\x59\x31\xd2\xb2\x12\xcd"
                   "\x80\x31\xc0\xb0\x01\x31\xdb\xb3"
                   "\x01\xcd\x80\xe8\xe2\xff\xff\xff"
                   "\x20\x79\x30\x75\x20\x73\x70\x33"
                   "\x34\x6b\x20\x31\x33\x33\x37\x20"
                   "\x3f\x20";


int main(int argc, char **argv) {
    (*(void(*)())shellcode)();
    return 0;
}

Il existe certaines fonctionnalités de sécurité dans les compilateurs modernes comme la protection NX qui empêche l'exécution de code dans le segment ou la pile de données. Nous devons donc spécifier explicitement le compilateur pour les désactiver.

$ gcc -g -Wall -fno-stack-protector -z execstack launcher.c -o launcher

Maintenant le launcher peut être invoqué pour lancer le shellcode.

$ ./launcher
 y0u sp34k 1337 ? $ 

Pour les shellcodes plus complexes, il y aurait un autre obstacle. Les noyaux Linux modernes ont ASLR ou Address Space Layout Randomization Vous devrez peut-être désactiver cela avant d'injecter le shellcode, en particulier lorsqu'il s'agit de débordements de tampon.

[email protected]:~# echo 0 > /proc/sys/kernel/randomize_va_space 

Lorsque vous injectez ce shellcode, vous ne savez pas ce qu'il y a en message :

mov ecx, message

dans le processus injecté, ça peut être n'importe quoi mais ce ne sera pas "Hello world!\r\n" puisqu'il se trouve dans la section des données alors que vous ne videz que la section de texte. Vous pouvez voir que votre shellcode n'a pas "Hello world!\r\n" :

"\xb8\x04\x00\x00\x00"
"\xbb\x01\x00\x00\x00"
"\xb9\x00\x00\x00\x00"
"\xba\x0f\x00\x00\x00"
"\xcd\x80\xb8\x01\x00"
"\x00\x00\xbb\x00\x00"
"\x00\x00\xcd\x80";

C'est un problème courant dans le développement de shellcode, la façon de le contourner est la suivante :

global _start

section .text

_start:
    jmp MESSAGE      ; 1) lets jump to MESSAGE

GOBACK:
    mov eax, 0x4
    mov ebx, 0x1
    pop ecx          ; 3) we are poping into `ecx`, now we have the
                     ; address of "Hello, World!\r\n" 
    mov edx, 0xF
    int 0x80

    mov eax, 0x1
    mov ebx, 0x0
    int 0x80

MESSAGE:
    call GOBACK       ; 2) we are going back, since we used `call`, that means
                      ; the return address, which is in this case the address 
                      ; of "Hello, World!\r\n", is pushed into the stack.
    db "Hello, World!", 0dh, 0ah

section .data

Videz maintenant la section de texte :

$ nasm -f elf shellcode.asm
$ ld shellcode.o -o shellcode
$ ./shellcode 
Hello, World!
$ objdump -d shellcode

shellcode:     file format elf32-i386


Disassembly of section .text:

08048060 <_start>:
 8048060:   e9 1e 00 00 00   jmp    8048083 <MESSAGE>

08048065 <GOBACK>:
 8048065:   b8 04 00 00 00   mov    $0x4,%eax
 804806a:   bb 01 00 00 00   mov    $0x1,%ebx
 804806f:   59               pop    %ecx
 8048070:   ba 0f 00 00 00   mov    $0xf,%edx
 8048075:   cd 80            int    $0x80
 8048077:   b8 01 00 00 00   mov    $0x1,%eax
 804807c:   bb 00 00 00 00   mov    $0x0,%ebx
 8048081:   cd 80            int    $0x80

08048083 <MESSAGE>:
 8048083:   e8 dd ff ff ff   call   8048065 <GOBACK>
 8048088:   48               dec    %eax                    <-+
 8048089:   65               gs                               |
 804808a:   6c               insb   (%dx),%es:(%edi)          |
 804808b:   6c               insb   (%dx),%es:(%edi)          |
 804808c:   6f               outsl  %ds:(%esi),(%dx)          |
 804808d:   2c 20            sub    $0x20,%al                 |
 804808f:   57               push   %edi                      |
 8048090:   6f               outsl  %ds:(%esi),(%dx)          |
 8048091:   72 6c            jb     80480ff <MESSAGE+0x7c>    |
 8048093:   64               fs                               |
 8048094:   21               .byte 0x21                       |
 8048095:   0d               .byte 0xd                        |
 8048096:   0a               .byte 0xa                      <-+

$

Les lignes que j'ai marquées sont nos "Hello, World!\r\n" chaîne :

$ printf "\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c\x64\x21\x0d\x0a"
Hello, World!

$ 

Ainsi, notre wrapper C sera :

char code[] = 

    "\xe9\x1e\x00\x00\x00"  //          jmp    (relative) <MESSAGE>
    "\xb8\x04\x00\x00\x00"  //          mov    $0x4,%eax
    "\xbb\x01\x00\x00\x00"  //          mov    $0x1,%ebx
    "\x59"                  //          pop    %ecx
    "\xba\x0f\x00\x00\x00"  //          mov    $0xf,%edx
    "\xcd\x80"              //          int    $0x80
    "\xb8\x01\x00\x00\x00"  //          mov    $0x1,%eax
    "\xbb\x00\x00\x00\x00"  //          mov    $0x0,%ebx
    "\xcd\x80"              //          int    $0x80
    "\xe8\xdd\xff\xff\xff"  //          call   (relative) <GOBACK>
    "Hello wolrd!\r\n";     // OR       "\x48\x65\x6c\x6c\x6f\x2c\x20\x57"
                            //          "\x6f\x72\x6c\x64\x21\x0d\x0a"


int main(int argc, char **argv)
{
    (*(void(*)())code)();

    return 0;
}

Testons-le, en utilisant -z execstack pour activer read-implies-exec (à l'échelle du processus, malgré "stack" dans le nom) afin que nous puissions exécuter du code dans le .data ou .rodata rubriques :

$ gcc -m32 test.c -z execstack -o test
$ ./test 
Hello wolrd!

Ça marche. (-m32 est également nécessaire sur les systèmes 64 bits. Le int $0x80 L'ABI 32 bits ne fonctionne pas avec les adresses 64 bits telles que .rodata dans un exécutable PIE. De plus, le code machine a été assemblé pour 32 bits. Il arrive que la même séquence d'octets soit décodée en instructions équivalentes en mode 64 bits, mais ce n'est pas toujours le cas.)

GNU moderne ld met .rodata dans un segment distinct de .text , il peut donc être non exécutable. Auparavant, il suffisait d'utiliser const char code[] pour mettre du code exécutable dans une page de données en lecture seule. Au moins pour le shellcode qui ne veut pas se modifier.


Linux
  1. Exemple Cobol Hello World :comment écrire, compiler et exécuter un programme Cobol sur un système d'exploitation Linux

  2. Exemple Tcl Hello World :comment écrire, compiler et exécuter un programme Tcl sur un système d'exploitation Linux

  3. Existe-t-il un équivalent au .Net FileSystemWatcher dans le monde Linux ?

  4. Exécutez le code C # sur le terminal Linux

  5. Comment monter un lecteur réseau WD MyBook World sous Linux ?

Commande W sous Linux

Utilisez la commande Linux grep

Un gars Windows dans un monde Linux :VS Code et SSH distant

Un gars Windows dans un monde Linux :Configuration de la commande SSH sous Linux

Un gars Windows dans un monde Linux :utilisateurs et autorisations de fichiers

Un gars Windows dans un monde Linux :YaST et le bureau