programing

리눅스 셸코드 "안녕, 세상이여!"

powerit 2023. 10. 10. 21:14
반응형

리눅스 셸코드 "안녕, 세상이여!"

작동하는 NASM 코드는 다음과 같습니다.

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!\n"를 화면에 출력합니다.이전 NASM 개체 코드가 들어있는 다음 C 포장지도 가지고 있습니다.

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

하지만 코드를 실행하면 어셈블러 코드가 실행되지 않는 것 같은데 프로그램은 잘 종료됩니다.무슨 생각 있어요?

감사해요.

셸에 알 수 .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    (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;
}

사용하여 코드를 실행할 수 있도록 읽기-구현-실행(이름에 "스택"이 있음에도 불구하고 프로세스 전반에 걸쳐)을 활성화하여 테스트해 보겠습니다..data아니면.rodata섹션:

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

효과가 있습니다. (-m3264비트 시스템에서도 필요합니다. 더int $0x80는 32비트 ABI은 64다와 .rodataPIE 실행 파일또한 32비트용으로 기계 코드를 조립하였습니다.에서 동일한 는 않습니다64다.

GNUld.rodata.text 할 수 실행 .했습니다를 해도 충분했습니다.const char code[]실행 코드를 읽기 전용 데이터 페이지에 입력합니다.적어도 자신을 수정하고 싶지 않은 셸코드에 대해서는 말입니다.

BSH가 언급한 바와 같이 셸코드에는 메시지 바이트가 포함되어 있지 않습니다.점프 투 더MESSAGE을 붙이고를 부름GOBACK틴을 을 수행합니다.msg는 msg이었습니다에 수 였습니다.ecx됩니다.에 msg 의됩니다.

하지만 당신의 코드와 BSH 코드 모두 약간의 한계가 있습니다.들어있습니다NULL bytes ( \x00 )함수 포인터에 의해 역참조될 때 문자열의 끝으로 간주됩니다.

이것을 해결할 수 있는 현명한 방법이 있습니다.seax, ebx and edx다에 한각을 수 로 작습니다.al, bl and dl각각 다음과 같다.위쪽 니블에는 고물 값이 포함되어 있어 xored할 수 있습니다.

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

이제 hello 바이너리에서 셸 코드를 추출합니다.

$ 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 프로텍션과 같은 최신 컴파일러에는 데이터 세그먼트나 스택에서 코드를 실행할 수 없도록 하는 특정한 보안 기능이 있습니다.그래서 우리는 컴파일러를 명시적으로 지정하여 이것들을 비활성화해야 합니다.

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

이제.launcher를 호출하여 셸 코드를 시작할 수 있습니다.

$ ./launcher
 y0u sp34k 1337 ? $ 

좀 더 복잡한 셸 코드의 경우, 또 다른 장애물이 있을 것입니다.현대 리눅스 커널들은 ASLR을 갖거나Address Space Layout Randomization셸 코드를 주입하기 전에 이를 비활성화해야 할 수도 있습니다. 특히 버퍼 오버플로를 통해 셸 코드를 주입할 때 그렇습니다.

root@localhost:~# echo 0 > /proc/sys/kernel/randomize_va_space 

언급URL : https://stackoverflow.com/questions/15593214/linux-shellcode-hello-world

반응형