AI Engine Plugin Affected by Critical Vulnerability

Published 9 January 2024
Updated 10 January 2024
Table of Contents

This blog post is about an AI Engine plugin vulnerability. If you’re an AI Engine user, please update the plugin to at least version 1.9.99.

You can sign up for the Patchstack Community plan to be notified about vulnerabilities as soon as they become disclosed.

For plugin developers, we have security audit services and Threat Intelligence Feed API for hosting companies.

About the AI Engine Plugin

The plugin AI Engine (free version), which has over 50,000 active installations, is known as the more popular AI-related plugin in WordPress.

This plugin enables us to create chatbot, craft content, coordinate AI-related work using templates, play with AI Copilot in the editor for faster work, track statistics, and more. The AI Playground offers a range of AI tools, including translation, correction, SEO, suggestions, WooCommerce product fields, and others. There is also an internal API so other plugins can tap into its capabilities.

The security vulnerability

This plugin suffers from an unauthenticated arbitrary file upload vulnerability. This vulnerability allows any unauthenticated user to upload arbitrary files, including php files, that could lead to remote code execution. The described vulnerability was fixed in version 1.9.99 and assigned CVE-2023-51409.

Unauthenticated Arbitrary File Upload

The underlying vulnerable code exists in the rest_upload function:

public function rest_upload() {
    require_once( ABSPATH . 'wp-admin/includes/image.php' );
    require_once( ABSPATH . 'wp-admin/includes/file.php' );
    require_once( ABSPATH . 'wp-admin/includes/media.php' );
    $file = $_FILES['file'];
    $error = null;
    if ( empty( $file ) ) {
			return new WP_REST_Response( [ 'success' => false, 'message' => 'No file provided.' ], 400 );
    }
    $local_upload = $this->core->get_option( 'image_local_upload' );
    $image_expires_seconds = $this->core->get_option( 'image_expires' );
    $expires = ( empty( $image_expires_seconds ) || $image_expires_seconds === 'never' ) ? null : 
      date( 'Y-m-d H:i:s', time() + $image_expires_seconds );
    $fileId = null;
    $url = null;
    if ( $local_upload === 'uploads' ) {
      if ( !$this->check_db() ) {
        return new WP_REST_Response( [ 'success' => false, 'message' => 'Could not create database table.' ], 500 );
      }
      $upload_dir = wp_upload_dir();
      $filename = wp_unique_filename( $upload_dir['path'], $file['name'] );
      $path = $upload_dir['path'] . '/' . $filename;
      if ( !move_uploaded_file( $file['tmp_name'], $path ) ) {
        return new WP_REST_Response( [ 'success' => false, 'message' => 'Could not move the file.' ], 500 );
      }
      $url = $upload_dir['url'] . '/' . $filename;
      $fileId = md5( $url );
      $this->wpdb->insert( $this->table_files, [
        'fileId' => $fileId,
        'type' => 'image',
        'status' => 'uploaded',
        'created' => date( 'Y-m-d H:i:s' ),
        'updated' => date( 'Y-m-d H:i:s' ),
        'expires' => $expires,
        'path' => $path,
        'url' => $url
      ]);
    }
    else if ( $local_upload === 'library' ) {
      $id = media_handle_upload( 'file', 0 );
      if ( is_wp_error( $id ) ) {
        $error = $id->get_error_message();
        return new WP_REST_Response([ 'success' => false, 'message' => $error ], 500);
      }
      $url = wp_get_attachment_url( $id );
      $fileId = md5( $url );
      update_post_meta( $id, '_mwai_file_id', $fileId );
      update_post_meta( $id, '_mwai_file_expires', $expires );
    }
    return new WP_REST_Response( [
			'success' => true,
			'data' => [ 'id' => $fileId, 'url' => $url ]
    ], 200 );
}

This function handles requests to the mwai-ui/v1/files/upload REST API endpoint:


public function rest_api_init() {
    register_rest_route( $this->namespace, '/files/upload', array(
          'methods' => 'POST',
          'callback' => array( $this, 'rest_upload' ),
          'permission_callback' => '__return_true'
      ) );
    register_rest_route( $this->namespace, '/files/delete', array(
          'methods' => 'POST',
          'callback' => array( $this, 'rest_delete' ),
          'permission_callback' => '__return_true'
      ) );
}

Note that the permission_callback parameter of the REST endpoint is set to __return_true which allows any unauthenticated user to trigger the rest_upload function.

Back to the rest_upload function, notice that in one of the conditions, the code will try to save the uploaded files with $path value using the move_uploaded_file function. The $path itself is constructed from $filename variable and it contains the $file['name'] value which we have full control over. Since there is no proper file type and extension validation on the function, we can simply upload an arbitrary file such as .php file to achieve RCE.

Note that this vulnerability can be reproduced from by an unauthenticated user on a default installation of the plugin without any additional conditions or requirements.

The patch

The team decided to apply a permission check to the custom REST API endpoint and also apply a check on the file type and extension using the wp_check_filetype_and_ext function. The patch can be seen below:

Conclusion

Always check every process of $_FILES parameter in the plugin or theme code. Make sure to apply a check on the filename and extension before uploading the file. Also, pay extra attention to the permission checks on the custom REST API endpoints.

Timeline

16 November, 2023We found the vulnerability and reached out to the plugin vendor.
17 November, 2023AI Engine plugin version 1.9.99 released to patch the reported issues.
09 January, 2023Added the vulnerabilities to the Patchstack vulnerability database. Security advisory article publicly released.

Help us make the Internet a safer place

Making the WordPress ecosystem more secure is a team effort, and we believe that plugin developers and security researchers should work together.

  • If you’re a plugin developer, join our mVDP program that makes it easier to report, manage and address vulnerabilities in your software.
  • If you’re a security researcher, join Patchstack Alliance to report vulnerabilities & earn rewards.

The latest in Security advisories

Looks like your browser is blocking our support chat widget. Turn off adblockers and reload the page.
crossmenu