Merge branch 'PHP-8.4'

This commit is contained in:
Jakub Zelenka 2024-11-20 11:14:32 +01:00
commit 4b211a7769
No known key found for this signature in database
GPG Key ID: 1C0779DC5C0A9DE4
30 changed files with 2028 additions and 37 deletions

View File

@ -3759,13 +3759,23 @@ static zend_string* php_ldap_do_escape(const bool *map, const char *value, size_
zend_string *ret; zend_string *ret;
for (i = 0; i < valuelen; i++) { for (i = 0; i < valuelen; i++) {
len += (map[(unsigned char) value[i]]) ? 3 : 1; size_t addend = (map[(unsigned char) value[i]]) ? 3 : 1;
if (len > ZSTR_MAX_LEN - addend) {
return NULL;
}
len += addend;
} }
/* Per RFC 4514, a leading and trailing space must be escaped */ /* Per RFC 4514, a leading and trailing space must be escaped */
if ((flags & PHP_LDAP_ESCAPE_DN) && (value[0] == ' ')) { if ((flags & PHP_LDAP_ESCAPE_DN) && (value[0] == ' ')) {
if (len > ZSTR_MAX_LEN - 2) {
return NULL;
}
len += 2; len += 2;
} }
if ((flags & PHP_LDAP_ESCAPE_DN) && ((valuelen > 1) && (value[valuelen - 1] == ' '))) { if ((flags & PHP_LDAP_ESCAPE_DN) && ((valuelen > 1) && (value[valuelen - 1] == ' '))) {
if (len > ZSTR_MAX_LEN - 2) {
return NULL;
}
len += 2; len += 2;
} }
@ -3832,7 +3842,13 @@ PHP_FUNCTION(ldap_escape)
php_ldap_escape_map_set_chars(map, ignores, ignoreslen, 0); php_ldap_escape_map_set_chars(map, ignores, ignoreslen, 0);
} }
RETURN_NEW_STR(php_ldap_do_escape(map, value, valuelen, flags)); zend_string *result = php_ldap_do_escape(map, value, valuelen, flags);
if (UNEXPECTED(!result)) {
zend_argument_value_error(1, "is too long");
RETURN_THROWS();
}
RETURN_NEW_STR(result);
} }
#ifdef STR_TRANSLATION #ifdef STR_TRANSLATION

View File

@ -0,0 +1,28 @@
--TEST--
GHSA-g665-fm4p-vhff (OOB access in ldap_escape)
--EXTENSIONS--
ldap
--INI--
memory_limit=-1
--SKIPIF--
<?php
if (PHP_INT_SIZE !== 4) die("skip only for 32-bit");
if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
?>
--FILE--
<?php
try {
ldap_escape(' '.str_repeat("#", 1431655758), "", LDAP_ESCAPE_DN);
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
try {
ldap_escape(str_repeat("#", 1431655758).' ', "", LDAP_ESCAPE_DN);
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
ldap_escape(): Argument #1 ($value) is too long
ldap_escape(): Argument #1 ($value) is too long

View File

@ -0,0 +1,29 @@
--TEST--
GHSA-g665-fm4p-vhff (OOB access in ldap_escape)
--EXTENSIONS--
ldap
--INI--
memory_limit=-1
--SKIPIF--
<?php
if (PHP_INT_SIZE !== 4) die("skip only for 32-bit");
if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
?>
--FILE--
<?php
try {
ldap_escape(str_repeat("*", 1431655759), "", LDAP_ESCAPE_FILTER);
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
// would allocate a string of length 2
try {
ldap_escape(str_repeat("*", 1431655766), "", LDAP_ESCAPE_FILTER);
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
ldap_escape(): Argument #1 ($value) is too long
ldap_escape(): Argument #1 ($value) is too long

View File

@ -0,0 +1,856 @@
<?php
function my_mysqli_data_fields(): array
{
return [
'intval' => [
'type' => '03',
'charset' => '3f00',
'length' => '0b000000',
'flags' => '0110',
'decimal' => '00',
'query_data_packet_length' => '080000',
'query_data_value' => '023134',
'stmt_data_packet_length' => '0b0000',
'stmt_data_value' => '0e000000'
],
'fltval' => [
'type' => '04',
'charset' => '3f00',
'length' => '0c000000',
'flags' => '0110',
'decimal' => '1f',
'query_data_packet_length' => '090000',
'query_data_value' => '03322e33',
'stmt_data_packet_length' => '0b0000',
'stmt_data_value' => '33331340',
],
'dblval' => [
'type' => '05',
'charset' => '3f00',
'length' => '16000000',
'flags' => '0110',
'decimal' => '1f',
'query_data_packet_length' => '090000',
'query_data_value' => '03312e32',
'stmt_data_packet_length' => '0f0000',
'stmt_data_value' => '333333333333f33f'
],
'datval' => [
'type' => '0a',
'charset' => '3f00',
'length' => '0a000000',
'flags' => '8110',
'decimal' => '00',
'query_data_packet_length' => '100000',
'query_data_value' => '0a323031342d31322d3135',
'stmt_data_packet_length' => '0c0000',
'stmt_data_value' => '04de070c0f'
],
'timval' => [
'type' => '0b',
'charset' => '3f00',
'length' => '0a000000',
'flags' => '8110',
'decimal' => '00',
'query_data_packet_length' => '0e0000',
'query_data_value' => '0831333a30303a3032',
'stmt_data_packet_length' => '100000',
'stmt_data_value' => '080000000000150801'
],
'dtival' => [
'type' => '0c',
'charset' => '3f00',
'length' => '13000000',
'flags' => '8110',
'decimal' => '00',
'query_data_packet_length' => '190000',
'query_data_value' => '13323031342d31322d31362031333a30303a3031',
'stmt_data_packet_length' => '0f0000',
'stmt_data_value' => '07de070c100d0001'
],
'bitval' => [
'type' => '10',
'charset' => '3f00',
'length' => '40000000',
'flags' => '2110',
'decimal' => '00',
'query_data_packet_length' => '0e0000',
'query_data_value' => '080808080808080808',
'stmt_data_packet_length' => '100000',
'stmt_data_value' => '080808080808080808'
],
'strval' => [
'type' => 'fd',
'charset' => 'e000',
'length' => 'c8000000',
'flags' => '0110',
'decimal' => '00',
'query_data_packet_length' => '0a0000',
'query_data_value' => '0474657374',
'stmt_data_packet_length' => '0c0000',
'stmt_data_value' => '0474657374'
],
];
}
function my_mysqli_data_field(string $field): array
{
$fields = my_mysqli_data_fields();
if (!isset($fields[$field])) {
throw new Exception("Unknown field $field");
}
return $fields[$field];
}
class my_mysqli_fake_packet_item
{
public function __construct(public string|null $name, public string $value, public bool $is_hex = true)
{
}
}
class my_mysqli_fake_packet
{
private array $data = array();
public function __get(string $name)
{
foreach ($this->data as $item) {
if ($item->name === $name) {
return $item->value;
}
}
return null;
}
public function __set(string $name, string|my_mysqli_fake_packet_item $value)
{
if ($value instanceof my_mysqli_fake_packet_item) {
if ($value->name === null) {
$value->name = $name;
}
} else {
$value = new my_mysqli_fake_packet_item($name, $value, true);
}
for ($i = 0; $i < count($this->data); $i++) {
if ($this->data[$i]->name === $name) {
$this->data[$i] = $value;
return;
}
}
$this->data[] = $value;
}
public function to_bytes(): string
{
$bytes = '';
foreach ($this->data as $item) {
$bytes .= $item->is_hex ? hex2bin($item->value) : $item->value;
}
return $bytes;
}
}
class my_mysqli_fake_packet_generator
{
public static function create_packet_item(int|string $value, bool $is_hex = false, string $format = 'v'): my_mysqli_fake_packet_item
{
if (is_string($value)) {
$packed_value = $value;
} else {
$packed_value = pack($format, $value);
}
return new my_mysqli_fake_packet_item(null, $packed_value, $is_hex);
}
public function server_ok(): my_mysqli_fake_packet
{
$packet = new my_mysqli_fake_packet();
$packet->packet_length = "070000";
$packet->packet_number = "02";
$packet->header = "00"; // OK
$packet->affected_rows = "00";
$packet->last_insert_id = "00";
$packet->server_status = "0200";
$packet->warning_count = "0000";
return $packet;
}
public function server_greetings(): my_mysqli_fake_packet
{
$packet = new my_mysqli_fake_packet();
$packet->packet_length = "580000";
$packet->packet_number = "00";
$packet->proto_version = "0a";
$packet->version = self::create_packet_item('5.5.5-10.5.18-MariaDB' . chr(0));
$packet->thread_id = "03000000";
$packet->salt = "473e3f6047257c67";
$packet->filler = "00";
$packet->server_capabilities = self::create_packet_item(0b1111011111111110);
$packet->server_character_set = "08";
$packet->server_status = self::create_packet_item(0b000000000000010);
$packet->extended_server_capabilities = self::create_packet_item(0b1000000111111111);
$packet->auth_plugin = "15";
$packet->unused = "000000000000";
$packet->mariadb_extended_server_capabilities = self::create_packet_item(0b1111, false, 'V');
$packet->mariadb_extended_server_capabilities_salt = "6c6b55463f49335f686c643100";
$packet->mariadb_extended_server_capabilities_auth_plugin = self::create_packet_item('mysql_native_password');
return $packet;
}
public function server_tabular_query_response(): array
{
$qr1 = new my_mysqli_fake_packet();
$qr1->packet_length = "010000";
$qr1->packet_number = "01";
$qr1->field_count = "01";
$qr2 = new my_mysqli_fake_packet();
$qr2->packet_length = "190000";
$qr2->packet_number = "02";
$qr2->catalog_length_plus_name = "0164";
$qr2->db_length_plus_name = "0164";
$qr2->table_length_plus_name = "0164";
$qr2->original_t = "0164";
$qr2->name_length_plus_name = "0164";
$qr2->original_n = "0164";
$qr2->canary = "0c";
$qr2->charset = "3f00";
$qr2->length = "0b000000";
$qr2->type = "03";
$qr2->flags = "0350";
$qr2->decimals = "000000";
$qr3 = new my_mysqli_fake_packet();
$qr3->full = "05000003fe00002200";
$qr4 = new my_mysqli_fake_packet();
$qr4->full = "0400000401350174";
$qr5 = new my_mysqli_fake_packet();
$qr5->full = "05000005fe00002200";
return [$qr1, $qr2, $qr3, $qr4, $qr5];
}
public function server_upsert_query_response(): array
{
$qr1 = new my_mysqli_fake_packet();
$qr1->packet_length = "010000";
$qr1->packet_number = "01";
$qr1->field_count = "00"; // UPSERT
$qr1->affected_rows = "00";
$qr1->affected_rows = "00";
$qr1->last_insert_id = "00";
$qr1->server_status = "0000";
$qr1->warning_count = "0000";
$qr1->len = "01";
$qr1->filename = "65";
$qr1->packet_length = sprintf("%02x0000", strlen($qr1->to_bytes())-4);
return [$qr1];
}
public function server_stmt_prepare_response_start($num_field): my_mysqli_fake_packet
{
$pr1 = new my_mysqli_fake_packet();
$pr1->packet_length = "0c0000";
$pr1->packet_number = "01";
$pr1->response_code = '00'; // OK
$pr1->statement_id = '01000000';
$pr1->num_fields = $num_field;
$pr1->num_params = '0000';
$pr1->filler = '00';
$pr1->warnings = '0000';
return $pr1;
}
public function server_stmt_prepare_response_end($packer_number): my_mysqli_fake_packet
{
$pr3 = new my_mysqli_fake_packet();
$pr3->packet_length = "050000";
$pr3->packet_number = $packer_number;
$pr3->packet_type = 'fe'; // EOF
$pr3->warnings = '0000';
$pr3->server_status = '0200';
return $pr3;
}
public function server_stmt_prepare_items_response(): array
{
$pr1 = $this->server_stmt_prepare_response_start('0100');
$pr2 = new my_mysqli_fake_packet();
$pr2->packet_length = "300000";
$pr2->packet_number = "02";
$pr2->catalogue_len = '03';
$pr2->catalogue = '646566'; // def
$pr2->db_len = '08';
$pr2->db = '7068705f74657374'; // php_test
$pr2->table_len = '05';
$pr2->table = '6974656d73'; // items
$pr2->orig_table_len = '05';
$pr2->orig_table = '6974656d73'; // items
$pr2->name_len = '04';
$pr2->name = '6974656d';
$pr2->orig_name_len = '04';
$pr2->orig_name = '6974656d';
$pr2->something = '0c';
$pr2->charset = 'e000';
$pr2->length = 'c8000000';
$pr2->field_type = 'fd'; // FIELD_TYPE_VAR_STRING
$pr2->flags = '0110';
$pr2->decimal = '00';
$pr2->padding = '0000';
$pr3 = $this->server_stmt_prepare_response_end('03');
return [$pr1, $pr2, $pr3];
}
public function server_stmt_prepare_data_response_field($packet_number, $field_name): my_mysqli_fake_packet
{
if (strlen($field_name) != 6) {
throw new Exception("Invalid field length - only 6 is allowed");
}
$field = my_mysqli_data_field($field_name);
$pr = new my_mysqli_fake_packet();
$pr->packet_length = "320000";
$pr->packet_number = $packet_number;
$pr->catalogue_len = '03';
$pr->catalogue = bin2hex('def');
$pr->db_len = '08';
$pr->db = bin2hex('php_test');
$pr->table_len = '04';
$pr->table = bin2hex('data');
$pr->orig_table_len = '04';
$pr->orig_table = bin2hex('data');
$pr->name_len = '06';
$pr->name = bin2hex($field_name);
$pr->orig_name_len = '06';
$pr->orig_name = bin2hex($field_name);
$pr->something = '0c';
$pr->charset = $field['charset'];
$pr->length = $field['length'];
$pr->field_type = $field['type'];
$pr->flags = $field['flags'];
$pr->decimal = $field['decimal'];
$pr->padding = '0000';
return $pr;
}
public function server_stmt_prepare_data_response(string $field_name): array
{
$pr1 = $this->server_stmt_prepare_response_start('0200');
$pr2 = $this->server_stmt_prepare_data_response_field('02', 'strval');
$pr3 = $this->server_stmt_prepare_data_response_field('03', $field_name);
$pr4 = $this->server_stmt_prepare_response_end('04');
return [$pr1, $pr2, $pr3, $pr4];
}
public function server_stmt_execute_items_response(): array
{
$pr1 = new my_mysqli_fake_packet();
$pr1->packet_length = "010000";
$pr1->packet_number = "01";
$pr1->num_fields = '01';
$pr2 = new my_mysqli_fake_packet();
$pr2->packet_length = "300000";
$pr2->packet_number = "02";
$pr2->catalogue_len = '03';
$pr2->catalogue = '646566'; // def
$pr2->db_len = '08';
$pr2->db = '7068705f74657374'; // php_test
$pr2->table_len = '05';
$pr2->table = '6974656d73'; // items
$pr2->orig_table_len = '05';
$pr2->orig_table = '6974656d73'; // items
$pr2->name_len = '04';
$pr2->name = '6974656d';
$pr2->orig_name_len = '04';
$pr2->orig_name = '6974656d';
$pr2->something = '0c';
$pr2->charset = 'e000';
$pr2->length = 'c8000000';
$pr2->field_type = 'fd'; // FIELD_TYPE_VAR_STRING
$pr2->flags = '0110';
$pr2->decimal = '00';
$pr2->padding = '0000';
$pr3 = new my_mysqli_fake_packet();
$pr3->packet_length = "050000";
$pr3->packet_number = "03";
$pr3->packet_type = 'fe'; // EOF
$pr3->warnings = '0000';
$pr3->server_status = '2200';
$pr4 = new my_mysqli_fake_packet();
$pr4->packet_length = "070000";
$pr4->packet_number = "04";
$pr4->packet_type = '00'; // OK
$pr4->affected_rows = '00';
$pr4->row_data_len = '04';
$pr4->row_data = '74657374'; // item
$pr5 = new my_mysqli_fake_packet();
$pr5->full = '05000005fe00002200';
return [$pr1, $pr2, $pr3, $pr4, $pr5];
}
private function server_execute_data_response_start(string $field_name): array
{
$pr1 = new my_mysqli_fake_packet();
$pr1->packet_length = "010000";
$pr1->packet_number = "01";
$pr1->num_fields = '02';
$pr2 = new my_mysqli_fake_packet();
$pr2->packet_length = "320000";
$pr2->packet_number = "02";
$pr2->catalogue_len = '03';
$pr2->catalogue = '646566'; // def
$pr2->db_len = '08';
$pr2->db = '7068705f74657374'; // php_test
$pr2->table_len = '04';
$pr2->table = bin2hex('data');
$pr2->orig_table_len = '04';
$pr2->orig_table = bin2hex('data');
$pr2->name_len = '06';
$pr2->name = bin2hex('strval');
$pr2->orig_name_len = '06';
$pr2->orig_name = bin2hex('strval');
$pr2->something = '0c';
$pr2->charset = 'e000';
$pr2->length = 'c8000000';
$pr2->field_type = 'fd'; // FIELD_TYPE_VAR_STRING
$pr2->flags = '0110';
$pr2->decimal = '00';
$pr2->padding = '0000';
$field = my_mysqli_data_field($field_name);
$pr3 = new my_mysqli_fake_packet();
$pr3->packet_length = "320000";
$pr3->packet_number = "03";
$pr3->catalogue_len = '03';
$pr3->catalogue = '646566'; // def
$pr3->db_len = '08';
$pr3->db = '7068705f74657374'; // php_test
$pr3->table_len = '04';
$pr3->table = bin2hex('data');
$pr3->orig_table_len = '04';
$pr3->orig_table = bin2hex('data');
$pr3->name_len = '06';
$pr3->name = bin2hex($field_name);
$pr3->orig_name_len = '06';
$pr3->orig_name = bin2hex($field_name);
$pr3->something = '0c';
$pr3->charset = $field['charset'];
$pr3->length = $field['length'];
$pr3->field_type = $field['type'];
$pr3->flags = $field['flags'];
$pr3->decimal = $field['decimal'];
$pr3->padding = '0000';
$pr4 = new my_mysqli_fake_packet();
$pr4->packet_length = "050000";
$pr4->packet_number = "04";
$pr4->packet_type = 'fe'; // EOF
$pr4->warnings = '0000';
$pr4->server_status = '2200';
return [$field, $pr1, $pr2, $pr3, $pr4];
}
private function server_execute_data_response_end(): my_mysqli_fake_packet
{
$pr6 = new my_mysqli_fake_packet();
$pr6->packet_length = '050000';
$pr6->packet_number = "06";
$pr6->packet_type = 'fe'; // EOF
$pr6->warnings = '0000';
$pr6->server_status = '2200';
return $pr6;
}
public function server_stmt_execute_data_response(string $field_name): array
{
[$field, $pr1, $pr2, $pr3, $pr4] = $this->server_execute_data_response_start($field_name);
$pr5 = new my_mysqli_fake_packet();
$pr5->packet_length = $field['stmt_data_packet_length'];
$pr5->packet_number = "05";
$pr5->packet_type = '00'; // OK
$pr5->affected_rows = '00';
$pr5->row_field1_len = '04';
$pr5->row_field1_data = '74657374'; // test
$pr5->row_field2 = $field['stmt_data_value'];
return [$pr1, $pr2, $pr3, $pr4, $pr5, $this->server_execute_data_response_end()];
}
public function server_query_execute_data_response(string $field_name): array
{
[$field, $pr1, $pr2, $pr3, $pr4] = $this->server_execute_data_response_start($field_name);
$pr5 = new my_mysqli_fake_packet();
$pr5->packet_length = $field['query_data_packet_length'];
$pr5->packet_number = "05";
$pr5->row_field1_len = '04';
$pr5->row_field1_data = '74657374'; // test
$pr5->row_field2 = $field['query_data_value'];
return [$pr1, $pr2, $pr3, $pr4, $pr5, $this->server_execute_data_response_end()];
}
}
class my_mysqli_fake_server_conn
{
private $conn;
public $packet_generator;
public function __construct($socket)
{
$this->packet_generator = new my_mysqli_fake_packet_generator();
$this->conn = stream_socket_accept($socket);
if ($this->conn) {
fprintf(STDERR, "[*] Connection established\n");
} else {
fprintf(STDERR, "[*] Failed to establish connection\n");
}
}
public function packets_to_bytes(array $packets): string
{
return implode('', array_map(fn($s) => $s->to_bytes(), $packets));
}
public function send($payload, $message = null): void
{
if ($message) {
fprintf(STDERR, "[*] Sending - %s: %s\n", $message, bin2hex($payload));
}
fwrite($this->conn, $payload);
}
public function read($bytes_len = 1024)
{
// wait 10ms to fill the buffer
usleep(10000);
$data = fread($this->conn, $bytes_len);
if ($data) {
fprintf(STDERR, "[*] Received: %s\n", bin2hex($data));
}
}
public function close()
{
fclose($this->conn);
}
public function send_server_greetings()
{
$this->send($this->packet_generator->server_greetings()->to_bytes(), "Server Greeting");
}
public function send_server_ok()
{
$this->send($this->packet_generator->server_ok()->to_bytes(), "Server OK");
}
public function send_server_tabular_query_response(): void
{
$packets = $this->packet_generator->server_tabular_query_response();
$this->send($this->packets_to_bytes($packets), "Tabular response");
}
public function send_server_stmt_prepare_items_response(): void
{
$packets = $this->packet_generator->server_stmt_prepare_items_response();
$this->send($this->packets_to_bytes($packets), "Stmt prepare items");
}
public function send_server_stmt_prepare_data_response(string $field_name): void
{
$packets = $this->packet_generator->server_stmt_prepare_data_response($field_name);
$this->send($this->packets_to_bytes($packets), "Stmt prepare data $field_name");
}
public function send_server_stmt_execute_items_response(): void
{
$packets = $this->packet_generator->server_stmt_execute_items_response();
$this->send($this->packets_to_bytes($packets), "Stmt execute items");
}
public function send_server_stmt_execute_data_response(string $field_name): void
{
$packets = $this->packet_generator->server_stmt_execute_data_response($field_name);
$this->send($this->packets_to_bytes($packets), "Stmt execute data $field_name");
}
public function send_server_query_execute_data_response(string $field_name): void
{
$packets = $this->packet_generator->server_query_execute_data_response($field_name);
$this->send($this->packets_to_bytes($packets), "Query execute data $field_name");
}
}
class my_mysqli_fake_server_process
{
public function __construct(private $process, private array $pipes) {}
public function terminate(bool $wait = false)
{
if ($wait) {
$this->wait();
}
proc_terminate($this->process);
}
public function wait()
{
echo fgets($this->pipes[1]);
}
}
function my_mysqli_test_tabular_response_def_over_read(my_mysqli_fake_server_conn $conn): void
{
$rh = $conn->packet_generator->server_tabular_query_response();
// Length of the packet is modified to include the next added data
$rh[1]->packet_length = "1e0000";
// We add a length field encoded on 4 bytes which evaluates to 65536. If the process crashes because
// the heap has been overread, lower this value.
$rh[1]->extra_def_size = "fd000001"; # 65536
// Filler
$rh[1]->extra_def_data = "aa";
$trrh = $conn->packets_to_bytes($rh);
$conn->send_server_greetings();
$conn->read();
$conn->send_server_ok();
$conn->read();
$conn->send($trrh, "Malicious Tabular Response [Extract heap through buffer over-read]");
$conn->read(65536);
}
function my_mysqli_test_upsert_response_filename_over_read(my_mysqli_fake_server_conn $conn): void
{
$rh = $conn->packet_generator->server_upsert_query_response();
// Set extra length to overread
$rh[0]->len = "fa";
$trrh = $conn->packets_to_bytes($rh);
$conn->send_server_greetings();
$conn->read();
$conn->send_server_ok();
$conn->read();
$conn->send($trrh, "Malicious Tabular Response [Extract heap through buffer over-read]");
$conn->read(65536);
}
function my_mysqli_test_auth_response_message_over_read(my_mysqli_fake_server_conn $conn): void
{
$p = $conn->packet_generator->server_ok();
$p->packet_length = "090000";
$p->message_len = "fcff";
$conn->send_server_greetings();
$conn->read();
$conn->send($p->to_bytes(), "Malicious OK Auth Response [Extract heap through buffer over-read]");
$conn->read();
}
function my_mysqli_test_stmt_response_row_over_read_string(my_mysqli_fake_server_conn $conn): void
{
$rh = $conn->packet_generator->server_stmt_execute_items_response();
// Set extra length to overread
$rh[3]->row_data_len = "fa";
$conn->send_server_greetings();
$conn->read();
$conn->send_server_ok();
$conn->read();
$conn->send_server_stmt_prepare_items_response();
$conn->read();
$conn->send($conn->packets_to_bytes($rh), "Malicious Stmt Response for items [Extract heap through buffer over-read]");
$conn->read(65536);
}
function my_mysqli_test_stmt_response_row_over_read_two_fields(
my_mysqli_fake_server_conn $conn,
string $field_name,
string $row_field1_len = '06'
): void {
$rh = $conn->packet_generator->server_stmt_execute_data_response($field_name);
// Set extra length to overread by two bytes
$rh[4]->row_field1_len = $row_field1_len;
$conn->send_server_greetings();
$conn->read();
$conn->send_server_ok();
$conn->read();
$conn->send_server_stmt_prepare_data_response($field_name);
$conn->read();
$conn->send(
$conn->packets_to_bytes($rh),
"Malicious Stmt Response for data $field_name [Extract heap through buffer over-read]"
);
$conn->read(65536);
}
function my_mysqli_test_stmt_response_row_over_read_int(my_mysqli_fake_server_conn $conn): void
{
my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'intval');
}
function my_mysqli_test_stmt_response_row_over_read_float(my_mysqli_fake_server_conn $conn): void
{
my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'fltval');
}
function my_mysqli_test_stmt_response_row_over_read_double(my_mysqli_fake_server_conn $conn): void
{
my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'dblval');
}
function my_mysqli_test_stmt_response_row_over_read_date(my_mysqli_fake_server_conn $conn): void
{
my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'datval');
}
function my_mysqli_test_stmt_response_row_over_read_time(my_mysqli_fake_server_conn $conn): void
{
my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'timval', '0c');
}
function my_mysqli_test_stmt_response_row_over_read_datetime(my_mysqli_fake_server_conn $conn): void
{
my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'dtival');
}
function my_mysqli_test_stmt_response_row_no_space(my_mysqli_fake_server_conn $conn): void
{
my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'strval', '09');
}
function my_mysqli_test_stmt_response_row_over_read_bit(my_mysqli_fake_server_conn $conn): void
{
my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'bitval');
}
function my_mysqli_test_stmt_response_row_read_two_fields(my_mysqli_fake_server_conn $conn): void
{
$conn->send_server_greetings();
$conn->read();
$conn->send_server_ok();
$conn->read();
$field_names = array_keys(my_mysqli_data_fields());
foreach ($field_names as $field_name) {
$conn->send_server_stmt_prepare_data_response($field_name);
$conn->read(65536);
$conn->send_server_stmt_execute_data_response($field_name);
$conn->read(65536);
}
}
function my_mysqli_test_query_response_row_length_overflow(my_mysqli_fake_server_conn $conn): void
{
$rh = $conn->packet_generator->server_query_execute_data_response('strval');
// Set extra length to overread by two bytes
$rh[4]->row_field2 = 'fefefefefe';
$conn->send_server_greetings();
$conn->read();
$conn->send_server_ok();
$conn->read();
$conn->send($conn->packets_to_bytes($rh), "Malicious Query Response for data strval field [length overflow]");
$conn->read(65536);
}
function my_mysqli_test_query_response_row_read_two_fields(my_mysqli_fake_server_conn $conn): void
{
$conn->send_server_greetings();
$conn->read();
$conn->send_server_ok();
$conn->read();
$field_names = array_keys(my_mysqli_data_fields());
foreach ($field_names as $field_name) {
$conn->send_server_query_execute_data_response($field_name);
$conn->read();
}
}
function run_fake_server(string $test_function, $port = 33305): void
{
$address = '127.0.0.1';
$socket = @stream_socket_server("tcp://$address:$port", $errno, $errstr);
if (!$socket) {
die("Failed to create socket: $errstr ($errno)\n");
}
echo "[*] Server started\n";
try {
$conn = new my_mysqli_fake_server_conn($socket);
$test_function_name = 'my_mysqli_test_' . $test_function;
call_user_func($test_function_name, $conn);
$conn->close();
} catch (Exception $e) {
fprintf(STDERR, "[!] Exception: " . $e->getMessage() . "\n");
}
fclose($socket);
echo "[*] Server finished\n";
}
function run_fake_server_in_background($test_function, $port = 33305): my_mysqli_fake_server_process
{
$command = [PHP_BINARY, '-n', __FILE__, 'mysqli_fake_server', $test_function, $port];
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => STDERR,
);
$process = proc_open($command, $descriptorspec, $pipes);
if (is_resource($process)) {
return new my_mysqli_fake_server_process($process, $pipes);
} else {
throw new Exception("Failed to start server process");
}
}
if (isset($argv) && $argc > 2 && $argv[1] == 'mysqli_fake_server') {
run_fake_server($argv[2], $argv[3] ?? '33305');
}

View File

@ -0,0 +1,38 @@
--TEST--
GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - auth message buffer over-read)
--EXTENSIONS--
mysqli
--FILE--
<?php
require_once 'fake_server.inc';
$port = 50001;
$servername = "127.0.0.1";
$username = "root";
$password = "";
$process = run_fake_server_in_background('auth_response_message_over_read', $port);
$process->wait();
try {
$conn = new mysqli( $servername, $username, $password, "", $port );
$info = mysqli_info($conn);
var_dump($info);
} catch (Exception $e) {
echo $e->getMessage() . PHP_EOL;
}
$process->terminate();
print "done!";
?>
--EXPECTF--
[*] Server started
[*] Connection established
[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264
[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31
[*] Sending - Malicious OK Auth Response [Extract heap through buffer over-read]: 0900000200000002000000fcff
Warning: mysqli::__construct(): OK packet message length is past the packet size in %s on line %d
Unknown error while trying to connect via tcp://127.0.0.1:50001
done!

View File

@ -0,0 +1,47 @@
--TEST--
GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - tabular default)
--EXTENSIONS--
mysqli
--FILE--
<?php
require_once 'fake_server.inc';
$port = 33305;
$servername = "127.0.0.1";
$username = "root";
$password = "";
$process = run_fake_server_in_background('tabular_response_def_over_read', $port);
$process->wait();
$conn = new mysqli($servername, $username, $password, "", $port);
echo "[*] Running query on the fake server...\n";
$result = $conn->query("SELECT * from users");
if ($result) {
$all_fields = $result->fetch_fields();
var_dump($result->fetch_all(MYSQLI_ASSOC));
var_dump(get_object_vars($all_fields[0])["def"]);
}
$conn->close();
$process->terminate();
print "done!";
?>
--EXPECTF--
[*] Server started
[*] Connection established
[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264
[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31
[*] Sending - Server OK: 0700000200000002000000
[*] Running query on the fake server...
[*] Received: 140000000353454c454354202a2066726f6d207573657273
[*] Sending - Malicious Tabular Response [Extract heap through buffer over-read]: 01000001011e0000020164016401640164016401640c3f000b000000030350000000fd000001aa05000003fe00002200040000040135017405000005fe00002200
Warning: mysqli::query(): Protocol error. Server sent default for unsupported field list (mysqlnd_wireprotocol.c:%d) in %s on line %d
done!

View File

@ -0,0 +1,43 @@
--TEST--
GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - upsert filename buffer over-read)
--EXTENSIONS--
mysqli
--FILE--
<?php
require_once 'fake_server.inc';
$port = 33305;
$servername = "127.0.0.1";
$username = "root";
$password = "";
$process = run_fake_server_in_background('upsert_response_filename_over_read', $port);
$process->wait();
$conn = new mysqli($servername, $username, $password, "", $port);
echo "[*] Running query on the fake server...\n";
$result = $conn->query("SELECT * from users");
$info = mysqli_info($conn);
var_dump($info);
$process->terminate();
print "done!";
?>
--EXPECTF--
[*] Server started
[*] Connection established
[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264
[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31
[*] Sending - Server OK: 0700000200000002000000
[*] Running query on the fake server...
[*] Received: 140000000353454c454354202a2066726f6d207573657273
[*] Sending - Malicious Tabular Response [Extract heap through buffer over-read]: 0900000100000000000000fa65
Warning: mysqli::query(): RSET_HEADER packet additional data length is past 249 bytes the packet size in %s on line %d
Warning: mysqli::query(): Error reading result set's header in %s on line %d
NULL
done!

View File

@ -0,0 +1,48 @@
--TEST--
GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row no space for the field)
--EXTENSIONS--
mysqli
--FILE--
<?php
require_once 'fake_server.inc';
$port = 33305;
$servername = "127.0.0.1";
$username = "root";
$password = "";
$process = run_fake_server_in_background('query_response_row_length_overflow', $port);
$process->wait();
$conn = new mysqli($servername, $username, $password, "", $port);
echo "[*] Query the fake server...\n";
$sql = "SELECT strval, strval FROM data";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
var_dump($row['strval']);
}
}
$conn->close();
$process->terminate(true);
print "done!";
?>
--EXPECTF--
[*] Server started
[*] Connection established
[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264
[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31
[*] Sending - Server OK: 0700000200000002000000
[*] Query the fake server...
[*] Received: 200000000353454c4543542073747276616c2c2073747276616c2046524f4d2064617461
[*] Sending - Malicious Query Response for data strval field [length overflow]: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd011000000005000004fe000022000a0000050474657374fefefefefe05000006fe00002200
Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after end of packet in %s on line %d
[*] Received: 0100000001
[*] Server finished
done!

View File

@ -0,0 +1,53 @@
--TEST--
GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row bit buffer over-read)
--EXTENSIONS--
mysqli
--FILE--
<?php
require_once 'fake_server.inc';
$port = 33305;
$servername = "127.0.0.1";
$username = "root";
$password = "";
$process = run_fake_server_in_background('stmt_response_row_over_read_bit', $port);
$process->wait();
$conn = new mysqli($servername, $username, $password, "", $port);
echo "[*] Preparing statement on the fake server...\n";
$stmt = $conn->prepare("SELECT bitval, timval FROM data");
$stmt->execute();
$result = $stmt->get_result();
// Fetch and display the results
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
var_dump($row["bitval"]);
}
}
$stmt->close();
$conn->close();
$process->terminate(true);
print "done!";
?>
--EXPECTF--
[*] Server started
[*] Connection established
[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264
[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31
[*] Sending - Server OK: 0700000200000002000000
[*] Preparing statement on the fake server...
[*] Received: 200000001653454c4543542062697476616c2c2074696d76616c2046524f4d2064617461
[*] Sending - Stmt prepare data bitval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610662697476616c0662697476616c0c3f004000000010211000000005000004fe00000200
[*] Received: 0a00000017010000000001000000
[*] Sending - Malicious Stmt Response for data bitval [Extract heap through buffer over-read]: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610662697476616c0662697476616c0c3f004000000010211000000005000004fe00002200100000050000067465737408080808080808080805000006fe00002200
Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %d
[*] Received: 0500000019010000000100000001
[*] Server finished
done!

View File

@ -0,0 +1,53 @@
--TEST--
GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row date buffer over-read)
--EXTENSIONS--
mysqli
--FILE--
<?php
require_once 'fake_server.inc';
$port = 33305;
$servername = "127.0.0.1";
$username = "root";
$password = "";
$process = run_fake_server_in_background('stmt_response_row_over_read_date', $port);
$process->wait();
$conn = new mysqli($servername, $username, $password, "", $port);
echo "[*] Preparing statement on the fake server...\n";
$stmt = $conn->prepare("SELECT strval, datval FROM data");
$stmt->execute();
$result = $stmt->get_result();
// Fetch and display the results
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
var_dump($row["datval"]);
}
}
$stmt->close();
$conn->close();
$process->terminate(true);
print "done!";
?>
--EXPECTF--
[*] Server started
[*] Connection established
[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264
[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31
[*] Sending - Server OK: 0700000200000002000000
[*] Preparing statement on the fake server...
[*] Received: 200000001653454c4543542073747276616c2c2064617476616c2046524f4d2064617461
[*] Sending - Stmt prepare data datval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664617476616c0664617476616c0c3f000a0000000a811000000005000004fe00000200
[*] Received: 0a00000017010000000001000000
[*] Sending - Malicious Stmt Response for data datval [Extract heap through buffer over-read]: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664617476616c0664617476616c0c3f000a0000000a811000000005000004fe000022000c0000050000067465737404de070c0f05000006fe00002200
Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %d
[*] Received: 0500000019010000000100000001
[*] Server finished
done!

View File

@ -0,0 +1,53 @@
--TEST--
GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row datetime buffer over-read)
--EXTENSIONS--
mysqli
--FILE--
<?php
require_once 'fake_server.inc';
$port = 33305;
$servername = "127.0.0.1";
$username = "root";
$password = "";
$process = run_fake_server_in_background('stmt_response_row_over_read_datetime', $port);
$process->wait();
$conn = new mysqli($servername, $username, $password, "", $port);
echo "[*] Preparing statement on the fake server...\n";
$stmt = $conn->prepare("SELECT strval, dtival FROM data");
$stmt->execute();
$result = $stmt->get_result();
// Fetch and display the results
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
var_dump($row["dtival"]);
}
}
$stmt->close();
$conn->close();
$process->terminate(true);
print "done!";
?>
--EXPECTF--
[*] Server started
[*] Connection established
[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264
[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31
[*] Sending - Server OK: 0700000200000002000000
[*] Preparing statement on the fake server...
[*] Received: 200000001653454c4543542073747276616c2c2064746976616c2046524f4d2064617461
[*] Sending - Stmt prepare data dtival: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664746976616c0664746976616c0c3f00130000000c811000000005000004fe00000200
[*] Received: 0a00000017010000000001000000
[*] Sending - Malicious Stmt Response for data dtival [Extract heap through buffer over-read]: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664746976616c0664746976616c0c3f00130000000c811000000005000004fe000022000f0000050000067465737407de070c100d000105000006fe00002200
Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %d
[*] Received: 0500000019010000000100000001
[*] Server finished
done!

View File

@ -0,0 +1,53 @@
--TEST--
GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row double buffer over-read)
--EXTENSIONS--
mysqli
--FILE--
<?php
require_once 'fake_server.inc';
$port = 33305;
$servername = "127.0.0.1";
$username = "root";
$password = "";
$process = run_fake_server_in_background('stmt_response_row_over_read_double', $port);
$process->wait();
$conn = new mysqli($servername, $username, $password, "", $port);
echo "[*] Preparing statement on the fake server...\n";
$stmt = $conn->prepare("SELECT strval, dblval FROM data");
$stmt->execute();
$result = $stmt->get_result();
// Fetch and display the results
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
var_dump($row["dblval"]);
}
}
$stmt->close();
$conn->close();
$process->terminate(true);
print "done!";
?>
--EXPECTF--
[*] Server started
[*] Connection established
[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264
[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31
[*] Sending - Server OK: 0700000200000002000000
[*] Preparing statement on the fake server...
[*] Received: 200000001653454c4543542073747276616c2c2064626c76616c2046524f4d2064617461
[*] Sending - Stmt prepare data dblval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664626c76616c0664626c76616c0c3f00160000000501101f000005000004fe00000200
[*] Received: 0a00000017010000000001000000
[*] Sending - Malicious Stmt Response for data dblval [Extract heap through buffer over-read]: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664626c76616c0664626c76616c0c3f00160000000501101f000005000004fe000022000f00000500000674657374333333333333f33f05000006fe00002200
Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %d
[*] Received: 0500000019010000000100000001
[*] Server finished
done!

View File

@ -0,0 +1,53 @@
--TEST--
GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row int buffer over-read)
--EXTENSIONS--
mysqli
--FILE--
<?php
require_once 'fake_server.inc';
$port = 33305;
$servername = "127.0.0.1";
$username = "root";
$password = "";
$process = run_fake_server_in_background('stmt_response_row_over_read_float', $port);
$process->wait();
$conn = new mysqli($servername, $username, $password, "", $port);
echo "[*] Preparing statement on the fake server...\n";
$stmt = $conn->prepare("SELECT strval, fltval FROM data");
$stmt->execute();
$result = $stmt->get_result();
// Fetch and display the results
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
var_dump($row["fltval"]);
}
}
$stmt->close();
$conn->close();
$process->terminate(true);
print "done!";
?>
--EXPECTF--
[*] Server started
[*] Connection established
[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264
[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31
[*] Sending - Server OK: 0700000200000002000000
[*] Preparing statement on the fake server...
[*] Received: 200000001653454c4543542073747276616c2c20666c7476616c2046524f4d2064617461
[*] Sending - Stmt prepare data fltval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106666c7476616c06666c7476616c0c3f000c0000000401101f000005000004fe00000200
[*] Received: 0a00000017010000000001000000
[*] Sending - Malicious Stmt Response for data fltval [Extract heap through buffer over-read]: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106666c7476616c06666c7476616c0c3f000c0000000401101f000005000004fe000022000b000005000006746573743333134005000006fe00002200
Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %d
[*] Received: 0500000019010000000100000001
[*] Server finished
done!

View File

@ -0,0 +1,53 @@
--TEST--
GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row int buffer over-read)
--EXTENSIONS--
mysqli
--FILE--
<?php
require_once 'fake_server.inc';
$port = 33305;
$servername = "127.0.0.1";
$username = "root";
$password = "";
$process = run_fake_server_in_background('stmt_response_row_over_read_int', $port);
$process->wait();
$conn = new mysqli($servername, $username, $password, "", $port);
echo "[*] Preparing statement on the fake server...\n";
$stmt = $conn->prepare("SELECT strval, intval FROM data");
$stmt->execute();
$result = $stmt->get_result();
// Fetch and display the results
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
var_dump($row["intval"]);
}
}
$stmt->close();
$conn->close();
$process->terminate(true);
print "done!";
?>
--EXPECTF--
[*] Server started
[*] Connection established
[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264
[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31
[*] Sending - Server OK: 0700000200000002000000
[*] Preparing statement on the fake server...
[*] Received: 200000001653454c4543542073747276616c2c20696e7476616c2046524f4d2064617461
[*] Sending - Stmt prepare data intval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106696e7476616c06696e7476616c0c3f000b00000003011000000005000004fe00000200
[*] Received: 0a00000017010000000001000000
[*] Sending - Malicious Stmt Response for data intval [Extract heap through buffer over-read]: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106696e7476616c06696e7476616c0c3f000b00000003011000000005000004fe000022000b000005000006746573740e00000005000006fe00002200
Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %d
[*] Received: 0500000019010000000100000001
[*] Server finished
done!

View File

@ -0,0 +1,53 @@
--TEST--
GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row no space for the field)
--EXTENSIONS--
mysqli
--FILE--
<?php
require_once 'fake_server.inc';
$port = 33305;
$servername = "127.0.0.1";
$username = "root";
$password = "";
$process = run_fake_server_in_background('stmt_response_row_no_space', $port);
$process->wait();
$conn = new mysqli($servername, $username, $password, "", $port);
echo "[*] Preparing statement on the fake server...\n";
$stmt = $conn->prepare("SELECT strval, strval FROM data");
$stmt->execute();
$result = $stmt->get_result();
// Fetch and display the results
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
var_dump($row["strval"]);
}
}
$stmt->close();
$conn->close();
$process->terminate(true);
print "done!";
?>
--EXPECTF--
[*] Server started
[*] Connection established
[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264
[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31
[*] Sending - Server OK: 0700000200000002000000
[*] Preparing statement on the fake server...
[*] Received: 200000001653454c4543542073747276616c2c2073747276616c2046524f4d2064617461
[*] Sending - Stmt prepare data strval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd011000000005000004fe00000200
[*] Received: 0a00000017010000000001000000
[*] Sending - Malicious Stmt Response for data strval [Extract heap through buffer over-read]: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd011000000005000004fe000022000c00000500000974657374047465737405000006fe00002200
Warning: mysqli_result::fetch_assoc(): Malformed server packet. No packet space left for the field in %s on line %d
[*] Received: 0500000019010000000100000001
[*] Server finished
done!

View File

@ -0,0 +1,53 @@
--TEST--
GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row string buffer over-read)
--EXTENSIONS--
mysqli
--FILE--
<?php
require_once 'fake_server.inc';
$port = 33305;
$servername = "127.0.0.1";
$username = "root";
$password = "";
$process = run_fake_server_in_background('stmt_response_row_over_read_string', $port);
$process->wait();
$conn = new mysqli($servername, $username, $password, "", $port);
echo "[*] Preparing statement on the fake server...\n";
$stmt = $conn->prepare("SELECT item FROM items");
$stmt->execute();
$result = $stmt->get_result();
// Fetch and display the results
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
var_dump($row["item"]);
}
}
$stmt->close();
$conn->close();
$process->terminate(true);
print "done!";
?>
--EXPECTF--
[*] Server started
[*] Connection established
[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264
[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31
[*] Sending - Server OK: 0700000200000002000000
[*] Preparing statement on the fake server...
[*] Received: 170000001653454c454354206974656d2046524f4d206974656d73
[*] Sending - Stmt prepare items: 0c0000010001000000010000000000003000000203646566087068705f74657374056974656d73056974656d73046974656d046974656d0ce000c8000000fd011000000005000003fe00000200
[*] Received: 0a00000017010000000001000000
[*] Sending - Malicious Stmt Response for items [Extract heap through buffer over-read]: 01000001013000000203646566087068705f74657374056974656d73056974656d73046974656d046974656d0ce000c8000000fd011000000005000003fe00002200070000040000fa7465737405000005fe00002200
Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %d
[*] Received: 0500000019010000000100000001
[*] Server finished
done!

View File

@ -0,0 +1,53 @@
--TEST--
GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row time buffer over-read)
--EXTENSIONS--
mysqli
--FILE--
<?php
require_once 'fake_server.inc';
$port = 33305;
$servername = "127.0.0.1";
$username = "root";
$password = "";
$process = run_fake_server_in_background('stmt_response_row_over_read_time', $port);
$process->wait();
$conn = new mysqli($servername, $username, $password, "", $port);
echo "[*] Preparing statement on the fake server...\n";
$stmt = $conn->prepare("SELECT strval, timval FROM data");
$stmt->execute();
$result = $stmt->get_result();
// Fetch and display the results
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
var_dump($row["timval"]);
}
}
$stmt->close();
$conn->close();
$process->terminate(true);
print "done!";
?>
--EXPECTF--
[*] Server started
[*] Connection established
[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264
[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31
[*] Sending - Server OK: 0700000200000002000000
[*] Preparing statement on the fake server...
[*] Received: 200000001653454c4543542073747276616c2c2074696d76616c2046524f4d2064617461
[*] Sending - Stmt prepare data timval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610674696d76616c0674696d76616c0c3f000a0000000b811000000005000004fe00000200
[*] Received: 0a00000017010000000001000000
[*] Sending - Malicious Stmt Response for data timval [Extract heap through buffer over-read]: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610674696d76616c0674696d76616c0c3f000a0000000b811000000005000004fe000022001000000500000c7465737408000000000015080105000006fe00002200
Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %d
[*] Received: 0500000019010000000100000001
[*] Server finished
done!

View File

@ -0,0 +1,74 @@
--TEST--
MySQL protocol - statement row data fetch)
--EXTENSIONS--
mysqli
--FILE--
<?php
require_once 'fake_server.inc';
$port = 33305;
$servername = "127.0.0.1";
$username = "root";
$password = "";
$process = run_fake_server_in_background('query_response_row_read_two_fields', $port);
$process->wait();
$conn = new mysqli($servername, $username, $password, "", $port);
function my_query($conn, $field)
{
$sql = "SELECT strval, $field FROM data";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
var_dump($row[$field]);
}
}
}
foreach (my_mysqli_data_fields() as $field_name => $field) {
my_query($conn, $field_name);
}
$conn->close();
$process->terminate(true);
print "done!";
?>
--EXPECT--
[*] Server started
[*] Connection established
[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264
[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31
[*] Sending - Server OK: 0700000200000002000000
[*] Received: 200000000353454c4543542073747276616c2c20696e7476616c2046524f4d2064617461
[*] Sending - Query execute data intval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106696e7476616c06696e7476616c0c3f000b00000003011000000005000004fe0000220008000005047465737402313405000006fe00002200
string(2) "14"
[*] Received: 200000000353454c4543542073747276616c2c20666c7476616c2046524f4d2064617461
[*] Sending - Query execute data fltval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106666c7476616c06666c7476616c0c3f000c0000000401101f000005000004fe0000220009000005047465737403322e3305000006fe00002200
string(3) "2.3"
[*] Received: 200000000353454c4543542073747276616c2c2064626c76616c2046524f4d2064617461
[*] Sending - Query execute data dblval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664626c76616c0664626c76616c0c3f00160000000501101f000005000004fe0000220009000005047465737403312e3205000006fe00002200
string(3) "1.2"
[*] Received: 200000000353454c4543542073747276616c2c2064617476616c2046524f4d2064617461
[*] Sending - Query execute data datval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664617476616c0664617476616c0c3f000a0000000a811000000005000004fe000022001000000504746573740a323031342d31322d313505000006fe00002200
string(10) "2014-12-15"
[*] Received: 200000000353454c4543542073747276616c2c2074696d76616c2046524f4d2064617461
[*] Sending - Query execute data timval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610674696d76616c0674696d76616c0c3f000a0000000b811000000005000004fe000022000e00000504746573740831333a30303a303205000006fe00002200
string(8) "13:00:02"
[*] Received: 200000000353454c4543542073747276616c2c2064746976616c2046524f4d2064617461
[*] Sending - Query execute data dtival: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664746976616c0664746976616c0c3f00130000000c811000000005000004fe0000220019000005047465737413323031342d31322d31362031333a30303a303105000006fe00002200
string(19) "2014-12-16 13:00:01"
[*] Received: 200000000353454c4543542073747276616c2c2062697476616c2046524f4d2064617461
[*] Sending - Query execute data bitval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610662697476616c0662697476616c0c3f004000000010211000000005000004fe000022000e000005047465737408080808080808080805000006fe00002200
string(18) "578721382704613384"
[*] Received: 200000000353454c4543542073747276616c2c2073747276616c2046524f4d2064617461
[*] Sending - Query execute data strval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd011000000005000004fe000022000a0000050474657374047465737405000006fe00002200
string(4) "test"
[*] Received: 0100000001
[*] Server finished
done!

View File

@ -0,0 +1,91 @@
--TEST--
MySQL protocol - statement row data fetch)
--EXTENSIONS--
mysqli
--FILE--
<?php
require_once 'fake_server.inc';
$port = 33305;
$servername = "127.0.0.1";
$username = "root";
$password = "";
$process = run_fake_server_in_background('stmt_response_row_read_two_fields', $port);
$process->wait();
$conn = new mysqli($servername, $username, $password, "", $port);
function my_query($conn, $field)
{
$stmt = $conn->prepare("SELECT strval, $field FROM data");
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
var_dump($row[$field]);
}
}
}
foreach (my_mysqli_data_fields() as $field_name => $field) {
my_query($conn, $field_name);
}
$conn->close();
$process->terminate(true);
print "done!";
?>
--EXPECTF--
[*] Server started
[*] Connection established
[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264
[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31
[*] Sending - Server OK: 0700000200000002000000
[*] Received: 200000001653454c4543542073747276616c2c20696e7476616c2046524f4d2064617461
[*] Sending - Stmt prepare data intval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106696e7476616c06696e7476616c0c3f000b00000003011000000005000004fe00000200
[*] Received: 0a00000017010000000001000000
[*] Sending - Stmt execute data intval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106696e7476616c06696e7476616c0c3f000b00000003011000000005000004fe000022000b000005000004746573740e00000005000006fe00002200
int(14)
[*] Received: 050000001901000000200000001653454c4543542073747276616c2c20666c7476616c2046524f4d2064617461
[*] Sending - Stmt prepare data fltval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106666c7476616c06666c7476616c0c3f000c0000000401101f000005000004fe00000200
[*] Received: 0a00000017010000000001000000
[*] Sending - Stmt execute data fltval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106666c7476616c06666c7476616c0c3f000c0000000401101f000005000004fe000022000b000005000004746573743333134005000006fe00002200
float(2.3)
[*] Received: 050000001901000000200000001653454c4543542073747276616c2c2064626c76616c2046524f4d2064617461
[*] Sending - Stmt prepare data dblval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664626c76616c0664626c76616c0c3f00160000000501101f000005000004fe00000200
[*] Received: 0a00000017010000000001000000
[*] Sending - Stmt execute data dblval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664626c76616c0664626c76616c0c3f00160000000501101f000005000004fe000022000f00000500000474657374333333333333f33f05000006fe00002200
float(1.2)
[*] Received: 050000001901000000200000001653454c4543542073747276616c2c2064617476616c2046524f4d2064617461
[*] Sending - Stmt prepare data datval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664617476616c0664617476616c0c3f000a0000000a811000000005000004fe00000200
[*] Received: 0a00000017010000000001000000
[*] Sending - Stmt execute data datval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664617476616c0664617476616c0c3f000a0000000a811000000005000004fe000022000c0000050000047465737404de070c0f05000006fe00002200
string(10) "2014-12-15"
[*] Received: 050000001901000000200000001653454c4543542073747276616c2c2074696d76616c2046524f4d2064617461
[*] Sending - Stmt prepare data timval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610674696d76616c0674696d76616c0c3f000a0000000b811000000005000004fe00000200
[*] Received: 0a00000017010000000001000000
[*] Sending - Stmt execute data timval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610674696d76616c0674696d76616c0c3f000a0000000b811000000005000004fe00002200100000050000047465737408000000000015080105000006fe00002200
string(8) "21:08:01"
[*] Received: 050000001901000000200000001653454c4543542073747276616c2c2064746976616c2046524f4d2064617461
[*] Sending - Stmt prepare data dtival: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664746976616c0664746976616c0c3f00130000000c811000000005000004fe00000200
[*] Received: 0a00000017010000000001000000
[*] Sending - Stmt execute data dtival: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664746976616c0664746976616c0c3f00130000000c811000000005000004fe000022000f0000050000047465737407de070c100d000105000006fe00002200
string(19) "2014-12-16 13:00:01"
[*] Received: 050000001901000000200000001653454c4543542073747276616c2c2062697476616c2046524f4d2064617461
[*] Sending - Stmt prepare data bitval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610662697476616c0662697476616c0c3f004000000010211000000005000004fe00000200
[*] Received: 0a00000017010000000001000000
[*] Sending - Stmt execute data bitval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610662697476616c0662697476616c0c3f004000000010211000000005000004fe00002200100000050000047465737408080808080808080805000006fe00002200
%s578721382704613384%s
[*] Received: 050000001901000000200000001653454c4543542073747276616c2c2073747276616c2046524f4d2064617461
[*] Sending - Stmt prepare data strval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd011000000005000004fe00000200
[*] Received: 0a00000017010000000001000000
[*] Sending - Stmt execute data strval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd011000000005000004fe000022000c00000500000474657374047465737405000006fe00002200
string(4) "test"
[*] Received: 0500000019010000000100000001
[*] Server finished
done!

View File

@ -50,11 +50,46 @@ struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1];
#define MYSQLND_PS_SKIP_RESULT_W_LEN -1 #define MYSQLND_PS_SKIP_RESULT_W_LEN -1
#define MYSQLND_PS_SKIP_RESULT_STR -2 #define MYSQLND_PS_SKIP_RESULT_STR -2
static inline void ps_fetch_over_read_error(const zend_uchar ** row)
{
php_error_docref(NULL, E_WARNING, "Malformed server packet. Field length pointing after the end of packet");
*row = NULL;
}
static inline bool ps_fetch_is_packet_over_read_with_variable_length(const unsigned int pack_len,
const zend_uchar ** row, const zend_uchar *p, unsigned int length)
{
if (pack_len == 0) {
return false;
}
size_t length_len = *row - p;
if (length_len > pack_len || length > pack_len - length_len) {
ps_fetch_over_read_error(row);
return true;
}
return false;
}
static inline bool ps_fetch_is_packet_over_read_with_static_length(const unsigned int pack_len,
const zend_uchar ** row, unsigned int length)
{
if (pack_len > 0 && length > pack_len) {
ps_fetch_over_read_error(row);
return true;
}
return false;
}
/* {{{ ps_fetch_from_1_to_8_bytes */ /* {{{ ps_fetch_from_1_to_8_bytes */
void void
ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len,
const zend_uchar ** row, unsigned int byte_count) const zend_uchar ** row, unsigned int byte_count)
{ {
if (UNEXPECTED(ps_fetch_is_packet_over_read_with_static_length(pack_len, row, byte_count))) {
return;
}
bool is_bit = field->type == MYSQL_TYPE_BIT; bool is_bit = field->type == MYSQL_TYPE_BIT;
DBG_ENTER("ps_fetch_from_1_to_8_bytes"); DBG_ENTER("ps_fetch_from_1_to_8_bytes");
DBG_INF_FMT("zv=%p byte_count=%u", zv, byte_count); DBG_INF_FMT("zv=%p byte_count=%u", zv, byte_count);
@ -174,6 +209,11 @@ ps_fetch_float(zval * zv, const MYSQLND_FIELD * const field, const unsigned int
float fval; float fval;
double dval; double dval;
DBG_ENTER("ps_fetch_float"); DBG_ENTER("ps_fetch_float");
if (UNEXPECTED(ps_fetch_is_packet_over_read_with_static_length(pack_len, row, 4))) {
return;
}
float4get(fval, *row); float4get(fval, *row);
(*row)+= 4; (*row)+= 4;
DBG_INF_FMT("value=%f", fval); DBG_INF_FMT("value=%f", fval);
@ -196,6 +236,11 @@ ps_fetch_double(zval * zv, const MYSQLND_FIELD * const field, const unsigned int
{ {
double value; double value;
DBG_ENTER("ps_fetch_double"); DBG_ENTER("ps_fetch_double");
if (UNEXPECTED(ps_fetch_is_packet_over_read_with_static_length(pack_len, row, 8))) {
return;
}
float8get(value, *row); float8get(value, *row);
ZVAL_DOUBLE(zv, value); ZVAL_DOUBLE(zv, value);
(*row)+= 8; (*row)+= 8;
@ -211,9 +256,14 @@ ps_fetch_time(zval * zv, const MYSQLND_FIELD * const field, const unsigned int p
{ {
struct st_mysqlnd_time t; struct st_mysqlnd_time t;
zend_ulong length; /* First byte encodes the length */ zend_ulong length; /* First byte encodes the length */
const zend_uchar *p = *row;
DBG_ENTER("ps_fetch_time"); DBG_ENTER("ps_fetch_time");
if ((length = php_mysqlnd_net_field_length(row))) { if ((length = php_mysqlnd_net_field_length(row))) {
if (UNEXPECTED(ps_fetch_is_packet_over_read_with_variable_length(pack_len, row, p, length))) {
return;
}
const zend_uchar * to = *row; const zend_uchar * to = *row;
t.time_type = MYSQLND_TIMESTAMP_TIME; t.time_type = MYSQLND_TIMESTAMP_TIME;
@ -256,9 +306,14 @@ ps_fetch_date(zval * zv, const MYSQLND_FIELD * const field, const unsigned int p
{ {
struct st_mysqlnd_time t = {0}; struct st_mysqlnd_time t = {0};
zend_ulong length; /* First byte encodes the length*/ zend_ulong length; /* First byte encodes the length*/
const zend_uchar *p = *row;
DBG_ENTER("ps_fetch_date"); DBG_ENTER("ps_fetch_date");
if ((length = php_mysqlnd_net_field_length(row))) { if ((length = php_mysqlnd_net_field_length(row))) {
if (UNEXPECTED(ps_fetch_is_packet_over_read_with_variable_length(pack_len, row, p, length))) {
return;
}
const zend_uchar * to = *row; const zend_uchar * to = *row;
t.time_type = MYSQLND_TIMESTAMP_DATE; t.time_type = MYSQLND_TIMESTAMP_DATE;
@ -288,9 +343,14 @@ ps_fetch_datetime(zval * zv, const MYSQLND_FIELD * const field, const unsigned i
{ {
struct st_mysqlnd_time t; struct st_mysqlnd_time t;
zend_ulong length; /* First byte encodes the length*/ zend_ulong length; /* First byte encodes the length*/
const zend_uchar *p = *row;
DBG_ENTER("ps_fetch_datetime"); DBG_ENTER("ps_fetch_datetime");
if ((length = php_mysqlnd_net_field_length(row))) { if ((length = php_mysqlnd_net_field_length(row))) {
if (UNEXPECTED(ps_fetch_is_packet_over_read_with_variable_length(pack_len, row, p, length))) {
return;
}
const zend_uchar * to = *row; const zend_uchar * to = *row;
t.time_type = MYSQLND_TIMESTAMP_DATETIME; t.time_type = MYSQLND_TIMESTAMP_DATETIME;
@ -332,7 +392,11 @@ ps_fetch_datetime(zval * zv, const MYSQLND_FIELD * const field, const unsigned i
static void static void
ps_fetch_string(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row) ps_fetch_string(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{ {
const zend_uchar *p = *row;
const zend_ulong length = php_mysqlnd_net_field_length(row); const zend_ulong length = php_mysqlnd_net_field_length(row);
if (UNEXPECTED(ps_fetch_is_packet_over_read_with_variable_length(pack_len, row, p, length))) {
return;
}
DBG_ENTER("ps_fetch_string"); DBG_ENTER("ps_fetch_string");
DBG_INF_FMT("len = " ZEND_ULONG_FMT, length); DBG_INF_FMT("len = " ZEND_ULONG_FMT, length);
DBG_INF("copying from the row buffer"); DBG_INF("copying from the row buffer");
@ -348,7 +412,11 @@ ps_fetch_string(zval * zv, const MYSQLND_FIELD * const field, const unsigned int
static void static void
ps_fetch_bit(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row) ps_fetch_bit(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
{ {
const zend_uchar *p = *row;
const zend_ulong length = php_mysqlnd_net_field_length(row); const zend_ulong length = php_mysqlnd_net_field_length(row);
if (UNEXPECTED(ps_fetch_is_packet_over_read_with_variable_length(pack_len, row, p, length))) {
return;
}
ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, length); ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, length);
} }
/* }}} */ /* }}} */

View File

@ -455,8 +455,31 @@ php_mysqlnd_greet_read(MYSQLND_CONN_DATA * conn, void * _packet)
if (packet->server_capabilities & CLIENT_PLUGIN_AUTH) { if (packet->server_capabilities & CLIENT_PLUGIN_AUTH) {
BAIL_IF_NO_MORE_DATA; BAIL_IF_NO_MORE_DATA;
/* The server is 5.5.x and supports authentication plugins */ /* The server is 5.5.x and supports authentication plugins */
packet->auth_protocol = estrdup((char *)p); size_t remaining_size = packet->header.size - (size_t)(p - buf);
p+= strlen(packet->auth_protocol) + 1; /* eat the '\0' */ if (remaining_size == 0) {
/* Might be better to fail but this will fail anyway */
packet->auth_protocol = estrdup("");
} else {
/* Check if NUL present */
char *null_terminator = memchr(p, '\0', remaining_size);
size_t auth_protocol_len;
if (null_terminator) {
/* If present, do basically estrdup */
auth_protocol_len = null_terminator - (char *)p;
} else {
/* If not present, copy the rest of the buffer */
auth_protocol_len = remaining_size;
}
char *auth_protocol = emalloc(auth_protocol_len + 1);
memcpy(auth_protocol, p, auth_protocol_len);
auth_protocol[auth_protocol_len] = '\0';
packet->auth_protocol = auth_protocol;
p += auth_protocol_len;
if (null_terminator) {
p++;
}
}
} }
DBG_INF_FMT("proto=%u server=%s thread_id=%u", DBG_INF_FMT("proto=%u server=%s thread_id=%u",
@ -712,7 +735,14 @@ php_mysqlnd_auth_response_read(MYSQLND_CONN_DATA * conn, void * _packet)
/* There is a message */ /* There is a message */
if (packet->header.size > (size_t) (p - buf) && (net_len = php_mysqlnd_net_field_length(&p))) { if (packet->header.size > (size_t) (p - buf) && (net_len = php_mysqlnd_net_field_length(&p))) {
packet->message_len = MIN(net_len, buf_len - (p - begin)); /* p can get past packet size when getting field length so it needs to be checked first
* and after that it can be checked that the net_len is not greater than the packet size */
if ((p - buf) > packet->header.size || packet->header.size - (p - buf) < net_len) {
DBG_ERR_FMT("OK packet message length is past the packet size");
php_error_docref(NULL, E_WARNING, "OK packet message length is past the packet size");
DBG_RETURN(FAIL);
}
packet->message_len = net_len;
packet->message = mnd_pestrndup((char *)p, packet->message_len, FALSE); packet->message = mnd_pestrndup((char *)p, packet->message_len, FALSE);
} else { } else {
packet->message = NULL; packet->message = NULL;
@ -1092,6 +1122,17 @@ php_mysqlnd_rset_header_read(MYSQLND_CONN_DATA * conn, void * _packet)
BAIL_IF_NO_MORE_DATA; BAIL_IF_NO_MORE_DATA;
/* Check for additional textual data */ /* Check for additional textual data */
if (packet->header.size > (size_t) (p - buf) && (len = php_mysqlnd_net_field_length(&p))) { if (packet->header.size > (size_t) (p - buf) && (len = php_mysqlnd_net_field_length(&p))) {
/* p can get past packet size when getting field length so it needs to be checked first
* and after that it can be checked that the len is not greater than the packet size */
if ((p - buf) > packet->header.size || packet->header.size - (p - buf) < len) {
size_t local_file_name_over_read = ((p - buf) - packet->header.size) + len;
DBG_ERR_FMT("RSET_HEADER packet additional data length is past %zu bytes the packet size",
local_file_name_over_read);
php_error_docref(NULL, E_WARNING,
"RSET_HEADER packet additional data length is past %zu bytes the packet size",
local_file_name_over_read);
DBG_RETURN(FAIL);
}
packet->info_or_local_file.s = mnd_emalloc(len + 1); packet->info_or_local_file.s = mnd_emalloc(len + 1);
memcpy(packet->info_or_local_file.s, p, len); memcpy(packet->info_or_local_file.s, p, len);
packet->info_or_local_file.s[len] = '\0'; packet->info_or_local_file.s[len] = '\0';
@ -1242,23 +1283,16 @@ php_mysqlnd_rset_field_read(MYSQLND_CONN_DATA * conn, void * _packet)
meta->flags |= NUM_FLAG; meta->flags |= NUM_FLAG;
} }
/* COM_FIELD_LIST is no longer supported so def should not be present */
/*
def could be empty, thus don't allocate on the root.
NULL_LENGTH (0xFB) comes from COM_FIELD_LIST when the default value is NULL.
Otherwise the string is length encoded.
*/
if (packet->header.size > (size_t) (p - buf) && if (packet->header.size > (size_t) (p - buf) &&
(len = php_mysqlnd_net_field_length(&p)) && (len = php_mysqlnd_net_field_length(&p)) &&
len != MYSQLND_NULL_LENGTH) len != MYSQLND_NULL_LENGTH)
{ {
BAIL_IF_NO_MORE_DATA; DBG_ERR_FMT("Protocol error. Server sent default for unsupported field list");
DBG_INF_FMT("Def found, length " ZEND_ULONG_FMT, len); php_error_docref(NULL, E_WARNING,
meta->def = packet->memory_pool->get_chunk(packet->memory_pool, len + 1); "Protocol error. Server sent default for unsupported field list (mysqlnd_wireprotocol.c:%u)",
memcpy(meta->def, p, len); __LINE__);
meta->def[len] = '\0'; DBG_RETURN(FAIL);
meta->def_length = len;
p += len;
} }
root_ptr = meta->root = packet->memory_pool->get_chunk(packet->memory_pool, total_len); root_ptr = meta->root = packet->memory_pool->get_chunk(packet->memory_pool, total_len);
@ -1421,8 +1455,10 @@ php_mysqlnd_rowp_read_binary_protocol(MYSQLND_ROW_BUFFER * row_buffer, zval * fi
const unsigned int field_count, const MYSQLND_FIELD * const fields_metadata, const unsigned int field_count, const MYSQLND_FIELD * const fields_metadata,
const bool as_int_or_float, MYSQLND_STATS * const stats) const bool as_int_or_float, MYSQLND_STATS * const stats)
{ {
unsigned int i; unsigned int i, j;
const zend_uchar * p = row_buffer->ptr; size_t rbs = row_buffer->size;
const zend_uchar * rbp = row_buffer->ptr;
const zend_uchar * p = rbp;
const zend_uchar * null_ptr; const zend_uchar * null_ptr;
zend_uchar bit; zend_uchar bit;
zval *current_field, *end_field, *start_field; zval *current_field, *end_field, *start_field;
@ -1455,7 +1491,21 @@ php_mysqlnd_rowp_read_binary_protocol(MYSQLND_ROW_BUFFER * row_buffer, zval * fi
statistic = STAT_BINARY_TYPE_FETCHED_NULL; statistic = STAT_BINARY_TYPE_FETCHED_NULL;
} else { } else {
enum_mysqlnd_field_types type = fields_metadata[i].type; enum_mysqlnd_field_types type = fields_metadata[i].type;
mysqlnd_ps_fetch_functions[type].func(current_field, &fields_metadata[i], 0, &p); size_t row_position = p - rbp;
if (rbs <= row_position) {
for (j = 0, current_field = start_field; j < i; current_field++, j++) {
zval_ptr_dtor(current_field);
}
php_error_docref(NULL, E_WARNING, "Malformed server packet. No packet space left for the field");
DBG_RETURN(FAIL);
}
mysqlnd_ps_fetch_functions[type].func(current_field, &fields_metadata[i], rbs - row_position, &p);
if (p == NULL) {
for (j = 0, current_field = start_field; j < i; current_field++, j++) {
zval_ptr_dtor(current_field);
}
DBG_RETURN(FAIL);
}
if (MYSQLND_G(collect_statistics)) { if (MYSQLND_G(collect_statistics)) {
switch (fields_metadata[i].type) { switch (fields_metadata[i].type) {
@ -1513,7 +1563,7 @@ php_mysqlnd_rowp_read_text_protocol(MYSQLND_ROW_BUFFER * row_buffer, zval * fiel
unsigned int field_count, const MYSQLND_FIELD * fields_metadata, unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
bool as_int_or_float, MYSQLND_STATS * stats) bool as_int_or_float, MYSQLND_STATS * stats)
{ {
unsigned int i; unsigned int i, j;
zval *current_field, *end_field, *start_field; zval *current_field, *end_field, *start_field;
zend_uchar * p = row_buffer->ptr; zend_uchar * p = row_buffer->ptr;
const size_t data_size = row_buffer->size; const size_t data_size = row_buffer->size;
@ -1534,9 +1584,11 @@ php_mysqlnd_rowp_read_text_protocol(MYSQLND_ROW_BUFFER * row_buffer, zval * fiel
/* NULL or NOT NULL, this is the question! */ /* NULL or NOT NULL, this is the question! */
if (len == MYSQLND_NULL_LENGTH) { if (len == MYSQLND_NULL_LENGTH) {
ZVAL_NULL(current_field); ZVAL_NULL(current_field);
} else if ((p + len) > packet_end) { } else if (p > packet_end || len > packet_end - p) {
php_error_docref(NULL, E_WARNING, "Malformed server packet. Field length pointing %zu" php_error_docref(NULL, E_WARNING, "Malformed server packet. Field length pointing after end of packet");
" bytes after end of packet", (p + len) - packet_end - 1); for (j = 0, current_field = start_field; j < i; current_field++, j++) {
zval_ptr_dtor(current_field);
}
DBG_RETURN(FAIL); DBG_RETURN(FAIL);
} else { } else {
struct st_mysqlnd_perm_bind perm_bind = struct st_mysqlnd_perm_bind perm_bind =

View File

@ -148,7 +148,7 @@ static zend_string* dblib_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquo
bool use_national_character_set = 0; bool use_national_character_set = 0;
size_t i; size_t i;
char *q; char *q;
size_t quotedlen = 0; size_t quotedlen = 0, extralen = 0;
zend_string *quoted_str; zend_string *quoted_str;
if (H->assume_national_character_set_strings) { if (H->assume_national_character_set_strings) {
@ -163,7 +163,7 @@ static zend_string* dblib_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquo
/* Detect quoted length, adding extra char for doubled single quotes */ /* Detect quoted length, adding extra char for doubled single quotes */
for (i = 0; i < ZSTR_LEN(unquoted); i++) { for (i = 0; i < ZSTR_LEN(unquoted); i++) {
if (ZSTR_VAL(unquoted)[i] == '\'') ++quotedlen; if (ZSTR_VAL(unquoted)[i] == '\'') ++extralen;
++quotedlen; ++quotedlen;
} }
@ -171,6 +171,12 @@ static zend_string* dblib_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquo
if (use_national_character_set) { if (use_national_character_set) {
++quotedlen; /* N prefix */ ++quotedlen; /* N prefix */
} }
if (UNEXPECTED(quotedlen > ZSTR_MAX_LEN - extralen)) {
return NULL;
}
quotedlen += extralen;
quoted_str = zend_string_alloc(quotedlen, 0); quoted_str = zend_string_alloc(quotedlen, 0);
q = ZSTR_VAL(quoted_str); q = ZSTR_VAL(quoted_str);
if (use_national_character_set) { if (use_national_character_set) {

View File

@ -0,0 +1,24 @@
--TEST--
GHSA-5hqh-c84r-qjcv (Integer overflow in the dblib quoter causing OOB writes)
--EXTENSIONS--
pdo_dblib
--SKIPIF--
<?php
if (PHP_INT_SIZE != 4) die("skip for 32bit platforms only");
if (PHP_OS_FAMILY === "Windows") die("skip not for Windows because the virtual address space for application is only 2GiB");
if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
require __DIR__ . '/config.inc';
getDbConnection();
?>
--INI--
memory_limit=-1
--FILE--
<?php
require __DIR__ . '/config.inc';
$db = getDbConnection();
var_dump($db->quote(str_repeat("'", 2147483646)));
?>
--EXPECT--
bool(false)

View File

@ -790,7 +790,7 @@ free_statement:
/* called by the PDO SQL parser to add quotes to values that are copied into SQL */ /* called by the PDO SQL parser to add quotes to values that are copied into SQL */
static zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype) static zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype)
{ {
int qcount = 0; size_t qcount = 0;
char const *co, *l, *r; char const *co, *l, *r;
char *c; char *c;
size_t quotedlen; size_t quotedlen;
@ -804,6 +804,10 @@ static zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *un
/* count the number of ' characters */ /* count the number of ' characters */
for (co = ZSTR_VAL(unquoted); (co = strchr(co,'\'')); qcount++, co++); for (co = ZSTR_VAL(unquoted); (co = strchr(co,'\'')); qcount++, co++);
if (UNEXPECTED(ZSTR_LEN(unquoted) + 2 > ZSTR_MAX_LEN - qcount)) {
return NULL;
}
quotedlen = ZSTR_LEN(unquoted) + qcount + 2; quotedlen = ZSTR_LEN(unquoted) + qcount + 2;
quoted_str = zend_string_alloc(quotedlen, 0); quoted_str = zend_string_alloc(quotedlen, 0);
c = ZSTR_VAL(quoted_str); c = ZSTR_VAL(quoted_str);

View File

@ -987,6 +987,9 @@ static php_conv_err_t php_conv_qprint_decode_convert(php_conv_qprint_decode *ins
} break; } break;
case 5: { case 5: {
if (icnt == 0) {
goto out;
}
if (!inst->lbchars && lb_cnt == 1 && *ps == '\n') { if (!inst->lbchars && lb_cnt == 1 && *ps == '\n') {
/* auto-detect soft line breaks, found network line break */ /* auto-detect soft line breaks, found network line break */
lb_cnt = lb_ptr = 0; lb_cnt = lb_ptr = 0;
@ -1000,15 +1003,13 @@ static php_conv_err_t php_conv_qprint_decode_convert(php_conv_qprint_decode *ins
/* soft line break */ /* soft line break */
lb_cnt = lb_ptr = 0; lb_cnt = lb_ptr = 0;
scan_stat = 0; scan_stat = 0;
} else if (icnt > 0) { } else {
if (*ps == (unsigned char)inst->lbchars[lb_cnt]) { if (*ps == (unsigned char)inst->lbchars[lb_cnt]) {
lb_cnt++; lb_cnt++;
ps++, icnt--; ps++, icnt--;
} else { } else {
scan_stat = 6; /* no break for short-cut */ scan_stat = 6; /* no break for short-cut */
} }
} else {
goto out;
} }
} break; } break;

View File

@ -212,6 +212,11 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
return NULL; return NULL;
} }
/* Should we send the entire path in the request line, default to no. */
if (context && (tmpzval = php_stream_context_get_option(context, "http", "request_fulluri")) != NULL) {
request_fulluri = zend_is_true(tmpzval);
}
use_ssl = (ZSTR_LEN(resource->scheme) > 4) && ZSTR_VAL(resource->scheme)[4] == 's'; use_ssl = (ZSTR_LEN(resource->scheme) > 4) && ZSTR_VAL(resource->scheme)[4] == 's';
/* choose default ports */ /* choose default ports */
if (use_ssl && resource->port == 0) if (use_ssl && resource->port == 0)
@ -230,6 +235,13 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
} }
} }
if (request_fulluri && (strchr(path, '\n') != NULL || strchr(path, '\r') != NULL)) {
php_stream_wrapper_log_error(wrapper, options, "HTTP wrapper full URI path does not allow CR or LF characters");
php_url_free(resource);
zend_string_release(transport_string);
return NULL;
}
if (context && (tmpzval = php_stream_context_get_option(context, wrapper->wops->label, "timeout")) != NULL) { if (context && (tmpzval = php_stream_context_get_option(context, wrapper->wops->label, "timeout")) != NULL) {
double d = zval_get_double(tmpzval); double d = zval_get_double(tmpzval);
#ifndef PHP_WIN32 #ifndef PHP_WIN32
@ -376,12 +388,6 @@ finish:
smart_str_appends(&req_buf, "GET "); smart_str_appends(&req_buf, "GET ");
} }
/* Should we send the entire path in the request line, default to no. */
if (!request_fulluri && context &&
(tmpzval = php_stream_context_get_option(context, "http", "request_fulluri")) != NULL) {
request_fulluri = zend_is_true(tmpzval);
}
if (request_fulluri) { if (request_fulluri) {
/* Ask for everything */ /* Ask for everything */
smart_str_appends(&req_buf, path); smart_str_appends(&req_buf, path);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,28 @@
--TEST--
GHSA-c5f2-jwm7-mmq2 (Configuring a proxy in a stream context might allow for CRLF injection in URIs)
--INI--
allow_url_fopen=1
--CONFLICTS--
server
--FILE--
<?php
$serverCode = <<<'CODE'
echo $_SERVER['REQUEST_URI'];
CODE;
include __DIR__."/../../../../sapi/cli/tests/php_cli_server.inc";
php_cli_server_start($serverCode, null, []);
$host = PHP_CLI_SERVER_ADDRESS;
$userinput = "index.php HTTP/1.1\r\nHost: $host\r\n\r\nGET /index2.php HTTP/1.1\r\nHost: $host\r\n\r\nGET /index.php";
$context = stream_context_create(['http' => ['proxy' => 'tcp://' . $host, 'request_fulluri' => true]]);
echo file_get_contents("http://$host/$userinput", false, $context);
?>
--EXPECTF--
Warning: file_get_contents(http://localhost:%d/index.php HTTP/1.1
Host: localhost:%d
GET /index2.php HTTP/1.1
Host: localhost:%d
GET /index.php): Failed to open stream: HTTP wrapper full URI path does not allow CR or LF characters in %s on line %d

View File

@ -1946,6 +1946,8 @@ static void php_cli_server_client_populate_request_info(const php_cli_server_cli
request_info->auth_user = request_info->auth_password = request_info->auth_digest = NULL; request_info->auth_user = request_info->auth_password = request_info->auth_digest = NULL;
if (NULL != (val = zend_hash_str_find(&client->request.headers, "content-type", sizeof("content-type")-1))) { if (NULL != (val = zend_hash_str_find(&client->request.headers, "content-type", sizeof("content-type")-1))) {
request_info->content_type = Z_STRVAL_P(val); request_info->content_type = Z_STRVAL_P(val);
} else {
request_info->content_type = NULL;
} }
} /* }}} */ } /* }}} */

View File

@ -0,0 +1,41 @@
--TEST--
GHSA-4w77-75f9-2c8w (Heap-Use-After-Free in sapi_read_post_data Processing in CLI SAPI Interface)
--INI--
allow_url_fopen=1
--SKIPIF--
<?php
include "skipif.inc";
?>
--FILE--
<?php
include "php_cli_server.inc";
$serverCode = <<<'CODE'
var_dump(file_get_contents('php://input'));
CODE;
php_cli_server_start($serverCode, null, []);
$options = [
"http" => [
"method" => "POST",
"header" => "Content-Type: application/x-www-form-urlencoded",
"content" => "AAAAA",
],
];
$context = stream_context_create($options);
echo file_get_contents("http://" . PHP_CLI_SERVER_ADDRESS . "/", context: $context);
$options = [
"http" => [
"method" => "POST",
],
];
$context = stream_context_create($options);
echo file_get_contents("http://" . PHP_CLI_SERVER_ADDRESS . "/", context: $context);
?>
--EXPECT--
string(5) "AAAAA"
string(0) ""