Nette框架未授权任意代码执行漏洞分析

1. 前言

Nette Framework 是个强大,基于组件的事件驱动 PHP 框架,用来创建 web 应用。Nette Framework 是个现代化风格的 PHP 框架,主要用于国外网站开发,因此国内对该框架的研究比较少。2020年10月,Nette框架被爆出存在未授权任意代码执行漏洞,可通过自有框架控制器MicroPresenter执行任意PHP方法,我们对漏洞进行了复现和分析,发现该漏洞危害严重,请广大用户及时进行升级修复。

2. 环境准备

通过查询发现该漏洞影响自2.0以来的几乎所有大版本,详细的范围如下

nette/application 3.0.6 (or 3.0.2.1, 3.1.0-RC2 or dev)

nette/application 2.4.16

nette/application 2.3.14

nette/application 2.2.10

nette/nette 2.1.13

nette/nette 2.0.19

我们通过composer安装受影响的3.0.0版本,执行composer create-project nette/web-project nette-blog 3.0.0@dev,安装完成后整体的目录结构如下:

其中app为应用程序的主目录,包含presenters控制类目录、config配置文件、router路由器类目录以及Booting.php应用启动文件。而vendor/nette目录下包含Nette的所有框架文件,www目录包含整个Web程序能够直接访问的文件,如静态资源、入口文件index.php等,同时在index.php中调用Booting中的boot方法引导启动。

Nette会创建Configurator类对象来对启动环境进行配置,如设置日志文件目录、临时文件目录、加载配置文件等。

后续会根据配置调用createContainer创建一个DI容器,并通过getByType方法实例化Nette框架主程序Nette\Application\Application对象,最后调用了Application对象的run方法。

3. 漏洞分析

我们还是根据网上披露的POC来进一步分析代码,从Application:run函数处打下断点,函数会调用createInitialRequest方法来初始化Request对象。

跟进到createInitialRequest方法,108行通过调用路由类的match方法来处理http请求,109行则是获取需要调用的控制器类,119行则是返回处理好的Request对象。

我们跟进match方法,发现路由器类的调用路径如下:

Nette\Application\Routers\RouteList->Nette\Routing\RouteList->Nette\Application\Routers\Route->Nette\Routing\Route::match(),该方法主要将http请求处理后转换成数组。121行将网站请求路径中的基础路径删除赋值给$path,131行将$path按照presenter/action/id形式的正则进行匹配,并将匹配的部分分别标记为p0、p4、p13,148行则将匹配出的字符型key从

$this->aliases数组中取出对应的描述字段,其中p0对应为presenter,因此取出的控制器为

$params[‘presenter’]= nette.micro。

继续跟进到173行,调用了$params[$name] = $meta[self::FILTER_IN]((string) $params[$name])处理$params,其中$meta[self::FILTER_IN]对应了path2presenter处理控制器部分,将nette.micro转成Nette:Micro。

随后我们返回Application.php中的processRequest方法,这里我们主要关注控制器部分Nette:Micro如何被调用的,跟进114行到presenterFactory类的formatPresenterClass方法,调用路径为createPresenter->getPresenterClass->formatPresenterClass。

在该方法中,120行将$presenter用冒号分割,并对$mapping赋值为$this->mapping[‘Nette’]。

126行则将$mapping的第三个元素*Presenter中的*替换为Micro,并将$mapping的第一个元素和替换后的字符串相连,最终得到控制器为NetteModule\MicroPresenter。在Application.php的144-149行,程序调用了控制器NetteModule\MicroPresenter中的run函数,对应为nette/application/src/Application/MicroPresenter.php,69行获取传递进来的GET参数。

其中74行会检查callback参数传递进来的变量可否被当做函数调用,85行调用combineArgs方法获取回调函数中的默认参数名,并于GET请求传递过来的参数名比对,如果相同则保存在$res数组并返回到$params中。

返回到MicroPresenter.php的90行,这是一个典型的可变函数调用,在诸多PHP后门中比较常见,这里函数名$callback和参数$params都可控,可导致任意代码执行。我们以shell_exec方法为例,默认参数为cmd,我们构造对应的POC请求:

参考链接:

https://blog.nette.org/en/cve-2020-15227-potential-remote-code-execution-vulnerability

https://www.kancloud.cn/aspvb/nette/271800