CVE-2026-17543
Patch Available.
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
PHP is a general-purpose scripting language used mostly for web development. It runs behind web servers through server APIs such as Apache mod_php and PHP-FPM, and powers content management systems and frameworks such as WordPress, Drupal, Moodle and Laravel. Its pgsql extension connects PHP applications to PostgreSQL and offers helper functions that build INSERT, UPDATE, SELECT and DELETE statements from PHP arrays.
A SQL Injection vulnerability (CVE-2026-17543) has been identified in the pg_convert() function of PHP's pgsql extension, which pg_insert(), pg_update(), pg_select() and pg_delete() use to escape values, and which allows an attacker who controls one of those values to end the quoted string early with a backslash and inject arbitrary SQL. PHP tracks this issue as GHSA-7qpv-r5mr-78m4.
Per OWASP: A SQL injection attack consists of insertion or "injection" of a SQL query via the input data from the client to the application. A successful SQL injection exploit can read sensitive data from the database, modify database data (Insert/Update/Delete), execute administration operations on the database (such as shutdown the DBMS), recover the content of a given file present on the DBMS file system and in some cases issue commands to the operating system.
This issue affects all versions of PHP before 8.2.33, PHP 8.3 before 8.3.33, PHP 8.4 before 8.4.24 and PHP 8.5 before 8.5.9, which includes the End-of-Life PHP 5.6, 7.0, 7.1, 7.2, 7.3, 7.4, 8.0 and 8.1 release lines through their final releases.
Details
Module Info
- Product: PHP
- Affected packages:
php-src(ext/pgsql,pg_convert(),pg_insert(),pg_update(),pg_select(),pg_delete()) - Affected versions: <8.2.33, >=8.3.0 <8.3.33, >=8.4.0 <8.4.24, >=8.5.0 <8.5.9
- GitHub repository: https://github.com/php/php-src
- Published packages: https://www.php.net/downloads
- Package manager: N/A
- Fixed in:
- OSS PHP 8.2.33, 8.3.33, 8.4.24, 8.5.9
- NES for PHP 5.6.1 (based on PHP 5.6.40)
- NES for PHP 7.2.1 (based on PHP 7.2.34)
- NES for PHP 7.4.1 (based on PHP 7.4.33)
Vulnerability Info
This High-severity vulnerability is found in the pgsql extension of all PHP versions before 8.2.33, 8.3 before 8.3.33, 8.4 before 8.4.24 and 8.5 before 8.5.9, including the End-of-Life PHP 5.6, 7.0, 7.1, 7.2, 7.3, 7.4, 8.0 and 8.1 release lines, which never received the upstream fix. The PHP project scores it 8.1 under CVSS 4.0 (CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:U/AU:Y/R:U).
pg_convert() checks each value against the column types of a table and turns it into an SQL literal. For text and similar columns it escapes the value with libpq's PQescapeStringConn() and then wraps it in quotes with php_pgsql_add_quotes(). In PHP 7.4.33, that helper writes an escape string constant, E'...':
smart_str_appendc(&str, 'E');
smart_str_appendc(&str, '\'');
smart_str_appendl(&str, Z_STRVAL_P(src), Z_STRLEN_P(src));
smart_str_appendc(&str, '\'');
smart_str_0(&str);
The two steps assume different quoting rules. With standard_conforming_strings on, which is PostgreSQL's default since 9.1, PQescapeStringConn() doubles single quotes but leaves backslashes alone, because a backslash has no special meaning in an ordinary '...' literal. Inside E'...', however, a backslash escapes the next character. A value such as x\' OR 1=1 -- becomes E'x\'' OR 1=1 --': the backslash consumes the first quote, the second quote closes the literal, and the rest of the value runs as SQL. The advisory's proof of concept uses this with pg_select() to return every row of a table.
Exploitation needs code that passes attacker-supplied values to pg_convert(), pg_insert(), pg_update(), pg_select() or pg_delete(), under any SAPI, against a PostgreSQL server with standard_conforming_strings on. The attacker can then read or change data with the privileges of the application's database user. Applications that build their queries with pg_query_params() or pg_execute() instead of these helpers are not exposed.
The fix quotes the escaped value as a plain '...' literal, the form PQescapeStringConn() is made for:
static zend_string *php_pgsql_add_quotes(zend_string *src)
{
return zend_string_concat3("'", strlen("'"), ZSTR_VAL(src), ZSTR_LEN(src), "'", strlen("'"));
}
As a side effect, the SQL these functions build, and the string PGSQL_DML_STRING returns, now uses '...' instead of E'...'. The NES releases for PHP 5.6, 7.2 and 7.4 carry ports of the upstream fix.
Mitigation
The PHP project publishes security fixes only for its supported release lines. PHP 5.6, 7.0, 7.1, 7.2, 7.3, 7.4, 8.0 and 8.1 are End-of-Life and will not receive any public updates to address this issue.
Until an upgrade is possible, use prepared statements with bound parameters (pg_query_params(), pg_prepare() and pg_execute()) instead of passing user input to pg_insert(), pg_update(), pg_select(), pg_delete() or pg_convert().
Users of the affected components should apply one of the following mitigations:
- Upgrade to PHP 8.2.33, 8.3.33, 8.4.24, 8.5.9, or later.
- Leverage a commercial support partner like HeroDevs (NES for PHP) for post-EOL security support.
Credits
- ExPatch-LLC (finder)
- Ilija Tovilo (remediation)