CVE-2023-0567
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.
An Authentication Bypass vulnerability (CVE-2023-0567) has been identified in PHP's bcrypt implementation behind crypt() and password_verify(), which allows an attacker who can place a malformed bcrypt hash in an application's password store to log in to that account with any password. A related flaw lets a two-byte hash make crypt() read past the end of the string. PHP tracks this issue as bug #81744 and GHSA-7fj2-8x79-rjf4.
Per MITRE CWE-287: When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
This issue affects all versions of PHP before 8.0.28, PHP 8.1 before 8.1.16 and PHP 8.2 before 8.2.3, which includes every release of the End-of-Life PHP 5.6 and 7.0 through 7.4 release lines.
Details
Module Info
- Product: PHP
- Affected packages:
php-src(ext/standard) - Affected versions: <8.0.28, >=8.1.0 <8.1.16, >=8.2.0 <8.2.3
- GitHub repository: https://github.com/php/php-src
- Published packages: https://www.php.net/downloads
- Package manager: N/A
- Fixed in:
- OSS PHP 8.0.28, 8.1.16, 8.2.3
- 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 High-severity vulnerability is found in the standard extension of all versions of PHP before 8.0.28, PHP 8.1 before 8.1.16 and PHP 8.2 before 8.2.3, including the End-of-Life PHP 5.6 and 7.0 through 7.4 release lines, which never received the upstream fix. The PHP project scores it 7.7 under CVSS 3.1 (CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N). CISA-ADP rates it 8.1 with an adjacent-network attack vector.
A bcrypt hash such as $2y$10$... carries a cost and a 22-character salt, and password_verify() checks a password by hashing it again with that salt and comparing the result to the stored hash. PHP's copy of the crypt_blowfish library differs from the upstream Openwall code by a "PHP hack": the salt decoder stops at a $ character and fills the rest of the salt with zeros. In PHP 7.4.33 the decoder reads:
#define BF_safe_atoi64(dst, src) \
{ \
tmp = (unsigned char)(src); \
if (tmp == '$') break; /* PHP hack */ \
if ((unsigned int)(tmp -= 0x20) >= 0x60) return -1; \
tmp = BF_atoi64[tmp]; \
if (tmp > 63) return -1; \
(dst) = tmp; \
}A hash whose salt is cut short by a $ is not a valid bcrypt hash, but PHP accepted it. For some of these malformed hashes, crypt() returns the hash string itself, so password_verify(), or code that compares the output of crypt() with the stored value, reports a match for any password. The reporter demonstrated this with a hash of the form $2y$10$am$2y$10$am. A second flaw in php_crypt() let a two-byte $2 hash make PHP read past the end of the string before it checked the length.
Exploitation needs a malformed Blowfish hash to end up in the password store, for example through imported or migrated hashes, or by an attacker who can write a hash value for an account. The application then accepts any password for that account. Any SAPI is affected, the code is part of the always-built standard extension, and OPcache does not change this. Correctly generated hashes, such as those produced by password_hash(), are not affected.
The fix, by Tim Düsterhus, removes the PHP hack so a salt containing $ is rejected and crypt() fails for such hashes, and adds a length check before php_crypt() reads the hash prefix. NES for PHP carries both changes to PHP 5.6, 7.2 and 7.4.
Mitigation
The PHP project publishes security fixes only for its supported release lines. PHP 5.6, 7.0, 7.1, 7.2, 7.3 and 7.4 are End-of-Life and will not receive any public updates to address this issue.
Until an upgrade is possible, verify that every stored password hash is well formed before trusting it, and do not import password hashes from untrusted sources.
Users of the affected components should apply one of the following mitigations:
- Upgrade to PHP 8.0.28, 8.1.16, 8.2.3, or later.
- Leverage a commercial support partner like HeroDevs (NES for PHP) for post-EOL security support.
Credits
- mkdgs (finder)
- Tim Düsterhus (remediation)