GNU/Linux >> Znalost Linux >  >> Linux

Linux Shellcode Dobrý den, světe!

Jak zmínil BSH, váš kód shellu neobsahuje bajty zprávy. Skok na MESSAGE a volání GOBACK rutina těsně před definováním msg byte byl dobrý krok, protože adresa msg by byla na vrcholu zásobníku jako návratová adresa, kterou by bylo možné přepnout na ecx , kde je uložena adresa msg.

Ale váš i BSH kód má mírné omezení. Obsahuje NULL bytes ( \x00 ) který by byl považován za konec řetězce při dereferencování ukazatelem funkce.

Existuje chytrý způsob, jak to obejít. Hodnoty, které uložíte do eax, ebx and edx jsou dostatečně malé na to, aby je bylo možné přímo zapsat do spodních kousků příslušných registrů najednou přístupem al, bl and dl respektive. Horní nibble může obsahovat nevyžádanou hodnotu, takže může být xored.

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


se stává

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


Na rozdíl od předchozí instrukční sady neobsahuje nová instrukční sada žádný bajt NULL.

Takže konečný program vypadá takto:

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

Sestavení a propojení :

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

Nyní extrahujte shell kód z binárky hello :

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

výstup:

\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

Nyní můžeme mít náš ovladač, který spustí shell kód.

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

V moderních kompilátorech existují určité bezpečnostní funkce, jako je ochrana NX, která zabraňuje spuštění kódu v datovém segmentu nebo zásobníku. Měli bychom tedy explicitně specifikovat kompilátor, který je zakáže.

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

Nyní launcher lze vyvolat ke spuštění kódu shellu.

$ ./launcher
 y0u sp34k 1337 ? $ 

U složitějších shell kódů by byla další překážka. Moderní linuxová jádra mají ASLR nebo Address Space Layout Randomization Možná budete muset tuto funkci deaktivovat, než vložíte kód shellu, zvláště když dojde k přetečení vyrovnávací paměti.

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

Když vložíte tento shell kód, nevíte, co je na message :

mov ecx, message

v injektovaném procesu to může být cokoliv, ale nebude to "Hello world!\r\n" protože je v sekci dat, zatímco vyhazujete pouze textovou sekci. Můžete vidět, že váš shell kód nemá "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";

Toto je běžný problém při vývoji shellkódu, způsob, jak to obejít, je tento:

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

Nyní vypište textovou sekci:

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

$

Řádky, které jsem označil, jsou naše "Hello, World!\r\n" řetězec:

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

$ 

Takže náš obal C bude:

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

Pojďme to otestovat pomocí -z execstack abychom povolili read-implies-exec (celoprocesové, navzdory "zásobníku" v názvu), abychom mohli spouštět kód v .data nebo .rodata sekce:

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

Funguje to. (-m32 je nezbytný také na 64bitových systémech. int $0x80 32bitové rozhraní ABI nefunguje s 64bitovými adresami, jako je .rodata ve spustitelném souboru PIE. Strojový kód byl také sestaven pro 32-bit. Stává se, že stejná sekvence bajtů by se dekódovala na ekvivalentní instrukce v 64bitovém režimu, ale není tomu tak vždy.)

Moderní GNU ld vloží .rodata v samostatném segmentu od .text , takže může být nespustitelný. Dříve stačilo použít const char code[] vložit spustitelný kód na stránku s daty pouze pro čtení. Alespoň pro shell kód, který se nechce sám upravovat.


Linux
  1. Smalltalk Hello World Příklad:Jak napsat a spustit program Smalltalk na OS Linux

  2. Příklad Fortran Hello World:Jak napsat a spustit program Fortran na OS Linux

  3. Příklad XQuery Hello World:Jak napsat a spustit program XQuery na OS Linux

  1. Příklad Tcl Hello World:Jak psát, kompilovat a spouštět program Tcl na OS Linux

  2. Existuje ve světě Linuxu ekvivalent k .Net FileSystemWatcher?

  3. Jak přidat svůj vlastní software do balíčku Buildroot Linux?

  1. Začínáme s linuxovým příkazem cat

  2. Spusťte C# kód na linuxovém terminálu

  3. Jak připojím síťový disk WD MyBook World v linuxu?