Home / os / wince

PHP 7.0.10 Heap overflow in mysqlnd related to BIT fields

Posted on 30 November -0001

<HTML><HEAD><TITLE>PHP 7.0.10 Heap overflow in mysqlnd related to BIT fields</TITLE><META http-equiv="Content-Type" content="text/html; charset=utf-8"></HEAD><BODY>Description: ------------ mysqlnd extension assumes the `flags` returned for a BIT field necessarily contain UNSIGNED_FLAG; this might not be the case, with a rogue mysql server, or a MITM attack. In php_mysqlnd_rowp_read_text_protocol_aux (mysqlnd_wireprotocol.c) BIT fields are treated specially and use extra space pre-allocated for that processing at the end of `row_buffer` (see the comment there). The size of that extra space that is allocated is calculated in mysqlnd_res_meta::read_metadata (in mysqlnd_result_meta.c), and depends on the number of BIT fields and their respective size in bytes. For a BIT(8) field, which has 8 bits, the function reserves 3 bytes. This is fine if the field is UNSIGNED (which BIT values should always be with a normal mysql server), however if the server returns BIT fields without the UNSIGNED_FLAG set, a BIT(8) will then be interpreted as signed, and can contain negative values such as -127, which no longer fit the reserved space: To read BIT values off a row_buffer, php_mysqlnd_rowp_read_text_protocol_aux uses the generic function ps_fetch_from_1_to_8_bytes (mysqlnd_ps_codec.c) which starts exactly with a check of the UNSIGNED_FLAG but is not aware of whether it is processing a BIT field. Thus, a malicious mysql server or MITM can return field metadata for BIT fields that does not contain the UNSIGNED_FLAG, leading to a heap overflow. Tested in 5.6.x and latest packaged PHP 7.0.7, but should affect a lot more versions. Affects queries through mysql / mysqli / anything that uses mysqlnd. To simulate a rogue mysql server apply the following patch to mysqlnd_ps_codec.c before running the test case: < if (field->flags & UNSIGNED_FLAG) { > if (field->flags & UNSIGNED_FLAG && field->type != MYSQL_TYPE_BIT) { Test script: --------------- <?php /* Please setup the following database/table: CREATE DATABASE php; USE php; CREATE TABLE `php` (`moo` bit(8) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1; INSERT INTO `php` VALUES (0x81); # -127 when signed */ $link = mysqli_connect('127.0.0.1', 'root', '', 'php'); if (!$link) die("Cannot connect"); $s = str_repeat("moo,", 60000); /* can play with this value a bit to see different corruption */ $result = mysqli_query($link, "SELECT $s 1 FROM php"); while($row = mysqli_fetch_row($result)) { $v = print_r($row, true); /* just to exercise heap */}; mysqli_close($link); ?></BODY></HTML>

 

TOP