SA-CORE-2019-008 Drupal访问绕过漏洞分析

2019-07-25 约 106 字 预计阅读 1 分钟

声明:本文 【SA-CORE-2019-008 Drupal访问绕过漏洞分析】 由作者 tornado 于 2019-07-25 09:26:00 首发 先知社区 曾经 浏览数 59 次

感谢 tornado 的辛苦付出!

0x01 概述

7月17日,Drupal官方发布Drupal核心安全更新公告,修复了一个访问绕过漏洞,攻击者可以在未授权的情况下发布/修改/删除文章,CVE编号CVE-2019-6342

公告地址:https://www.drupal.org/sa-core-2019-008

0x02 受影响的版本

  • Drupal Version == 8.7.4

0x03 漏洞复现

安装Drupal 8.7.4版本,登录管理员账户,进入后台/admin/modules,勾选Workspaces模块并安装

在页面上方出现如下页面则安装成功,管理员可以切换Stage模式或者Live模式

另外开启一个浏览器访问首页(未登录任何账户),访问http://127.0.0.1/drupal-8.7.4/node/add/article

可直接添加文章,无需作者或管理员权限。

受影响操作包括基本文章操作(添加、修改、删除、上传附件等)

0x04 漏洞分析

Workspaces的功能

WorkspacesDrupal 8.6核心新增的实验模块,主要功能是方便管理员一次性发布/修改多个内容。

Workspaces有两种模式,分别为Stage模式和Live模式,,默认为Live模式,两者的区别在于:

  • Stage模式下修改内容不会及时更新,所有文章修改完毕后管理员可以通过Deploy to Live发布到实际环境,相当于一个暂存区;

  • Live下更新是即时的,发布后站点内容立即更新。

在这两种模式下,由于编码失误导致存在一个缺陷:匿名用户无需登录即可创建/发布/修改/删除文章,问题点出现在权限鉴定模块EntityAccess下。

漏洞分析

当用户发起请求时,会根据当前操作回调相关权限检查模块对当前用户权限进行检查,请求调用为事件监听器(EventListener)的RouterListener类,在其onKernelRequest()方法中调用AccessAwareRouter类的matchRequest()方法,随后调用AccessManager->checkRequest()方法,最后在AccessManager->performCheck()方法中通过call_user_func_array回调对应的操作进入到具体的操作权限检查

例如发布文章时回调的是access_check.node.add,相关方法在NodeAccessControlHandler控制器中定义,这个控制器继承自EntityAccessControlHandler,在父类的createAccess()方法中回调对应操作的create_access权限,过程中会拼接上模块名和相应钩子作为回调函数,

$function = module . '_' . $hook;

例如此处回调的是workspaces_entity_create_access()方法,进入到Workspaces中。

在调用entityCreateAccess()方法时有一个关键操作bypassAccessResult

bypassAccessResult()方法是一个检查用户是否有"绕过节点访问权限(bypass node access)"的操作,是Workspaces中特有的,这个方法决定了"如果用户在各自的激活的工作区中,那么他将拥有所有权限",这里的所有权限指文章相关的增删改操作。

这个权限虽然奇怪但确实是一个设计好的功能,正常操作应该在后台admin/people/permissions中配置好用户是否拥有这个权限,默认情况下匿名用户和认证用户都没有权限

当开启了Bypass content entity access in own workspace权限后用户才可以在未登录的情况下发布/删除文章,而此次漏洞就绕过了这个配置,默认情况下进行了越权操作。

具体分析一下bypassAccessResult()的实现,整个过程返回的是AccessResultAllowed对象或者AccessResultNeutral对象,所谓"中立"是因为后续还可能会对结果再做判断,但在这个漏洞中其实就是accessforbidden的区别:

首先获取了当前激活的工作区,然后通过allowedIf判断当前用户是否有权限,随后这些数据存入缓存,包括缓存内容、缓存标签和过期时间。然后再经过一次allowedIfHasPermission判断,这个方法的作用是,如果权限不对就设置一个reason,在这个漏洞中没有起到作用,到目前为止权限校验都是正常的,在没有配置后台工作区匿名权限的时候,返回的是一个AccessResultNeutral对象,也就是"禁止"。

接下来就是出现问题的地方

$owner_has_access->orIf(access_bypass);

通过补丁可以发现漏洞就修补了这行语句,把orIf换成了andIf

这两个方法的设计逻辑比较复杂,最主要的功能是对一个如果返回为"中立"的结果做后续判断,如果采用orIf方法合并,那么是否允许由调用者决定;如果以andIf方法合并,则被当做禁止。

具体到此次漏洞上的区别如下方图片所示:

  • orIf()

返回的是AccessResultAllowed对象

  • andIf()

返回的是AccessResultNeutral对象

在检查完毕后会回到AccessAwareRouter->checkAccess()方法,在该方法中对返回结果进行了判断,AccessResultNeutralisAllowed()返回false,因此会抛出异常

返回到页面上则是Access denied

更新补丁后只有在开启后台匿名用户权限后才能进行文章操作,该选项默认不开启。

相关调用栈为

Drupal\workspaces\EntityAccess->bypassAccessResult()
Drupal\workspaces\EntityAccess->entityCreateAccess()
...
Drupal\Core\Extension\ModuleHandler->invokeAll()
Drupal\node\NodeAccessControlHandler->createAccess()
Drupal\node\Access\NodeAddAccessCheck->access()
Drupal\Core\Access\AccessManager->performCheck()
Drupal\Core\Routing\AccessAwareRouter->checkAccess()
Drupal\Core\Routing\AccessAwareRouter->matchRequest()
Symfony\Component\HttpKernel\EventListener\RouterListener->onKernelRequest()
...
DrupalKernel.php:693, Drupal\Core\DrupalKernel->handle()
index.php:19, {main}()

0x05 总结

此次漏洞出现在设计过程的一个疏忽,在默认没有分配权限的情况下用户可以绕过权限检查进行发布/删除/修改文章操作,但由于该漏洞仅影响Drupal 8.7.4版本,并且需要开启Workspaces模块,这又是一个实验功能,默认不启用,因此漏洞影响减弱了不少,用户可以升级Drupal版本或者关闭Workspaces模块以消除漏洞影响。

关键词:[‘安全技术’, ‘漏洞分析’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now