CVE-2019-13575 (published 2019-07-18T15:15Z)

A SQL injection vulnerability exists in WPEverest Everest Forms plugin for WordPress through 1.4.9. Successful exploitation of this vulnerability would allow a remote attacker to execute arbitrary SQL commands on the affected system via includes/evf-entry-functions.php

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-89
cwe_name: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
description: The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.
extended_description: 
            
            
         
         

Fix information:
repo_url: https://github.com/wpeverest/everest-forms
commit_details: repo_url: https://github.com/wpeverest/everest-forms
author: Shiva Poudel
author_date: 2019-07-12 11:39:21+05:45
msg: Fix - Security issue reported by Tin Duong on entries SQL query
repository: repo_url: https://github.com/wpeverest/everest-forms
repo_name: wpeverest/everest-forms
description: A drag and drop form builder plugin to create powerful WordPress forms in minutes.
repo_language: JavaScript
owner: wpeverest
file_changes: filename: evf-entry-functions.php
change_type: ModificationType.MODIFY
diff: @@ -75,55 +75,45 @@ function evf_search_entries( $args ) {
 		)
 	);
 
+	$statuses     = array_keys( evf_get_entry_statuses() );
+	$valid_fields = array( 'date', 'form_id', 'title', 'status' );
+
 	// Check if form ID is valid for entries.
 	if ( ! array_key_exists( $args['form_id'], evf_get_all_forms() ) ) {
 		return array();
 	}
 
-	$orderby       = isset( $args['orderby'] ) ? sanitize_key( $args['orderby'] ) : 'entry_id';
-	$order         = "ORDER BY {$orderby} " . esc_sql( strtoupper( $args['order'] ) );
-	$limit         = -1 < $args['limit'] ? $wpdb->prepare( 'LIMIT %d', $args['limit'] ) : '';
-	$offset        = 0 < $args['offset'] ? $wpdb->prepare( 'OFFSET %d', $args['offset'] ) : '';
-	$status        = ! empty( $args['status'] ) ? "AND `status` = '" . sanitize_key( $args['status'] ) . "'" : '';
-	$search        = ! empty( $args['search'] ) ? "AND `meta_value` LIKE '%" . $wpdb->esc_like( sanitize_text_field( $args['search'] ) ) . "%'" : '';
-	$include       = ! empty( $args['form_id'] ) ? "AND `form_id` = '" . absint( $args['form_id'] ) . "'" : '';
-	$exclude       = '';
-	$date_created  = '';
-	$date_modified = '';
-
-	if ( ! empty( $args['after'] ) || ! empty( $args['before'] ) ) {
-		$args['after']  = empty( $args['after'] ) ? '0000-00-00' : $args['after'];
-		$args['before'] = empty( $args['before'] ) ? current_time( 'mysql', 1 ) : $args['before'];
-
-		$date_created = "AND `date_created_gmt` BETWEEN STR_TO_DATE('" . esc_sql( $args['after'] ) . "', '%Y-%m-%d %H:%i:%s') and STR_TO_DATE('" . esc_sql( $args['before'] ) . "', '%Y-%m-%d %H:%i:%s')";
+	$query   = array();
+	$query[] = "SELECT DISTINCT {$wpdb->prefix}evf_entries.entry_id FROM {$wpdb->prefix}evf_entries INNER JOIN {$wpdb->prefix}evf_entrymeta WHERE {$wpdb->prefix}evf_entries.entry_id = {$wpdb->prefix}evf_entrymeta.entry_id";
+
+	if ( ! empty( $args['search'] ) ) {
+		$like    = '%' . $wpdb->esc_like( $args['search'] ) . '%';
+		$query[] = $wpdb->prepare( 'AND meta_value LIKE %s', $like );
 	}
 
-	if ( ! empty( $args['modified_after'] ) || ! empty( $args['modified_before'] ) ) {
-		$args['modified_after']  = empty( $args['modified_after'] ) ? '0000-00-00' : $args['modified_after'];
-		$args['modified_before'] = empty( $args['modified_before'] ) ? current_time( 'mysql', 1 ) : $args['modified_before'];
+	if ( ! empty( $args['form_id'] ) ) {
+		$query[] = $wpdb->prepare( 'AND form_id = %d', absint( $args['form_id'] ) );
+	}
 
-		$date_modified = "AND `date_modified_gmt` BETWEEN STR_TO_DATE('" . esc_sql( $args['modified_after'] ) . "', '%Y-%m-%d %H:%i:%s') and STR_TO_DATE('" . esc_sql( $args['modified_before'] ) . "', '%Y-%m-%d %H:%i:%s')";
+	if ( ! empty( $args['status'] ) ) {
+		$query[] = $wpdb->prepare( 'AND `status` = %s', isset( $statuses[ $args['status'] ] ) ? $statuses[ $args['status'] ] : 'publish' );
 	}
 
-	$query = trim(
-		"
-		SELECT DISTINCT {$wpdb->prefix}evf_entries.entry_id
-		FROM {$wpdb->prefix}evf_entries
-		INNER JOIN {$wpdb->prefix}evf_entrymeta
-		WHERE {$wpdb->prefix}evf_entries.entry_id = {$wpdb->prefix}evf_entrymeta.entry_id
-		{$status}
-		{$search}
-		{$include}
-		{$exclude}
-		{$date_created}
-		{$date_modified}
-		{$order}
-		{$limit}
-		{$offset}
-		"
-	);
+	$orderby     = in_array( $args['orderby'], $valid_fields, true ) ? $args['orderby'] : 'entry_id';
+	$order       = 'DESC' === strtoupper( $args['order'] ) ? 'DESC' : 'ASC';
+	$orderby_sql = sanitize_sql_orderby( "{$orderby} {$order}" );
+	$query[]     = "ORDER BY {$orderby_sql}";
+
+	if ( -1 < $args['limit'] ) {
+		$query[] = $wpdb->prepare( 'LIMIT %d', absint( $args['limit'] ) );
+	}
+
+	if ( 0 < $args['offset'] ) {
+		$query[] = $wpdb->prepare( 'LIMIT %d', absint( $args['offset'] ) );
+	}
 
-	$results = $wpdb->get_results( $query ); // WPCS: cache ok, DB call ok, unprepared SQL ok.
+	// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+	$results = $wpdb->get_results( implode( ' ', $query ), ARRAY_A );
 
 	$ids = wp_list_pluck( $results, 'entry_id' );
 

programming_language: PHP