beetl 模板注入学习
beetl 模板注入学习
依赖
测试使用的依赖
| 1 | <dependency> | 
语法
主要还是参考官方文档
注意到这个

任意方法调用
写一个demo看看是怎么阻止恶意方法调用的
| 1 | package org.example.beetl; | 

在template.render下断点 看看哪里进行了过滤
在template的renderTo方法里面 设置了context的上下文环境 设置了加载了一些共享变量 当不存在ajax 的时候 自动调用program.execute(ctx); 进行渲染
这里走到run方法

run方法里面遍历执行了每一个成员 走到execute 方法
在execute方法里面调用了expression.evaluate 方法计算成员的值
一系列计算之后 走到checkPermit 检测这个类是否可以加载

然后再checkPermit 方法里面 调用了getNativeSecurity().permit 比较执行类

permit 方法比较简单 就是获取类 然后直接比较class是否在黑名单里面

黑名单很短 如下
| 1 | if (pkgName.startsWith("java.lang")) { | 
这里被检测出来了 然后就直接抛出错误 不执行下去了
过滤类很少 可以试着用ClassLoader的命令执行方式绕过一下试一下
| 1 | @java.lang.ClassLoader.getSystemClassLoader().LoadClass('java.Lang.Runtime').getRuntime().exec('calc') | 
依然过不去
问题出现在这一步

这里的经过递归 targetcls确实成功得到了我们需要的classloader

可以注意到 在执行是
| 1 | targetObj = ObjectUtil.invoke(targetObj, mf, args); | 
这里的targetObj 为null 执行时会调用反射 尝试去执行我们的方法
每次执行结束之后 会获取此时getclass的class类 储存在targetCls内 进入下一次执行

后面寻找方法的ObjectMethodMatchConf mf = ObjectUtil.findMethod(targetCls, method, parameterType); 就是使用targetCls 寻找的 由于这个class不存在loadclass方法 所以显然不能找到这方法导致产生异常抛出

模板注入
还是有办法绕过的
| 1 | ${.forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("js").eval("java.lang.Runtime.getRuntime().exec('calc');")} | 
调试下
首先获得的targetCls 的值是class 执行了forName 方法 通过反射获取这个类 然后尝试调用这个方法

这里反射获取了这个方法 执行

这里执行的结果 会返回给targetObj 也就是获取我们需要的类

这个时候已经成功获取js这个执行类了 同时不在黑名单里面 这个时候就可以正常执行这个类了
成功命令执行 弹出计算器

其中的调用栈如下

网上的绕过手段很多 就不细搞了
反思
尝试使用ClassLoader 执行命令失败 是因为ClassLoader是一个抽象类
在这里调用getSystemClassLoader() 返回的对象类型是 ClassLoader,但实际的运行时类型通常是一个 ClassLoader 的具体实现类 这里返回了sun.misc.Launcher$AppClassLoader 使用这个类去尝试LoadClass 系统会提示找不到对应的方法 然后抛出
由于可以使用class#forname方法 意味着我们可以执行除了黑名单内的各种代码 例如文件读取
| 1 | String payload = "${@Class.forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"js\").eval(\"var Files = java.nio.file.Files; var Paths = java.nio.file.Paths; var path = Paths.get('D:/1.txt'); var content = Files.readAllBytes(path); new java.lang.String(content);\")}"; | 

后续
修复了 大致流程不变 添加了很多黑名单 3.16.0
| 1 | public boolean permit(Object resourceId, Class c, Object target, String method) { | 
但是又爆出来一个CVE-2024-22533 网上没现成的poc 我能力不足也分析不出来 就先放着 之后出来了在学习
