Headline
CVE-2023-0385: AdminClass.php in custom-404-pro/tags/3.7.1/admin – WordPress Plugin Repository
The Custom 404 Pro plugin for WordPress is vulnerable to Cross-Site Request Forgery in versions up to, and including, 3.7.1. This is due to missing or incorrect nonce validation on the custom_404_pro_admin_init function. This makes it possible for unauthenticated attackers to delete logs, via forged request granted they can trick a site administrator into performing an action such as clicking on a link.
1<?php23class AdminClass {45 public function __construct() {6 $this->helpers = Helpers::singleton();7 }89 public function create_menu() {10 if(current_user_can(‘administrator’)) {11 add_menu_page( 'Custom 404 Pro’, 'Custom 404 Pro’, 'manage_options’, 'c4p-main’, array( $this, ‘page_logs’ ), ‘dashicons-chart-bar’ );12 add_submenu_page( 'c4p-main’, 'Logs’, 'Logs’, 'manage_options’, 'c4p-main’, array( $this, ‘page_logs’ ) );13 add_submenu_page( 'c4p-main’, 'Settings’, 'Settings’, 'manage_options’, 'c4p-settings’, array( $this, ‘page_settings’ ) );14 add_submenu_page( 'c4p-main’, 'About’, 'About’, 'manage_options’, 'c4p-about’, array( $this, ‘page_about’ ) );15 }16 }1718 public function page_logs() {19 include 'views/logs.php’;20 }2122 public function page_settings() {23 include 'views/settings.php’;24 }2526 public function page_about() {27 include 'views/about.php’;28 }2930 public function enqueue_styles() {31 if(current_user_can(‘administrator’)) {32 if ( array_key_exists( 'page’, $_REQUEST ) ) {33 $request = sanitize_text_field($_REQUEST[‘page’]);34 if ( $request === ‘c4p-settings’ || $request === ‘c4p-main’ || $request === ‘c4p-about’ ) {35 wp_enqueue_style( 'custom-404-pro-admin-css’, plugin_dir_url( __FILE__ ) . 'css/custom-404-pro-admin.css’, array(), ‘3.2.0’ );36 }37 }38 }39 }4041 public function enqueue_scripts() {42 if(current_user_can(‘administrator’)) {43 if ( array_key_exists( 'page’, $_REQUEST ) ) {44 $request = sanitize_text_field($_REQUEST[‘page’]);45 if ( $request === ‘c4p-settings’ || $request === ‘c4p-main’ ) {46 wp_enqueue_script( 'custom-404-pro-admin-js’, plugin_dir_url( __FILE__ ) . 'js/custom-404-pro-admin.js’, array( ‘jquery’ ), '3.2.0’, false );47 }48 }49 }50 }5152 public function custom_404_pro_notices() {53 $message = '’;54 $messageType = 'success’;55 $html = '’;56 if(current_user_can(‘administrator’)) {57 if ( array_key_exists( 'c4pmessage’, $_REQUEST ) ) {58 $message = urldecode( sanitize_text_field($_REQUEST[‘c4pmessage’]) );59 if ( array_key_exists( 'c4pmessageType’, $_REQUEST ) ) {60 $messageType = sanitize_text_field($_REQUEST[‘c4pmessageType’]);61 }62 $html .= '<div class="notice notice-' . $messageType . ' is-dismissible">’;63 $html .= ‘<p>’ . $message . '</p>’;64 $html .= '</div>’;65 echo $html;66 }67 }68 }6970 public function form_settings_global_redirect() {71 global $wpdb;72 if(wp_verify_nonce($_POST[‘form-settings-global-redirect’], ‘form-settings-global-redirect’) && check_admin_referer("form-settings-global-redirect", “form-settings-global-redirect”) && current_user_can(‘administrator’)) {73 $mode = sanitize_text_field($_POST[‘mode’]);74 $page = sanitize_text_field($_POST[‘mode_page’]);75 $url = sanitize_text_field($_POST[‘mode_url’]);76 self::update_mode( $mode, $page, $url );77 $message = urlencode( ‘Saved!’ );78 wp_redirect( admin_url( ‘admin.php?page=c4p-settings&tab=global-redirect&c4pmessage=’ . $message . ‘&c4pmessageType=success’ ) );79 }80 }8182 public function form_settings_general() {83 global $wpdb;84 if(wp_verify_nonce($_POST[‘form-settings-general’], ‘form-settings-general’) && check_admin_referer("form-settings-general", “form-settings-general”) && current_user_can(‘administrator’)) {85 $send_email = sanitize_text_field($_POST[‘send_email’]);86 $logging_enabled = sanitize_text_field($_POST[‘logging_enabled’]);87 $log_ip = sanitize_text_field($_POST[‘log_ip’]);88 $field_redirect_error_code = sanitize_text_field($_POST[‘redirect_error_code’]);89 if ( isset( $send_email ) && $send_email === ‘on’ ) {90 $field_send_email = true;91 } else {92 $field_send_email = false;93 }94 if ( isset( $logging_enabled ) && $logging_enabled === ‘enabled’ ) {95 $field_logging_enabled = true;96 } else {97 $field_logging_enabled = false;98 }99 if ( isset( $log_ip ) && $log_ip === ‘on’ ) {100 $field_log_ip = true;101 } else {102 $field_log_ip = false;103 }104 $this->helpers->update_option( 'send_email’, $field_send_email );105 $this->helpers->update_option( 'logging_enabled’, $field_logging_enabled );106 $this->helpers->update_option( 'redirect_error_code’, $field_redirect_error_code );107 // New options108 $this->helpers->upsert_option( 'log_ip’, $field_log_ip );109 $message = urlencode( ‘Saved!’ );110 wp_redirect( admin_url( ‘admin.php?page=c4p-settings&tab=general&c4pmessage=’ . $message . ‘&c4pmessageType=success’ ) );111 }112 }113114 public function custom_404_pro_admin_init() {115 global $wpdb;116 if(current_user_can(‘administrator’)) {117 if ( array_key_exists( 'action’, $_REQUEST ) ) {118 $action = sanitize_text_field($_REQUEST[‘action’]);119 if ( $action === ‘c4p-logs–delete’ ) {120 if ( array_key_exists( 'path’, $_REQUEST ) ) {121 $this->helpers->delete_logs( sanitize_url($_REQUEST[‘path’]) );122 $message = urlencode( ‘Log(s) successfully deleted!’ );123 wp_redirect( admin_url( ‘admin.php?page=c4p-main&c4pmessage=’ . $message . ‘&c4pmessageType=success’ ) );124 } else {125 $message = urlencode( ‘Please select a few logs to delete and try again.’ );126 wp_redirect( admin_url( ‘admin.php?page=c4p-main&c4pmessage=’ . $message . ‘&c4pmessageType=warning’ ) );127 }128 } elseif ( $action === ‘c4p-logs–delete-all’ ) {129 $this->helpers->delete_logs( ‘all’ );130 $message = urlencode( ‘All Logs successfully deleted!’ );131 wp_redirect( admin_url( ‘admin.php?page=c4p-main&c4pmessage=’ . $message . ‘&c4pmessageType=success’ ) );132 } elseif ( $action === ‘c4p-logs–export-csv’ ) {133 $this->helpers->export_logs_csv();134 }135 }136 }137 }138139 public function custom_404_pro_redirect() {140 global $wpdb;141 if ( is_404() ) {142 $sql = 'SELECT * FROM ' . $wpdb->prefix . $this->helpers->table_options;143 $sql_result = $wpdb->get_results( $sql );144 $row_mode = $sql_result[0];145 $row_mode_page = $sql_result[1];146 $row_mode_url = $sql_result[2];147 $row_send_email = $sql_result[3];148 $row_logging_enabled = $sql_result[4];149 $row_redirect_error_code = $sql_result[5];150 if ( $row_logging_enabled->value ) {151 self::custom_404_pro_log( $row_send_email->value );152 }153 if ( $row_mode->value === ‘page’ ) {154 $page = get_post( $row_mode_page->value );155 wp_redirect( $page->guid, $row_redirect_error_code->value );156 } elseif ( $row_mode->value === ‘url’ ) {157 wp_redirect( $row_mode_url->value, $row_redirect_error_code->value );158 }159 }160 }161162 private function custom_404_pro_log( $is_email ) {163 global $wpdb;164 if ( ! $this->helpers->is_option( ‘log_ip’ ) ) {165 $this->helpers->insert_option( 'log_ip’, true );166 }167 if ( empty( $this->helpers->get_option( ‘log_ip’ ) ) ) {168 $ip = 'N/A’;169 } else {170 if ( ! empty( $_SERVER[‘HTTP_CLIENT_IP’] ) ) {171 $ip = $_SERVER[‘HTTP_CLIENT_IP’];172 } elseif ( ! empty( $_SERVER[‘HTTP_X_FORWARDED_FOR’] ) ) {173 $ip = $_SERVER[‘HTTP_X_FORWARDED_FOR’];174 } else {175 $ip = $_SERVER[‘REMOTE_ADDR’];176 }177 }178 $path = $_SERVER[‘REQUEST_URI’];179 $referer = '’;180 if ( array_key_exists( 'HTTP_REFERER’, $_SERVER ) ) {181 $referer = $_SERVER[‘HTTP_REFERER’];182 }183 $user_agent = $_SERVER[‘HTTP_USER_AGENT’];184 $sql_save = 'INSERT INTO ' . $wpdb->prefix . $this->helpers->table_logs . " (ip, path, referer, user_agent) VALUES ('$ip’, '$path’, '$referer’, ‘$user_agent’)";185 $wpdb->query( $sql_save );186 if ( ! empty( $is_email ) ) {187 self::custom_404_pro_send_mail( $ip, $path, $referer, $user_agent );188 }189 }190191 private function custom_404_pro_send_mail( $ip, $path, $referer, $user_agent ) {192 $admin_email = get_option( ‘admin_email’ );193 if ( is_multisite() ) {194 global $blog_id;195 $current_blog_details = get_blog_details( array( ‘blog_id’ => $blog_id ) );196 $current_site_name = $current_blog_details->blogname;197 } else {198 $current_site_name = get_bloginfo( ‘name’ );199 }200 $headers[] = ‘From: Site Admin <’ . $admin_email . ‘>’ . "\r\n";201 $headers[] = 'Content-Type: text/html; charset=UTF-8’;202 $message = '<p>Here are the 404 Log Details:</p>’;203 $message .= '<table>’;204 $message .= '<tr>’;205 $message .= '<th>Site</th>’;206 $message .= ‘<td>’ . $current_site_name . '</td>’;207 $message .= '</tr>’;208 $message .= '<tr>’;209 $message .= '<th>User IP</th>’;210 $message .= ‘<td>’ . $ip . '</td>’;211 $message .= '</tr>’;212 $message .= '<tr>’;213 $message .= '<th>404 Path</th>’;214 $message .= ‘<td>’ . $path . '</td>’;215 $message .= '</tr>’;216 $message .= '<tr>’;217 $message .= '<th>Referer</th>’;218 $message .= ‘<td>’ . $referer . '</td>’;219 $message .= '</tr>’;220 $message .= '<tr>’;221 $message .= '<th>User Agent</th>’;222 $message .= ‘<td>’ . $user_agent . '</td>’;223 $message .= '</tr>’;224 $message .= '</table>’;225 $is_sent = wp_mail(226 $admin_email,227 '404 Error on Site’,228 $message,229 $headers230 );231 }232233 private function update_mode( $mode, $page, $url ) {234 global $wpdb;235 if(current_user_can(‘administrator’)) {236 $mode_val = '’;237 $mode_page_val = '’;238 $mode_url_val = '’;239 switch ( $mode ) {240 case 'page’:241 $mode_val = 'page’;242 $mode_page_val = $page;243 $mode_url_val = '’;244 break;245 case 'url’:246 $mode_val = 'url’;247 $mode_page_val = '’;248 $mode_url_val = $url;249 break;250 case '’:251 $mode_val = '’;252 $mode_page_val = '’;253 $mode_url_val = '’;254 break;255 }256 $this->helpers->update_option( 'mode’, $mode_val );257 $this->helpers->update_option( 'mode_page’, $mode_page_val );258 $this->helpers->update_option( 'mode_url’, $mode_url_val );259 }260 }261262 // Not needed for now, commenting263 // public function custom_404_pro_upgrader( $upgrader_object, $options ) {264 // global $wpdb;265 // if(current_user_can(‘administrator’)) {266 // if ( $options[‘action’] === ‘update’ && $options[‘type’] === ‘plugin’ ) {267 // if ( ! empty( get_option( ‘c4p_mode’ ) ) ) {268 // $mode = get_option( ‘c4p_mode’ );269 // $page = get_option( ‘c4p_selected_page’ );270 // $url = get_option( ‘c4p_selected_url’ );271 // self::update_mode( $mode, $page, $url );272 // delete_option( ‘c4p_mode’ );273 // delete_option( ‘c4p_selected_page’ );274 // delete_option( ‘c4p_selected_url’ );275 // }276 // // When new features are requested by customers, they usually get a new option.277 // // This is where we add new option keys when customers upgrade the plugin.278 // $this->helpers->upsert_option( 'log_ip’, true );279 // }280 // }281 // // TODO: Migrate old logs282 // }283}