> 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!
