近日,流行的开源内容管理框架Drupal曝出一个远程代码执行漏洞,漏洞威胁等级为高危,攻击者可以利用该漏洞执行恶意代码,导致网站完全被控制。漏洞对应的CVE编号为CVE-2018-7600。

本篇文章对Drupal 8 – CVE-2017-7600漏洞进行了详细分析。这个漏洞看起来是一个漏洞,其实我认为,它是由两个小的鸡肋问题组成的。具体是什么呢?

CVE-2018-7600 漏洞分析

这个漏洞的根本原因出在drupal对表单的渲染上:

可见,在drupal中,我们不需要直接写html表单,而是先创建一个数组,表单呈现引擎通过位于\drupal\core\lib\Drupal\Core\Form\FormBuilder.php文件中的buildForm方法构造出一个名为$form表单,然后成对应的html表单进行呈现。

通过下图buildform的定义,可以看出它是用来构造一个表单的

最终的$form是如下图这个样子:

这个漏洞,恰恰就出在了这里。

但是对于一个drupal框架的应用程序来说,后台表单数组都是开发者写好的,像这个样子

攻击者是无法改变表单数组元素的key值的。

很多应用都提供了如下的一个便利的方法:

比如要注册一个用户,用户名、密码、邮箱、电话,这些东西都填好了。当点击提交的时候,网站告诉你,用户名已存在。

这时候,你会发现,密码、邮箱、电话这些元素不需要你再次填写了,页面已经将保存下来了。

drupal系统同样有这样的功能,具体如何实现的呢?下面我们做个试验:

我们先提交个正常的表单

先在buildform函数返回处下断后

填写表单并提交

页面跳转到注册成功页面,

我们在buildform函数返回处下的断点根本没有断下来。

接着我们再按着上面的表单一模一样的注册一个看看:

但这次呢,在断点处成功断下了:

在这处断点,我们把name的值改为”kingsguard_test_1”试试

这次的返回页面如下:

整个流程是:

  1. 用户填写表单->表单没有问题->返回注册成功页面
  2. 用户填写表单->表单内容有问题(例如用户名已被注册)->调用buildform方法,把用户传入的内容一同构造为表单数组->渲染表单数组为html页面返回

这就是刚刚在buildform断点处把name值由kingsguard改为kingsguard_test_1,返回的页面里username值也变成kingsguard_test_1的原因。

到这里,攻击链已经很明确了,攻击者传入的值,可以通过buildform(方法构造表单数组,并且这个表单数组接下来还会被drupal表单呈现引擎解析为html页面。

当我们在这个注册表单页面里,如果想上传一张图片

这时候发送的请求如下

当上传成功后,往往有一个缩略图显示在那,如下图菊花处:

这个缩略图,是通过drupal\core\modules\file\src\Element\ManagedFile.php文件中的uploadAjaxCallback方法来解析。

注意,还记的上文buildform方法吗?buildform生成$form数组后,将生成的$form数组传递给uploadAjaxCallback方法来解析,目的是在返回页面上显示那个缩率的菊花。

既然流程已经捋顺了,我们通过构造poc来动态调试下,发送如下图post包:

首先会进入buildform函数来构造表单数组,接下来这个表单数组($form)会进入uploadAjaxCallback方法。

看下这个uploadAjaxCallback方法:

传入uploadAjaxCallback方法中的$form变量,就是buildform方法生成的表单数组:

$form数组传入uploadAjaxCallback方法中后,可以看到有这么一行(下图红框处):

$form_parents变量竟然可以从get中传入,意味着这个变量可控,其实就是我们poc中的element_parents=account/mail/%23value。

通过poc,此处的$form_parents变量如下图

$form_parents变量和$form通过NestedArray::getValue方法后,结果值赋给$form

新的form变量如下:

接下来看这里的renderRoot方法:

此处传入的$form变量为:

继续看renderRoot方法:

 

里面调用了render方法

继续看render方法:

里面调用了doRender方法

继续看doRender方法:

在这个方法的505行

调用call_user_func方法

此处的参数如下:

可见,这里的

$callable=”exec”

$elements[‘#children’]=”kingsguard_text”(这里我们传入的恶意代码,这里我就不演示了)

总结:

这个漏洞看起来是一个漏洞,其实我认为,它是由两个小的鸡肋的问题组成的,第一次就是在buildform处,用户传入的变量没有受到限制,导致可以传入mail[#post_render]、mail[#type]这样的变量,但是单单这个问题,还不严重,因为对于最终渲染的html页面来说,传入的数组仍然是数组,不能被当成元素来解析。但是偏偏uploadAjaxCallback方法中的$form_parents变量是直接通过get(‘element_parents’)得来的,这下两个一结合,$form_parents把之前传入的数值当成元素了,这下就造成了一个大洞。

源链接

Hacking more

...