CVE-2024-8929
Patch Available.
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
PHP is a general-purpose scripting language used mostly for web development. It runs behind web servers through server APIs such as Apache mod_php and PHP-FPM, and powers content management systems and frameworks such as WordPress, Drupal, Moodle and Laravel. Its mysqlnd library implements the MySQL client protocol natively and is used by the mysqli and pdo_mysql extensions (and, on PHP 5.6, by the legacy mysql extension).
An Information Exposure vulnerability (CVE-2024-8929) has been identified in the packet decoding code of PHP's mysqlnd library, which allows a hostile or impersonated MySQL server to make the PHP client read past its packet buffers and return the extra heap memory, which may contain data from other queries or other users of the same PHP worker, to the application. PHP tracks this issue as GHSA-h35g-vwh6-m678.
Per MITRE CWE-200: The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
This issue affects all versions of PHP before 8.1.31, PHP 8.2 before 8.2.26, PHP 8.3 before 8.3.14 and PHP 8.4.0, which includes the End-of-Life PHP 5.6, 7.0, 7.1, 7.2, 7.3, 7.4 and 8.0 release lines through their final releases.
Details
Module Info
- Product: PHP
- Affected packages:
php-src(ext/mysqlnd, used byext/mysqliandext/pdo_mysql) - Affected versions: <8.1.31, >=8.2.0 <8.2.26, >=8.3.0 <8.3.14, >=8.4.0 <8.4.1
- GitHub repository: https://github.com/php/php-src
- Published packages: https://www.php.net/downloads
- Package manager: N/A
- Fixed in:
- OSS PHP 8.1.31, 8.2.26, 8.3.14, 8.4.1
- NES for PHP 5.6.1 (based on PHP 5.6.40)
- NES for PHP 7.2.1 (based on PHP 7.2.34)
- NES for PHP 7.4.1 (based on PHP 7.4.33)
Vulnerability Info
This Medium-severity vulnerability is found in the mysqlnd library of all PHP versions before 8.1.31, 8.2 before 8.2.26, 8.3 before 8.3.14 and 8.4.0, including the End-of-Life PHP 5.6, 7.0, 7.1, 7.2, 7.3, 7.4 and 8.0 release lines, which never received the upstream fix. The PHP project scores it 5.8 under CVSS 3.1 (CVSS:3.1/AV:A/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N). The CVE record's version list gives 8.2.24 for the 8.2 line, but its description and the PHP advisory both name 8.2.26 as the fixed release.
mysqlnd reads each response from the server into a buffer and decodes it field by field. Many fields are length-prefixed, and several decoders trusted the length sent by the server without checking it against the bytes actually left in the packet. The advisory describes the clearest case in php_mysqlnd_rset_field_read(), which parses column metadata and still accepts a default value meant only for the COM_FIELD_LIST command. In PHP 7.4.33, it reads:
if (packet->header.size > (size_t) (p - buf) &&
(len = php_mysqlnd_net_field_length(&p)) &&
len != MYSQLND_NULL_LENGTH)
{
BAIL_IF_NO_MORE_DATA;
DBG_INF_FMT("Def found, length %lu", len);
meta->def = packet->memory_pool->get_chunk(packet->memory_pool, len + 1);
memcpy(meta->def, p, len);
meta->def[len] = '\0';
meta->def_length = len;
p += len;
}The length len comes from the server and can be far larger than the 4096-byte command buffer, so memcpy() copies whatever lies beyond the packet into the column's default value, which the application can read back. The advisory notes that the response buffer is not cleared between requests, so this memory can hold the results of earlier queries, and the reporter confirmed extracting a previous query's response. The same kind of unchecked length was found in the OK packet message, the LOCAL INFILE file name, the prepared-statement row decoders (ps_fetch_*) and, on 32-bit builds, text-protocol row data. HeroDevs' analysis also covers the authentication plugin name sent in the server greeting.
Exploitation needs a PHP application that connects to a MySQL server the attacker controls or can impersonate, for example a configurable database host, a connection without TLS on a network the attacker can reach, or a database administration tool pointed at an attacker's server. HeroDevs' analysis lists mysqli, pdo_mysql and, on PHP 5.6, the mysql extension through mysqlnd, under any SAPI. On persistent PHP-FPM workers the leaked memory may belong to other users of the same pool. Applications that connect only to trusted servers over authenticated TLS connections are not exposed.
The fix checks each length against the bytes remaining in the packet and fails with a protocol error instead of reading further. Because PHP no longer sends COM_FIELD_LIST, upstream now rejects a column default outright:
/* COM_FIELD_LIST is no longer supported so def should not be present */
if (packet->header.size > (size_t) (p - buf) &&
(len = php_mysqlnd_net_field_length(&p)) &&
len != MYSQLND_NULL_LENGTH)
{
DBG_ERR_FMT("Protocol error. Server sent default for unsupported field list");
php_error_docref(NULL, E_WARNING,
"Protocol error. Server sent default for unsupported field list (mysqlnd_wireprotocol.c:%u)",
__LINE__);
DBG_RETURN(FAIL);
}HeroDevs' analysis notes that PHP 5.6 still sends COM_FIELD_LIST for mysql_list_fields(), so the fix for that line keeps column defaults but requires them to fit inside the packet. The NES releases also harden the new error paths so that a row that fails these checks leaves no value behind to be freed twice.
Mitigation
The PHP project publishes security fixes only for its supported release lines. PHP 5.6, 7.0, 7.1, 7.2, 7.3, 7.4 and 8.0 are End-of-Life and will not receive any public updates to address this issue.
Until an upgrade is possible, connect only to trusted MySQL servers, use TLS with server certificate verification for database connections, and do not let users choose the database host.
Users of the affected components should apply one of the following mitigations:
- Upgrade to PHP 8.1.31, 8.2.26, 8.3.14, 8.4.1, or later.
- Leverage a commercial support partner like HeroDevs (NES for PHP) for post-EOL security support.
Credits
- Sébastien Rolland (finder)
- Jakub Zelenka (remediation)