安全矩阵

 找回密码
 立即注册
搜索
查看: 1482|回复: 0

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

[复制链接]

251

主题

270

帖子

1783

积分

金牌会员

Rank: 6Rank: 6

积分
1783
发表于 2022-8-7 11:46:23 | 显示全部楼层 |阅读模式
本帖最后由 Meng0f 于 2022-8-7 11:47 编辑

记一次实战之若依SSTI注入绕过玄某盾
转载于:TGAO [url=]衡阳信安[/url] 2022-08-07 00:36 发表于山东

前言
前几天挖src,遇到4.7.1版本的若依系统GitHub - yangzongzhuan/RuoYi-fast: (RuoYi)官方仓库 基于SpringBoot的权限管理系统 易读易懂、界面简洁美观。 核心技术采用Spring、MyBatis、Shiro没有任何其它重度依赖。直接运行即可用 ,此版本存在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进行拦截:
  • fragment=${}:检测了${},但使用__${}__::.x,玄某盾不拦截
  • 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不是很懂,可以先去了解下,传送门:

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

上述几个红色标记处是很标准的SpEL-API调用,来执行SpEL表达式。我的绕过方式便是在SpEL表达式解析及执行过程中发现的,具体如下。
测试环境
springboot 2.7.1+jdk8 关于SpEL的测试代码如下:


解析过程中的发现
进入org.springframework.expression.spel.standard.InternalSpelExpressionParser#doParseExpression方法。

跟进org.springframework.expression.spel.standard.Tokenizer#Tokenizer 可以看到在SpEL表达式最后添加了个空白字符,用来标记SpEL表达式的结束,
接着跟进org.springframework.expression.spel.standard.Tokenizer#process方法 此方法整体逻辑:以字符为单位遍历表达式内容,若当前字符为a-z或者A-Z,则执行lexIdentifier方法,在lexIdentifier方法中,继续遍历表达式内容,直到遍历到的字符不是a-z A-Z、0-9、_、$结束此次遍历,并将此次遍历的所有字符封装在Token对象中,最后存储List<Token> tokens中。否则走else分支
在else分支中,若遇到\u0000、\r、\n、\t、`不做任何处理,直接跳出switch`语句,并进入下一个字符的判断
`\u0000、\r、\n、\t、5个字符的url`编码如下
因此,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) 成功弹出计算器

执行过程中的发现
在payload中SpEL表达式以T开头,T对应的类为org.springframework.expression.spel.ast.TypeReference 跟入org.springframework.expression.spel.ast.TypeReference#getValueInternal方法,根据字符串typeName获取对应的Class对象实例

继续跟入org.springframework.expression.spel.ExpressionState#findType,发现通过SpEL表达式上下文对象去寻找typeName对应的Class对象实例
在Thymeleaf中,此时默认的SpEL上下文对象为org.thymeleaf.spring5.expression.ThymeleafEvaluationContext对象实例,可看到继承org.springframework.expression.spel.support.StandardEvaluationContext对象,而StandardEvaluationContext支持type references,具体可看官方文档:Core Technologies

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

进入org.springframework.expression.spel.support.StandardTypeLocator#StandardTypeLocator()构造方法
继续跟进org.springframework.expression.spel.support.StandardTypeLocator#registerImport 发现java.lang被添加到knownPackagePrefixes集合中
初始化StandardTypeLocator对象后,会调用org.springframework.expression.spel.support.StandardTypeLocator#findType方法,可以发现此方法在异常出现时进行了一次补救:当通过typeName没有找到对应的Class对象时,则拼接前缀java.lang后继续获取对应的Class对象。
因此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) 成功弹出计算器

返回目标环境
讲过上文的分析,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产品,均未测试,有条件的同志们可以去测试一下~。


注:如有绘画请联系删除
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-4-20 21:16 , Processed in 0.016435 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表