Skip to content

GHSA-7qpv-r5mr-78m4

CVE Information

php_pgsql_convert() is used to convert and escape user-provided parameters in pg_insert(), pg_update(), pg_select(), and pg_delete(). It does so using PQescapeStringConn() and then wraps the result in an escape string constant, E'...' (via php_pgsql_add_quotes()).

https://github.com/php/php-src/blob/cbc0489126a7682796aad1e5fb4e51de74af162c/ext/pgsql/pgsql.c#L4751-L4757

With standard_conforming_strings = on (the default since PostgreSQL 9.1), PQescapeStringConn() does not correctly escape values for the escape string constant E'...', as it does not escape \ under this configuration. When PQescapeStringConn() escapes ' as '', an attacker can trivially terminate the string by escaping the first single quote.

$result = pg_select($db, 'user', ['name' => "zzz\\' OR 1=1 --"]);
// SELECT * FROM "user" WHERE "name"='zzz\'' OR 1=1 --';
var_dump($result); // returned all rows

Note that the doubled \\ is a PHP escape sequence and that the \ appears only once in the parameter. Also note that pg_select() escapes the ' by doubling it but does not escape the \. Consequently, the first of the two ' characters is escaped (meaning that it represents a literal '), while the second terminates the string. Everything after that is interpreted as part of the query.

The solution changes php_pgsql_convert() to wrap parameters in non-escaping string constants instead.