c linux assembly nasm shellcode

Shellcode de Linux "¡Hola, mundo!"



assembly nasm (2)

Tengo el siguiente código NASM funcional:

global _start section .text _start: mov eax, 0x4 mov ebx, 0x1 mov ecx, message mov edx, 0xF int 0x80 mov eax, 0x1 mov ebx, 0x0 int 0x80 section .data message: db "Hello, World!", 0dh, 0ah

que imprime "Hello, World! / n" en la pantalla. También tengo el siguiente contenedor C que contiene el código de objeto NASM anterior:

char code[] = "/xb8/x04/x00/x00/x00" "/xbb/x01/x00/x00/x00" "/xb9/x00/x00/x00/x00" "/xba/x0f/x00/x00/x00" "/xcd/x80/xb8/x01/x00" "/x00/x00/xbb/x00/x00" "/x00/x00/xcd/x80"; int main(void) { (*(void(*)())code)(); }

Sin embargo, cuando ejecuto el código, parece que el código del ensamblador no se ejecuta, pero el programa sale bien. ¿Algunas ideas?

Gracias


Como mencionó BSH , su shellcode no contiene los bytes del mensaje. Saltar a la etiqueta MESSAGE y llamar a la rutina GOBACK justo antes de definir el byte msg fue una buena jugada ya que la dirección de msg estaría en la parte superior de la pila como dirección de retorno que podría aparecer en ecx , donde se almacena la dirección de msg .

Pero tanto el suyo como el código de BSH tienen una pequeña limitación. Contiene NULL bytes ( /x00 ) que se considerarían como fin de cadena cuando el puntero de función desreferencia.

Hay una manera inteligente de evitar esto. Los valores que almacena en eax, ebx and edx son lo suficientemente pequeños para escribirse directamente en los nibbles inferiores de los registros respectivos de una vez accediendo a al, bl and dl respectivamente. El nibble superior puede contener valor no deseado por lo que puede ser definido.

b8 04 00 00 00 ------ mov $0x4,%eax


se convierte

b0 04 ------ mov $0x4,%al 31 c0 ------ xor %eax,%eax


A diferencia del conjunto de instrucciones anterior, el nuevo conjunto de instrucciones no contiene ningún byte NULL.

Entonces, el programa final se ve así:

global _start section .text _start: jmp message proc: xor eax, eax mov al, 0x04 xor ebx, ebx mov bl, 0x01 pop ecx xor edx, edx mov dl, 0x16 int 0x80 xor eax, eax mov al, 0x01 xor ebx, ebx mov bl, 0x01 ; return 1 int 0x80 message: call proc msg db " y0u sp34k 1337 ? " section .data

Ensamblaje y enlace:

$ nasm -f elf hello.asm -o hello.o $ ld -s -m elf_i386 hello.o -o hello $ ./hello y0u sp34k 1337 ? $

Ahora extrae el código de shell del hola binario:

$ for i in `objdump -d hello | tr ''/t'' '' '' | tr '' '' ''/n'' | egrep ''^[0-9a-f]{2}$'' ` ; do echo -n "//x$i" ; done

salida:

/xeb/x19/x31/xc0/xb0/x04/x31/xdb/xb3/x01/x59/x31/xd2/xb2/x12/xcd/x80/x31/xc0/xb0/x01/x31/xdb/xb3/x01/xcd/x80/xe8/xe2/xff/xff/xff/x20/x79/x30/x75/x20/x73/x70/x33/x34/x6b/x20/x31/x33/x33/x37/x20/x3f/x20

Ahora podemos tener nuestro programa de controlador para iniciar Shellcode.

#include <stdio.h> char shellcode[] = "/xeb/x19/x31/xc0/xb0/x04/x31/xdb" "/xb3/x01/x59/x31/xd2/xb2/x12/xcd" "/x80/x31/xc0/xb0/x01/x31/xdb/xb3" "/x01/xcd/x80/xe8/xe2/xff/xff/xff" "/x20/x79/x30/x75/x20/x73/x70/x33" "/x34/x6b/x20/x31/x33/x33/x37/x20" "/x3f/x20"; int main(int argc, char **argv) { (*(void(*)())shellcode)(); return 0; }

Existen ciertas características de seguridad en compiladores modernos como la protección NX que evita la ejecución de código en el segmento de datos o la pila. Por lo tanto, debemos especificar explícitamente el compilador para deshabilitar estos.

$ gcc -g -Wall -fno-stack-protector -z execstack launcher.c -o launcher

Ahora el launcher se puede invocar para iniciar el shellcode.

$ ./launcher y0u sp34k 1337 ? $

Para shellcodes más complejos, habría otro obstáculo. Los núcleos Linux modernos tienen ASLR o Address Space Layout Randomization Es posible que deba deshabilitar esto antes de inyectar el código de shell, especialmente cuando se trata de desbordamientos de búfer.

root@localhost:~# echo 0 > /proc/sys/kernel/randomize_va_space


Cuando inyectas este Shellcode, no sabes qué hay en el message :

mov ecx, message

en el proceso inyectado, puede ser cualquier cosa, pero no será "Hello world!/r/n" ya que está en la sección de datos mientras está descargando solo la sección de texto. Puedes ver que tu shellcode no tiene "Hello world!/r/n" :

"/xb8/x04/x00/x00/x00" "/xbb/x01/x00/x00/x00" "/xb9/x00/x00/x00/x00" "/xba/x0f/x00/x00/x00" "/xcd/x80/xb8/x01/x00" "/x00/x00/xbb/x00/x00" "/x00/x00/xcd/x80";

Este es un problema común en el desarrollo de shellcode, la forma de evitarlo es de esta manera:

global _start section .text _start: jmp MESSAGE ; 1) lets jump to MESSAGE GOBACK: mov eax, 0x4 mov ebx, 0x1 pop ecx ; 3) we are poping into `ecx`, now we have the ; address of "Hello, World!/r/n" mov edx, 0xF int 0x80 mov eax, 0x1 mov ebx, 0x0 int 0x80 MESSAGE: call GOBACK ; 2) we are going back, since we used `call`, that means ; the return address, which is in this case the address ; of "Hello, World!/r/n", is pushed into the stack. db "Hello, World!", 0dh, 0ah section .data

Ahora descargue la sección de texto:

$ nasm -f elf shellcode.asm $ ld shellcode.o -o shellcode $ ./shellcode Hello, World! $ objdump -d shellcode shellcode: file format elf32-i386 Disassembly of section .text: 08048060 <_start>: 8048060: e9 1e 00 00 00 jmp 8048083 <MESSAGE> 08048065 <GOBACK>: 8048065: b8 04 00 00 00 mov $0x4,%eax 804806a: bb 01 00 00 00 mov $0x1,%ebx 804806f: 59 pop %ecx 8048070: ba 0f 00 00 00 mov $0xf,%edx 8048075: cd 80 int $0x80 8048077: b8 01 00 00 00 mov $0x1,%eax 804807c: bb 00 00 00 00 mov $0x0,%ebx 8048081: cd 80 int $0x80 08048083 <MESSAGE>: 8048083: e8 dd ff ff ff call 8048065 <GOBACK> 8048088: 48 dec %eax <-+ 8048089: 65 gs | 804808a: 6c insb (%dx),%es:(%edi) | 804808b: 6c insb (%dx),%es:(%edi) | 804808c: 6f outsl %ds:(%esi),(%dx) | 804808d: 2c 20 sub $0x20,%al | 804808f: 57 push %edi | 8048090: 6f outsl %ds:(%esi),(%dx) | 8048091: 72 6c jb 80480ff <MESSAGE+0x7c> | 8048093: 64 fs | 8048094: 21 .byte 0x21 | 8048095: 0d .byte 0xd | 8048096: 0a .byte 0xa <-+ $

Las líneas que marqué son nuestra cadena "Hello, World!/r/n" :

$ printf "/x48/x65/x6c/x6c/x6f/x2c/x20/x57/x6f/x72/x6c/x64/x21/x0d/x0a" Hello, World! $

Entonces nuestro envoltorio de C será:

char code[] = "/xe9/x1e/x00/x00/x00" // jmp 8048083 <MESSAGE> "/xb8/x04/x00/x00/x00" // mov $0x4,%eax "/xbb/x01/x00/x00/x00" // mov $0x1,%ebx "/x59" // pop %ecx "/xba/x0f/x00/x00/x00" // mov $0xf,%edx "/xcd/x80" // int $0x80 "/xb8/x01/x00/x00/x00" // mov $0x1,%eax "/xbb/x00/x00/x00/x00" // mov $0x0,%ebx "/xcd/x80" // int $0x80 "/xe8/xdd/xff/xff/xff" // call 8048065 <GOBACK> "Hello wolrd!/r/n"; // OR "/x48/x65/x6c/x6c/x6f/x2c/x20/x57" // "/x6f/x72/x6c/x64/x21/x0d/x0a" int main(int argc, char **argv) { (*(void(*)())code)(); return 0; }

Vamos a probarlo:

$ gcc test.c -o test $ ./test Hello wolrd! $

funciona.