# Demo 利用
https://github.com/artsploit/yaml-payload
将源码简单修改下
1 | package test; |
(使用图片中自己 cv 的代码没有执行成功,卡了半天,干脆直接 down 了 github 的 poc 才执行成功的
原来还有个 SPI 机制
# SPI 机制
SPI ,全称为 Service Provider Interface,是一种服务发现机制。它通过在 ClassPath 路径下的 META-INF/services 文件夹查找文件,自动加载文件里所定义的类。也就是动态为某个接口寻找服务实现
如果需要使用 SPI 机制需要在 Java classpath 下的 META-INF/services/
目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类
也就是说,我们在 META-INF/services 下创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类的全类名,在加载这个接口的时候就会实例化里面写上的类
SPI 会通过 java.util.ServiceLoder
进行动态加载实现,在调用的时候,SPI 机制通过 Class.forNam
反射加载并且 newInstance()
反射创建对象的时候,静态代码块进行执行,从而达到命令执行的目的。
这里先插一句说下!!是什么
!! 就相当于 fastjson 里的 @type,用于指定要反序列化的全类名。
跟进到 loadFromReader 下 setComposer,指定反序列化全类名
上一步 name 取到 javax.script.ScriptEngineManager,这里反射创建 javax.script.ScriptEngineManager 对象
这里创建数组列表,调用 node.getType ().getDeclaredConstructors () 遍历完的结果通过 possibleConstructors.add 再添加到
Class.forName 进行创建反射对象并且赋值给 note 的 type 里面。而后这里 getDeclaredConstructors () 获取它的无参构造方法。
再到这里返回实例对象
construct 构造器加载进来
value 就是恶意类
再加载一轮
就拿到了 javax.script.ScriptEngineManager 实例化对象
反射调用将数组对象赋值给 c,最后再实例化
这里再返回要加载地址
最后
下面再来看下 SPI 机制的实现
断点下在 ScriptEngineManager
#75
ServiceLoader 动态加载类
hasNexService 方法
加载 META-INF/services/javax.script.ScriptEngineFactory
获取实现类
实例化
走到后面就执行成功
# 漏洞修复
漏洞涉及到了全版本,只要反序列化内容可控,那么就可以去进行反序列化攻击
修复方案:加入 new SafeConstructor()
类进行过滤
1 | public class main { |
再次执行会抛出异常
也可以拒绝不安全的反序列化操作,反序列化数据经过校验或者拒绝反序列化数据可控
在审计中其实就可以直接定位 yaml.load();
,然后进行回溯,如若参数可控,那么就可以尝试传入 payload。
一些绕过手法
1 | public static final String PREFIX = "tag:yaml.org,2002:"; |