PHP 7.0.10 Heap overflow in mysqlnd related to BIT fields

2016.09.15
Credit: themadbat
Risk: High
Local: Yes
Remote: No
CVE: N/A
CWE: N/A

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); ?>

References:

https://bugs.php.net/bug.php?id=72293
http://git.php.net/?p=php-src.git;a=commit;h=28f80baf3c53e267c9ce46a2a0fadbb981585132


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2026, cxsecurity.com

 

Back to Top