From 6a80d9d36a5ea210ea6cc411864c08c71f7417ec Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 27 Jul 2026 16:49:38 +0200 Subject: [PATCH 2/5] Fix SQL injection in ext-pgsql via E'...' backslash breakout php_pgsql_add_quotes() quotes the string with E'...', but PQescapeStringConn() does not escape \ unless standard_conforming_strings is off. PQescapeStringConn() is meant to be used with '', so do that instead. Fixes GHSA-7qpv-r5mr-78m4 (cherry picked from commit ab048bd83b578119cf81b456526d50498421d617) (cherry picked from commit eb0de306e0569f8b50065a42f13dd3e2b3729532) (cherry picked from commit 531196af8ccc43c26ca97aef27b0816e8b4e2f60) --- ext/pgsql/pgsql.c | 5 +- ext/pgsql/tests/10pg_convert_9.phpt | 4 +- ext/pgsql/tests/10pg_convert_json_array.phpt | 4 +- ext/pgsql/tests/12pg_insert_9.phpt | 2 +- ext/pgsql/tests/14pg_update_9.phpt | 2 +- ext/pgsql/tests/GHSA-7qpv-r5mr-78m4.phpt | 48 ++++++++++++++++++++ ext/pgsql/tests/bug64609.phpt | 2 +- ext/pgsql/tests/bug68638.phpt | 2 +- 8 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 ext/pgsql/tests/GHSA-7qpv-r5mr-78m4.phpt diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 9e06497125..37e6fae705 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -5838,7 +5838,6 @@ static int php_pgsql_add_quotes(zval *src, zend_bool should_free) assert(Z_TYPE_P(src) == IS_STRING); assert(should_free == 1 || should_free == 0); - 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, '\''); @@ -6124,8 +6123,8 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con zend_string *str; /* PostgreSQL ignores \0 */ str = zend_string_alloc(Z_STRLEN_P(val) * 2, 0); - /* better to use PGSQLescapeLiteral since PGescapeStringConn does not handle special \ */ - ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), Z_STRVAL_P(val), Z_STRLEN_P(val), &escape_err); + ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), + Z_STRVAL_P(val), Z_STRLEN_P(val), &escape_err); if (escape_err) { err = 1; } else { diff --git a/ext/pgsql/tests/10pg_convert_9.phpt b/ext/pgsql/tests/10pg_convert_9.phpt index a8395315c6..d956552cdd 100644 --- a/ext/pgsql/tests/10pg_convert_9.phpt +++ b/ext/pgsql/tests/10pg_convert_9.phpt @@ -24,7 +24,7 @@ array(3) { [""num""]=> string(4) "1234" [""str""]=> - string(6) "E'AAA'" + string(5) "'AAA'" [""bin""]=> - string(12) "E'\\x424242'" + string(11) "'\\x424242'" } diff --git a/ext/pgsql/tests/10pg_convert_json_array.phpt b/ext/pgsql/tests/10pg_convert_json_array.phpt index 960cfd6c99..711d0dff39 100644 --- a/ext/pgsql/tests/10pg_convert_json_array.phpt +++ b/ext/pgsql/tests/10pg_convert_json_array.phpt @@ -30,8 +30,8 @@ if (!pg_insert($db, $table_name_92, $fields)) { --EXPECT-- array(2) { [""textary""]=> - string(51) "E'{"meeting", "lunch", "training", "presentation"}'" + string(50) "'{"meeting", "lunch", "training", "presentation"}'" [""jsn""]=> - string(22) "E'{"f1":1,"f2":"foo"}'" + string(21) "'{"f1":1,"f2":"foo"}'" } OK diff --git a/ext/pgsql/tests/12pg_insert_9.phpt b/ext/pgsql/tests/12pg_insert_9.phpt index 275afc55e1..c7e7a98a20 100644 --- a/ext/pgsql/tests/12pg_insert_9.phpt +++ b/ext/pgsql/tests/12pg_insert_9.phpt @@ -24,7 +24,7 @@ var_dump( pg_insert($db, $table_name, $fields, PGSQL_DML_EXEC) ); // Return reso echo "Ok\n"; ?> --EXPECTF-- -INSERT INTO "php_pgsql_test" ("num","str","bin") VALUES (1234,E'AAA',E'\\x424242'); +INSERT INTO "php_pgsql_test" ("num","str","bin") VALUES (1234,'AAA','\\x424242'); INSERT INTO "php_pgsql_test" ("num","str","bin") VALUES ('1234','AAA','BBB'); resource(%d) of type (pgsql result) Ok diff --git a/ext/pgsql/tests/14pg_update_9.phpt b/ext/pgsql/tests/14pg_update_9.phpt index e3e802b7a2..1e2ae37227 100644 --- a/ext/pgsql/tests/14pg_update_9.phpt +++ b/ext/pgsql/tests/14pg_update_9.phpt @@ -24,6 +24,6 @@ echo pg_update($db, $table_name, $fields, $ids, PGSQL_DML_STRING|PGSQL_DML_ESCAP echo "Ok\n"; ?> --EXPECT-- -UPDATE "php_pgsql_test" SET "num"=1234,"str"=E'ABC',"bin"=E'\\x58595a' WHERE "num"=1234; +UPDATE "php_pgsql_test" SET "num"=1234,"str"='ABC',"bin"='\\x58595a' WHERE "num"=1234; UPDATE "php_pgsql_test" SET "num"='1234',"str"='ABC',"bin"='XYZ' WHERE "num"='1234'; Ok diff --git a/ext/pgsql/tests/GHSA-7qpv-r5mr-78m4.phpt b/ext/pgsql/tests/GHSA-7qpv-r5mr-78m4.phpt new file mode 100644 index 0000000000..f7f0dc8aea --- /dev/null +++ b/ext/pgsql/tests/GHSA-7qpv-r5mr-78m4.phpt @@ -0,0 +1,48 @@ +--TEST-- +GHSA-7qpv-r5mr-78m4: SQL injection via E'...' backslash breakout +--CREDITS-- +expatch.llc +--SKIPIF-- + +--FILE-- + "zzz' OR 1=1 --"]; +var_dump(pg_select($db, 'ghsa_7qpv_r5mr_78m4', $params)); + +$params = ['name' => "zzz\\' OR 1=1 --"]; +var_dump(pg_select($db, 'ghsa_7qpv_r5mr_78m4', $params)); + +$params = ['name' => "john\\', true) --", 'admin' => 'false']; +echo pg_insert($db, 'ghsa_7qpv_r5mr_78m4', $params, PGSQL_DML_STRING) . "\n"; +pg_insert($db, 'ghsa_7qpv_r5mr_78m4', $params); +var_dump(pg_select($db, 'ghsa_7qpv_r5mr_78m4', ['id' => 3])[0]['admin']); + +$params = ['name' => "jake\\', true) --", 'admin' => 'f']; +echo pg_insert($db, 'ghsa_7qpv_r5mr_78m4', $params, PGSQL_DML_ESCAPE|PGSQL_DML_STRING) . "\n"; +pg_insert($db, 'ghsa_7qpv_r5mr_78m4', $params, PGSQL_DML_EXEC|PGSQL_DML_ESCAPE); +var_dump(pg_select($db, 'ghsa_7qpv_r5mr_78m4', ['id' => 4])[0]['admin']); + +?> +--EXPECT-- +bool(false) +bool(false) +INSERT INTO "ghsa_7qpv_r5mr_78m4" ("name","admin") VALUES ('john\'', true) --','f'); +string(1) "f" +INSERT INTO "ghsa_7qpv_r5mr_78m4" ("name","admin") VALUES ('jake\'', true) --','f'); +string(1) "f" +--CLEAN-- + diff --git a/ext/pgsql/tests/bug64609.phpt b/ext/pgsql/tests/bug64609.phpt index e31ec8e019..03cee4aeca 100644 --- a/ext/pgsql/tests/bug64609.phpt +++ b/ext/pgsql/tests/bug64609.phpt @@ -26,5 +26,5 @@ var_dump($converted); --EXPECT-- array(1) { [""a""]=> - string(5) "E'ok'" + string(4) "'ok'" } diff --git a/ext/pgsql/tests/bug68638.phpt b/ext/pgsql/tests/bug68638.phpt index e0701a79f5..336438c86b 100644 --- a/ext/pgsql/tests/bug68638.phpt +++ b/ext/pgsql/tests/bug68638.phpt @@ -32,7 +32,7 @@ pg_query("DROP TABLE $table"); ?> --EXPECT-- -string(52) "UPDATE "test_68638" SET "value"=E'inf' WHERE "id"=1;" +string(51) "UPDATE "test_68638" SET "value"='inf' WHERE "id"=1;" array(2) { ["id"]=> string(1) "1" -- 2.55.0