CVE-2023-0567 - How PHP's `password_verify()` Accepted Invalid Blowfish Hashes (Exploit Details & Code Example)
PHP is one of the most widely used programming languages for web development. It comes with a handy password_hash() and password_verify() system to store and check user passwords securely. But in early 2023, security researchers found a critical bug, now detailed as CVE-2023-0567, affecting the password_verify() function. This bug could let *any password* work under the right circumstances—a catastrophic problem for sites handling sensitive logins.
This post covers the cause, exploitation, and implications, all in simple language, with examples and links for further reading.
PHP 8.2.x before 8.2.3
Here, the vulnerable function is password_verify(), used everywhere to check a user's typed password against the database hash.
The bug?
If your database somehow contains a "malformed" (incorrectly formatted) Blowfish-style hash, password_verify() may accept any password as valid for this record.
Why Is This Dangerous?
Imagine a hacker finds a way to insert a special fake hash into your database (through SQL injection, backup restoration, developer error, or another bug). Anyone who tries to login as that user could enter *any password*, and the login would succeed. It could be used for:
Framing attacks as legitimate activity.
All without ever needing to guess or brute-force real user passwords.
What Is a "Blowfish" Hash?
PHP supports several hashing algorithms for passwords. The *Blowfish* algorithm uses the $2y$ prefix. A typical hash looks like this:
$2y$10$wH7Bx93B/ooJenfGBHZ3u2lMBt3DwS9d8LooYAA5JeTD3/nJ74py
PHP expects the string to follow a specific format (length, characters, $ signs in correct places).
The Core Problem
In vulnerable PHP versions, if your Blowfish hash is malformed (e.g., too short, wrong character, etc.), password_verify() could fail the internal hash comparison, *then simply accept any password*.
Below is a minimal, full example using PHP 8.1.12 (vulnerable) that demonstrates the bug
<?php
// This is an INVALID Blowfish hash (e.g., too short, missing stuff)
$malformedHash = '$2y$10$1234567890123456789012'; // Not a real hash!
$passwordsToTest = ['password', '123456', 'admin', 'banana', 'any-password-at-all'];
foreach ($passwordsToTest as $pw) {
if (password_verify($pw, $malformedHash)) {
echo "Password '$pw' is ACCEPTED!\n";
} else {
echo "Password '$pw' is rejected.\n";
}
}
Expected Output (on a vulnerable PHP)
Password 'password' is ACCEPTED!
Password '123456' is ACCEPTED!
Password 'admin' is ACCEPTED!
Password 'banana' is ACCEPTED!
Password 'any-password-at-all' is ACCEPTED!
Insert Invalid Hash.
Through SQL injection, privilege to alter database, or accidental insertion/recovery processes.
Fixing and Protecting Against CVE-2023-0567
Upgrade PHP!
8.2.3
PHP now properly checks the format of the Blowfish hash argument, and *immediately fails* if invalid, no matter the password.
Check your current version
php -v
Always sanitize and validate any password hashes you restore or import.
References & Further Reading
- PHP Official Patch Notes
- Debian Security Tracker CVE-2023-0567
- PHP: password_verify() Manual
- NIST Vulnerability Database
Final Thoughts
*Even the best tools can fail in edge cases.*
CVE-2023-0567 is a lesson on always using recommended software versions, and never trusting unchecked input—even for cryptographic data like password hashes.
Timeline
Published on: 03/01/2023 08:15:00 UTC
Last modified on: 03/10/2023 17:32:00 UTC