Ele tem um medium:
https://bxmbn.medium.com/
Github query
lang:php /\$(col|column|field|sort|order_by|sort_by)\w*\s*=\s*\$_(GET|REQUEST)/ /->prepare\s*\(\s*["']SELECT\s+.*\`\$\w+/Então, o que fica de aprendizado nisso é que o prepared statement, mesmo na emulação do PDO, faz sim o papel de evitar que o atacante "saia das aspas" da concatenação.
Contudo, como pode ser visto nesse cenário, essa proteção falha quando temos um código que mistura a construção dinâmica de uma parte da query (como o nome da coluna) com o uso de parâmetros seguros, e o atacante usa um null byte para enganar o próprio analisador (parser) do PDO, convencendo-o a mover o ponto seguro da injeção para um local totalmente vulnerável.
Contudo, como pode ser visto nesse cenário, essa proteção falha quando temos um código que mistura a construção dinâmica de uma parte da query (como o nome da coluna) com o uso de parâmetros seguros, e o atacante usa um null byte para enganar o próprio analisador (parser) do PDO, convencendo-o a mover o ponto seguro da injeção para um local totalmente vulnerável.
Deu um trabalho criar todos esses passos (sim usei IA pra ajudar na explicação mas mesmo assim ela não entendia muitos pontos) hahaha, dá uma reação aí se curtiu
tmj
tmj
Passo 4 (e último): O Backend (MySQL)
Como o PDO encontrou apenas um marcador, ele pega o único valor do
O MySQL recebe a consulta final, já montada e reestruturada:
Como o PDO encontrou apenas um marcador, ele pega o único valor do
execute() (o payload de name) e o insere ali.O MySQL recebe a consulta final, já montada e reestruturada:
SELECTO MySQL executa a primeira parte (\'x' FROM (SELECT table_name AS `\'xfrom information_schema.tables)y;#'#\0` FROM fruit WHERE name = ?
SELECT ... FROM ...;) e, por causa do ponto e vírgula, encerra a instrução. O resto da string é ignorado, e a lista de tabelas é retornada ao atacante.Passo 3: O Parser do PDO (A Confusão)
Agora, vamos analisar com calma como o PDO interpreta essa query que ele mesmo montou.
O Gatilho da Confusão: O parser do PDO encontra a crase de abertura
que encontrar aqui dentro."
A Quebra: No entanto, dentro dessas crases, ele encontra o payload do atacante. O null byte (
A Reinterpretação: Nessa reavaliação, ele vê o
A "Cegueira" do Parser: Imediatamente após, o parser vê o caractere
O resultado é que o PDO conclui que a consulta tem apenas um marcador (
Agora, vamos analisar com calma como o PDO interpreta essa query que ele mesmo montou.
O Gatilho da Confusão: O parser do PDO encontra a crase de abertura
(`\) que estava no código original e pensa: "Ok, estou lendo um nome de coluna. Vou ignorar qualquer ?`
que encontrar aqui dentro."
A Quebra: No entanto, dentro dessas crases, ele encontra o payload do atacante. O null byte (
\0) no final age como um terminador inesperado e quebra a leitura do nome da coluna. O parser fica confuso e reavalia o que acabou de ler.A Reinterpretação: Nessa reavaliação, ele vê o
? não mais como parte de um nome de coluna, mas como um marcador de parâmetro válido.A "Cegueira" do Parser: Imediatamente após, o parser vê o caractere
#. Ele o reconhece como o início de um comentário do MySQL e para de analisar a query ali. Por isso, ele ignora completamente a parte " FROM fruit WHERE name = ?", pois acredita que tudo aquilo é um comentário.O resultado é que o PDO conclui que a consulta tem apenas um marcador (
?) para substituir: aquele que o atacante injetou.Passo 2: O Backend (PHP)
O script PHP pega o payload do atacante (
O script PHP pega o payload do atacante (
\?#\0) e o insere dentro das crases que já existem no código. A string final que ele constrói para enviar ao PDO é:SELECT `\?#\0` FROM fruit WHERE name = ?Passo 1: O Atacante
O atacante envia a seguinte requisição para o servidor:
O atacante envia a seguinte requisição para o servidor:
.../script.php?col=\?%23%00&name='x' FROM (SELECT table_name AS 'x' from information_schema.tables)y;%23Você usa um código PDO parecido com este?
Caso sim, considere desabilitar a funcionalidade de emulação de prepared statements (P
Veja como o ataque ocorre passo a passo:
$col = $_GET['col']; // A variável é controlada pelo usuário
$stmt = $pdo->prepare("SELECT `$col` FROM fruit WHERE name = ?");
$stmt->execute([$_GET['name']]);Caso sim, considere desabilitar a funcionalidade de emulação de prepared statements (P
DO::ATTR_EMULATE_PREPARES => false). O pesquisador Adam Kues aponta que, com a emulação ativa (padrão no MySQL), um código como esse é vulnerável a uma injeção de SQL complexa.Veja como o ataque ocorre passo a passo:
To preparando um resumão agora desse artigo mt bom aqui pra vocês:
https://slcyber.io/assetnote-security-research-center/a-novel-technique-for-sql-injection-in-pdos-prepared-statements/
Fiquei sabendo de sua existência pelo post do Shubs no linkedin:
https://www.linkedin.com/posts/shubhamshah_a-novel-technique-for-sql-injection-in-pdo-activity-7352898366252478464-ajo5
https://slcyber.io/assetnote-security-research-center/a-novel-technique-for-sql-injection-in-pdos-prepared-statements/
Fiquei sabendo de sua existência pelo post do Shubs no linkedin:
https://www.linkedin.com/posts/shubhamshah_a-novel-technique-for-sql-injection-in-pdo-activity-7352898366252478464-ajo5
Abro o twitter enquanto espero o ifood chegar e me dou de cara com a planilha que eu tava procurando 10 minutos atras quando tava testando uma aplicação em bugbounty
https://portswigger.net/web-security/ssrf/url-validation-bypass-cheat-sheet
https://github.com/aliasrobotics/cai?tab=readme-ov-file
"We encourage you to read CAI's the technical report at https://arxiv.org/pdf/2504.06017."