【漏洞分析】Drupal内核远程代码执行漏洞CVE-2018-7602

Drupal发布安全通告称有1个高危漏洞(CVE-2018-7602)影响Drupal 7.x和8.x等版本,攻击者可以通过不同方式利用该漏洞远程执行代码。CVE-2018-7602这个漏洞是CVE-2018-7600的另一个利用点,只是入口方式不一样;所以,一旦参数可控并且没有经过正确的过滤,就很有可能出问题。

0x01 概述

4月25日,Drupal官方发布通告,Drupal Core 存在一个远程代码执行漏洞,影响 7.x 和 8.x 版本。据分析,这个漏洞是CVE-2018-7600的绕过利用,两个漏洞原理是一样的,通告还称,已经发现了这个漏洞和CVE-2018-7600的在野利用

详情请看

https://www.drupal.org/sa-core-2018-004

0x02 影响版本

Drupal 6.x,7.x,8.x

修复版本
Drupal 7.59,Drupal 8.4.8,Drupal 8.5.3

 

0x03 漏洞分析

分析还是以7.57版本为例。跟7600漏洞的7.x版本很相似,只不过入口不一样,可以参考

https://blog.nsfocus.net/cve-2018-7600-drupal-7-x/

上回漏洞的关键点是让系统缓存一个form_build_id,这个form存着我们传入的恶意参数,第二个请求从中取出来然后执行。
这次的原理还是一样,触发漏洞还是需要发两个post包,一个存入form_build_id一个取出后执行。

这次的问题出在删除文章的时候,因此需要文章删除权限,我们先走一遍正常删除文章的逻辑


请求中每个node即代表一篇文章。
可以看到是会重定向到文章页面的,根据上个漏洞的分析我们猜测,一定还是走到了drupal_redirect_form(),我们已经知道如果走到drupal_redirect_form()分支,是不会往数据库缓存form_build_id的,我们的目的还是让程序不满足一定条件从而不进行表单提交后重定向,所以还是跟着CVE-2018-7600的套路来走

从代码层面看一下

之前的流程还是一样,直接跳到drupal_build_form()方法第386行

drupal_process_form($form_id, $form, $form_state);

跟入drupal_process_form()


还是一样,$form_state['submitted']被设置为true

回到902行

if ($form_state['submitted'] && !form_get_errors() && !$form_state['rebuild'])
条件被满足,进入这个分支便会执行drupal_redirect_form()

而在这一步之前需要经过的判断是_form_element_triggered_scripted_submission()
所以回到一开始的问题,构造一个_triggering_element_value使得键值对相等,从而不进行rebuild

我们传入_triggering_element_name=form_id



可以看到条件被满足,$form_state['submitted']没有被设置为true,还是保持默认值false


进入drupal_rebuild_form()


表单被缓存


然后我们发送第二个post包来取出我们构造好的form,向file/ajax/actions/cancel/%23options/path发起请求

参数传递进去


最终还是跟入到
$output = drupal_render($form);
根据前几次的经验,我们还是选择'#post_render'参数,


假如我们能控制这个参数,在drupal_render()方法里就会把这个参数作为$function函数名,而传给它的参数则是[%23markup]

所以问题回到了一开始,我们需要传递什么样的恶意参数,可以让系统直接接收而不经过过滤,还是之前的套路,搜索module下删除文章的相关操作


可以看到node_form_delete_submit()方法从get方法直接接收参数destination,与最初分析正常删除文章的参数正是同一个,那么我们就可以利用destination传进恶意参数

构造如下
destination=a?q[%2523post_render][]=passthru%26q[%23type]=markup%26q[%23markup]=dir

a参数是次要的,主要是q参数,因为在includes/common.incdrupal_parse_url()方法

if (isset($options['query']['q'])) {
    $options['path'] = $options['query']['q'];
    unset($options['query']['q']);
  }

从q取出值赋给$options['path'],也就是a被覆盖了,这个时候的$options['path']就是我们传入的数组

注意q的元素需要转义百分号,对#进行二次编码,以绕过CVE-2018-7600的补丁,不然在取值时会被认为q[是一个值


参数缓存进整个form后通过第二个请求取出,同样经过

foreach ($form_parents as $parent) {
    $form = $form[$parent];
  }

遍历叶子节点取出参数


进入drupal_render()执行

0x04 PoC

0x05 补丁

7.x的补丁

https://github.com/drupal/drupal/commit/080daa38f265ea28444c540832509a48861587d0


其中一个重要操作就是对destination参数进行了净化

0x06 总结

总的来说这个漏洞是CVE-2018-7600的另一个利用点,只是入口方式不一样,最终执行点还是相同的,所以还是那句话,一旦参数可控并且没有经过正确的过滤,就很有可能出问题。

Spread the word. Share this post!

Meet The Author

Leave Comment