CVE-2024-3096
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 password_hash() function is the standard way for PHP applications to store and check user passwords, with bcrypt as the default algorithm.
An Authentication Bypass vulnerability (CVE-2024-3096) has been identified in PHP's bcrypt support for password_hash(), which allows an attacker to log in with an empty password to an account whose stored password was hashed from a value starting with a NUL byte. PHP tracks this issue as GHSA-h746-cjrr-wfmr.
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 PHP 8.1 before 8.1.28, PHP 8.2 before 8.2.18 and PHP 8.3 before 8.3.5, as well as all releases of the End-of-Life PHP 5.6 through 8.0 release lines.
Details
Module Info
- Product: PHP
- Affected packages:
php-src(ext/standard) - Affected versions: <8.1.28, >=8.2.0 <8.2.18, >=8.3.0 <8.3.5
- 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.28, 8.2.18, 8.3.5
- 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 password hashing functions of PHP 8.1 before 8.1.28, 8.2 before 8.2.18 and 8.3 before 8.3.5, and in the End-of-Life PHP 5.6 through 8.0 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:R/S:U/C:H/I:N/A:N).
For bcrypt, password_hash() builds a salt string and passes the password to PHP's crypt implementation. A PHP string can contain NUL bytes, but bcrypt reads the password as a C string, so it stops at the first NUL. In PHP 7.4.33 the hashing function passes the password through without looking at its contents:
/* This cast is safe, since both values are defined here in code and cannot overflow */
result = php_crypt(ZSTR_VAL(password), (int)ZSTR_LEN(password), ZSTR_VAL(hash), (int)ZSTR_LEN(hash), 1);
zend_string_release_ex(hash, 0);A password that starts with a NUL byte is therefore hashed exactly like the empty string. When that hash is later checked, password_hash() computes the same hash for an empty password and returns true. More generally, anything after the first NUL byte in a bcrypt password is ignored.
Exploitation needs an application that lets an attacker choose a password containing a leading NUL byte and hashes it with PASSWORD_BCRYPT or PASSWORD_DEFAULT, for example at registration or password reset, and a login form that accepts an empty password. The attacker, or anyone else, can then sign in to that account without knowing the password. Any SAPI is affected, since ext/standard is always built. Applications that reject empty passwords at login, or that do not accept binary input in passwords, limit the impact, and hashes created with Argon2 are not affected.
The fix, by Jakub Zelenka, makes password_hash() refuse bcrypt passwords that contain a NUL byte. Upstream PHP 8 throws a ValueError. The NES releases refuse such passwords too; on PHP 5.6, 7.2 and 7.4 they emit a warning and return NULL instead of throwing, matching how those versions report other password_hash() errors. The upstream check reads:
if (memchr(ZSTR_VAL(password), '\0', ZSTR_LEN(password))) {
zend_value_error("Bcrypt password must not contain null character");
return NULL;
}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, reject passwords that contain NUL bytes before passing them to password_hash(), and reject empty passwords at login.
Users of the affected components should apply one of the following mitigations:
- Upgrade to PHP 8.1.28, 8.2.18, 8.3.5, or later.
- Leverage a commercial support partner like HeroDevs (NES for PHP) for post-EOL security support.
Credits
- Eric Stern (finder)
- Jakub Zelenka (remediation)