2013-03-23 14 views
23

Mam następujący kod nasm pracy:Linux Shellcode "Hello, World!"

global _start 

section .text 

_start: 
    mov eax, 0x4 
    mov ebx, 0x1 
    mov ecx, message 
    mov edx, 0xF 
    int 0x80 

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

section .data 
    message: db "Hello, World!", 0dh, 0ah 

która drukuje "Hello, World \ n" na ekranie. Mam także następujące C opakowanie, które zawiera poprzednią nasm kodu wynikowego:

char code[] = 
"\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"; 

int main(void) 
{ 
    (*(void(*)())code)(); 
} 

Jednak gdy uruchamiam kod, wydaje się, że w kodzie asemblera nie jest wykonywany, ale program wychodzi dobrze. Jakieś pomysły?

Dzięki

Odpowiedz

63

Kiedy wstrzyknąć ten szelkod, nie wiem co jest w message:

mov ecx, message 

w wtryskiwanego procesu, może to być cokolwiek, ale to nie będzie "Hello world!\r\n" ponieważ jest w sekcji danych podczas przesyłania tylko sekcji tekstowej. Można zobaczyć, że szelkod nie posiada "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"; 

Jest to powszechny problem w rozwoju szelkod, sposób obejść to w ten sposób:

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 

teraz zrzucić sekcji tekstowej:

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

$ 

linie I oznaczone są naszą "Hello, World!\r\n" ciąg:

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

$ 

Więc nasz wrapper C będą:

char code[] = 

    "\xe9\x1e\x00\x00\x00" //   jmp 8048083 <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 8048065 <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; 
} 

Lets go przetestować:

$ gcc test.c -o test 
$ ./test 
Hello wolrd! 
$ 

to działa.

+2

Nie jestem pewna, dlaczego nie udało Ci się zdobyć żadnych głosów, ale to była świetna odpowiedź. Dzięki za pomoc. –

+0

bajty null powinny zostać usunięte, aby wykonać kod powłoki tho – REALFREE

+2

@REALFREE Bajt Null byłby problemem, jeśli pracujesz z funkcją, która wymaga zakończonych znakiem NUL ciągów jak funkcje łańcuchowe jako 'strcpy', nie odczyta całego kodu powłoki, łańcucha. W przeciwnym razie jest OK. –

18

Jako wspomniany numer BSH, Twój kod powłoki nie zawiera bajtów wiadomości. Przeskakiwanie do etykiety MESSAGE i wywoływanie procedury GOBACK tuż przed zdefiniowaniem bajtu było dobrym posunięciem, ponieważ adres msg byłby na wierzchu stosu jako adres zwrotny, który mógłby zostać wyświetlony na ecx, gdzie przechowywany jest adres msg .

Ale zarówno twój, jak i kod BSH ma niewielkie ograniczenie. Zawiera NULL bytes (\x00), który byłby uważany za koniec ciągu, gdy został odwołany przez wskaźnik funkcji.

Istnieje sprytny sposób obejścia tego. Wartości, które przechowujesz pod eax, ebx and edx, są na tyle małe, że można je zapisać bezpośrednio w niższych numerach poszczególnych rejestrów za jednym razem, uzyskując dostęp do odpowiednio al, bl and dl. Górna partia może zawierać wartość śmieciową, dzięki czemu może zostać zmieniona.

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


staje

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


przeciwieństwie do wcześniejszego zestawu instrukcji, nowy zestaw instrukcji nie zawiera żadnych bajt NULL.

więc końcowy program wygląda następująco:

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 

Montaż i łączenie:

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

Teraz wyodrębnić szelkod od binarnego Witaj

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

wyjściowa:

\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 

Teraz możemy mieć program do uruchomienia kodu powłoki.

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

Istnieją pewne cechy bezpieczeństwa w nowoczesnych kompilatorów jak NX protection który uniemożliwia wykonanie kodu w segmencie danych lub stosu. Dlatego powinniśmy wyraźnie określić kompilator, aby je wyłączyć.

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

Teraz launcher mogą być powoływane w celu uruchomienia szelkod.

$ ./launcher 
y0u sp34k 1337 ? $ 

Dla bardziej złożonych skorup, pojawiłaby się kolejna przeszkoda. Współczesne jądra Linux mają ASLR lub Address Space Layout Randomization Może być konieczne wyłączenie tego przed wstrzyknięciem kodu powłoki, szczególnie gdy jest to spowodowane przepełnieniem bufora.

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