Drupal Contextual Link RCE

Drupal Contextual Link RCE

0x00 参考链接

0x01 前言

10 月 17 日,Drupal 官方发布安全更新 SA-CORE-2018-006 ,修复了 5 个安全漏洞,其中包括 2 个高危漏洞和 3 个中危漏洞。其中 2 个高危漏洞为远程代码执行漏洞。

这里对其中 Drupal 8 Contextual Links 验证问题导致远程代码执行的漏洞进行复现。

根据官方漏洞简要说明得知该漏洞首先需要拥有 Contextual Links 模块的权限,并且由于对请求的links缺乏很好的验证导致了rce。

查看commit内容,怀疑如下内容为漏洞入口

0x02 环境搭建

通过如下命令搭建好drupal 8.5.2版本。

根据权限要求登录管理账号添加 Contextual Links 模块权限,之后退出账号。

0x03 过程调试

配置好代理burpsuite后,访问主页可抓取到如下请求。

core/modules/contextual/src/ContextualController.phprender 函数中挂上断点,并只保留如下 ids 内容: ids[]=block:block=bartik_footer:langcode=en|menu:menu=footer:langcode=en

其中 #contextual_links 将会通过 _contextual_id_to_links($id) 函数获取 ,函数内容如下:

该函数将会将 $id 的内容通过 | 拆分,并通过 : 分割至变量 $group, $route_parameters_raw, $metadata_raw,之后 $route_parameters_raw, $metadata_raw 将会经过 parse_str 解析成变量,并最终赋值给 $contextual_links[$group]

这里将 ids 内容再缩短至: ids[]=block:block=bartik_footer:langcode=en

之后进入 renderRoot

继续跟进 executeInRenderContext 函数,其中将 $this->render($elements, TRUE) 作为回调函数

$this->render 最终将会进入 doRender 函数。

之后经过 $this->elementInfo->getInfo 函数。

这里主要是增加一些新属性。

其中添加了 #pre_render 将会进入预处理 preRenderLinks

其中跟进 $contextual_links_manager->getContextualLinksArrayByGroup

这里将会通过 $group_name$this->pluginsByGroup 进行一个匹配搜索,这里匹配成功进入后续循环体。

但是这里由于并没有登录,所有没有访问权限跳出循环体,导致 links 变量为空。

从而 $element['#links'] 也为空,之后进入 alter,这里函数内容过程,主要关注最后的 $function($data, $context1, $context2);

将会依次调用 contextual_contextual_links_view_alterviews_ui_contextual_links_view_alter

其中 contextual_contextual_links_view_alter 代码如下:

可以看到,当存在 $element['#contextual_links']['contextual']) 时,将会将 $element['#contextual_links']['contextual']['metadata']['contextual-views-field-links'] 内容赋值给 $element['#links'],这由于我们的 $element['#contextual_links']['contextual']) 没有设置,所以 $element['#links'] 依旧为空,但是这部分我们是可控的。

之后执行完函数回到之前时,由于 $element['#links'] 为空,则设置 $element['#printed'] = TRUE;

这将导致返回空字符串内容。

0x04 控制 $element['#links'] 变量

所以我们可通过设置 $element['#contextual_links']['contextual']) 以及 $element['#contextual_links']['contextual']['metadata']['contextual-views-field-links'] 来确保 $element['#links'] 不为空。

对照 #contextual_links 中的变量进行修改,并且 $element['#contextual_links']['contextual']['metadata']['contextual-views-field-links'] 需要为 json 格式,并且由于会通过 : 进行拆分所以 : 需要进行二次编码。

得到 ids[]=contextual:block=bartik_footer:contextual-views-field-links={"aaa" %253A "bbb"}

成功设置 $element['#links']

0x05 寻找漏洞触发点

$element['#links'] 虽然可控但是距离触发漏洞还有一定的距离,继续在 doRender 函数中查找漏洞触发点。

调试过之前drupal drupalgeddon漏洞的同学应该会知道,如果对 $elements 数组变量可控,可通过设置 $elements['#pre_render'] 来触发 call_user_func 来达到RCE的目的,这里严重怀疑该漏洞也是通过该种方法得以触发。

继续调试,跟进 $this->theme->render

经过一系列繁琐的操作后将会依次调用如下函数,其中 $variables[links] 为我们所控。

跟进其中的 template_preprocess_links 函数,部分内容如下:

其中 $link_element 将通过 $link 变量进行设置,可以看到我们可控制其中的 #title#options#url 以及 #ajax 变量,这里由于传递的为 {"aaa":"bbb"} ,所以 "bbb" 不为数组导致报错。

修改一下传递参数 contextual-views-field-links 内容:

成功设置 $link_element 变量。

最后设置给了 $variables['links']

最终进入页面render过程。

调用栈如下:

$context 内容如下:

之后代码简化如下:

首先将 $context["links"] 赋值给 $context['_seq'],接着遍历键值对并根据是否包含相应的属性(links, text_attributes)进入不同的条件语句中,传递不同的属性值给 escapeFilter 函数。

这里由于包含 link 属性,即传递 $context["item"]["link"]

之后跟进 escapeFilter,该函数将通过 $arg 变量类型来返回不同的值。

该代码中可以看到,因为$arg为数组, 所以is_scalar($arg)=false,使得$return=NULL,从而进入最后的$this->renderer->render($arg)

$this->renderer->render 即为熟悉的 doRender

所以综上可知 $context["item"] 的属性将会传递给 doRender 函数,只要完全控制属性值内容即可控制 doRender 函数的 $elements 变量,从而达到RCE的目的,但是这里传递的是 link 属性,并不完全可控。

回到前面 if 条件判断,如果不包含 links 属性的时候将会传递 text 属性,而 text 属性值内容完全可控,所以可以利用 text 属性来达到rce的目的。

要想使得没有 link 属性在 template_preprocess_links 函数中可以看到,当没有 url 时,将不会设置 $item['link']

所以只需删除请求参数 ids[] 中的 url 部分即可,并且将 title 值内容修改为数组:

得到:

可以看到 item 没有 link 属性了。

进入 render 函数

从而控制了 doRender 函数的 $elements 参数内容,之后可通过设置 #pre_render 等属性达到rce的目的。

0x06 RCE

这里不提供完整exp,顺带说明一下这里只是通过commit调试下来的结果,并不清楚触发点是否与原漏洞发现者相同。

0x07 漏洞修复状况

官方通过验证签名 `token` 的方式,使得 `token` 错误导致无法利用。

编辑于 2018-11-13

文章被以下专栏收录