This post describes the second of two vulnerabilities I found in WordPress. The first, a XSS vulnerability, was described last week. While the vulnerability discussed here is applicable in fewer cases than the previous one, it is an example of a comparatively rare class, oracle attacks, so I think merits further exposition.
An oracle attack is where an attacker can abuse a facility provided by a system to gain unauthorized access to protected information. The term originates from cryptology, and such attacks still crop up regularly; for example in banking security devices and protocols. The occurrence of an oracle attack in WordPress illustrates the need for a better understanding of cryptography, even by the authors of applications not conventionally considered to be cryptographic software. Also more forgiving primitives and better robustness principles could reduce the risk of future weaknesses.
The vulnerability is a variant of the ‘cache’ shell injection bug reported by rgodm. This is caused by an unfortunate series of design choices by the WordPress team, leading to arbitrary PHP execution. The WordPress cache stores commonly accessed information from the database, such as user profile data, in files for faster retrieval. Despite them being needed only by the server, they are still accessible from the web, which is commonly considered bad practice. To prevent the content being read remotely, the data is placed in .php
files, commented out with //
. Thus when executed by the web server, in response to a remote query, they return an empty file.
However, putting user controlled data in executable files is inherently a risky choice. If the attacker can escape from the comment then arbitrary PHP can be executed. rgodm’s shell injection bug does this by inserting a newline into the display name. Now all the attacker must do is guess the name of the .php
file which stores his cached profile information, and invoke it to run the injected PHP. WordPress puts an index.php
in the cache directory to suppress directory indexing, and filenames are generated as MD5(username || DB_PASSWORD
) || “.php”, which creates hard to guess name. The original bug report suggested brute forcing DB_PASSWORD, the MySQL authentication password, but the oracle attack described here will succeed even if a strong password is chosen.
The new attack relies on the popular Subscribe to Comments plugin, so will not work on a default installation of WordPress. The plugin allows blog visitors to be notified by email when new comments are posted to a blog entry. To protect against malicious un-subscription, the plugin sends a cryptographic challenge to subscribers, which must be presented to modify their preferences. It is generated as MD5(email address || DB_PASSWORD
), strikingly similar to the user profile data filename.
Thus an attacker can create a profile with a username that is also an email address (perfectly permissible), and inject malicious PHP after a newline in their display name. Then the attacker subscribes to a post, adds a comment and along with the notification is sent a challenge, which contains the “unguessable” string. This allows the injected PHP to be invoked, without ever knowing the database password.
Re-using passwords or keys for multiple purposes is considered bad-practice, and these vulnerabilities are an example of why. Administrators of WordPress installations, where access to the database server is restricted, may feel safe to have a weak or even null password (although this is not prudent). However the original vulnerability means that weak passwords permit attackers to run arbitrary PHP. Even if the password was strong, the oracle attack presented here would still work, so is further evidence against secret re-use.
It is also not recommended to use a password directly as a key, because they seldom have sufficient entropy to withstand brute force attacks, but that is a different topic. Finally, using keys with hash functions should be done with care because prepending and appending passwords lead to length-extension and collision attacks respectively. HMAC fixes both problems and only requires an extra invocation of the hash function.
Using separately generated cryptographically secure random numbers for cache filename and challenge generation would have resisted both the original and oracle attacks, and is the approach aimed for by both WordPress 2.0.3 and Subscribe to Comments 2.0.4, although PHP does not make it easy to get unpredictable pseudorandom numbers. WordPress also fixed the bug allowing attackers to break out of the PHP comment, but defense in depth is a sensible choice in complex systems. As mentioned last week, upgrading WordPress is strongly recommended.
Good analysis. Obviously shows that embedding passwords into lesser hashes isn’t always a good idea. 😉
Also, it would have been good to point out that the caching system at fault is disabled in 2.0.4, and should never have been defaulting enabled given it only helps a fraction of a percent of the WP population. Only an expert admin would want/need to activate it. Not only does it open security issues, but it also can be ‘buggy’ and overload certain server configs.
Rather than commenting out the entire file, couldn’t you just put as the first line in each?
The caching system at fault was disabled by default in 2.0.2 and 2.0.1 as well it was only enabled by default in the 2.0 release.
The fix in 2.0.3 for the cache removes the ability for any attack on the caching system giving you the ability to put executable code in the cache files and the serialized data is now base64 encoded and placed inside a multi line php comment.