VAITP Dataset

← Back to the dataset

CVE-2024-39289

ROS rosparam vulnerable to code execution via unsanitized parameters.

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

A code execution vulnerability has been discovered in the Robot Operating System (ROS) 'rosparam' tool, affecting ROS distributions Noetic Ninjemys and earlier. The vulnerability stems from the use of the eval() function to process unsanitized, user-supplied parameter values via special converters for angle representations in radians. This flaw allowed attackers to craft and execute arbitrary Python code.

CVSS base score
7.8
Published
2025-07-17
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
Robot Operat
Fixed by upgrading
Yes

Solution

Update the `rosparam` package. For ROS Noetic, upgrade to version `1.15.15-1` or newer.

Vulnerable code sample

import os
import math

def load_rosparam_value(value):
    """
    Simulates the vulnerable rosparam loading mechanism for angle converters.
    This function is a representation of the logic before the fix for CVE-2024-39289.
    
    WARNING: This code is intentionally vulnerable and uses eval() on unsanitized
    input. Do not use this in production environments.
    """
    if isinstance(value, str):
        # Check for the vulnerable $(rad ...) converter syntax
        if value.strip().startswith('$(rad '):
            # Extract the expression inside the parentheses
            expression = value.strip()[5:-1]
            try:
                # Vulnerable call to eval() on user-supplied content
                return eval(expression)
            except Exception as e:
                # In a real scenario, this might raise a specific ROS exception
                print(f"Error evaluating expression: {e}")
                return None
    return value

# --- Demonstration of the vulnerability ---

# 1. Benign Use Case (intended functionality)
# This simulates a value from a YAML file like: angle: $(rad 3.14159 / 2)
benign_input = "$(rad math.pi / 2)"
result = load_rosparam_value(benign_input)
# print(f"Benign input '{benign_input}' evaluated to: {result}") # Expected: 1.570795...

# 2. Malicious Use Case (the exploit)
# An attacker crafts a string that executes arbitrary code.
# This simulates a value from a YAML file like:
# pwned: $(rad __import__('os').system('echo VULNERABLE_CODE_EXECUTED'))
malicious_input = "$(rad __import__('os').system('echo PoC: Arbitrary Code Execution successful!'))"
print(f"Processing malicious input: {malicious_input}")
load_rosparam_value(malicious_input)

Patched code sample

import math
import ast

def get_safe_rosparam_loader():
    """
    This function returns a safe loader that mimics the patched rosparam behavior.
    The original vulnerability in CVE-2024-39289 existed because a function
    similar to this would call eval() on the entire input string for angle
    conversions, allowing arbitrary code execution.

    The fix involves two key changes demonstrated here:
    1.  Explicitly parsing the 'rad(...)' structure instead of evaluating it
        directly.
    2.  Using a sandboxed eval() with a restricted 'globals' dictionary for the
        mathematical expression *inside* 'rad(...)'. This prevents access to
        harmful built-in functions like `__import__`.
    3.  Using the much safer `ast.literal_eval` for any other data that
        is not a plain string, which handles basic Python types without
        executing code.
    """
    # Define a safe evaluation context for mathematical expressions.
    # It explicitly provides allowed names (like 'pi') and disables built-ins.
    safe_math_globals = {
        "__builtins__": None,
        "pi": math.pi,
        "e": math.e,
        "sin": math.sin,
        "cos": math.cos,
        "tan": math.tan,
    }

    def safe_load(value_str):
        """
        Represents the fixed logic for loading a rosparam value.
        """
        # A malicious input that would have been executed by the vulnerable version:
        # "rad(__import__('os').system('echo pwned'))"
        
        s = value_str.strip()

        # Check specifically for the 'rad(...)' converter syntax
        if s.startswith('rad(') and s.endswith(')'):
            # Extract only the expression inside the parentheses
            expression = s[4:-1]
            try:
                # CORE OF THE FIX: Evaluate the expression in a sandboxed environment
                # with a strictly limited set of globals and no locals.
                return eval(expression, safe_math_globals, {})
            except Exception as e:
                raise ValueError(f"Invalid or unsafe expression in rad(): {expression}") from e
        
        # For all other values, use the safe ast.literal_eval.
        # This handles numbers, lists, dictionaries, etc., without code execution.
        try:
            return ast.literal_eval(s)
        except (ValueError, SyntaxError, TypeError):
            # If it's not a valid Python literal, it's treated as a plain string.
            # This is the standard rosparam behavior for non-special values.
            return value_str

    return safe_load

if __name__ == '__main__':
    # Demonstration of the fixed loader
    
    safe_loader = get_safe_rosparam_loader()
    
    # --- Test Cases ---

    # 1. Benign: A valid angle in radians using a math expression
    valid_angle_str = "rad(pi / 2)"
    try:
        result = safe_loader(valid_angle_str)
        print(f"Input: '{valid_angle_str}'\nResult: {result} (Correctly evaluated to {math.pi / 2})")
    except Exception as e:
        print(f"Input: '{valid_angle_str}'\nError: {e}")
    
    print("-" * 20)

    # 2. Benign: A simple number
    valid_number_str = "123.45"
    try:
        result = safe_loader(valid_number_str)
        print(f"Input: '{valid_number_str}'\nResult: {result} (Type: {type(result)})")
    except Exception as e:
        print(f"Input: '{valid_number_str}'\nError: {e}")

    print("-" * 20)

    # 3. Benign: A list
    valid_list_str = "[1, 2, 'hello']"
    try:
        result = safe_loader(valid_list_str)
        print(f"Input: '{valid_list_str}'\nResult: {result} (Type: {type(result)})")
    except Exception as e:
        print(f"Input: '{valid_list_str}'\nError: {e}")

    print("-" * 20)

    # 4. Malicious: Attempted code execution (THE VULNERABILITY)
    # In the old system, this would have executed the command.
    # In the new, fixed system, it raises an error.
    malicious_str = "rad(__import__('os').system('echo vulnerable'))"
    try:
        result = safe_loader(malicious_str)
        print(f"Input: '{malicious_str}'\nResult: {result}")
    except Exception as e:
        print(f"Input: '{malicious_str}'\nSuccessfully Blocked! Error: {e}")

Payload

my_param: !radians __import__('os').system('touch /tmp/pwned')

Cite this entry

@misc{vaitp:cve202439289,
  title        = {{ROS rosparam vulnerable to code execution via unsanitized parameters.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2025},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2024-39289},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2024-39289/}}
}
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 ::