Headline
CVE-2023-1169: upload-class.php in ooohboi-steroids-for-elementor/tags/2.1.3/inc/exopite-simple-options – WordPress Plugin Repository
The OoohBoi Steroids for Elementor plugin for WordPress is vulnerable to missing authorization due to a missing capability check on the ‘file_uploader_callback’ function in versions up to, and including, 2.1.4. This makes it possible for subscriber-level attackers to upload image attachments to the site.
1<?php if ( ! defined( ‘ABSPATH’ ) ) {2 die;3} // Cannot access pages directly.4/**5 * Chunks6 * - https://docs.fineuploader.com/features/chunking.html7 */8/**9 *10 * Options Class11 *12 * @since 1.0.013 * @version 1.0.014 *15 */16if ( ! class_exists( ‘Exopite_Simple_Options_Framework_Upload’ ) ) {1718 class Exopite_Simple_Options_Framework_Upload {1920 public static function add_hooks() {2122 add_action( 'wp_ajax_exopite-sof-file_uploader’, array(23 'Exopite_Simple_Options_Framework_Upload’,24 'file_uploader_callback’25 ) );26 add_action( 'wp_ajax_exopite-sof-file-batch-delete’, array(27 'Exopite_Simple_Options_Framework_Upload’,28 'file_batch_delete_callback’29 ) );3031 }3233 //DEGUB34 public static function write_log( $type, $log_line ) {3536 $hash = '’;37 $fn = plugin_dir_path( __FILE__ ) . ‘/’ . $type . '-' . $hash . '.log’;38 $log_in_file = file_put_contents( $fn, date( ‘Y-m-d H:i:s’ ) . ' - ' . $log_line . PHP_EOL, FILE_APPEND );3940 }4142 /**43 * Handle Response44 *45 * Code based on:46 * @link https://wordpress.org/plugins/wp-multi-file-uploader/47 * @link https://wordpress.org/plugins/wp-dropzone/48 */49 public static function file_uploader_callback() {5051 // file_put_contents( dirname(__FILE__) . '\test.log’, var_export( $_POST, true ) . PHP_EOL . PHP_EOL, FILE_APPEND );5253 if ( strtoupper( sanitize_key( $_POST[‘_method’] ) ) == ‘DELETE’ && isset( $_POST[‘qquuid’] ) ) {5455 /**56 * Delete file on AJAX request with qquuid57 *58 * @link https://docs.fineuploader.com/features/delete.html59 * @link https://docs.fineuploader.com/branch/master/api/options.html#deleteFile60 */61 $result = self::file_delete();6263// $protocol = ( isset( $_SERVER[‘SERVER_PROTOCOL’] ) ? $_SERVER[‘SERVER_PROTOCOL’] : ‘HTTP/1.0’ );64 $protocol = wp_get_server_protocol();6566 header( $protocol . $result );6768 } else {6970 $result = self::file_uploader();7172 header( “Content-Type: text/plain” );73 echo json_encode( $result );7475 }7677 die();7879 }8081 public static function file_batch_delete_callback() {8283 $deleted = array();8485 if ( isset( $_POST[‘media-ids’] ) && is_array( $_POST[‘media-ids’] ) ) {8687 // Sanitize attachment ids, these should be absint88 $attachment_id_array = array_map( 'absint’, $_POST[‘media-ids’] );899091 foreach ( $attachment_id_array as $attachmentid ) {9293 $deleted_item = wp_delete_attachment( $attachmentid, true );94 $deleted[] = $deleted_item->ID;9596 }9798 }99100 die( json_encode( $deleted ) );101102 }103104 public static function file_delete() {105106 /**107 * Query attachments108 *109 * @link https://gist.github.com/BronsonQuick/1971067110 */111 $args = array(112 ‘post_type’ => 'attachment’,113 ‘numberposts’ => 1,114 ‘post_status’ => null,115 ‘meta_query’ => array(116 array(117 ‘key’ => 'qquuid’,118 ‘value’ => sanitize_text_field( $_POST[‘qquuid’] ),119 ‘compare’ => '=’,120 )121 )122123 );124125 $attachments = get_posts( $args );126127 if ( $attachments ) {128129 foreach ( $attachments as $attachment ) {130131 if ( wp_delete_post( $attachment->ID, true ) ) {132133 return ' 200 OK’;134135 }136 }137138 }139140 // Restore original post data.141 wp_reset_postdata();142 wp_reset_query();143144 return ' 404 Not Found’;145146 }147148 public static function file_uploader() {149150 // Make sure all files are allowed151 if ( ! self::check_file_type( $_FILES[‘qqfile’][‘name’] ) ) {152153 return array( ‘error’ => ‘Unsupported file type’ );154155 }156157 // Including file library if not exist158 if ( ! function_exists( ‘wp_handle_upload’ ) ) {159160 require_once ABSPATH . 'wp-admin/includes/file.php’;161162 }163164 if ( isset( $_POST[‘qqfilename’] ) ) {165 $_FILES[‘qqfile’][‘name’] = sanitize_file_name( $_POST[‘qqfilename’] );166 }167168 // Uploading file to server169 $uploaded_file = $_FILES[‘qqfile’];170171 $upload_overrides = array( ‘test_form’ => false );172 $movefile = wp_handle_upload( $uploaded_file, $upload_overrides );173174 if ( $movefile ) {175176 $wp_upload_dir = wp_upload_dir();177 $filename = str_replace( $wp_upload_dir[‘url’] . '/’, '’, $movefile[‘url’] );178179 $attachment = self::add_attachment( $movefile[‘url’], $movefile[‘file’] );180181 // Add qquuid for possibility to delete with AJAX fine uploader182 update_post_meta( $attachment, 'qquuid’, sanitize_text_field( $_POST[‘qquuid’] ) );183184 // Generate ALT attribute based on file name185 $alt = substr( $_FILES[‘qqfile’][‘name’], 0, strrpos( $_FILES[‘qqfile’][‘name’], “.” ) );186 $alt = sanitize_title( $alt );187 $alt = str_replace( '-', ' ', $alt );188 $alt = ucwords( strtolower( $alt ) );189 update_post_meta( $attachment, '_wp_attachment_image_alt’, $alt );190191 return array(192 ‘success’ => $movefile,193 ‘attachmentId’ => $attachment194 );195196 } else {197198 return array( ‘error’ => ‘Can not move file’ );199200 }201202 return array( ‘error’ => ‘General error’ );203204 }205206 /**207 * Handle Attachment208 */209 public static function add_attachment( $url, $filepath ) {210211 $updated_url = trim( substr( $url, strpos( $url, ‘uploads/’) + 8 ));212213 $wp_upload_dir = wp_upload_dir();214 $filename = str_replace( $wp_upload_dir[‘url’] . '/’, '’, $url );215216 $wp_filetype = wp_check_filetype( basename( $filename ), null );217218 $attachment = array(219 ‘guid’ => $url,220 ‘post_mime_type’ => $wp_filetype[‘type’],221 ‘post_title’ => preg_replace( '/\.[^.]+$/’, '’, basename( $filename ) ),222 ‘post_content’ => '’,223 ‘post_status’ => 'inherit’224 );225226 // Attach file to post if attach is true and it is uploaded in a post (metabox)227 $post_id = ( isset( $_POST[‘postId’] ) ) ? intval( $_POST[‘postId’] ) : 0;228 $attach_id = wp_insert_attachment( $attachment, $updated_url, $post_id );229230 // Determines if attachment is an image.231 if ( wp_attachment_is_image( $attach_id ) ) {232233 if ( ! function_exists( ‘wp_generate_attachment_metadata’ ) || ! function_exists( ‘wp_update_attachment_metadata’ ) ) {234235 require_once( ABSPATH . ‘wp-admin/includes/image.php’ );236237 }238239 $attach_data = wp_generate_attachment_metadata( $attach_id, $filepath );240241 wp_update_attachment_metadata( $attach_id, $attach_data );242243 }244245 return $attach_id;246 }247248 /**249 * Create Image Sizes250 */251 function create_image_sizes( $filepath ) {252 $sizes = array();253254 foreach ( get_intermediate_image_sizes() as $size ) {255256 $sizes[ $size ] = array( ‘width’ => '’, ‘height’ => '’, ‘crop’ => true );257 $sizes[ $size ][‘width’] = get_option( “{$size}_size_w” ); // For default sizes set in options258 $sizes[ $size ][‘height’] = get_option( “{$size}_size_h” ); // For default sizes set in options259 $sizes[ $size ][‘crop’] = get_option( “{$size}_crop” ); // For default sizes set in options260261 }262263 $sizes = apply_filters( 'intermediate_image_sizes_advanced’, $sizes );264265 $metadata = array();266267 foreach ( $sizes as $size => $size_data ) {268269 $resized = image_make_intermediate_size(270 $filepath,271 $size_data[‘width’],272 $size_data[‘height’],273 $size_data[‘crop’]274 );275276 if ( $resized ) {277278 $metadata[ $size ] = $resized;279280 }281282 }283284 return $metadata;285 }286287 /**288 * Get WordPress Default Allowed Mime Types289 *290 * @since 1.1.0291 */292 public static function allowed_mime_types() {293 // Work through the WordPress supported mime types294 $wp_allowed_mime_types = get_allowed_mime_types();295296 $allowed_mime_types = array();297298 foreach ( $wp_allowed_mime_types as $key => $value ) {299300 $mime_types = explode( '|’, $key );301302 // Build mime types array out of WordPress allowed mime types303 foreach ( $mime_types as $mime_type ) {304305 $allowed_mime_types[] = $mime_type;306307 }308309 }310311312 return $allowed_mime_types;313 }314315 /**316 * Check File Type317 */318 public static function check_file_type( $file_name ) {319320 // Get file info321 $filetype = wp_check_filetype( $file_name );322323 // Get from options324 $allowed_exts = self::allowed_mime_types();325326 // Make sure the file type is allowed327 $ext = ( isset( $filetype[‘ext’] ) ) ? strtolower( $filetype[‘ext’] ) : '’;328329 if ( in_array( $ext, $allowed_exts ) ) {330331 return true;332333 }334335 return false;336 }337338 /*339 * Returns a file size limit in bytes based on the PHP upload_max_filesize and post_max_size340 *341 * @link https://stackoverflow.com/questions/13076480/php-get-actual-maximum-upload-size/25370978#25370978342 */343 public static function file_upload_max_size() {344 static $max_size = - 1;345346 if ( $max_size < 0 ) {347348 // Start with post_max_size.349 $post_max_size = self::parse_size( ini_get( ‘post_max_size’ ) );350351 if ( $post_max_size > 0 ) {352353 $max_size = $post_max_size;354355 }356357 // If upload_max_size is less, then reduce. Except if upload_max_size is358 // zero, which indicates no limit.359 $upload_max = self::parse_size( ini_get( ‘upload_max_filesize’ ) );360 if ( $upload_max > 0 && $upload_max < $max_size ) {361362 $max_size = $upload_max;363364 }365366 }367368 return $max_size;369 }370371 public static function parse_size( $size ) {372373 $unit = preg_replace( '/[^bkmgtpezy]/i’, '’, $size ); // Remove the non-unit characters from the size.374 $size = preg_replace( '/[^0-9\.]/’, '’, $size ); // Remove the non-numeric characters from the size.375376 if ( $unit ) {377378 // Find the position of the unit in the ordered string which is the power of magnitude to multiply a kilobyte by.379 return round( $size * pow( 1024, stripos( 'bkmgtpezy’, $unit[0] ) ) );380381 } else {382383 return round( $size );384385 }386387 }388389 }390391}