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.
- CWE
- CWE-94
- 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 ::
