- Fixed SQL injection via E'...' backslash breakout CVE-2026-17543 - Fixed GHSA-vc5h-9ppw-p5f3 Crash via recursive symlinks CVE-2026-7260 Resolves: RHEL-223940
227 lines
8.3 KiB
Diff
227 lines
8.3 KiB
Diff
From 531196af8ccc43c26ca97aef27b0816e8b4e2f60 Mon Sep 17 00:00:00 2001
|
|
From: Ilija Tovilo <ilija.tovilo@me.com>
|
|
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)
|
|
---
|
|
ext/pgsql/pgsql.c | 5 +-
|
|
ext/pgsql/tests/10pg_convert_9.phpt | 18 +++++-
|
|
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 | 58 ++++++++++++++++++++
|
|
ext/pgsql/tests/bug64609.phpt | 2 +-
|
|
ext/pgsql/tests/bug68638.phpt | 2 +-
|
|
8 files changed, 82 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 e9a68a8555..4cffeb31d6 100644
|
|
--- a/ext/pgsql/pgsql.c
|
|
+++ b/ext/pgsql/pgsql.c
|
|
@@ -4562,7 +4562,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, '\'');
|
|
@@ -4852,8 +4851,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 0a2828a247..8a2c3cd972 100644
|
|
--- a/ext/pgsql/tests/10pg_convert_9.phpt
|
|
+++ b/ext/pgsql/tests/10pg_convert_9.phpt
|
|
@@ -19,6 +19,8 @@ $converted = pg_convert($db, $table_name, $fields);
|
|
|
|
var_dump($converted);
|
|
|
|
+var_dump(pg_convert($db, $table_name, ['str' => "\\' OR 1=1"]));
|
|
+
|
|
/* Invalid values */
|
|
try {
|
|
$converted = pg_convert($db, $table_name, [5 => 'AAA']);
|
|
@@ -46,18 +48,30 @@ try {
|
|
} catch (\TypeError $e) {
|
|
echo $e->getMessage(), \PHP_EOL;
|
|
}
|
|
+
|
|
+/* standard_conforming_strings = 1 */
|
|
+pg_query($db, "SET standard_conforming_strings = 1");
|
|
+var_dump(pg_convert($db, $table_name, ['str' => "\\' OR 1=1"]));
|
|
?>
|
|
--EXPECT--
|
|
array(3) {
|
|
[""num""]=>
|
|
string(4) "1234"
|
|
[""str""]=>
|
|
- string(6) "E'AAA'"
|
|
+ string(5) "'AAA'"
|
|
[""bin""]=>
|
|
- string(12) "E'\\x424242'"
|
|
+ string(11) "'\\x424242'"
|
|
+}
|
|
+array(1) {
|
|
+ [""str""]=>
|
|
+ string(13) "'\\'' OR 1=1'"
|
|
}
|
|
Array of values must be an associative array with string keys
|
|
Array of values must be an associative array with string keys
|
|
Values must be of type string|int|float|bool|null, array given
|
|
Values must be of type string|int|float|bool|null, stdClass given
|
|
Values must be of type string|int|float|bool|null, resource given
|
|
+array(1) {
|
|
+ [""str""]=>
|
|
+ string(12) "'\'' OR 1=1'"
|
|
+}
|
|
diff --git a/ext/pgsql/tests/10pg_convert_json_array.phpt b/ext/pgsql/tests/10pg_convert_json_array.phpt
|
|
index 46aec3ffbc..7f51cb003f 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 11a401f358..f0e2e623ec 100644
|
|
--- a/ext/pgsql/tests/12pg_insert_9.phpt
|
|
+++ b/ext/pgsql/tests/12pg_insert_9.phpt
|
|
@@ -52,7 +52,7 @@ try {
|
|
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)
|
|
Array of values must be an associative array with string keys
|
|
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..88b37a8432
|
|
--- /dev/null
|
|
+++ b/ext/pgsql/tests/GHSA-7qpv-r5mr-78m4.phpt
|
|
@@ -0,0 +1,58 @@
|
|
+--TEST--
|
|
+GHSA-7qpv-r5mr-78m4: SQL injection via E'...' backslash breakout
|
|
+--CREDITS--
|
|
+expatch.llc
|
|
+--EXTENSIONS--
|
|
+pgsql
|
|
+--SKIPIF--
|
|
+<?php include("skipif.inc"); ?>
|
|
+--FILE--
|
|
+<?php
|
|
+include 'config.inc';
|
|
+
|
|
+$db = pg_connect($conn_str);
|
|
+
|
|
+pg_query($db, "SET standard_conforming_strings = 1");
|
|
+
|
|
+pg_query($db, "DROP TABLE IF EXISTS ghsa_7qpv_r5mr_78m4");
|
|
+pg_query($db, "CREATE TABLE ghsa_7qpv_r5mr_78m4 (id serial primary key, name text, admin boolean)");
|
|
+pg_query($db, "INSERT INTO ghsa_7qpv_r5mr_78m4 (name, admin) VALUES ('alice', false), ('bob', false)");
|
|
+
|
|
+$params = ['name' => "zzz' OR 1=1 --"];
|
|
+echo pg_select($db, 'ghsa_7qpv_r5mr_78m4', $params, PGSQL_DML_STRING) . "\n";
|
|
+printf("returned: %d\n\n", count(pg_select($db, 'ghsa_7qpv_r5mr_78m4', $params)));
|
|
+
|
|
+$params = ['name' => "zzz\\' OR 1=1 --"];
|
|
+echo pg_select($db, 'ghsa_7qpv_r5mr_78m4', $params, PGSQL_DML_STRING) . "\n";
|
|
+printf("returned: %d\n\n", count(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']);
|
|
+echo "\n";
|
|
+
|
|
+$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--
|
|
+SELECT * FROM "ghsa_7qpv_r5mr_78m4" WHERE "name"='zzz'' OR 1=1 --';
|
|
+returned: 0
|
|
+
|
|
+SELECT * FROM "ghsa_7qpv_r5mr_78m4" WHERE "name"='zzz\'' OR 1=1 --';
|
|
+returned: 0
|
|
+
|
|
+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--
|
|
+<?php
|
|
+include('config.inc');
|
|
+$db = pg_connect($conn_str);
|
|
+pg_query($db, "DROP TABLE IF EXISTS ghsa_7qpv_r5mr_78m4");
|
|
+?>
|
|
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
|
|
|