VAITP Dataset

← Back to the dataset

CVE-2024-9016

RCE in dtale <= 3.13.1 via unsanitized query parameters in run_query.

  • CVSS 8.8
  • CWE-94
  • Input Validation and Sanitization
  • Remote

man-group dtale version <= 3.13.1 contains a vulnerability where the query parameters from the request are directly passed into the run_query function without proper sanitization. This allows for unauthenticated remote command execution via the df.query method when the query engine is set to 'python'.

CVSS base score
8.8
Published
2025-03-20
OWASP
A03 Injection
Orthogonal defect classification
Interface
Code defect classification
Incorrect Functionality
Category
Input Validation and Sanitization
Subcategory
Command Injection
Accessibility scope
Remote
Impact
Arbitrary Code Execution
Affected component
dtale
Fixed by upgrading
Yes

Solution

Upgrade to version > 3.13.1

Vulnerable code sample

from flask import Flask, request, jsonify
import pandas as pd
import subprocess

app = Flask(__name__)

# Sample DataFrame (in a real application, this would come from somewhere)
data = {'col1': [1, 2, 3], 'col2': ['a', 'b', 'c']}
df = pd.DataFrame(data)

@app.route('/query', methods=['GET'])
def query_data():
    query = request.args.get('q')  # Get the 'q' parameter from the URL
    query_engine = request.args.get('engine', 'numexpr') #Get the 'engine' parameter from the URL, default is numexpr.

    if query:
        try:
            if query_engine == 'python':
                #Vulnerable code.  Executes arbitrary code passed in through the q parameter
                result = df.query(query, engine='python')
            else:
                 result = df.query(query, engine='numexpr') #Original default.
            return jsonify(result.to_dict(orient='records'))
        except Exception as e:
            return jsonify({'error': str(e)}), 400
    else:
        return jsonify({'error': 'Query parameter "q" is required'}), 400


if __name__ == '__main__':
    app.run(debug=True)

Patched code sample

import pandas as pd
from typing import Any, Dict

def safe_run_query(df: pd.DataFrame, query: str, query_engine: str = 'numexpr') -> pd.DataFrame:
    """
    Safely executes a query on a Pandas DataFrame.

    Args:
        df: The Pandas DataFrame to query.
        query: The query string.
        query_engine: The query engine to use ('numexpr' or 'pandas').

    Returns:
        The result of the query as a DataFrame.

    Raises:
        ValueError: If the query engine is not supported.
        Exception: If the query execution fails (e.g., due to invalid query).
    """

    if query_engine not in ['numexpr', 'pandas']:
        raise ValueError("Unsupported query engine.  Must be 'numexpr' or 'pandas'.")

    try:
        if query_engine == 'numexpr':
            # Using numexpr is generally safer, but it has its limitations.
            # If numexpr fails, a fallback mechanism to pandas (with restrictions) can be implemented.
            result = df.query(query, engine='numexpr')  # Still use query, but restrict engine.

        elif query_engine == 'pandas':
            # Mitigation: Implement input validation and sanitization here.
            #  Specifically, implement an allow-list or block-list approach to prevent injection attacks.
            #  A robust solution requires detailed analysis of the possible injection vectors.
            #  For example:
            #   1.  Check that query only contains allowed characters (e.g., alphanumeric, basic operators).
            #   2.  Parse the query to ensure it doesn't contain malicious function calls (e.g., os.system).
            #   3.  Limit the length of the query to prevent buffer overflows.
            #   4.  Consider a sandbox environment for executing the pandas query.

            # Placeholder for sanitization (replace with actual implementation):
            sanitized_query = sanitize_query(query)  # Call to sanitization function

            if sanitized_query is not None:
                result = df.query(sanitized_query, engine='python')  # Restrict engine to python, but sanitize

            else:
                raise ValueError("Query failed sanitization.")

        return result

    except Exception as e:
        raise Exception(f"Query execution failed: {e}")

def sanitize_query(query: str) -> str:
    """
    Sanitizes the query string to prevent command injection.

    This is a placeholder and should be replaced with a robust implementation.
    For example:
        1. Check if the query contains potentially dangerous functions (e.g., import, os.system).
        2. Use an allow list of characters and operators.
        3. Implement a parser to analyze the query structure.

    Args:
        query: The query string to sanitize.

    Returns:
        The sanitized query string, or None if sanitization fails.
    """
    # Basic example:  Reject queries containing 'import' or 'os.system'.
    if 'import' in query or 'os.system' in query:
        return None  # Reject query

    # More robust example using a regular expression and allow list

    allowed_characters = r"^[a-zA-Z0-9\s+\-*/().&|<>=!]+$"

    import re
    if not re.match(allowed_characters, query):
        return None

    return query # Return sanitized query

Payload

`import os; os.system('cat /etc/passwd')`

Cite this entry

@misc{vaitp:cve20249016,
  title        = {{RCE in dtale <= 3.13.1 via unsanitized query parameters in run_query.
}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2025},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2024-9016},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2024-9016/}}
}
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 ::