# Spring框架标签EL表达式执行漏洞分析（CVE-2011-2730）

Author: 乌云历史资料库 (@wooyun_archive)
Published: 2015-04-23T02:30:00Z
Canonical: https://wepostx.com/topics/794

> 乌云历史资料归档
>
> **原始作者：** lupin
> **原始编号：** superkieran-wooyundrops:544
> **原始发布时间：** 2015-04-23 10:30
> **声明：内容仅用于技术研究和个人使用，版权归 wooyun.org。**

---

# 0x00 前言

这个漏洞已经出来很久了，以前简单分析过，但是由于时间关系，没能深入研究原理，网上对这个漏洞的分析也不太多，最近由于工作原因，深入分析了一下这个漏洞的原理，这里重点将漏洞调试过程，以及一些之前遇到的一些奇怪问题的原因记录下来。
首先来看一下官方对这个漏洞的描述，如下图：

![原文图片](/media/2016/06/8a09e54f3029ee95c74ea456cabe52e0)

可以看到，这个漏洞的形成，是因为在早于JSP2.0的版本上，由于没有EL表达式的支持，Spring标签为了兼容这部分版本，Spring的一部分标签独立于Servlet/JSP容器添加了对EL表达式的支持。但这样做引起了一个问题，就是当这部分标签在支持EL表达式的容器中运行的时候，Spring标签的属性会被当做EL表达式执行两次，第一次是被容器当做EL执行，第二次是被Spring标签自身执行。所以，如果攻击者可以控制标签属性的内容，就可以执行自己提交的EL表达式，会造成信息泄露、代码执行等风险。

# 0x01 漏洞原理

首先编写一段测试JSP代码，并且引入存在漏洞的Spring标签，我这里使用的是Tomcat 7.0.57和Spring 3.0.5

```text
#!html <%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%> <spring:message text="${param.a}"></spring:message>
```
这里使用message标签，text属性用el表达式从请求参数中取值，这样当访问

```text
http://localhost/tag.jsp?a=${applicationScope}
```
${applicationScope}这个字符串，就会被当做EL表达式执行，如下图

![原文图片](/media/2016/06/612908a01881d53e22f3a89fe8e649e3)

下面我们就从代码中，研究一下这个漏洞的原理。
首先，${param.a}这个EL表达式要在JSP里面执行，这里有必要简单说一下JSP的执行原理，tag.jsp在执行的时候，会先通过规则变成tag_jsp.java，这个文件可以在tomcatRoot/ work/Catalina/localhost/项目名称/org/apache/jsp/路径下找到，这个类其实是一个servlet，通过查看这个文件的代码，我们可以发现${param.a}这个EL表达式，其实是被转化成了这样的代码org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate("${param.a }", .......)，这行代码调用了Tomcat的一个专用方法来执行EL表达式，这段代码执行了之后，我们通过请求参数提交的${applicationScope}就被容器获取，并且传入了Spring标签，如下图：

![原文图片](/media/2016/06/fcc0b2d883f2eb2295d0beca600f5335)

还需要说一下关于标签的一些内容，标签都有一个tld文件，这个文件里面有标签的各种信息，包括标签实现类、属性信息等等，这个标签对应的spring.tld文件可以在Spring的jar包中找到，打开这个文件搜索message，就可以定位到如下图的位置：

![原文图片](/media/2016/06/d878bd7dcb87ed3e3ca6280f38ad6653)

从上面可以看到org.springframework.web.servlet.tags.MessageTag就是这个标签的实现类，关于标签的规范，我就不多说了，有兴趣的朋友可以去翻翻相关的规范。
Debug程序，在doStartTagInternal方法下断点，如下图：

![原文图片](/media/2016/06/02a6bd5836e06cbab3ed11dc7941a1ee)

然后跟进resolveMessage方法，因为程序执行到这里，容器已经第一次执行了EL表达式${parm.a}，将参数值${applicationScope}取了出来，可以看到text变量，这时候就是我们传入的${applicationScope}，如下图：

![原文图片](/media/2016/06/96415a02988d882c70840d80cb600647)

最后跟踪到org.springframework.web.util.ExpressionEvaluationUtils类的evaluateExpression方法，在这里可以看到Spring的代码调用了getExpressionEvaluator方法获取了容器执行El表达式求值对象，并执行了EL表达式，到这里可以看到我们通过请求参数提交的字符串，被当做EL表达式执行了，如下图：

![原文图片](/media/2016/06/f3c29b94238cb9d79bb15936996a11da)

从上面的过程可以看到，容器第一次执行EL表达式${param.a}获得了我们输入的${applicationScope}，然后Spring标签获取容器的EL表达式求值对象，把${applicationScope}再次执行掉，形成了漏洞。

# 0x02 Tomcat不能远程代码执行研究

这个漏洞既然能执行攻击者提交的EL表达式，那么获取敏感信息、XSS等攻击自然不是问题，我这里主要说一下代码执行的一些问题。 这个漏洞之所以能造成远程代码执行，是因为从JEE6开始，EL（EL2.2）表达式不仅支持获取对象属性，还加入了对方法调用的支持，而Tomcat、Resin等容器都加入了EL表达式对方法调用的支持，之前知识库里面已经有大牛公布的针对Resin和GlassFish的代码执行POC，虽然由于容器对EL表达式的执行实现上有差异，每个容器POC有所差异，但是都可以成功实现方法调用，我这里就不多说了，而我在最初测试这个漏洞的时候，发现Tomcat始终没法调用方法，当时没有深入研究，通过前面的分析，其实原因已经可以看出来了，这里还是测试一下，先写一段JSP测试代码：

```text
#!html <body> ${“aaa”.replace(‘a’,’b’)} </body>
```
打开这个页面，发现a都已经被b替换，说明replace方法被成功调用：

![原文图片](/media/2016/06/bc0dfb2d84b53aed2ba8be530c1de16f)

但是当我们通过之前的tag.jsp尝试提交http://localhost/tag.jsp?a=${“aaaa”.replace(‘a’,’b’)}的时候却爆出了错误，如下图：

![原文图片](/media/2016/06/13c8a036f0a364847f6042fa676ffd35)

从Tomcat 7开始已经支持EL表达式方法调用，为什么第二次会失败呢？
其实前面的分析中已经看到，在JSP中直接写EL表达式，Tomcat调用了org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate方法，这个方法已经加入了对EL表达式方法调用的支持，而通过Spring标签执行EL表达式，前面已经看到是通过pageContext的getExpressionEvaluator方法，而在EL表达式的新标准中这个方法已经“废弃”，Tomcat并没有在这个方法的实现中加入对方法调用的支持，所以造成执行失败，这也让这个漏洞在Tomcat上显得有点鸡肋了，谁能保证Tomcat哪天不会抽风，改变代码呢？说不定到时候，这个漏洞，就可以在Tomcat上造成代码执行了。
参考
http://support.springsource.com/security/cve-2011-2730
http://drops.wooyun.org/tips/2892

## Replies
