VYPR
Moderate severityNVD Advisory· Published Dec 11, 2018· Updated Aug 5, 2024

CVE-2018-19968

CVE-2018-19968

Description

An attacker can exploit phpMyAdmin before 4.8.4 to leak the contents of a local file because of an error in the transformation feature. The attacker must have access to the phpMyAdmin Configuration Storage tables, although these can easily be created in any database to which the attacker has access. An attacker must have valid credentials to log in to phpMyAdmin; this vulnerability does not allow an attacker to circumvent the login system.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

An authenticated user of phpMyAdmin before 4.8.4 can read arbitrary local files by exploiting an error in the transformation feature.

Vulnerability

In phpMyAdmin versions prior to 4.8.4, the transformEditedValues() method directly included a transformation plugin file based on user-controllable input without verifying that the file is a valid transformation plugin. The code path is reachable when an attacker has access to the phpMyAdmin Configuration Storage tables (the pma__column_info table) and can log in to phpMyAdmin with valid credentials. The commit 6a1ba61 changes the logic to check for the existence of the expected class before including and instantiating the plugin, preventing arbitrary file inclusion [1][2][4].

Exploitation

The attacker needs valid login credentials for phpMyAdmin. They must also have the ability to insert or modify rows in the pma__column_info table (part of the phpMyAdmin Configuration Storage). By crafting a malicious entry in that table with a file path to a local system file, and then triggering a transformation action on a column, include_once would load the specified file, causing its contents to be leaked to the attacker through subsequent output. No additional race condition or user interaction beyond the normal edit operation is required [1][4].

Impact

Successful exploitation allows an authenticated attacker to read arbitrary local files that the web server process has access to. This can lead to disclosure of database credentials, configuration files, or any other sensitive data present on the server. The attacker does not gain code execution or privilege escalation from this vulnerability alone [1][4].

Mitigation

Users should upgrade to phpMyAdmin 4.8.4 or later, which was released on 2018-12-07 and includes the fix [2][4]. For systems that cannot upgrade immediately, the recommended workaround is to ensure that phpMyAdmin Configuration Storage tables are not writable by untrusted users, but this only reduces, not eliminates, the risk. The vulnerability is not known to be listed in CISA's KEV as of the publication date [4].

AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
phpmyadmin/phpmyadminPackagist
< 4.8.44.8.4

Affected products

3

Patches

1
6a1ba61e2900

Remove transformation plugin includes

https://github.com/phpmyadmin/phpmyadminMaurício Meneghini FauthNov 27, 2018via ghsa
5 files changed · +152 96
  • libraries/classes/Display/Results.php+19 18 modified
    @@ -2893,28 +2893,29 @@ private function _getRowValues(
     
                         if (@file_exists($include_file)) {
     
    -                        include_once $include_file;
                             $class_name = Transformations::getClassName($include_file);
    -                        // todo add $plugin_manager
    -                        $plugin_manager = null;
    -                        $transformation_plugin = new $class_name(
    -                            $plugin_manager
    -                        );
    +                        if (class_exists($class_name)) {
    +                            // todo add $plugin_manager
    +                            $plugin_manager = null;
    +                            $transformation_plugin = new $class_name(
    +                                $plugin_manager
    +                            );
     
    -                        $transform_options = Transformations::getOptions(
    -                            isset(
    -                                $mime_map[$orgFullColName]
    +                            $transform_options = Transformations::getOptions(
    +                                isset(
    +                                    $mime_map[$orgFullColName]
    +                                    ['transformation_options']
    +                                )
    +                                ? $mime_map[$orgFullColName]
                                     ['transformation_options']
    -                            )
    -                            ? $mime_map[$orgFullColName]
    -                            ['transformation_options']
    -                            : ''
    -                        );
    +                                : ''
    +                            );
     
    -                        $meta->mimetype = str_replace(
    -                            '_', '/',
    -                            $mime_map[$orgFullColName]['mimetype']
    -                        );
    +                            $meta->mimetype = str_replace(
    +                                '_', '/',
    +                                $mime_map[$orgFullColName]['mimetype']
    +                            );
    +                        }
     
                         } // end if file_exists
                     } // end if transformation is set
    
  • libraries/classes/InsertEdit.php+50 48 modified
    @@ -2478,7 +2478,6 @@ public function transformEditedValues(
         ) {
             $include_file = 'libraries/classes/Plugins/Transformations/' . $file;
             if (is_file($include_file)) {
    -            include_once $include_file;
                 $_url_params = array(
                     'db'            => $db,
                     'table'         => $table,
    @@ -2492,20 +2491,22 @@ public function transformEditedValues(
                 );
                 $transform_options['wrapper_link'] = Url::getCommon($_url_params);
                 $class_name = Transformations::getClassName($include_file);
    -            /** @var TransformationsPlugin $transformation_plugin */
    -            $transformation_plugin = new $class_name();
    -
    -            foreach ($edited_values as $cell_index => $curr_cell_edited_values) {
    -                if (isset($curr_cell_edited_values[$column_name])) {
    -                    $edited_values[$cell_index][$column_name]
    -                        = $extra_data['transformations'][$cell_index]
    -                            = $transformation_plugin->applyTransformation(
    -                                $curr_cell_edited_values[$column_name],
    -                                $transform_options,
    -                                ''
    -                            );
    -                }
    -            }   // end of loop for each transformation cell
    +            if (class_exists($class_name)) {
    +                /** @var TransformationsPlugin $transformation_plugin */
    +                $transformation_plugin = new $class_name();
    +
    +                foreach ($edited_values as $cell_index => $curr_cell_edited_values) {
    +                    if (isset($curr_cell_edited_values[$column_name])) {
    +                        $edited_values[$cell_index][$column_name]
    +                            = $extra_data['transformations'][$cell_index]
    +                                = $transformation_plugin->applyTransformation(
    +                                    $curr_cell_edited_values[$column_name],
    +                                    $transform_options,
    +                                    ''
    +                                );
    +                    }
    +                }   // end of loop for each transformation cell
    +            }
             }
             return $extra_data;
         }
    @@ -3268,42 +3269,43 @@ private function getHtmlForInsertEditFormColumn(
                 $file = $column_mime['input_transformation'];
                 $include_file = 'libraries/classes/Plugins/Transformations/' . $file;
                 if (is_file($include_file)) {
    -                include_once $include_file;
                     $class_name = Transformations::getClassName($include_file);
    -                $transformation_plugin = new $class_name();
    -                $transformation_options = Transformations::getOptions(
    -                    $column_mime['input_transformation_options']
    -                );
    -                $_url_params = array(
    -                    'db'            => $db,
    -                    'table'         => $table,
    -                    'transform_key' => $column['Field'],
    -                    'where_clause'  => $where_clause
    -                );
    -                $transformation_options['wrapper_link']
    -                    = Url::getCommon($_url_params);
    -                $current_value = '';
    -                if (isset($current_row[$column['Field']])) {
    -                    $current_value = $current_row[$column['Field']];
    -                }
    -                if (method_exists($transformation_plugin, 'getInputHtml')) {
    -                    $transformed_html = $transformation_plugin->getInputHtml(
    -                        $column,
    -                        $row_id,
    -                        $column_name_appendix,
    -                        $transformation_options,
    -                        $current_value,
    -                        $text_dir,
    -                        $tabindex,
    -                        $tabindex_for_value,
    -                        $idindex
    +                if (class_exists($class_name)) {
    +                    $transformation_plugin = new $class_name();
    +                    $transformation_options = Transformations::getOptions(
    +                        $column_mime['input_transformation_options']
                         );
    -                }
    -                if (method_exists($transformation_plugin, 'getScripts')) {
    -                    $GLOBALS['plugin_scripts'] = array_merge(
    -                        $GLOBALS['plugin_scripts'],
    -                        $transformation_plugin->getScripts()
    +                    $_url_params = array(
    +                        'db'            => $db,
    +                        'table'         => $table,
    +                        'transform_key' => $column['Field'],
    +                        'where_clause'  => $where_clause
                         );
    +                    $transformation_options['wrapper_link']
    +                        = Url::getCommon($_url_params);
    +                    $current_value = '';
    +                    if (isset($current_row[$column['Field']])) {
    +                        $current_value = $current_row[$column['Field']];
    +                    }
    +                    if (method_exists($transformation_plugin, 'getInputHtml')) {
    +                        $transformed_html = $transformation_plugin->getInputHtml(
    +                            $column,
    +                            $row_id,
    +                            $column_name_appendix,
    +                            $transformation_options,
    +                            $current_value,
    +                            $text_dir,
    +                            $tabindex,
    +                            $tabindex_for_value,
    +                            $idindex
    +                        );
    +                    }
    +                    if (method_exists($transformation_plugin, 'getScripts')) {
    +                        $GLOBALS['plugin_scripts'] = array_merge(
    +                            $GLOBALS['plugin_scripts'],
    +                            $transformation_plugin->getScripts()
    +                        );
    +                    }
                     }
                 }
             }
    
  • libraries/classes/Transformations.php+12 10 modified
    @@ -181,33 +181,35 @@ public static function getClassName($filename)
          *
          * @param string $file transformation file
          *
    -     * @return String the description of the transformation
    +     * @return string the description of the transformation
          */
         public static function getDescription($file)
         {
             $include_file = 'libraries/classes/Plugins/Transformations/' . $file;
    -        /* @var $class_name PhpMyAdmin\Plugins\TransformationsInterface */
    +        /* @var $class_name \PhpMyAdmin\Plugins\TransformationsInterface */
             $class_name = self::getClassName($include_file);
    -        // include and instantiate the class
    -        include_once $include_file;
    -        return $class_name::getInfo();
    +        if (class_exists($class_name)) {
    +            return $class_name::getInfo();
    +        }
    +        return '';
         }
     
         /**
          * Returns the name of the transformation
          *
          * @param string $file transformation file
          *
    -     * @return String the name of the transformation
    +     * @return string the name of the transformation
          */
         public static function getName($file)
         {
             $include_file = 'libraries/classes/Plugins/Transformations/' . $file;
    -        /* @var $class_name PhpMyAdmin\Plugins\TransformationsInterface */
    +        /* @var $class_name \PhpMyAdmin\Plugins\TransformationsInterface */
             $class_name = self::getClassName($include_file);
    -        // include and instantiate the class
    -        include_once $include_file;
    -        return $class_name::getName();
    +        if (class_exists($class_name)) {
    +            return $class_name::getName();
    +        }
    +        return '';
         }
     
         /**
    
  • tbl_replace.php+21 20 modified
    @@ -224,28 +224,29 @@
                 $filename = 'libraries/classes/Plugins/Transformations/'
                     . $mime_map[$column_name]['input_transformation'];
                 if (is_file($filename)) {
    -                include_once $filename;
                     $classname = Transformations::getClassName($filename);
    -                /** @var IOTransformationsPlugin $transformation_plugin */
    -                $transformation_plugin = new $classname();
    -                $transformation_options = Transformations::getOptions(
    -                    $mime_map[$column_name]['input_transformation_options']
    -                );
    -                $current_value = $transformation_plugin->applyTransformation(
    -                    $current_value, $transformation_options
    -                );
    -                // check if transformation was successful or not
    -                // and accordingly set error messages & insert_fail
    -                if (method_exists($transformation_plugin, 'isSuccess')
    -                    && !$transformation_plugin->isSuccess()
    -                ) {
    -                    $insert_fail = true;
    -                    $row_skipped = true;
    -                    $insert_errors[] = sprintf(
    -                        __('Row: %1$s, Column: %2$s, Error: %3$s'),
    -                        $rownumber, $column_name,
    -                        $transformation_plugin->getError()
    +                if (class_exists($classname)) {
    +                    /** @var IOTransformationsPlugin $transformation_plugin */
    +                    $transformation_plugin = new $classname();
    +                    $transformation_options = Transformations::getOptions(
    +                        $mime_map[$column_name]['input_transformation_options']
    +                    );
    +                    $current_value = $transformation_plugin->applyTransformation(
    +                        $current_value, $transformation_options
                         );
    +                    // check if transformation was successful or not
    +                    // and accordingly set error messages & insert_fail
    +                    if (method_exists($transformation_plugin, 'isSuccess')
    +                        && !$transformation_plugin->isSuccess()
    +                    ) {
    +                        $insert_fail = true;
    +                        $row_skipped = true;
    +                        $insert_errors[] = sprintf(
    +                            __('Row: %1$s, Column: %2$s, Error: %3$s'),
    +                            $rownumber, $column_name,
    +                            $transformation_plugin->getError()
    +                        );
    +                    }
                     }
                 }
             }
    
  • test/classes/TransformationsTest.php+50 0 modified
    @@ -287,4 +287,54 @@ public function fixupData()
                 ),
             );
         }
    +
    +    /**
    +     * Test for getDescription
    +     *
    +     * @param string $file                transformation file
    +     * @param string $expectedDescription expected description
    +     *
    +     * @dataProvider providerGetDescription
    +     */
    +    public function testGetDescription($file, $expectedDescription)
    +    {
    +        $this->assertEquals($expectedDescription, Transformations::getDescription($file));
    +    }
    +
    +    /**
    +     * @return array
    +     */
    +    public function providerGetDescription()
    +    {
    +        return [
    +            ['../../../../test', ''],
    +            ['Input/Text_Plain_SqlEditor', 'Syntax highlighted CodeMirror editor for SQL.'],
    +            ['Output/Text_Plain_Sql', 'Formats text as SQL query with syntax highlighting.']
    +        ];
    +    }
    +
    +    /**
    +     * Test for getName
    +     *
    +     * @param string $file         transformation file
    +     * @param string $expectedName expected name
    +     *
    +     * @dataProvider providerGetName
    +     */
    +    public function testGetName($file, $expectedName)
    +    {
    +        $this->assertEquals($expectedName, Transformations::getName($file));
    +    }
    +
    +    /**
    +     * @return array
    +     */
    +    public function providerGetName()
    +    {
    +        return [
    +            ['../../../../test', ''],
    +            ['Input/Text_Plain_SqlEditor', 'SQL'],
    +            ['Output/Text_Plain_Sql', 'SQL']
    +        ];
    +    }
     }
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.