2013-03-23 7 views
23

У меня есть следующий рабочий код NASM:Linux Shellcode «Привет, мир!»

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 

, которая печатает "Hello, World! \ П" к экрану. У меня также есть следующая C обертка, которая содержит предыдущий код объекта NASM:

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

Однако, когда я запускаю код, который, похоже, ассемблерный код не выполняется, а программа завершается нормально. Есть идеи?

Благодаря

ответ

63

Когда Вы вводите этот шеллкода, вы не знаете, что в message:

mov ecx, message 

в нагнетаемой процессе, это может быть что угодно, но это не будет "Hello world!\r\n", так как это в разделе данных, когда вы выгружаете только текстовый раздел. Вы можете видеть, что ваш Шеллкод не "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"; 

Это общая проблема в развитии шелкодов, способ обойти этот путь:

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 

Теперь дамп текстовый раздел:

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

$ 

линии, помеченные наш "Hello, World!\r\n" строка:

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

$ 

Таким образом, наша C обертка будет:

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

Позволяет проверить:

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

это работает.

+2

Не знаете, почему у вас не было никаких переломов, но это был отличный ответ. Спасибо за помощь. –

+0

нулевые байты должны быть удалены, чтобы выполнить шеллкода Тхо – REALFREE

+2

@REALFREE Нулевой байт будет проблемой, если вы работаете с функцией требуется нулем строку, как строковые функции как 'strcpy', он не будет читать всю шеллкода, строку. В противном случае все нормально. – 2013-10-15 13:38:21

18

Как указано BSH, ваш шеллкод не содержит байтов сообщения. Переход к метке MESSAGE и вызов подпрограммы GOBACK перед определением байта msg был хорошим шагом, так как адрес msg находился бы в верхней части стека в качестве адреса возврата, который можно было бы выскочить на ecx, где хранится адрес msg ,

Но и ваш, и код BSH имеет небольшое ограничение. Он содержит NULL bytes (\x00), который будет рассматриваться как конец строки при разыменовании указателем функции.

Существует разумный способ обойти это. Значения, которые вы храните в eax, ebx and edx, достаточно малы, чтобы их можно было непосредственно записать в нижние полубайты соответствующих регистров за один проход, обратившись к al, bl and dl соответственно. Верхний полубайт может содержать нежелательное значение, чтобы его можно было хранить.

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


становится

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


В отличие от множества ранее инструкций, новый набор команд не содержит NULL байт.

Таким образом, окончательная программа выглядит следующим образом:

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 

Сборка и соединение:

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

Теперь извлечь шеллкод из приветствия двоичного:

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

выхода:

\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 

Теперь мы можем иметь нашу программу драйвера для запуска шеллкода.

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

Есть определенные функции безопасности в современных компиляторов, как NX protection, который предотвращает выполнение кода в сегменте данных или стека. Поэтому мы должны явно указать компилятор, чтобы отключить их.

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

Теперь launcher может быть вызван для запуска шеллкода.

$ ./launcher 
y0u sp34k 1337 ? $ 

Для более сложных шельфовых кодов было бы еще одно препятствие. Современные ядра Linux имеют ASLR или Address Space Layout Randomization Вам может понадобиться отключить это перед вашим впрыскивать шеллкод, особенно когда речь идет через переполнение буфера.

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