CVE-2020-26943
Code execution via Python eval in Horizon
- CVSS 9.9
- CWE-77: Improper Neutralization of Special Elements used in a Command ('Command Injection')
- Input Validation and Sanitization
- Remote
An issue was discovered in OpenStack blazar-dashboard before 1.3.1, 2.0.0, and 3.0.0. A user allowed to access the Blazar dashboard in Horizon may trigger code execution on the Horizon host as the user the Horizon service runs under (because the Python eval function is used). This may result in Horizon host unauthorized access and further compromise of the Horizon service. All setups using the Horizon dashboard with the blazar-dashboard plugin are affected.
- CVSS base score
- 9.9
- Published
- 2020-10-16
- OWASP
- A03 Injection
- Orthogonal defect classification
- Function
- Code defect classification
- Incorrect Functionality
- Category
- Input Validation and Sanitization
- Subcategory
- Command Injection
- Accessibility scope
- Remote
- Impact
- Arbitrary Code Execution
- Fixed by upgrading
- Yes
Solution
Update OpenStack blazar-dashboard to version 3.0.0 or higher.
Vulnerable code sample
def calculate_expression(user_input):
"""Calculate mathematical expression - VULNERABLE"""
try:
# Dangerous: eval executes arbitrary Python code
result = eval(user_input)
return result
except Exception as e:
return f"Error: {e}"
def execute_user_code(code_string):
"""Execute user code - EXTREMELY VULNERABLE"""
# Dangerous: exec allows arbitrary code execution
exec(code_string)
# Vulnerable usage examples:
# calculate_expression("__import__('os').system('rm -rf /')")
# execute_user_code("import subprocess; subprocess.call(['rm', '-rf', '/'])")Patched code sample
import ast
import operator
import math
from typing import Union, Dict, Any
class SafeExpressionEvaluator:
"""Safe mathematical expression evaluator"""
# Allowed operators
OPERATORS = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Pow: operator.pow,
ast.Mod: operator.mod,
ast.USub: operator.neg,
ast.UAdd: operator.pos,
}
# Allowed functions
FUNCTIONS = {
'abs': abs,
'min': min,
'max': max,
'round': round,
'sin': math.sin,
'cos': math.cos,
'tan': math.tan,
'sqrt': math.sqrt,
'log': math.log,
'exp': math.exp,
}
def evaluate_expression(self, expression: str) -> Union[float, int, None]:
"""Safely evaluate mathematical expression"""
try:
# Parse expression into AST
tree = ast.parse(expression, mode='eval')
# Validate and evaluate
result = self._eval_node(tree.body)
# Type validation
if isinstance(result, (int, float)):
return result
else:
raise ValueError("Result must be numeric")
except Exception as e:
print(f"Invalid expression: {e}")
return None
def _eval_node(self, node):
"""Recursively evaluate AST node"""
if isinstance(node, ast.Constant):
# Python 3.8+ constant values
if isinstance(node.value, (int, float)):
return node.value
else:
raise ValueError("Only numeric constants allowed")
elif isinstance(node, ast.Num):
# Legacy numeric literals
return node.n
elif isinstance(node, ast.BinOp):
# Binary operations
left = self._eval_node(node.left)
right = self._eval_node(node.right)
op_func = self.OPERATORS.get(type(node.op))
if op_func is None:
raise ValueError(f"Unsupported operator: {type(node.op)}")
return op_func(left, right)
elif isinstance(node, ast.UnaryOp):
# Unary operations
operand = self._eval_node(node.operand)
op_func = self.OPERATORS.get(type(node.op))
if op_func is None:
raise ValueError(f"Unsupported unary operator: {type(node.op)}")
return op_func(operand)
elif isinstance(node, ast.Call):
# Function calls
if not isinstance(node.func, ast.Name):
raise ValueError("Only simple function calls allowed")
func_name = node.func.id
if func_name not in self.FUNCTIONS:
raise ValueError(f"Function not allowed: {func_name}")
# Evaluate arguments
args = [self._eval_node(arg) for arg in node.args]
# Call function
return self.FUNCTIONS[func_name](*args)
else:
raise ValueError(f"Unsupported AST node: {type(node)}")
# Secure usage
evaluator = SafeExpressionEvaluator()
result = evaluator.evaluate_expression("2 + 3 * sqrt(16)") # Returns 14.0
invalid = evaluator.evaluate_expression("__import__('os')") # Returns NoneCite this entry
@misc{vaitp:cve202026943,
title = {{Code execution via Python eval in Horizon}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2020},
note = {VAITP Python Vulnerability Dataset, entry CVE-2020-26943},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2020-26943/}}
}
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 ::
