文章首先发布在先知社区,转载请注明出处。
框架介绍
Yii框架是一个通用的WEB编程框架, 其代码简洁优雅,具有性能高,易于扩展等优点,在国内国内均具有庞大的使用群体。
漏洞介绍
首先需要说明的时候,这个漏洞不具备黑盒测试通用性,只有开发者利用yii所编写的应用存在某种用法,才有可能导致触发,但是对代码安全审计人员是一个很好的漏洞挖掘点。
由于控制器(Controller)向模板(View)注入变量的时候,采取了extract($_params_, EXTR_OVERWRITE)
的模式,导致后面包含模板文件操作的$_file_
变量可以在某些条件下任意覆盖,从而导致任意本地文件包含漏洞,严重可以导致在某些低php版本下执行任意php命令和远程文件包含操作。
漏洞详情
问题出现在 framework/base/View.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
| public function renderPhpFile($_file_, $_params_ = []) { $_obInitialLevel_ = ob_get_level(); ob_start(); ob_implicit_flush(false); extract($_params_, EXTR_OVERWRITE); //overwrite 直接覆盖变量 l4yn3 try { require $_file_; //直接require $_file_变量,造成文件包含 l4yn3 return ob_get_clean(); } catch (\Exception $e) { while (ob_get_level() > $_obInitialLevel_) { if (!@ob_end_clean()) { ob_clean(); } } throw $e; } catch (\Throwable $e) { while (ob_get_level() > $_obInitialLevel_) { if (!@ob_end_clean()) { ob_clean(); } } throw $e; } }
|
这个方法当中存在任意变量覆盖问题,如果$_param_
这个变量我们能控制,就能覆盖掉下面的$_file_
变量。
跟进这个方法的调用链,发现同一个文件的renderFile($viewFile, $params = [], $context = null)
方法调用了这个方法:
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
| public function renderFile($viewFile, $params = [], $context = null) { $viewFile = $requestedFile = Yii::getAlias($viewFile);
if ($this->theme !== null) { $viewFile = $this->theme->applyTo($viewFile); } if (is_file($viewFile)) { $viewFile = FileHelper::localize($viewFile); } else { throw new ViewNotFoundException("The view file does not exist: $viewFile"); }
$oldContext = $this->context; if ($context !== null) { $this->context = $context; } $output = ''; $this->_viewFiles[] = [ 'resolved' => $viewFile, 'requested' => $requestedFile ];
if ($this->beforeRender($viewFile, $params)) { Yii::debug("Rendering view file: $viewFile", __METHOD__); $ext = pathinfo($viewFile, PATHINFO_EXTENSION); if (isset($this->renderers[$ext])) { if (is_array($this->renderers[$ext]) || is_string($this->renderers[$ext])) { $this->renderers[$ext] = Yii::createObject($this->renderers[$ext]); } /* @var $renderer ViewRenderer */ $renderer = $this->renderers[$ext]; $output = $renderer->render($this, $viewFile, $params); } else { $output = $this->renderPhpFile($viewFile, $params); //这里调用了漏洞方法l4yn3 } $this->afterRender($viewFile, $params, $output); }
array_pop($this->_viewFiles); $this->context = $oldContext;
return $output; }
|
继续跟进,发现同样文件View.php
的render()
方法调用了上面的renderFile()
方法,就此漏洞调用链出现。
1 2 3 4 5 6 7 8 9
| render($view, $params = [], $context = null)
调用了
renderFile($viewFile, $params = [], $context = null)
调用了
renderPhpFile($_file_, $_params_ = []) //存在漏洞
|
render($view, $params = [], $context = null)
这个方法是Yii的Controller用来渲染视图的方法,也就是说我们只要控制了render()
方法的$params
变量,就完成了漏洞利用。
到此这个漏洞发展成了一个和 《codeigniter框架内核设计缺陷可能导致任意代码执行》一样的漏洞。
漏洞利用
存在漏洞的写法如下:
1 2 3 4 5
| public function actionIndex() { $data = Yii::$app->request->get(); return $this->render('index', $data); }
|
这种情况下我们可以传递_file_=/etc/passwd
来覆盖掉require $_file_;
从而造成任意文件包含漏洞。
最后
这个漏洞已经提交给了Yii官方,但是一直没有收到任何回复。希望这篇文章能够帮助甲方用到Yii框架的代码审计人员,避免由这个问题造成严重的安全漏洞。