• OGNL表达式注入分析


    OGNL基础

    依赖

    <dependency>
    	<groupId>ognlgroupId>
    	<artifactId>ognlartifactId>
    	<version>3.1.19version>
    dependency>
    

    OGNL三要素

    • Expression表达式
    • root根对象、即操作对象
    • context上下文,用于保存对象运行的属性及值,有点类似运行环境的意思,保存了环境变量

    看个例子

    package org.example;
    
    public class Tester {
        public User user;
    
        public User getUser() {
            return user;
        }
    
        public void setUser(User user) {
            this.user = user;
        }
    }
    
    package org.example;
    
    public class User {
        private String name;
        private int age;
        public String getName(){
            return name;
        }
        public int getAge(){
            return age;
        }
        public void setName(String name){
            this.name = name;
        }
        public void setAge(int age){
            this.age = age;
        }
        public User(String name, int age){
            this.name = name;
            this.age = age;
        }
    }
    
    
    package org.example;
    
    import ognl.Ognl;
    import ognl.OgnlContext;
    import ognl.OgnlException;
    
    public class Main {
        public static void main(String[] args) throws OgnlException {
            Tester tester = new Tester();
            User user = new User("F12", 20);
            tester.setUser(user);
            // 创建context, 设置root
            OgnlContext context = new OgnlContext();
            context.setRoot(tester);
            // 设置表达式
            String expression = "user.name";
            // 解析表达式
            Object ognl = Ognl.parseExpression(expression);
            // 调用获取值
            Object value = Ognl.getValue(ognl, context, context.getRoot());
            System.out.println(value);
        }
    }
    
    // 输出
    F12
    

    运行以上代码就是获取org.example.Tester.user.name的值,上述我们是创建了一个tester,并且让他的user属性为一个User对象,且tester设置为root,表达式为user.name也就是获取root即tester的user属性的name属性。

    OGNL语法

    • .操作符

    一个例子,(#a=new java.lang.String("calc")).(@java.lang.Runtime@getRuntime().exec(#a)),也可以这样(#a=new java.lang.String("calc")),(@java.lang.Runtime@getRuntime().exec(#a)),中间的点换成逗号。可以发现它执行的方式有点类似递归,他把.前面的表达式当做结果给后面的表达式执行了这里需要注意一下#前我们用括号包裹起来了,这是为了符合语法,假如去掉那一层包裹会报错

    • #操作符

    用于调用非root对象

    package org.example;
    
    import ognl.Ognl;
    import ognl.OgnlContext;
    import ognl.OgnlException;
    
    public class Main {
        public static void main(String[] args) throws OgnlException {
            Tester tester = new Tester();
            User user = new User("F12", 20);
            tester.setUser(user);
            // 创建context, 设置root
            OgnlContext context = new OgnlContext();
    //        context.setRoot(tester);
            context.put("user", user);
            // 设置表达式
            String expression = "#user.name";
            // 解析表达式
            Object ognl = Ognl.parseExpression(expression);
            // 调用获取值
            Object value = Ognl.getValue(ognl, context, context.getRoot());
            System.out.println(value);
        }
    }
    // 输出
    F12
    

    用于创建Map
    #{"name": "f12", "level": "noob"}
    用于定义变量
    如一开始的例子#a=new java.lang.String("calc"),定义了一个字符串常量

    • @操作符

    用于调用静态属性、静态方法、静态变量,如上述的@java.lang.Runtime@getRuntime().exec

    OGNL版本限制

    在OGNL>=3.1.25版本中设置了黑名单

    public static Object invokeMethod(Object target, Method method, Object[] argsArray)
        throws InvocationTargetException, IllegalAccessException
    {
    
        if (_useStricterInvocation) {
            final Class methodDeclaringClass = method.getDeclaringClass();  // Note: synchronized(method) call below will already NPE, so no null check.
            if ( (AO_SETACCESSIBLE_REF != null && AO_SETACCESSIBLE_REF.equals(method)) ||
                (AO_SETACCESSIBLE_ARR_REF != null && AO_SETACCESSIBLE_ARR_REF.equals(method)) ||
                (SYS_EXIT_REF != null && SYS_EXIT_REF.equals(method)) ||
                (SYS_CONSOLE_REF != null && SYS_CONSOLE_REF.equals(method)) ||
                AccessibleObjectHandler.class.isAssignableFrom(methodDeclaringClass) ||
                ClassResolver.class.isAssignableFrom(methodDeclaringClass) ||
                MethodAccessor.class.isAssignableFrom(methodDeclaringClass) ||
                MemberAccess.class.isAssignableFrom(methodDeclaringClass) ||
                OgnlContext.class.isAssignableFrom(methodDeclaringClass) ||
                Runtime.class.isAssignableFrom(methodDeclaringClass) ||
                ClassLoader.class.isAssignableFrom(methodDeclaringClass) ||
                ProcessBuilder.class.isAssignableFrom(methodDeclaringClass) ||
                AccessibleObjectHandlerJDK9Plus.unsafeOrDescendant(methodDeclaringClass) ) {
                throw new IllegalAccessException("........");
            }
    

    投影与选择

    OGNL 支持类似数据库当中的选择与投影功能。

    • 投影:选出集合当中的相同属性组合成一个新的集合。语法为 collection.{XXX},XXX 就是集合中每个元素的公共属性。
    • 选择:选择就是选择出集合当中符合条件的元素组合成新的集合。语法为 collection.{Y XXX},其中 Y 是一个选择操作符,XXX 是选择用的逻辑表达式。选择操作符有 3 种:
      • ? :选择满足条件的所有元素
      • ^:选择满足条件的第一个元素
      • $:选择满足条件的最后一个元素
    User p1 = new User("name1", 11);
    User p2 = new User("name2", 22);
    User p3 = new User("name3", 33);
    User p4 = new User("name4", 44);
    Map context = new HashMap();
    ArrayList list = new ArrayList();
    list.add(p1);
    list.add(p2);
    list.add(p3);
    list.add(p4);
    context.put("list", list);
    System.out.println(Ognl.getValue("#list.{age}", context, list));
    // [11, 22, 33, 44]
    System.out.println(Ognl.getValue("#list.{age + '-' + name}", context, list));
    // [11-name1, 22-name2, 33-name3, 44-name4]
    System.out.println(Ognl.getValue("#list.{? #this.age > 22}", context, list));
    // [org.example.User@6433a2, org.example.User@5910e440]
    System.out.println(Ognl.getValue("#list.{^ #this.age > 22}", context, list));
    // [org.example.User@6433a2]
    System.out.println(Ognl.getValue("#list.{$ #this.age > 22}", context, list));
    // [org.example.User@5910e440]
    

    OGNL Expression解析流程

    getValue处打个断点,跟进,注意这个node的类型ASTchain在OGNL表达式中,解析和执行就是通过ASTXXXX这些方法去解析执行的,一共有ASTChainASTConstASTCtorASTInstanceofASTListASTMethodASTStaticFieldASTStaticMethod.....等多种方法,其中最根本的就是chain

    进入chain的getValue方法

    进入evaluateGetValueBody方法,这里判断context是不是const,这里并不是

    往下走进入getValueBody,获取子节点,并进入子节点的getValue方法,然后就这样一直循环

    最后进入OgnlRuntime.callMethod

    一直往下走,这里invoke,弹出计算器


    __EOF__

  • 本文作者: F12
  • 本文链接: https://www.cnblogs.com/f12-blog/p/18102189
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    webpack打包速度优化
    Leetcode646. 最长数对链
    操作系统性能参数调优
    [spring]spring的使用笔记大全
    酷快讯:Eminem和Snoop Dogg无聊猿新MV加持后ApeCoin上涨22%
    mac日历与iphone日历不无法同步问题
    Unity解决:Unity SpriteRenderer屏幕自适应的多种模式
    MIT CS143 lecture 02 Overview of COOL
    item_recommend - 获取推荐商品列表
    新加坡亲子游景点推荐
  • 原文地址:https://www.cnblogs.com/F12-blog/p/18102189