Headline
CVE-2022-4290: cyr-to-lat.php in cyr3lat/trunk – WordPress Plugin Repository
The Cyr to Lat plugin for WordPress is vulnerable to authenticated SQL Injection via the ‘ctl_sanitize_title’ function in versions up to, and including, 3.5 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This potentially allows authenticated users with the ability to add or modify terms or tags to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database. A partial patch became available in version 3.6 and the issue was fully patched in version 3.7.
1<?php2/*3Plugin Name: Cyr to Lat enhanced4Plugin URI: http://wordpress.org/plugins/cyr3lat/5Description: Converts Cyrillic, European and Georgian characters in post, term slugs and media file names to Latin characters. Useful for creating human-readable URLs. Based on the original plugin by Anton Skorobogatov.6Author: Sol, Sergey Biryukov, Nikolay Karev, Dmitri Gogelia7Author URI: http://karevn.com/8Version: 3.59*/ 1011function ctl_sanitize_title($title) {12 global $wpdb;1314 $iso9_table = array(15 ‘А’ => 'A’, ‘Б’ => 'B’, ‘В’ => 'V’, ‘Г’ => 'G’, ‘Ѓ’ => 'G’,16 ‘Ґ’ => 'G’, ‘Д’ => 'D’, ‘Е’ => 'E’, ‘Ё’ => 'YO’, ‘Є’ => 'YE’,17 ‘Ж’ => 'ZH’, ‘З’ => 'Z’, ‘Ѕ’ => 'Z’, ‘И’ => 'I’, ‘Й’ => 'J’,18 ‘Ј’ => 'J’, ‘І’ => 'I’, ‘Ї’ => 'YI’, ‘К’ => 'K’, ‘Ќ’ => 'K’,19 ‘Л’ => 'L’, ‘Љ’ => 'L’, ‘М’ => 'M’, ‘Н’ => 'N’, ‘Њ’ => 'N’,20 ‘О’ => 'O’, ‘П’ => 'P’, ‘Р’ => 'R’, ‘С’ => 'S’, ‘Т’ => 'T’,21 ‘У’ => 'U’, ‘Ў’ => 'U’, ‘Ф’ => 'F’, ‘Х’ => 'H’, ‘Ц’ => 'TS’,22 ‘Ч’ => 'CH’, ‘Џ’ => 'DH’, ‘Ш’ => 'SH’, ‘Щ’ => 'SHH’, ‘Ъ’ => '’,23 ‘Ы’ => 'Y’, ‘Ь’ => '’, ‘Э’ => 'E’, ‘Ю’ => 'YU’, ‘Я’ => 'YA’,24 ‘а’ => 'a’, ‘б’ => 'b’, ‘в’ => 'v’, ‘г’ => 'g’, ‘ѓ’ => 'g’,25 ‘ґ’ => 'g’, ‘д’ => 'd’, ‘е’ => 'e’, ‘ё’ => 'yo’, ‘є’ => 'ye’,26 ‘ж’ => 'zh’, ‘з’ => 'z’, ‘ѕ’ => 'z’, ‘и’ => 'i’, ‘й’ => 'j’,27 ‘ј’ => 'j’, ‘і’ => 'i’, ‘ї’ => 'yi’, ‘к’ => 'k’, ‘ќ’ => 'k’,28 ‘л’ => 'l’, ‘љ’ => 'l’, ‘м’ => 'm’, ‘н’ => 'n’, ‘њ’ => 'n’,29 ‘о’ => 'o’, ‘п’ => 'p’, ‘р’ => 'r’, ‘с’ => 's’, ‘т’ => 't’,30 ‘у’ => 'u’, ‘ў’ => 'u’, ‘ф’ => 'f’, ‘х’ => 'h’, ‘ц’ => 'ts’,31 ‘ч’ => 'ch’, ‘џ’ => 'dh’, ‘ш’ => 'sh’, ‘щ’ => 'shh’, ‘ъ’ => '’,32 ‘ы’ => 'y’, ‘ь’ => '’, ‘э’ => 'e’, ‘ю’ => 'yu’, ‘я’ => 'ya’33 );34 $geo2lat = array(35 ‘ა’ => 'a’, ‘ბ’ => 'b’, ‘გ’ => 'g’, ‘დ’ => 'd’, ‘ე’ => 'e’, ‘ვ’ => 'v’,36 ‘ზ’ => 'z’, ‘თ’ => 'th’, ‘ი’ => 'i’, ‘კ’ => 'k’, ‘ლ’ => 'l’, ‘მ’ => 'm’,37 ‘ნ’ => 'n’, ‘ო’ => 'o’, ‘პ’ => ‘p’,’ჟ’ => ‘zh’,’რ’ => ‘r’,’ს’ => 's’,38 ‘ტ’ => ‘t’,’უ’ => ‘u’,’ფ’ => ‘ph’,’ქ’ => ‘q’,’ღ’ => ‘gh’,’ყ’ => 'qh’,39 ‘შ’ => ‘sh’,’ჩ’ => ‘ch’,’ც’ => ‘ts’,’ძ’ => ‘dz’,’წ’ => ‘ts’,’ჭ’ => 'tch’,40 ‘ხ’ => ‘kh’,’ჯ’ => ‘j’,’ჰ’ => 'h’41 );42 $iso9_table = array_merge($iso9_table, $geo2lat);4344 $locale = get_locale();45 switch ( $locale ) {46 case 'bg_BG’:47 $iso9_table[‘Щ’] = 'SHT’;48 $iso9_table[‘щ’] = 'sht’; 49 $iso9_table[‘Ъ’] = 'A’;50 $iso9_table[‘ъ’] = 'a’;51 break;52 case 'uk’:53 case 'uk_ua’:54 case 'uk_UA’:55 $iso9_table[‘И’] = 'Y’;56 $iso9_table[‘и’] = 'y’;57 break;58 }5960 $is_term = false;61 $backtrace = debug_backtrace();62 foreach ( $backtrace as $backtrace_entry ) {63 if ( $backtrace_entry[‘function’] == ‘wp_insert_term’ ) {64 $is_term = true;65 break;66 }67 }6869 $term = $is_term ? $wpdb->get_var(“SELECT slug FROM {$wpdb->terms} WHERE name = '$title’”) : '’;70 if ( empty($term) ) {71 $title = strtr($title, apply_filters('ctl_table’, $iso9_table));72 if (function_exists(‘iconv’)){73 $title = iconv('UTF-8’, 'UTF-8//TRANSLIT//IGNORE’, $title);74 }75 $title = preg_replace("/[^A-Za-z0-9’_\-\.]/", '-', $title);76 $title = preg_replace('/\-+/’, '-', $title);77 $title = preg_replace('/^-+/’, '’, $title);78 $title = preg_replace('/-+$/’, '’, $title);79 } else {80 $title = $term;81 }8283 return $title;84}85add_filter('sanitize_title’, 'ctl_sanitize_title’, 9);86add_filter(‘sanitize_file_name’, ‘ctl_sanitize_title’);8788function ctl_convert_existing_slugs() {89 global $wpdb;9091 $posts = $wpdb->get_results("SELECT ID, post_name FROM {$wpdb->posts} WHERE post_name REGEXP('[^A-Za-z0-9\-]+’) AND post_status IN ('publish’, 'future’, ‘private’)");92 foreach ( (array) $posts as $post ) {93 $sanitized_name = ctl_sanitize_title(urldecode($post->post_name));94 if ( $post->post_name != $sanitized_name ) {95 add_post_meta($post->ID, ‘_wp_old_slug’, $post->post_name);96 $wpdb->update($wpdb->posts, array( ‘post_name’ => $sanitized_name ), array( ‘ID’ => $post->ID ));97 }98 }99100 $terms = $wpdb->get_results("SELECT term_id, slug FROM {$wpdb->terms} WHERE slug REGEXP('[^A-Za-z0-9\-]+’) ");101 foreach ( (array) $terms as $term ) {102 $sanitized_slug = ctl_sanitize_title(urldecode($term->slug));103 if ( $term->slug != $sanitized_slug ) {104 $wpdb->update($wpdb->terms, array( ‘slug’ => $sanitized_slug ), array( ‘term_id’ => $term->term_id ));105 }106 }107}108109function ctl_schedule_conversion() {110 add_action('shutdown’, ‘ctl_convert_existing_slugs’);111}112register_activation_hook(__FILE__, ‘ctl_schedule_conversion’);