Skip to content

LangChain PALChain 任意代码执行

一句话

据 NVD 与 GitHub Advisory,LangChain PALChain 在部分旧版本中把 LLM 生成的 Python 代码交给执行路径,形成 ToLO-Exec

怎么读这个案例

PALChainProgram-Aided Language Models(PAL)的代表场景:让模型写一段程序来辅助推理。论文出处:Gao et al. 2022 “PAL: Program-Aided Language Models”

对学习 ToLO 来说,它是最典型的 “功能设计本身需要执行 LLM 输出” 案例。

因此不要只把它理解成”某个地方用了 exec”。更重要的是理解:当产品功能允许模型生成代码时,安全边界必须重新设计,不能靠”模型应该生成好代码”这个假设

PAL 和 LLMMathChain 的差别:

LLMMathChainPALChain
设计目的计算数学题多步程序推理(可以写循环、条件、变量)
模型输出一段表达式一整段 Python 程序
修复方向换求值器移除任意 exec / AST 限制 / 显式确认

影响组件

影响组件是 LangChain 的 PALChain,相关功能是 Program-Aided Language Models 的数学/推理链。

来源affected_versionsfixed_in
NVDbefore 0.0.2360.0.236
GitHub Advisory< 0.0.2470.0.247

两者版本口径不一致,因此本页保持 verification: pending

ToLO 路径

内容
SourceS_LLM^frameworkPALChainLLMChain 取得的生成代码文本
Transform链将用户问题送入模型,把模型输出作为 Python 代码片段传给执行工具
SinkPythonREPL.run(...) 及其底层 exec 路径(ToLO-Exec / CWE-94)
Guard 缺失缺少与代码执行匹配的 C_SAFE^allowlist(可执行语法白名单) / C_SAFE^capability(执行隔离)

这条路径的关键不是”用户输入里出现了 Python 代码”,而是 PAL 设计本身允许 LLM 生成程序片段并交给执行环境。也就是说,危险来自框架功能边界:自然语言推理结果被提升为可执行代码。

教学骨架(简化)

# PAL 简化骨架
question = "Alice 有 3 个苹果,Bob 给了她 5 个,她吃了 2 个,还有几个?"
prompt = """
请把下面的数学问题写成 Python 程序求解。
问题: {question}
输出格式:
```python
# 解题代码
answer = ...

"""

llm_output = llm.invoke(prompt.format(question=question))

模型可能输出:

```python

alice = 3

alice += 5

alice -= 2

answer = alice

```

从输出提取代码块

code = extract_code_block(llm_output)

!!! 修复前:直接执行

exec(code) # ← ToLO-Exec print(answer)

如果攻击者通过 prompt 诱导模型输出 `import os; os.system('id')`,**这段代码会原样执行**。
## 修复方式
据 PR `#6003` 与 commit `e294ba4`,上游做了三件事:
### 1. 引入 `PALValidation` —— AST 检查
在执行前用 Python `ast` 模块**解析生成的代码**,**默认禁止**:
- `import` / `from ... import ...`(无法引入任意模块)
- 调用 builtins 中危险函数(`open`、`exec`、`eval`、`compile`、`__import__` 等)
- 文件 / 系统访问相关 API
伪代码示意:
```python
import ast
ALLOWED_NODE_TYPES = {ast.Module, ast.Assign, ast.Name, ast.Num, ast.BinOp, ...}
FORBIDDEN_NAMES = {"open", "exec", "eval", "__import__", "compile", "globals", "locals"}
def validate(code: str) -> bool:
tree = ast.parse(code)
for node in ast.walk(tree):
if type(node) not in ALLOWED_NODE_TYPES:
raise ValueError(f"forbidden node: {type(node).__name__}")
if isinstance(node, ast.Name) and node.id in FORBIDDEN_NAMES:
raise ValueError(f"forbidden name: {node.id}")
return True

2. 给 PythonREPL.run 增加 timeout

防 DoS,但不防代码执行本身

3. 后续 allow_dangerous_code 标志

更后续的版本中,LangChain 把 PALChain 默认设为 allow_dangerous_code=False,要求调用方显式启用,等价于一个 explicit-approval gate。

修复属于哪一类 C_SAFE?

  • C_SAFE^allowlist:AST 节点 / 名字白名单 — 主要 sanitizer。
  • C_SAFE^capability:allow_dangerous_code flag — explicit approval。
  • 残余:即使 AST 校验,仍要看上下文里的全局变量、__class__ / __subclasses__ 等元编程绕过可能。

Python AST 白名单历史上多次被绕过(().__class__.__mro__[1].__subclasses__() 是经典 SSTI / sandbox 逃逸 payload)。AST 白名单需要配合执行隔离(进程沙箱、syscall 限制)才稳。

对 ToLO 分类的启发

该案例支持 ToLO-Exec 子类:sink 是传统 CWE-94 代码执行,但 source 是框架内部的 LLM 输出。

不需要新增 sink 类,但提示 PAL / code-interpreter 类功能必须有明确执行能力边界

它也说明 “功能是有意设计的”并不自动代表安全。PAL 的目标本来就是让模型生成可执行推理程序;ToLO 关心的是这个设计是否给了攻击者影响执行内容的路径,以及执行前是否有独立边界

类似设计还有:OpenAI Code Interpreter、Anthropic Computer Use、Claude Code、SmolAgents、AutoGPT 等。所有”让 LLM 写代码并执行”的产品都属于 ToLO-Exec 高危区。

学习者要带走什么

  1. ToLO-Exec 常出现在高级功能中,不一定是开发者”误用” API。PAL、code-interpreter、agent 自动化都是有意设计执行 LLM 代码。
  2. AST 检查属于尝试收窄语言能力,但仍要看是否覆盖危险语义。Python 的元编程(__class____bases____subclasses__)历史上多次绕 sandbox。
  3. timeout 能降低资源滥用风险,但不能单独解决任意代码执行风险
  4. allow_dangerous_code=False 是 explicit approval 模式 capability,把责任明确转给调用方 —— 但调用方常常不读 doc 直接 =True

读完检查

如果一个框架说”我们只执行模型生成的数学辅助代码”,你会检查哪些边界?至少包括:

  • 允许的语法(只算术表达式?支持赋值?循环?)
  • 允许的 import(默认应禁止)
  • 可访问的变量(global / local 是否清空)
  • 运行权限(同进程? subprocess? 容器?)
  • 文件 / 网络访问(syscall 限制?)
  • 超时(防资源滥用)
  • 审计(是否记录所有 exec 的代码?)

至少这 7 条都做了,才算工程可接受的 ToLO-Exec 防御。

公开来源

下一步阅读