> For the complete documentation index, see [llms.txt](https://shibudocs.gitbook.io/htb-writeups/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://shibudocs.gitbook.io/htb-writeups/hack-the-boo-2023/hauntmart.md).

# Hauntmart

This challenge was about a SSRF with a filter bypass to create a privileged user at an api endpoint.

* At first glance I could see that the flag is copied to /flag in the dockerfile. The file entrypoint.sh is loaded, which sets up a database like so:

```sql
DROP DATABASE IF EXISTS hauntmart;
CREATE DATABASE hauntmart;
CREATE TABLE hauntmart.users (
    id INTEGER PRIMARY KEY AUTO_INCREMENT,
    username varchar(255) NOT NULL UNIQUE,
    password varchar(255) NOT NULL,
    role varchar(255) NOT NULL DEFAULT 'user'
);

CREATE TABLE hauntmart.products (
    id INTEGER PRIMARY KEY AUTO_INCREMENT,
    name varchar(255) NOT NULL,
    price varchar(255) NOT NULL,
    description TEXT NOT NULL
);

CREATE USER 'xclow3n'@'localhost' IDENTIFIED BY 'xclow3n';
GRANT SELECT, INSERT, UPDATE ON hauntmart.users TO 'xclow3n'@'localhost';
GRANT SELECT, INSERT, UPDATE ON hauntmart.products TO 'xclow3n'@'localhost';

FLUSH PRIVILEGES;
```

* The file web\_hauntmart/challenge/application/blueprints/routes.py is interesting. It contains route definitions for web and api endpoints. At the web endpoint /home the flag is defined as flag=current\_app.config\['FLAG'] with the template index.html
* For /home and /product it seems like you need to be authenticated
* The /register API seems interesting too. You can possibly register a user from an unauthenticated standpoint:

<figure><img src="/files/EFlRxVaXo3nwm6qD4wRC" alt=""><figcaption></figcaption></figure>

* On the live website I could register an account immediately and access the site.
* Under Sell Product it seems like you can send data to the server. This site really stands out, it says that you can send a manual url which will be visited by the admins. My first Idea is to host a javascript that triggers another request to me, containing the admin cookie.
* The idea of reflected blind XSS did not work as I could not find a working injection. But what we state in the url field is indeed accessed by the server, which is interesting.
* This is written in the index.html:

```
{% if user['role'] == 'admin' %}
{{flag}}
{% endif %}
```

* This code in util.py might also be interesting as it seems to download what we give it in the manual url field under certain circumstances:

```python
def downloadManual(url):
    safeUrl = isSafeUrl(url)
    if safeUrl:
        try:
            local_filename = url.split("/")[-1]
            r = requests.get(url)
            
            with open(f"/opt/manualFiles/{local_filename}", "wb") as f:
                for chunk in r.iter_content(chunk_size=1024):
                    if chunk:
                        f.write(chunk)
            return True
        except:
            return False
    
    return False
```

* This might be the way in. I found out before that /api is prepended to the url of api calls. so we should be able to directly access /api/addAdmin. Here is the code from routes.py that shows that:

```python
@api.route('/addAdmin', methods=['GET'])
@isFromLocalhost
def addAdmin():
    username = request.args.get('username')
    
    if not username:
        return response('Invalid username'), 400
    
    result = makeUserAdmin(username)

    if result:
        return response('User updated!')
    return response('Invalid username'), 400
```

* Trying to access it shows a 403, which is expected because of @isFromLocalhost:

<figure><img src="/files/SphSuZTwp8OReTWCNiH5" alt=""><figcaption></figcaption></figure>

* Lets check the isFromLocalhost function; Maybe we can bypass that. It is defined in util.py:

```python
def isFromLocalhost(func):
    @wraps(func)
    def check_ip(*args, **kwargs):
        if request.remote_addr != "127.0.0.1":
            return abort(403)
        return func(*args, **kwargs)

    return check_ip
```

Well, we have a chance to perform a localhost request. Here:

<figure><img src="/files/3sPpliyuuUY4T7Iegb8W" alt=""><figcaption></figcaption></figure>

* According to the addAdmin code, the payload <http://127.0.0.1:12345/api/addAdmin?username=hacker> should work. But no, we receive “Invalid URL”. We see that there is a blacklist for manual url requests defined in util.py:

```python
blocked_host = ["127.0.0.1", "localhost", "0.0.0.0"]

def isSafeUrl(url):
    for hosts in blocked_host:
        if hosts in url:
            return False
    
    return True
```

* There are many ways to bypass this as you can see in this reference: <https://book.hacktricks.xyz/pentesting-web/ssrf-server-side-request-forgery/url-format-bypass>
* Using 0 as the IP did work to bypass the filter and get the flag


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://shibudocs.gitbook.io/htb-writeups/hack-the-boo-2023/hauntmart.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
