CWE-328, Use of Weak Hash, applies when a product uses a hash algorithm whose output does not meet security expectations. An attacker can work out the original input, find another input with the same hash, or find multiple inputs that collide.
Password storage, file integrity checks, signatures and request authentication all rely on hashes, so any of them can be affected.
How it happens
A hash function maps data of any size to a fixed size digest. MITRE lists the basic properties: it is not invertible and it is deterministic. A cryptographic hash must also stop a malicious actor from exploiting the function. The three attacks named in the record are:
- Preimage: determine the original input from the hash.
- Second preimage: find a different input producing the same hash.
- Birthday: find multiple inputs that evaluate to the same hash.
Weak hashes fail one or more of these. Fast hashes fail in another way for passwords: they let attackers test guesses at enormous speed.
# Vulnerable: fast, general purpose hash for passwords
stored = hashlib.md5(password.encode()).hexdigest()
Observed examples include CVE-2005-4900, where SHA-1 was not collision resistant, CVE-2022-30320, a controller using an insecure hash for passwords, and CVE-2020-25685, a DNS product using a weak hash (CRC32 or SHA-1) of the query name, allowing an attacker to forge responses by computing the hash.
What an attacker gains
The record lists bypassing a protection mechanism. For stored passwords that means recovering them from a leaked database. For integrity checks it means substituting content that passes verification. For the DNS example it means forging responses.
How to find it
Static analysis can find some instances. Search code and configuration for md5, sha1 and crc32 used in a security role. Not every use is a flaw, since a checksum for catching accidental corruption is a different job, but each use should have a stated reason.
How to fix it
MITRE's high effectiveness mitigation is an adaptive hash function: one whose computational effort can be configured, through iteration count ("stretching") or memory use. Some perform salting automatically. These raise the cost of brute force substantially compared with intentionally fast functions such as MD5, and rainbow table attacks become infeasible. Because computing power keeps getting cheaper, you can raise the workload later without replacing the algorithm. MITRE names bcrypt, scrypt and PBKDF2 as functions with these properties, and notes there is active debate about which is most effective, though all are stronger than a salt on a cheap hash.
One caution from the record: these functions cost CPU and memory, so tune them to avoid creating a denial of service.
# Fixed: salted, adaptive hash
import bcrypt
stored = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))
ok = bcrypt.checkpw(candidate.encode(), stored)
For integrity and signatures, move to a current member of the SHA-2 family or newer, and verify with a maintained library.
Quick check
Is salting an MD5 password hash enough?
No. MITRE says adaptive functions are stronger than using salts with hashes that have very little computing overhead.
Can I raise the work factor later?
Yes. That is one advantage of adaptive hashing. Rehash on next login with the new setting.