GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Linux-Shellcode Hallo, Welt!

Wie BSH erwähnt, enthält Ihr Shellcode die Nachrichtenbytes nicht. Springen zu MESSAGE Label und Aufruf der GOBACK Routine kurz vor der Definition von msg byte war ein guter Schachzug, da die Adresse von msg oben auf dem Stack als Rücksprungadresse stehen würde, die in ecx eingefügt werden könnte , wo die Adresse von msg gespeichert ist.

Aber sowohl Ihr als auch der Code von BSH hat eine leichte Einschränkung. Er enthält NULL bytes ( \x00 ) die als Ende der Zeichenfolge betrachtet würde, wenn sie vom Funktionszeiger dereferenziert wird.

Es gibt einen cleveren Weg, dies zu umgehen. Die Werte speichern Sie in eax, ebx and edx sind klein genug, um durch Zugriff auf al, bl and dl auf einmal direkt in die unteren Nibbles der jeweiligen Register geschrieben zu werden Das obere Halbbyte kann Junk-Wert enthalten, sodass es xored werden kann.

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


wird

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


Im Gegensatz zum vorherigen Befehlssatz enthält der neue Befehlssatz kein NULL-Byte.

Das fertige Programm sieht also so aus:

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

Zusammenbauen und Verlinken :

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

Extrahieren Sie nun den Shellcode aus der Hello-Binärdatei :

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

Ausgabe:

\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

Jetzt können wir unser Treiberprogramm haben, um den Shellcode zu starten.

#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;
}

In modernen Compilern gibt es bestimmte Sicherheitsfunktionen wie den NX-Schutz, der die Ausführung von Code in Datensegmenten oder Stacks verhindert. Daher sollten wir den Compiler explizit angeben, um diese zu deaktivieren.

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

Jetzt die launcher kann aufgerufen werden, um den Shellcode zu starten.

$ ./launcher
 y0u sp34k 1337 ? $ 

Bei komplexeren Shellcodes gäbe es eine weitere Hürde. Moderne Linux-Kernel haben ASLR oder Address Space Layout Randomization Möglicherweise müssen Sie dies deaktivieren, bevor Sie den Shellcode einfügen, insbesondere wenn es durch Pufferüberläufe geht.

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

Wenn Sie diesen Shellcode einfügen, wissen Sie nicht, was bei message ist :

mov ecx, message

im injizierten Prozess kann es alles sein, aber es wird nicht "Hello world!\r\n" sein da es sich im Datenabschnitt befindet, während Sie nur den Textabschnitt ausgeben. Sie können sehen, dass Ihr Shellcode nicht "Hello world!\r\n" hat :

"\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";

Dies ist ein häufiges Problem in der Shellcode-Entwicklung, der Weg, es zu umgehen, ist folgender:

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

Sichern Sie nun den Textabschnitt:

$ 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                      <-+

$

Die Zeilen, die ich markiert habe, sind unsere "Hello, World!\r\n" Zeichenkette:

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

$ 

Unser C-Wrapper wird also sein:

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;
}

Testen wir es mit -z execstack um read-implies-exec (prozessweit, trotz "stack" im Namen) zu aktivieren, damit wir Code in .data ausführen können oder .rodata Abschnitte:

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

Es klappt. (-m32 ist auch auf 64-Bit-Systemen erforderlich. Die int $0x80 32-Bit-ABI funktioniert nicht mit 64-Bit-Adressen wie .rodata in einer ausführbaren PIE-Datei. Außerdem wurde der Maschinencode für 32-Bit zusammengestellt. Es kommt vor, dass dieselbe Folge von Bytes im 64-Bit-Modus in äquivalente Anweisungen dekodiert wird, aber das ist nicht immer der Fall.)

Modernes GNU ld setzt .rodata in einem separaten Segment von .text , kann also nicht ausführbar sein. Früher reichte es aus, const char code[] zu verwenden um ausführbaren Code in eine Seite mit schreibgeschützten Daten einzufügen. Zumindest für Shellcode, der sich nicht selbst modifizieren will.


Linux
  1. Bash-Skript – Beispiel „Hello World“.

  2. Smalltalk Hello World Beispiel:Wie man ein Smalltalk-Programm unter Linux OS schreibt und ausführt

  3. Fortran Hello World Beispiel:Wie man ein Fortran-Programm unter Linux OS schreibt und ausführt

  4. Gibt es in der Linux-Welt ein Äquivalent zum .Net FileSystemWatcher?

  5. Wie füge ich meine eigene Software zu einem Buildroot-Linux-Paket hinzu?

W-Befehl unter Linux

Verwenden Sie den Linux-Befehl grep

Ein Windows-Typ in einer Linux-Welt:VS-Code und Remote-SSH

Ein Windows-Typ in einer Linux-Welt:Einrichten des SSH-Befehls in Linux

Ein Windows-Typ in einer Linux-Welt:Benutzer- und Dateiberechtigungen

Ein Windows-Typ in einer Linux-Welt:YaST und der Desktop