VYPR
Critical severityNVD Advisory· Published Apr 16, 2024· Updated Aug 1, 2024

Command Injection in run-llama/llama_index

CVE-2024-3271

Description

A command injection vulnerability exists in the run-llama/llama_index repository, specifically within the safe_eval function. Attackers can bypass the intended security mechanism, which checks for the presence of underscores in code generated by LLM, to execute arbitrary code. This is achieved by crafting input that does not contain an underscore but still results in the execution of OS commands. The vulnerability allows for remote code execution (RCE) on the server hosting the application.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

CVE-2024-3271 is a command injection flaw in LlamaIndex's safe_eval function, allowing remote code execution by bypassing underscore checks in LLM-generated code.

A command injection vulnerability exists in the safe_eval function of the run-llama/llama_index repository. The root cause is an insufficient security mechanism that only checks for underscores in LLM-generated code before evaluation. An attacker can craft input that avoids underscores but still executes arbitrary OS commands, bypassing the intended restriction [1].

Exploitation does not require authentication if the vulnerable endpoint is exposed. The attacker can provide crafted input to the LLM or directly to the component that calls safe_eval, such as the PandasQueryEngine. The bypass is achieved by using built-in functions or attribute accesses that do not contain underscores but are not in the allowlist of safe builtins. References show that the original code only checked for private entity access (starting with '_'), but later patches added checks for disallowed builtins and import modules to strengthen the protection [3][4].

The impact is remote code execution (RCE) on the server hosting the application, allowing full compromise of the system [1].

Mitigation is available via commits that restrict allowed builtins and block imports in the safe_eval function [3][4]. Users should update to a version containing these patches. There is no indication that the vulnerability is currently listed in CISA's KEV.

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
llama-index-corePyPI
< 0.10.240.10.24

Affected products

2

Patches

3
5fbcb5a8b9f2

stricter access to builting in pandas query engine

https://github.com/run-llama/llama_indexLogan MarkewichMar 26, 2024via ghsa
1 file changed · +11 7
  • llama-index-core/llama_index/core/exec_utils.py+11 7 modified
    @@ -45,20 +45,16 @@ def _restricted_import(
         "float": float,
         "format": format,
         "frozenset": frozenset,
    -    "getattr": getattr,
    -    "hasattr": hasattr,
         "hash": hash,
         "hex": hex,
         "int": int,
         "isinstance": isinstance,
         "issubclass": issubclass,
    -    "iter": iter,
         "len": len,
         "list": list,
         "map": map,
         "max": max,
         "min": min,
    -    "next": next,
         "oct": oct,
         "ord": ord,
         "pow": pow,
    @@ -68,7 +64,6 @@ def _restricted_import(
         "reversed": reversed,
         "round": round,
         "set": set,
    -    "setattr": setattr,
         "slice": slice,
         "sorted": sorted,
         "str": str,
    @@ -94,23 +89,31 @@ def _get_restricted_globals(__globals: Union[dict, None]) -> Any:
     class DunderVisitor(ast.NodeVisitor):
         def __init__(self) -> None:
             self.has_access_to_private_entity = False
    +        self.has_access_to_disallowed_builtin = False
     
         def visit_Name(self, node: ast.Name) -> None:
             if node.id.startswith("_"):
                 self.has_access_to_private_entity = True
    +        if node.id not in ALLOWED_BUILTINS:
    +            self.has_access_to_disallowed_builtin = True
             self.generic_visit(node)
     
         def visit_Attribute(self, node: ast.Attribute) -> None:
             if node.attr.startswith("_"):
                 self.has_access_to_private_entity = True
    +        if node.attr not in ALLOWED_BUILTINS:
    +            self.has_access_to_disallowed_builtin = True
             self.generic_visit(node)
     
     
     def _contains_protected_access(code: str) -> bool:
         tree = ast.parse(code)
         dunder_visitor = DunderVisitor()
         dunder_visitor.visit(tree)
    -    return dunder_visitor.has_access_to_private_entity
    +    return (
    +        dunder_visitor.has_access_to_private_entity
    +        or dunder_visitor.has_access_to_disallowed_builtin
    +    )
     
     
     def _verify_source_safety(__source: Union[str, bytes, CodeType]) -> None:
    @@ -124,7 +127,8 @@ def _verify_source_safety(__source: Union[str, bytes, CodeType]) -> None:
             __source = __source.decode()
         if _contains_protected_access(__source):
             raise RuntimeError(
    -            "Execution of code containing references to private or dunder methods is forbidden!"
    +            "Execution of code containing references to private or dunder methods, "
    +            "or disallowed builtins, is forbidden!"
             )
     
     
    
2c92e88838a5

stricter access to builting in pandas query engine (#12278)

2 files changed · +27 10
  • llama-index-core/llama_index/core/exec_utils.py+25 7 modified
    @@ -45,20 +45,16 @@ def _restricted_import(
         "float": float,
         "format": format,
         "frozenset": frozenset,
    -    "getattr": getattr,
    -    "hasattr": hasattr,
         "hash": hash,
         "hex": hex,
         "int": int,
         "isinstance": isinstance,
         "issubclass": issubclass,
    -    "iter": iter,
         "len": len,
         "list": list,
         "map": map,
         "max": max,
         "min": min,
    -    "next": next,
         "oct": oct,
         "ord": ord,
         "pow": pow,
    @@ -68,7 +64,6 @@ def _restricted_import(
         "reversed": reversed,
         "round": round,
         "set": set,
    -    "setattr": setattr,
         "slice": slice,
         "sorted": sorted,
         "str": str,
    @@ -94,23 +89,45 @@ def _get_restricted_globals(__globals: Union[dict, None]) -> Any:
     class DunderVisitor(ast.NodeVisitor):
         def __init__(self) -> None:
             self.has_access_to_private_entity = False
    +        self.has_access_to_disallowed_builtin = False
    +
    +        builtins = globals()["__builtins__"].keys()
    +        self._builtins = builtins
     
         def visit_Name(self, node: ast.Name) -> None:
             if node.id.startswith("_"):
                 self.has_access_to_private_entity = True
    +        if node.id not in ALLOWED_BUILTINS and node.id in self._builtins:
    +            self.has_access_to_disallowed_builtin = True
             self.generic_visit(node)
     
         def visit_Attribute(self, node: ast.Attribute) -> None:
             if node.attr.startswith("_"):
                 self.has_access_to_private_entity = True
    +        if node.attr not in ALLOWED_BUILTINS and node.attr in self._builtins:
    +            self.has_access_to_disallowed_builtin = True
             self.generic_visit(node)
     
     
     def _contains_protected_access(code: str) -> bool:
    +    # do not allow imports
    +    imports_modules = False
         tree = ast.parse(code)
    +    for node in ast.iter_child_nodes(tree):
    +        if isinstance(node, ast.Import):
    +            imports_modules = True
    +        elif isinstance(node, ast.ImportFrom):
    +            imports_modules = True
    +        else:
    +            continue
    +
         dunder_visitor = DunderVisitor()
         dunder_visitor.visit(tree)
    -    return dunder_visitor.has_access_to_private_entity
    +    return (
    +        dunder_visitor.has_access_to_private_entity
    +        or dunder_visitor.has_access_to_disallowed_builtin
    +        or imports_modules
    +    )
     
     
     def _verify_source_safety(__source: Union[str, bytes, CodeType]) -> None:
    @@ -124,7 +141,8 @@ def _verify_source_safety(__source: Union[str, bytes, CodeType]) -> None:
             __source = __source.decode()
         if _contains_protected_access(__source):
             raise RuntimeError(
    -            "Execution of code containing references to private or dunder methods is forbidden!"
    +            "Execution of code containing references to private or dunder methods, "
    +            "disallowed builtins, or any imports, is forbidden!"
             )
     
     
    
  • llama-index-core/tests/query_engine/test_pandas.py+2 3 modified
    @@ -111,8 +111,7 @@ def test_default_output_processor_rce2() -> None:
         output = parser.parse(injected_code)
     
         assert (
    -        "Execution of code containing references to private or dunder methods is forbidden!"
    -        in output
    +        "Execution of code containing references to private or dunder methods" in output
         ), "Injected code executed successfully!"
     
     
    @@ -152,7 +151,7 @@ def test_default_output_processor_e2e(tmp_path: Path) -> None:
         assert isinstance(response, Response)
         # raw df should be equal to slice of dataframe that's just population at location 2
         rmetadata = cast(Dict[str, Any], response.metadata)
    -    assert rmetadata["raw_pandas_output"] == str(df["population"].iloc[2:3])
    +    assert rmetadata["raw_pandas_output"] == str(df["population"].iloc[2])
     
         # attack 1: fail!
         print("[+] Attack 1 starts, it should fail!")
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.