Remote SQL Injection in WordPress and WordPress MU
- Author
- Alexander Concha <alex at buayacorp dot com>
- Affected versions
- WordPress <= 2.2.2 and WordPress MU <= 1.2.4
Overview
WordPress is a state-of-the-art semantic personal publishing platform with a focus on aesthetics, web standards, and usability.
While testing WordPress, it has been discovered a SQL Injection vulnerability that allows an attacker to retrieve remotely any user credentials from a vulnerable site, this bug is caused because of early database escaping and the lack of validation in query string like parameters.
Introduction
Many WordPress functions accept and are called using query string like parameters, that kind of functions use internally parse_str
to parse parameters:
function wp_parse_args( $args, $defaults = '' ) { if ( is_object($args) ) $r = get_object_vars($args); else if ( is_array( $args ) ) $r =& $args; else wp_parse_str( $args, $r ); if ( is_array( $defaults ) ) return array_merge( $defaults, $r ); else return $r; } function wp_parse_str( $string, &$array ) { parse_str( $string, $array ); if ( get_magic_quotes_gpc() ) $array = stripslashes_deep( $array ); $array = apply_filters( 'wp_parse_str', $array ); }
Some code to illustrate the problem:
// This code does not exists on WP codebase function get_posts($args = '') { $defaults = array( 'post_type' => 'post', 'max_results' => 5 ); $r = wp_parse_args( $args, $defaults ); extract( $r, EXTR_SKIP ); echo " SELECT * FROM wp_posts WHERE post_type = '$post_type' LIMIT $max_results "; } ... // Somewhere in the application $args = 'post_type=' . $wpdb->escape($_GET['post_type']); $posts = get_posts($args); // Some results with different values for post_type $_GET['post_type'] = 'page' -------------------------------- SELECT * FROM wp_posts WHERE post_type = 'page' LIMIT 5 $_GET['post_type'] = 'demo%27 SQL INJECTION HERE /*' -------------------------------- SELECT * FROM wp_posts WHERE post_type = 'demo' SQL INJECTION HERE /*' LIMIT 5 $_GET['post_type'] = 'dummy&max_results=0 SQL INJECTION' -------------------------------- SELECT * FROM wp_posts WHERE post_type = 'dummy' LIMIT 0 SQL INJECTION
Because of early database escaping and the lack of validation in this query string parameters -- as shown by the example, WordPress is vulnerable to multiple SQL Injection vulnerabilities, some of them are remotely exploitable.
Details
WordPress provides and XMLRPC interface to list pingbacks for an specific post, it uses url_to_postid
function to determine the post ID
of an URL.
Relevant part of url_to_postid
function:
foreach ($rewrite as $match => $query) { // If the requesting file is the anchor of the match, prepend it // to the path info. if ( (! empty($url)) && (strpos($match, $url) === 0) ) { $request_match = $url . '/' . $request; } if ( preg_match("!^$match!", $request_match, $matches) ) { // Got a match. // Trim the query of everything up to the '?'. $query = preg_replace("!^.+\?!", '', $query); // Substitute the substring matches into the query. eval("\$query = \"$query\";"); $query = new WP_Query($query); if ( $query->is_single || $query->is_page ) return $query->post->ID; else return 0; } } ... An example of $match [->] $query category_base/([^/]+)(/[0-9]+)?/?$ [->] name=$matches[1]&page=$matches[2]
An attacker can prepare a crafted URL to pass arbitrary parameters to get_posts
method of WP_Query
class.
http://vulnerable.com/category_base/&post_type=%27) SQL INJECTION HERE %2F*
If a vulnerable blog has permalinks enabled and the above URL matches some rewrite rule, it will override the value of $post_type
variable.
Proof of Concept
An exploit code was developed to test a vulnerable version in 2.2 series, it uses XMLRPC API.
Solution
Upgrade as soon as posible to the latest version of WordPress [MU] or block the access to xmlrpc.php
.
Disclosure Timeline
- 07/28/2007 - Bug found
- 08/22/2007 - Vendor contact
- 09/07/2007 - WordPress 2.2.3 released
- 09/10/2007 - Public Disclosure