CVE-2020-7060
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 mbstring extension converts text between character encodings through the bundled libmbfl library, which includes decoders for the Traditional Chinese BIG5 encoding and Microsoft's CP950 variant of it.
An Out-of-Bounds Read vulnerability (CVE-2020-7060) has been identified in mbfl_filt_conv_big5_wchar(), the BIG5 and CP950 decoder of PHP's mbstring extension, which allows an attacker who can supply text that an application converts from CP950 (for example with mb_convert_encoding() or mb_decode_mimeheader()) to make PHP read past the end of a global table, which can disclose memory contents or crash the process. PHP tracks this issue as bug #79037.
Per MITRE CWE-125: The product reads data past the end, or before the beginning, of the intended buffer.
This issue affects all versions of PHP before 7.2.27, PHP 7.3 before 7.3.14 and PHP 7.4 before 7.4.2.
Details
Module Info
- Product: PHP
- Affected packages:
php-src(ext/mbstring/libmbfl) - Affected versions: <7.2.27, >=7.3.0 <7.3.14, >=7.4.0 <7.4.2
- GitHub repository: https://github.com/php/php-src
- Published packages: https://www.php.net/downloads
- Package manager: N/A
- Fixed in:
- OSS PHP 7.2.27, 7.3.14, 7.4.2
- NES for PHP 5.6.1 (based on PHP 5.6.40)
Vulnerability Info
This Medium-severity vulnerability is found in the mbstring extension of all PHP versions before 7.2.27, PHP 7.3 before 7.3.14 and 7.4 before 7.4.2, including the End-of-Life PHP 5.6, 7.0 and 7.1 release lines, which never received the upstream fix. The PHP project scores it 6.5 under CVSS 3.1 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L).
BIG5 and CP950 encode most characters as two bytes. CP950 also maps several lead-byte ranges to the Unicode private use area, described by the five-row table cp950_pua_tbl. When a two-byte character has no regular mapping, the decoder decides whether it falls in one of those ranges, searches the table for the matching row, and computes the code point from it. In PHP 7.2.26 that code reads:
c2 = c1 << 8 | c;
for (k = 0; k < sizeof(cp950_pua_tbl)/(sizeof(unsigned short)*4); k++) {
if (c2 >= cp950_pua_tbl[k][2] && c2 <= cp950_pua_tbl[k][3]) {
break;
}
}
if ((cp950_pua_tbl[k][2] & 0xff) == 0x40) {
w = 157*(c1 - (cp950_pua_tbl[k][2]>>8)) + c - (c >= 0xa1 ? 0x62 : 0x40)
+ cp950_pua_tbl[k][0];
} else {
w = c2 - cp950_pua_tbl[k][2] + cp950_pua_tbl[k][0];
}The check that sends a character into this block accepts second bytes from 0x3A, while the table ranges start at 0x40. A character with a matching lead byte and a second byte between 0x3A and 0x3F therefore matches no row, the loop ends with k equal to 5, and the code reads cp950_pua_tbl[5], one row past the end of the table. The reporter's sanitizer run shows this read just past the 40-byte table. The value read decides the code point PHP outputs, so it can leak neighboring memory into the converted string, or crash the process if the address is not readable.
Exploitation needs an application that converts attacker-supplied text from CP950, for example through mb_convert_encoding(), or decodes attacker-supplied MIME headers with mb_decode_mimeheader(), which converts from the charset the header names. Any SAPI is affected. Applications that never convert untrusted input from BIG5-family encodings are not exposed.
The fix, by Stanislav Malyshev, moves the range test into a helper that accepts only second bytes the table covers, so the loop always finds a row:
static inline int is_in_cp950_pua(int c1, int c) {
if ((c1 >= 0xfa && c1 <= 0xfe) || (c1 >= 0x8e && c1 <= 0xa0) ||
(c1 >= 0x81 && c1 <= 0x8d) || (c1 >= 0xc7 && c1 <= 0xc8)) {
return (c >=0x40 && c <= 0x7e) || (c >= 0xa1 && c <= 0xfe);
}
if (c1 == 0xc6) {
return c >= 0xa1 && c <= 0xfe;
}
return 0;
}Mitigation
The PHP project publishes security fixes only for its supported release lines. PHP 5.6, 7.0 and 7.1 are End-of-Life and will not receive any public updates to address this issue.
Until an upgrade is possible, do not convert untrusted input from CP950 or BIG5, for example by limiting the source encodings an application accepts for mb_convert_encoding() and mb_decode_mimeheader().
Users of the affected components should apply one of the following mitigations:
- Upgrade to PHP 7.2.27, 7.3.14, 7.4.2, or later.
- Leverage a commercial support partner like HeroDevs (NES for PHP) for post-EOL security support.
Credits
- reza (iSecLab) (finder)
- Stanislav Malyshev (remediation)