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.