diff --git a/export.php b/export.php index bbc682f2cd..0d48a3ca5d 100644 --- a/export.php +++ b/export.php @@ -14,13 +14,117 @@ require_once 'libraries/zip.lib.php'; require_once 'libraries/plugin_interface.lib.php'; /** - * Sets globals from all $_POST (in export.php only) - * Would it not be tiresome to list all export-plugin options here? + * Sets globals from $_POST + * + * - Please keep the parameters in order of their appearance in the form + * - Some of these parameters are not used, as the code below directly + * verifies from the superglobal $_POST or $_REQUEST */ -foreach ($_POST as $one_post_param => $one_post_value) { - $GLOBALS[$one_post_param] = $one_post_value; +$post_params = array( + 'db', + 'table', + 'single_table', + 'export_type', + 'export_method', + 'quick_or_custom', + 'limit_to', + 'limit_from', + 'allrows', + 'output_format', + 'filename_template', + 'remember_template', + 'charset_of_file', + 'compression', + 'what', + 'htmlword_structure_or_data', + 'htmlword_null', + 'htmlword_columns', + 'mediawiki_structure_or_data', + 'mediawiki_caption', + 'pdf_report_title', + 'pdf_structure_or_data', + 'odt_structure_or_data', + 'odt_relation', + 'odt_comments', + 'odt_mime', + 'odt_columns', + 'odt_null', + 'codegen_structure_or_data', + 'codegen_format', + 'excel_null', + 'excel_columns', + 'excel_edition', + 'excel_structure_or_data', + 'yaml_structure_or_data', + 'ods_null', + 'ods_structure_or_data', + 'ods_columns', + 'json_structure_or_data', + 'xml_structure_or_data', + 'xml_export_functions', + 'xml_export_procedures', + 'xml_export_tables', + 'xml_export_triggers', + 'xml_export_views', + 'xml_export_contents', + 'texytext_structure_or_data', + 'texytext_columns', + 'texytext_null', + 'phparray_structure_or_data', + 'sql_include_comments', + 'sql_header_comment', + 'sql_dates', + 'sql_relation', + 'sql_mime', + 'sql_use_transaction', + 'sql_disable_fk', + 'sql_compatibility', + 'sql_structure_or_data', + 'sql_drop_table', + 'sql_procedure_function', + 'sql_create_table_statements', + 'sql_if_not_exists', + 'sql_auto_increment', + 'sql_backquotes', + 'sql_truncate', + 'sql_delayed', + 'sql_ignore', + 'sql_type', + 'sql_insert_syntax', + 'sql_max_query_size', + 'sql_hex_for_blob', + 'sql_utc_time', + 'csv_separator', + 'csv_enclosed', + 'csv_escaped', + 'csv_terminated', + 'csv_null', + 'csv_columns', + 'csv_structure_or_data', + 'latex_caption', + 'latex_structure_or_data', + 'latex_structure_caption', + 'latex_structure_continued_caption', + 'latex_structure_label', + 'latex_relation', + 'latex_comments', + 'latex_mime', + 'latex_columns', + 'latex_data_caption', + 'latex_data_continued_caption', + 'latex_data_label', + 'latex_null' +); + +foreach ($post_params as $one_post_param) { + if (isset($_POST[$one_post_param])) { + $GLOBALS[$one_post_param] = $_POST[$one_post_param]; + } } +// sanitize this parameter which will be used below in a file inclusion +$what = PMA_securePath($what); + PMA_Util::checkParameters(array('what', 'export_type')); // export class instance, not array of properties, as before @@ -352,7 +456,9 @@ if ($asfile) { } } $filename = PMA_Util::expandUserString($filename_template); - $filename = PMA_sanitizeFilename($filename); + // remove dots in filename (coming from either the template or already + // part of the filename) to avoid a remote code execution vulnerability + $filename = PMA_sanitizeFilename($filename, $replaceDots = true); // Grab basic dump extension and mime type // Check if the user already added extension; diff --git a/libraries/Tracker.class.php b/libraries/Tracker.class.php index 2d33a24580..4e67c08a21 100644 --- a/libraries/Tracker.class.php +++ b/libraries/Tracker.class.php @@ -941,6 +941,9 @@ class PMA_Tracker if (empty($dbname)) { return; } + // Remove null bytes (preg_replace() is vulnerable in some + // PHP versions) + $dbname = str_replace("\0", "", $dbname); // If we found a valid statement if (isset($result['identifier'])) { diff --git a/libraries/mult_submits.inc.php b/libraries/mult_submits.inc.php index 181bfc1aa3..7423749f9d 100644 --- a/libraries/mult_submits.inc.php +++ b/libraries/mult_submits.inc.php @@ -12,6 +12,7 @@ require_once 'libraries/transformations.lib.php'; $request_params = array( 'clause_is_unique', + 'from_prefix', 'goto', 'mult_btn', 'original_sql_query', @@ -24,6 +25,7 @@ $request_params = array( 'sql_query', 'submit_mult', 'table_type', + 'to_prefix', 'url_query' ); @@ -481,15 +483,30 @@ if (!empty($submit_mult) && !empty($what)) { case 'replace_prefix_tbl': $current = $selected[$i]; - $newtablename = preg_replace("/^" . $_POST['from_prefix'] . "/", $_POST['to_prefix'], $current); - $a_query = 'ALTER TABLE ' . PMA_Util::backquote($selected[$i]) . ' RENAME ' . PMA_Util::backquote($newtablename); // CHANGE PREFIX PATTERN + if (substr($current, 0, strlen($from_prefix)) == $from_prefix) { + $newtablename = $to_prefix . substr($current, strlen($from_prefix)); + } else { + $newtablename = $current; + } + $a_query = 'ALTER TABLE ' + . PMA_Util::backquote($selected[$i]) + . ' RENAME ' + . PMA_Util::backquote($newtablename) ; // CHANGE PREFIX PATTERN $run_parts = true; break; case 'copy_tbl_change_prefix': $current = $selected[$i]; - $newtablename = preg_replace("/^" . $_POST['from_prefix'] . "/", $_POST['to_prefix'], $current); - $a_query = 'CREATE TABLE ' . PMA_Util::backquote($newtablename) . ' SELECT * FROM ' . PMA_Util::backquote($selected[$i]); // COPY TABLE AND CHANGE PREFIX PATTERN + if (substr($current, 0, strlen($from_prefix)) == $from_prefix) { + $newtablename = $to_prefix . substr($current, strlen($from_prefix)); + } else { + $newtablename = $current; + } + $newtablename = $to_prefix . substr($current, strlen($from_prefix)); + $a_query = 'CREATE TABLE ' + . PMA_Util::backquote($newtablename) + . ' SELECT * FROM ' + . PMA_Util::backquote($selected[$i]) ; // COPY TABLE AND CHANGE PREFIX PATTERN $run_parts = true; break; diff --git a/libraries/sanitizing.lib.php b/libraries/sanitizing.lib.php index 053c4cc048..0b107e263a 100644 --- a/libraries/sanitizing.lib.php +++ b/libraries/sanitizing.lib.php @@ -159,20 +159,30 @@ function PMA_sanitize($message, $escape = false, $safe = false) /** - * Sanitize a filename by removing anything besides A-Za-z0-9_.- + * Sanitize a filename by removing anything besides legit characters * * Intended usecase: * When using a filename in a Content-Disposition header * the value should not contain ; or " * - * @param string $filename The filename + * When exporting, avoiding generation of an unexpected double-extension file + * + * @param string $filename The filename + * @param boolean $replaceDots Whether to also replace dots * * @return string the sanitized filename * */ -function PMA_sanitizeFilename($filename) +function PMA_sanitizeFilename($filename, $replaceDots = false) { - $filename = preg_replace('/[^A-Za-z0-9_.-]/', '_', $filename); + $pattern = '/[^A-Za-z0-9_'; + // if we don't have to replace dots + if (! $replaceDots) { + // then add the dot to the list of legit characters + $pattern .= '.'; + } + $pattern .= '-]/'; + $filename = preg_replace($pattern, '_', $filename); return $filename; }