用户
搜索
  • TA的每日心情
    开心
    2021-6-26 11:05
  • 签到天数: 21 天

    连续签到: 1 天

    [LV.4]经常看看II

    i春秋作家

    Rank: 7Rank: 7Rank: 7

    18

    主题

    47

    帖子

    922

    魔法币
    收听
    0
    粉丝
    4
    注册时间
    2019-4-17

    i春秋签约作者

    发表于 2021-7-12 09:41:26 53726

    JAVA SPEL表达式注入

    本篇文章作者开关机,本篇文章参与i春秋作家连载计划所属开关机团队,未经许可禁止转载。

    一、SPEL 简介

    ​       Spring 表达式语言(简称 SpEL)是一种强大的表达式语言,支持在运行时查询和操作对象图。语言语法类似Struts2 的OGNL。但提供了额外的功能,最显着的是方法调用和基本的字符串模板功能,这进而也可能导致模板注入。

    g1.png

    ​       虽然还有其他几种可用的 Java 表达式语言,OGNL、MVEL 和 JBoss EL,但创建 Spring 表达式语言的目的是为 Spring 社区提供一种受良好支持的表达式语言,该语言可以在所有产品中使用。它的语言特性由 Spring 组合中项目的需求驱动,包括基于 Eclipse 的 SpringSource 工具套件中代码完成支持的工具需求。也就是说,SpEL 基于与技术无关的 API,它不直接与 Spring 相关联,可以独立使用。SPEL需要引入一些基础类,例如解析器。大多数 Spring 用户不需要引入该类包,只需要编写表达式字符串然后.Eval()调用。

    ​       主要的对象如下:

    类名 说明
    ExpressionParser 表达式解析器接口,包含了(Expression) parseExpression(String), (Expression) parseExpression(String, ParserContext)两个接口方法
    ParserContext 解析器上下文接口,主要是对解析器Token的抽象类,包含3个方法:getExpressionPrefix,getExpressionSuffix和isTemplate,就是表示表达式从什么符号开始什么符号结束,是否是作为模板(包含字面量和表达式)解析。一般保持默认。
    Expression 表达式的抽象,是经过解析后的字符串表达式的形式表示。通过expressionInstance.getValue方法,可以获取表示式的值。也可以通过调用getValue(EvaluationContext),从评估(evaluation)上下文中获取表达式对于当前上下文的值
    EvaluationContext 估值上下文接口,只有一个setter方法:setVariable(String, Object),通过调用该方法,可以为evaluation提供上下文变量

    二、SPEL 注入介绍

    ​       最早研究SPEL表达式注入在2012年12月14日,danamodio.com 研究Spring框架漏洞时发现SPEL存在注入风险。在2012年,SPEL相对于其他几种表达式语言,使用面相对较窄,但是这些年Spring框架被广泛推广,SPEL越来越具有研究的价值。

    ​       Spring 表达式语言注入(SPEL 注入)发生在攻击者可以部分或全部控制数据到表达式语言输入。SPEL语言可以支持查询并可以在运行时操作对象图。这可能有些难理解,但联想一下JSP表达式语言,JavaServer Pages 允许使用诸如 ${name} 之类的语法来访问 bean 以获取简单变量 。换言之,通过攻击者控制 SPEL 语法/查询并注入未经处理的数据,他们可以恶意提取敏感信息或在应用程序上下文中运行任意代码。

    ​       SpEL使用 #{…} 作为定界符,所有在大括号中的字符都将被认为是 SpEL表达式,我们可以在其中使用运算符,变量以及引用bean,属性和方法如:引用其他对象:#{car},调用其它方法 , 还可以链式操作:#{car.toString()}。

    ​       除引用对象,SPEL还有更强大的功能,使用T()运算符会调用类作用域的方法和常量,使用T()运算符#{T(java.lang.Math)},该T()运算符的结果会返回一个java.lang.Math类对象。

    三、SPEL 漏洞样例

    ​       在SQL注入中,攻击者通过传递非法变量,修改了SQL语句原定义的查询结构。SPEL注入的漏洞形态更像是命令注入,因此SPEL漏洞归为命令注入类。

    样例1

    @RequestMapping("/me")
    public String spel(String iss) {
        SpelExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(iss);
        return expression.getValue().toString()+"   Done!";
    }

    ​       样例1代码将输入的iss参数作表达式解析的参数,在解析过程中将造成命令执行。

    /me?iss={T(java.util.Arrays).toString(T(java.nio.file.Files).list(T(java.nio.file.Paths).get('d:\i4Tools7')).toArray())}

    g2.png

    样例2

    @RequestMapping("/you")
    public String test(String cc) throws IOException {
        SpelExpressionParser parser = new SpelExpressionParser();
        Expression exp = parser.parseExpression("T(java.lang.Runtime).getRuntime()");
        Runtime value = (Runtime) exp.getValue();
        return value.exec(cc).toString();
    }

    ​       在样例2中SPEL执行后返回类对象Object,然后再调用类中方法或属性,在SpEL中直接使用某个类名时(省略包名),此类必须在java.lang 包中,否则需要提供全名。

    g3.png

    四、SPEL 历史漏洞

    CVE-2018-1270

    ​       Spring框架中的 spring-messaging 模块提供了一种基于WebSocket的STOMP协议实现,STOMP消息代理在处理客户端消息时存在SpEL表达式注入漏洞,攻击者可以通过构造恶意的消息来实现远程代码执行。

    CVE-2018-1275

    ​       由于官方修补漏洞后其发布流程及代码管理上犯低级错误,导致4.3.14-4.3.15版本升级中该漏洞所涉及文件并未更新,所以在更新的版本中CVE-2018-1270并未修复,漏洞依然存在,进而有CVE-2018-1275漏洞,在4.3.16版本中再次得到修复。

    影响版本

    • Spring Framework 5.0 to 5.0.4
    • Spring Framework 4.3 to 4.3.14

    环境搭建

    https://github.com/spring-guides/gs-messaging-stomp-websocket

    g4.png

    POC构造

    ​       篡改前端app.js中Websocket connect函数中,插入恶意selector代码,PoC如下:

    var header  = {"selector":"T(java.lang.Runtime).getRuntime().exec('calc.exe')"};

    g5.png

    保持JS文件后,回到Web界面上重新Connect,然后Send若干任意字符即可触发服务端执行漏洞。

    g6.png

    漏洞成因

    ​       在 package org.springframework.messaging.simp.broker.class 第82行,对header参数处理:

    String selector = Sim*.getFirstNativeHeader(this.getSelectorHeaderName(), headers);

    expression = this.expressionParser.parseExpression(selector);

    当参数传递到org.springframework.messaging.simp.broker#filterSubscriptions(),执行.getValue()时触发漏洞。

    g7.png

    业务场景

    ​       Spring Messaging模块用于消息处理,在业务中常和RocketMQ搭配使用。如在代码审计中遇到RocketMQ-Spring协同案例,可重点关注项目pom 文件版本依赖org.springframework.boot ,判断是否为漏洞版本,若为漏洞版本,再继而跟踪 GreetingController#greeting()。

    修复方法

    ​       将pom.xml中的org.springframework.boot设置为2.0.1版本或更新版本。

    ...
        <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
        </parent>
    ...

    五、修复

    ​       SimpleEvaluationContext、StandardEvaluationContext 是 SpEL 提供的两个 EvaluationContext,使用 SimpleEvaluationContext 替换 StandardEvaluationContext,可有效防止SpEL滥用。

    • StandardEvaluationContext - 拥有全套 SpEL 语言功能和配置选项。您可以使用它来指定默认的根对象并配置每个可用的评估相关策略。

    • SimpleEvaluationContext - 针对不需要 SpEL 语言语法的全部范围并且应该受到有意限制的表达式类别,它不包括 Java 类型引用,构造函数和 bean 引用。

    六、小结

    ​       SPEL表达式较为灵活,其可以很好地适配业务中“经常变化”的部分,这可能是“业务规则”,也可能是“不同的数据处理逻辑”,常见的SPEL实现资源的注入有如:调用各种资源的情况,包含普通文件、网址、配置文件、系统环境变量。

    ​       在审计中,可使用关键字加快进展,如org.springframework.expression.spel.standard、  expression.getValue()expression.setValue()

    ​       在项目中运用Spel技术的开发人员编程水平通常较高,也正是这种巧妙运用,使得漏洞较为隐蔽,较难实现一致性修复,需要安全人员付出足够耐心去寻找查核修复完成情况。

    评分

    参与人数 2魔法币 +9 收起 理由
    cdxampp + 6 感谢你的分享,i春秋论坛有你更精彩!.
    li63033 + 3 感谢你的分享,i春秋论坛有你更精彩!.

    查看全部评分

    大佬厉害
    使用道具 举报 回复
    发表于 2021-7-17 22:14:48
    bangbangbang
    使用道具 举报 回复
    这种表达式注入方法可以
    使用道具 举报 回复
    发表于 2021-7-21 12:49:30
    加油加油加油
    使用道具 举报 回复
    发表于 2021-10-6 07:09:59
    感谢大佬分享,学习了
    使用道具 举报 回复
    发新帖
    您需要登录后才可以回帖 登录 | 立即注册