> For the complete documentation index, see [llms.txt](https://marufmurtuza.gitbook.io/home/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://marufmurtuza.gitbook.io/home/blogs/ctf-writeups/web/blackhat-mea-qualification-ctf-2024/watermelon.md).

# Watermelon

Recently, I participated in the [Blackhat MEA 2024 Qualification CTF](https://ctftime.org/event/2430), where I tackled a web challenge named “**Watermelon**.” This challenge was relatively straightforward but enjoyable. Here’s a step-by-step breakdown of how I solved it.

#### <mark style="color:yellow;">Challenge Overview</mark>

The challenge provided a file named `app.py`, which sets up the challenge API for managing users and their uploaded files. It includes basic security checks for file access and an admin-specific endpoint.

{% file src="/files/0CcORCfpnx2n2HrP10VT" %}
Given file
{% endfile %}

#### <mark style="color:yellow;">User Registration</mark>

Upon reviewing the `app.py` file, I discovered the user registration process:

```python
@app.post("/register")
def register():
    if not request.json or not "username" in request.json or not "password" in request.json:
        return jsonify({"Error": "Please fill all fields"}), 400
    
    username = request.json['username']
    password = request.json['password']

    if User.query.filter_by(username=username).first():
        return jsonify({"Error": "Username already exists"}), 409

    new_user = User(username=username, password=password)
    db.session.add(new_user)
    db.session.commit()

    return jsonify({"Message": "User registered successfully"}), 201
```

Here, I noted that I needed to make a POST request containing a username and password to register a user account. Following this, I registered my own user account successfully.

<figure><img src="/files/Rq5lNPgefSquzznoR2S0" alt=""><figcaption><p>Request and Response for user registration</p></figcaption></figure>

#### <mark style="color:yellow;">User Login</mark>

After registration, I could log into the user account, which involved a process similar to registration:

```python
@app.post("/login")
def login():
    if not request.json or not "username" in request.json or not "password" in request.json:
        return jsonify({"Error": "Please fill all fields"}), 400
    
    username = request.json['username']
    password = request.json['password']

    user = User.query.filter_by(username=username, password=password).first()
    if not user:
        return jsonify({"Error": "Invalid username or password"}), 401
    
    session['user_id'] = user.id
    session['username'] = user.username
    return jsonify({"Message": "Login successful"}), 200
```

With the user account registered, I was able to log in successfully.

<figure><img src="/files/RQ2o88ioDw9OL9cAgzcT" alt=""><figcaption><p>Request for user login</p></figcaption></figure>

<figure><img src="/files/sx5yuEiXfEY9R0GD2But" alt=""><figcaption><p>Login confirmation</p></figcaption></figure>

<figure><img src="/files/PZupiR7p2Xb4jIHMXLsO" alt=""><figcaption><p>User profile</p></figcaption></figure>

#### <mark style="color:yellow;">File Upload</mark>

Next, I explored the file upload functionality, which was also detailed in the `app.py` file:

```python
@app.route("/upload", methods=["POST"])
@login_required
def upload_file():
    if 'file' not in request.files:
        return jsonify({"Error": "No file part"}), 400
    
    file = request.files['file']
    if file.filename == '':
        return jsonify({"Error": "No selected file"}), 400
    
    user_id = session.get('user_id')
    if file:
        blocked = ["proc", "self", "environ", "env"]
        filename = file.filename

        if filename in blocked:
            return jsonify({"Error":"Why?"})

        user_dir = os.path.join(app.config['UPLOAD_FOLDER'], str(user_id))
        os.makedirs(user_dir, exist_ok=True)

        file_path = os.path.join(user_dir, filename)

        file.save(f"{user_dir}/{secure_filename(filename)}")
        
        new_file = File(filename=secure_filename(filename), filepath=file_path, user_id=user_id)
        db.session.add(new_file)
        db.session.commit()
        
        return jsonify({"Message": "File uploaded successfully", "file_path": file_path}), 201

    return jsonify({"Error": "File upload failed"}), 500
```

I successfully uploaded several files and was able to retrieve their file paths. However, I couldn’t find the flag at this stage. This led me to consider performing LFI by manipulating the filename.

#### <mark style="color:yellow;">Exploiting LFI</mark>

I renamed one of my uploaded files to `../../app.py` and uploaded it. This worked, allowing me to read the `app.py` file hosted on the challenge server. Within this file, I discovered the credentials for the admin user.

<figure><img src="/files/cqwY90yVv2wS9G44rDvw" alt=""><figcaption><p>Successful file upload</p></figcaption></figure>

<figure><img src="/files/FvM3yaFy1xMYXnKT2nqJ" alt=""><figcaption><p>Uploaded file details</p></figcaption></figure>

<figure><img src="/files/H0K2LpmhZnRsZ0Fmkx3v" alt=""><figcaption><p>Admin credentials found via LFI</p></figcaption></figure>

#### <mark style="color:yellow;">Accessing the Admin Account</mark>

Using the found credentials, I logged into the admin account. Once inside, I navigated to the `/admin` directory and successfully located the flag.

<figure><img src="/files/d1dFVqjb1LFtIThaESea" alt=""><figcaption><p>Request for login into the admin account</p></figcaption></figure>

<figure><img src="/files/NB5vjAcVTUfRJiCjVvKb" alt=""><figcaption><p>Flag found</p></figcaption></figure>

So, that was all about that challenge. See you in some other blog. PEACE!


---

# 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://marufmurtuza.gitbook.io/home/blogs/ctf-writeups/web/blackhat-mea-qualification-ctf-2024/watermelon.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.
