Introducción al Reversing – 0x0B Shellcode básica

Muy buenas a todos. Después de tanto tiempo he decidido seguir con el curso de ingeniería inversa ofrecido por el blog. En este apartado aprenderemos:

  • Programar ensamblador.
  • Ejecución de la Shellcode .

Usaremos el siguiente data en decimal.

85, 72, 137, 229, 72, 131, 236, 24, 72, 199, 69, 248, 79, 0, 0, 0, 72, 184, 21, 79, 231, 75, 1, 0, 0, 0, 72, 137, 69, 240, 72, 199, 69, 232, 4, 0, 0, 0, 72, 199, 69, 224, 3, 0, 0, 0, 72, 199, 69, 216, 19, 0, 0, 0, 72, 199, 69, 208, 21, 1, 0, 0, 72, 184, 97, 91, 100, 75, 207, 119, 0, 0, 72, 137, 69, 200, 72, 199, 69, 192, 2, 0, 0, 0, 72, 199, 69, 184, 17, 0, 0, 0, 72, 199, 69, 176, 193, 33, 0, 0, 72, 199, 69, 168, 233, 101, 34, 24, 72, 199, 69, 160, 51, 8, 0, 0, 72, 199, 69, 152, 171, 10, 0, 0, 72, 199, 69, 144, 173, 170, 141, 0, 72, 139, 69, 248, 72, 15, 175, 69, 240, 72, 137, 69, 136, 72, 139, 69, 232, 72, 15, 175, 69, 224, 72, 15, 175, 69, 216, 72, 15, 175, 69, 208, 72, 15, 175, 69, 200, 72, 137, 69, 128, 72, 139, 69, 192, 72, 15, 175, 69, 184, 72, 15, 175, 69, 176, 72, 15, 175, 69, 168, 72, 137, 133, 120, 255, 255, 255, 72, 139, 69, 160, 72, 15, 175, 69, 152, 72, 15, 175, 69, 144, 72, 137, 133, 112, 255, 255, 255, 184, 0, 0, 0, 0, 201

Usamos CyberChef para poder transformar ese data en Hexadecimal.

55 48 89 e5 48 83 ec 18 48 c7 45 f8 4f 00 00 00 48 b8 15 4f e7 4b 01 00 00 00 48 89 45 f0 48 c7 45 e8 04 00 00 00 48 c7 45 e0 03 00 00 00 48 c7 45 d8 13 00 00 00 48 c7 45 d0 15 01 00 00 48 b8 61 5b 64 4b cf 77 00 00 48 89 45 c8 48 c7 45 c0 02 00 00 00 48 c7 45 b8 11 00 00 00 48 c7 45 b0 c1 21 00 00 48 c7 45 a8 e9 65 22 18 48 c7 45 a0 33 08 00 00 48 c7 45 98 ab 0a 00 00 48 c7 45 90 ad aa 8d 00 48 8b 45 f8 48 0f af 45 f0 48 89 45 88 48 8b 45 e8 48 0f af 45 e0 48 0f af 45 d8 48 0f af 45 d0 48 0f af 45 c8 48 89 45 80 48 8b 45 c0 48 0f af 45 b8 48 0f af 45 b0 48 0f af 45 a8 48 89 85 78 ff ff ff 48 8b 45 a0 48 0f af 45 98 48 0f af 45 90 48 89 85 70 ff ff ff b8 00 00 00 00 c9

Nos llama la atención el opcode 55, correspondiente al inicio del prólogo de una función cualquiera en ensamblador. Si usamos la página defuse.ca obtenemos en ensamblador el código buscado. También tendríamos en forma literal la shellcode.

"\x55\x48\x89\xE5\x48\x83\xEC\x18\x48\xC7\x45\xF8\x4F\x00\x00\x00\x48\xB8\x15\x4F\xE7\x4B\x01\x00\x00\x00\x48\x89\x45\xF0\x48\xC7\x45\xE8\x04\x00\x00\x00\x48\xC7\x45\xE0\x03\x00\x00\x00\x48\xC7\x45\xD8\x13\x00\x00\x00\x48\xC7\x45\xD0\x15\x01\x00\x00\x48\xB8\x61\x5B\x64\x4B\xCF\x77\x00\x00\x48\x89\x45\xC8\x48\xC7\x45\xC0\x02\x00\x00\x00\x48\xC7\x45\xB8\x11\x00\x00\x00\x48\xC7\x45\xB0\xC1\x21\x00\x00\x48\xC7\x45\xA8\xE9\x65\x22\x18\x48\xC7\x45\xA0\x33\x08\x00\x00\x48\xC7\x45\x98\xAB\x0A\x00\x00\x48\xC7\x45\x90\xAD\xAA\x8D\x00\x48\x8B\x45\xF8\x48\x0F\xAF\x45\xF0\x48\x89\x45\x88\x48\x8B\x45\xE8\x48\x0F\xAF\x45\xE0\x48\x0F\xAF\x45\xD8\x48\x0F\xAF\x45\xD0\x48\x0F\xAF\x45\xC8\x48\x89\x45\x80\x48\x8B\x45\xC0\x48\x0F\xAF\x45\xB8\x48\x0F\xAF\x45\xB0\x48\x0F\xAF\x45\xA8\x48\x89\x85\x78\xFF\xFF\xFF\x48\x8B\x45\xA0\x48\x0F\xAF\x45\x98\x48\x0F\xAF\x45\x90\x48\x89\x85\x70\xFF\xFF\xFF\xB8\x00\x00\x00\x00\xC9"

Shellcode execution

Primero, usaremos una shellcode para ejecutar. Esta será la shellcode anterior. Todo esto es muy simple:

  • Declarar la shellcode en un array.
  • Crear un puntero de función que apunte a la shellcode.
  • Ejecutar esa función.

El código de la variable code contiene el shellcode.

#include <stdio.h>

unsigned char code[] = "\x55\x48\x89\xE5\x48\x83\xEC\x18\x48\xC7\x45\xF8\x4F\x00\x00\x00\x48\xB8\x15\x4F\xE7\x4B\x01\x00\x00\x00\x48\x89\x45\xF0\x48\xC7\x45\xE8\x04\x00\x00\x00\x48\xC7\x45\xE0\x03\x00\x00\x00\x48\xC7\x45\xD8\x13\x00\x00\x00\x48\xC7\x45\xD0\x15\x01\x00\x00\x48\xB8\x61\x5B\x64\x4B\xCF\x77\x00\x00\x48\x89\x45\xC8\x48\xC7\x45\xC0\x02\x00\x00\x00\x48\xC7\x45\xB8\x11\x00\x00\x00\x48\xC7\x45\xB0\xC1\x21\x00\x00\x48\xC7\x45\xA8\xE9\x65\x22\x18\x48\xC7\x45\xA0\x33\x08\x00\x00\x48\xC7\x45\x98\xAB\x0A\x00\x00\x48\xC7\x45\x90\xAD\xAA\x8D\x00\x48\x8B\x45\xF8\x48\x0F\xAF\x45\xF0\x48\x89\x45\x88\x48\x8B\x45\xE8\x48\x0F\xAF\x45\xE0\x48\x0F\xAF\x45\xD8\x48\x0F\xAF\x45\xD0\x48\x0F\xAF\x45\xC8\x48\x89\x45\x80\x48\x8B\x45\xC0\x48\x0F\xAF\x45\xB8\x48\x0F\xAF\x45\xB0\x48\x0F\xAF\x45\xA8\x48\x89\x85\x78\xFF\xFF\xFF\x48\x8B\x45\xA0\x48\x0F\xAF\x45\x98\x48\x0F\xAF\x45\x90\x48\x89\x85\x70\xFF\xFF\xFF\xB8\x00\x00\x00\x00\xC9";

int main(int argc, char **argv) {
  int foo_value = 0;

  int (*foo)() = (int(*)())code;
  foo_value = foo();

}

Creamos el puntero de función llamado foo para apuntar a ese bloque de código. Ejecuta el código, tomando el valor de retorno. Para compilar este código en C, usaremos lo siguiente:

gcc -fno-stack-protector -z execstack shellcode.c -o test

Ensamblador

Si analizamos el código que obtenemos o bien, ejecutando en un debugger el binario anterior, o bien, el ensamblador que obtenemos de la página web anterior, crea una cantidad de variables a través de la multiplicación de grandes números.

0:  55                      push   rbp
1:  48 89 e5                mov    rbp,rsp
4:  48 83 ec 18             sub    rsp,0x18
8:  48 c7 45 f8 4f 00 00    mov    QWORD PTR [rbp-0x8],0x4f
f:  00
10: 48 b8 15 4f e7 4b 01    movabs rax,0x14be74f15
17: 00 00 00
1a: 48 89 45 f0             mov    QWORD PTR [rbp-0x10],rax
1e: 48 c7 45 e8 04 00 00    mov    QWORD PTR [rbp-0x18],0x4
25: 00
26: 48 c7 45 e0 03 00 00    mov    QWORD PTR [rbp-0x20],0x3
2d: 00
2e: 48 c7 45 d8 13 00 00    mov    QWORD PTR [rbp-0x28],0x13
35: 00
36: 48 c7 45 d0 15 01 00    mov    QWORD PTR [rbp-0x30],0x115
3d: 00
3e: 48 b8 61 5b 64 4b cf    movabs rax,0x77cf4b645b61
45: 77 00 00
48: 48 89 45 c8             mov    QWORD PTR [rbp-0x38],rax
4c: 48 c7 45 c0 02 00 00    mov    QWORD PTR [rbp-0x40],0x2
53: 00
54: 48 c7 45 b8 11 00 00    mov    QWORD PTR [rbp-0x48],0x11
5b: 00
5c: 48 c7 45 b0 c1 21 00    mov    QWORD PTR [rbp-0x50],0x21c1
63: 00
64: 48 c7 45 a8 e9 65 22    mov    QWORD PTR [rbp-0x58],0x182265e9
6b: 18
6c: 48 c7 45 a0 33 08 00    mov    QWORD PTR [rbp-0x60],0x833
73: 00
74: 48 c7 45 98 ab 0a 00    mov    QWORD PTR [rbp-0x68],0xaab
7b: 00
7c: 48 c7 45 90 ad aa 8d    mov    QWORD PTR [rbp-0x70],0x8daaad
83: 00
84: 48 8b 45 f8             mov    rax,QWORD PTR [rbp-0x8]
88: 48 0f af 45 f0          imul   rax,QWORD PTR [rbp-0x10]
8d: 48 89 45 88             mov    QWORD PTR [rbp-0x78],rax
91: 48 8b 45 e8             mov    rax,QWORD PTR [rbp-0x18]
95: 48 0f af 45 e0          imul   rax,QWORD PTR [rbp-0x20]
9a: 48 0f af 45 d8          imul   rax,QWORD PTR [rbp-0x28]
9f: 48 0f af 45 d0          imul   rax,QWORD PTR [rbp-0x30]
a4: 48 0f af 45 c8          imul   rax,QWORD PTR [rbp-0x38]
a9: 48 89 45 80             mov    QWORD PTR [rbp-0x80],rax
ad: 48 8b 45 c0             mov    rax,QWORD PTR [rbp-0x40]
b1: 48 0f af 45 b8          imul   rax,QWORD PTR [rbp-0x48]
b6: 48 0f af 45 b0          imul   rax,QWORD PTR [rbp-0x50]
bb: 48 0f af 45 a8          imul   rax,QWORD PTR [rbp-0x58]
c0: 48 89 85 78 ff ff ff    mov    QWORD PTR [rbp-0x88],rax
c7: 48 8b 45 a0             mov    rax,QWORD PTR [rbp-0x60]
cb: 48 0f af 45 98          imul   rax,QWORD PTR [rbp-0x68]
d0: 48 0f af 45 90          imul   rax,QWORD PTR [rbp-0x70]
d5: 48 89 85 70 ff ff ff    mov    QWORD PTR [rbp-0x90],rax
dc: b8 00 00 00 00          mov    eax,0x0
e1: c9                      leave

Análisis en estático (muy útil y necesario de primeras, antes del dinámico):

mov    QWORD PTR [rbp-0x8],0x4f        --> rbp-0x8 = 0x4f
movabs rax,0x14be74f15                 --> rax = 0x14be74f15
mov    QWORD PTR [rbp-0x10],rax        --> rbp-0x10 = rax
mov    QWORD PTR [rbp-0x18],0x4        --> rbp-0x18 = 0x4
mov    QWORD PTR [rbp-0x20],0x3        --> rbp-0x20 = 0x3
mov    QWORD PTR [rbp-0x28],0x13       --> rbp-0x28 = 0x13
mov    QWORD PTR [rbp-0x30],0x115      -->rbp-0x30 = 0x115
movabs rax,0x77cf4b645b61              --> rax = 0x77cf4b645b61
mov    QWORD PTR [rbp-0x38],rax        --> rbp-0x38 = rax
mov    QWORD PTR [rbp-0x40],0x2        --> rbp-0x40 = 0x2
mov    QWORD PTR [rbp-0x48],0x11.      --> rbp-0x48 = 0x11
mov    QWORD PTR [rbp-0x50],0x21c1     --> rbp-0x50 = 0x21c1
mov    QWORD PTR [rbp-0x58],0x182265e9 --> rbp-0x58 = 0x182265e9
mov    QWORD PTR [rbp-0x60],0x833      --> rbp-0x60 = 0x833
mov    QWORD PTR [rbp-0x68],0xaab      --> rbp-0x68 = 0xaab
mov    QWORD PTR [rbp-0x70],0x8daaad   --> rbp-0x70 = 0x8daaad
mov    rax,QWORD PTR [rbp-0x8]         --> rax = 0x4f
imul   rax,QWORD PTR [rbp-0x10]        --> rax *= rbp-0x10
mov    QWORD PTR [rbp-0x78],rax        --> rbp-0x78 = rax
mov    rax,QWORD PTR [rbp-0x18]        --> rax = 0x4
imul   rax,QWORD PTR [rbp-0x20]
imul   rax,QWORD PTR [rbp-0x28]
imul   rax,QWORD PTR [rbp-0x30]
imul   rax,QWORD PTR [rbp-0x38]-->rax *= var20 * var28 * var30 * var38 
mov    QWORD PTR [rbp-0x80],rax        --> rbp-0x80 = rax
mov    rax,QWORD PTR [rbp-0x40]        --> rax = 0x2
imul   rax,QWORD PTR [rbp-0x48]
imul   rax,QWORD PTR [rbp-0x50]
imul   rax,QWORD PTR [rbp-0x58]       --> rax *= var48 * var50 * var58
mov    QWORD PTR [rbp-0x88],rax       --> rbp-0x88 = rax
mov    rax,QWORD PTR [rbp-0x60]       --> rax = 0x833
imul   rax,QWORD PTR [rbp-0x68]
imul   rax,QWORD PTR [rbp-0x70]       --> rax *= var68 * var70
mov    QWORD PTR [rbp-0x90],rax       --> rbp-0x90 = rax
mov    eax,0x0
leave

Podemos obtener el contenido con la multiplicación de las variables:

rbp-0x78 = 0x666c61677b
rbp-0x80 = 0x73757033725f7634
rbp-0x88 = 0x6c31645f7072
rbp-0x90 = 0x306772346d7d

Tenemos en memoria la solución: flag{sup3r_v4l1d_pr0gr4m}

Código ensamblador

section .text:
global _start
_start:

	push rbp
	mov rbp,rsp
	sub rsp,byte +0x18
	mov qword [rbp-0x8],0x4f
	mov rax,0x14be74f15
	mov [rbp-0x10],rax
	mov qword [rbp-0x18],0x4
	mov qword [rbp-0x20],0x3
	mov qword [rbp-0x28],0x13
	mov qword [rbp-0x30],0x115
	mov rax,0x77cf4b645b61
	mov [rbp-0x38],rax
	mov qword [rbp-0x40],0x2
	mov qword [rbp-0x48],0x11
	mov qword [rbp-0x50],0x21c1
	mov qword [rbp-0x58],0x182265e9
	mov qword [rbp-0x60],0x833
	mov qword [rbp-0x68],0xaab
	mov qword [rbp-0x70],0x8daaad
	mov rax,[rbp-0x8]
	imul rax,[rbp-0x10]
	mov [rbp-0x78],rax
	mov rax,[rbp-0x18]
	imul rax,[rbp-0x20]
	imul rax,[rbp-0x28]
	imul rax,[rbp-0x30]
	imul rax,[rbp-0x38]
	mov [rbp-0x80],rax
	mov rax,[rbp-0x40]
	imul rax,[rbp-0x48]
	imul rax,[rbp-0x50]
	imul rax,[rbp-0x58]
	mov [rbp-0x88],rax
	mov rax,[rbp-0x60]
	imul rax,[rbp-0x68]
	imul rax,[rbp-0x70]
	mov [rbp-0x90],rax
	mov eax,0x0
	leave

nasm -f elf64 shellcode.asm para ensamblar el programa, gcc shellcode.o -nostdlib -o shellcode para compilar el fichero objeto nasm producido en un fichero ejecutable, -nostdlib porque gcc enlazará por defecto las librerías estándar de C que ya contienen un _start que invoca el punto de entrada principal, chmod +x shellcode para hacerlo ejecutable.

Si tienen alguna duda o cuestión no duden en ponerse en contacto conmigo por telegram o twitter, @naivenom.