作者:且听安全
原文链接:https://mp.weixin.qq.com/s/nCMtSD7QH8ai6fpurJBXTg

漏洞信息

最近 Confluence 官方通报了一个严重漏洞 CVE-2022-26134 :

从漏洞描述来看,这仍然是一个 OGNL 表达式注入漏洞。影响版本如下:

  • from 1.3.0 before 7.4.17
  • from 7.13.0 before 7.13.7
  • from 7.14.0 before 7.14.3
  • from 7.15.0 before 7.15.2
  • from 7.16.0 before 7.16.4
  • from 7.17.0 before 7.17.4,
  • from 7.18.0 before 7.18.1

补丁描述:

主要是修改了 xwork-1.0.3-atlassian-10.jar 。下面将深入分析漏洞原理,并尝试绕过沙箱构造命令执行结果回显。

漏洞分析

新版本主要是修改了 xwork-1.0.3-atlassian-10.jar 。首先简单进行一下补丁对比:

改动的地方很多,但是最关键的地方位于 ActionChainResult#execute 函数,对提取 finalNamespacefinalActionName 的过程进行了更新。

Confluence 基于 Struts 架构进行开发。我们首先以登录请求为例,对 Confluence 请求处理的流程进行动态调试。访问 /login.action ,经过一系列 Filter 处理后,将进入Servlet 的分发器 ServletDispatcher (本质上是其子类 ConfluenceServletDispatcher 对象):

分别通过函数 getNameSpacegetActionNamegetRequestMapgetSessionMapgetApplicationMap 提取相应参数,对应关系如下:

  • getNameSpace(request) -> namespace
  • getActionName(request) -> actionName
  • getRequestMap(request) -> requestMap
  • getParameterMap(request) -> parameterMap
  • getSessionMap(request) -> sessionMap
  • getApplicationMap() -> applicationMap

然后调用 serviceAction 函数,进入子类 ConfluenceServletDispatcherserviceAction 函数:

实例化 DefaultActionProxy 对象,调用其 execute 函数:

进入 DefaultActionInvocation#invoke

这里开始调用 Struts Interceptor 拦截器对象对请求进行处理, DefaultActionInvocation 对象拦截器集合 interceptors 一共有 32 个:

函数 invoke 尝试通过 next 获取下一个拦截器对象,然后调用其 intercept 方法,大部分 Interceptor 对象的 intercept 函数格式如下所示:

继续调用 DefaultActionInvocation#invoke ,从而形成迭代循环。但是在调试中我们发现也有特殊的一些 Interceptor ,比如 ConfluenceAccessInterceptor

当满足一定条件时并不会继续调用 DefaultActionInvocation#invoke ,而是返回字符串 notpermitted ,我们分析一下 isAccessPermitted 函数:

主要是通过请求的 *.actionmethdName ,来判断当前用户 currentUser 是否有访问权限。也就是说,当访问一个无权访问的 *.action 时,DefaultActionInvocation#invoke 在迭代调用到 ConfluenceAccessInterceptor#intercept 后,将返回 notpermitted 并赋值给 resultCode,从而跳出迭代。我们替换测试请求为 /index.action

当处理到 ConfluenceAccessInterceptor 拦截器时,将不会继续迭代调用下一个拦截器,而是继续往下走,进入 executeResult 函数:

进入 ActionChainResult#execute

提取 namespace 参数,并调用 translateVariables 函数,进入:

典型的 OGNL 表达式解析过程,前面分析中可知, namespace 参数通过 ServletDispatcher#getNameSpace 函数获取,查看定义:

可见 namespace 取值为请求 servletPath 最后一个 / 之前的部分。

根据上面正则表达式规则,要想触发 OGNL 解析,我们很容易构造出相应的 URL :

成功触发 OGNL 表达式注入。

沙箱绕过与命令执行

网上现在已经公开的一些利用方式简单粗暴,只能针对 v7.14 及以下系列有效,因为从 v7.15 系列开始,Confluence 在 OGNL 表达式解析时加入了沙箱设置:

进入 isSafeExpression 函数:

主要的黑名单如下: (1) unsafePropertyNames

(2) unsafePackageNames

(3) unsafeMethodNames

0 = "getClass"
1 = "getClassLoader"

白名单 allowedClassNames 如下:

此外,对成员函数等也进行了检查( containsUnsafeExpression 函数 ):

小伙伴看到这里,应该很容易想到多种绕过的方法,感兴趣的小伙伴可以进入漏洞空间站进行交流。其中一种实现命令执行结果回显的方式如下(适用全部受影响的版本):


Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1912/