https://www.bilibili.com/video/BV16h411z7o9/?spm_id_from=333.788.video.desc.click
1.继承接口Serializable
的类才能序列化
2.带transient
的Object的内容不会被序列化
3.可能的利用点:
(1)入口类readObject(重写的)直接调用危险方法(一般不会有人在服务器这么写代码)
(2)入口类参数中包含可控类,该类有危险方法,readObject时调用
(3)入口类参数中包含可控类,该类有吊用其他危险方法的类,readObject时调用
(4)构造函数/静态代码块等类加载时隐式执行(HashMap,HashTable)
共同点:
1.继承Serialzable
2.入口类source(重写readObject 参数类型宽泛,最好是jdk自带的)
例:
入口A HashCode
目标类B URL
目标调用B.f HashCode.hashcode()
反序化后:
A.readObject() HashCode.readObject()
上面那个方法里有个putVal(hash(key), key, value, false, false);
(并不是put函数中的那个,说调用链里有put的纯纯的误导)
然后hash(key)
下会调用key.hashcode()
,而这时key就是B (URL),所以调用了URL.hashcode()
3.调用链gadget chain
执行类sink(rce ssrf 写文件等)
反射:
作用:让Java具有动态性
修改已有对象的属性
动态生成对象
动态调用方法
操作内部类私有方法
在反序化漏洞中的利用:
定制需要的对象
通过invoke调用出了同名函数以外的函数
通过Class类创建对象,引入不能序列化的类
URL的链
以key传入一个类,hashcode会调用key.hashCode,(相当于调用这个类的hashCode)
CommonsCollections
CC1
0.准备:
jdk版本:8u65(漏洞在8u71修复了)
maven:
commons-collections 3.2.1
1 | <dependencies> |
然后像sun这样的包是反编译出来的,所以无法做到下载注释和方法查询,所以必须自己下载(openjdk的同版本)https://hg.openjdk.java.net/jdk8u/jdk8u/jdk/log?rev=annotationinvocationhandler
然后找对应版本:https://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/af660750b2f4
下载zip解压,src/share/classes里面的sun文件,复制下来打开一开始的jdk文件,里面有个src.zip文件,解压,把sun复制进去,然后ideasdks加入刚刚解压的目录
org.apache.commons.collections.Transformer类
作用:接收一个对象,然后对接收的对象进行操作
ctrl + alt + b 查看实现类
可以看到各种实现类,然后可以看各种类的返回值
这里要用InvokerTransformer
方法值,参数类型,参数调用都是我们可以控制的
是个很标准的任意方法调用
1.寻找危险方法:
InvokerTransformer.transform
具体语句:new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r)
1 | package org.emample; |
然后我们本次的目的是:
找xxx.transform(p)的方法,xxx必须是可以随意赋值的,给它赋值new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
,然后p也必须是可以赋值的,赋值为r
2.找哪个方法里调用了transform方法
跟进transform方法,右键选择“查找用法”
直接忽略transform调用transform
找到org.apache.commons.collections.map
下面有DefaultedMap,LazyMap,TransformedMap都有get方法调用了transform
看TransformedMap类
1 | protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) { |
被decorate调用
1 | public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) { |
这样就实现了自定义valueTransformer的内容
然后目标是调用checkSetValue:
1 | protected Object checkSetValue(Object value) { |
查找用法发现只有一个调用点(AbstractInputCheckedMapDecorator.java下的MapEntry类,AbstractInputCheckedMapDecorator是TransformedMap的父类):
1 | public Object setValue(Object value) { |
MapEntry的entry是用来遍历map的
如果主函数里使用了类似以下的遍历:
1 | for(Map.Entry entry:map.entrySet()){ |
setValue()是一个接口,说明可以重写
就会调用map下的setValue(),如果没有重写调用的就是Map这个类的,如果重写了,调用的就是重写后的,所以只用调用一开始传入自定义内容的map类,让其遍历,就能调用被重写的setValue(),进而调用checkSetValue
实现弹计算器
这就证明这条链是可以用的
1 | Runtime r = Runtime.getRuntime(); |
这里证明:如果主函数有遍历map的地方,而且调用了setValue,那么就可以使用这个方法
3.然后找setValue()被哪里调用(查找用法)
(必须做导入openjdk的sun这一步)
sun.reflect.annotation下的AnnotationInvocationHandler.java的readObject会调用memberValue.setValue的方法
1 | private void readObject(java.io.ObjectInputStream s) |
只要提前构造memberValues的值就行
然后看构造函数:
1 | AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) { |
发现真的能自定义memberValues的值
然后实例化这个类,尝试调用它
不是public类型,只能在这个包底下才能获取,无法直接获取,必须反射获取
1 | Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); |
然后在后面加以前写好的序列化,反序化函数
1 | public static void serialize(Object obj) throws IOException { |
这里还有三个问题:
(1)Runtime.getRuntime()
没法序列化,需要反射
(2)sun.reflect.annotation的AnnotationInvocationHandler.java的447行有if (memberType != null)
判断,可能没运行到memberValue.setValue
方法
(3)chainedTransformer.transform(Runtime.class);
这句是无法使用的,必须加进反序化里
(1)Runtime.getRuntime()
没法序列化,需要反射,Runtime.class
可以序列化
构建使用方法:
1 | Class c= Runtime.class; |
逐条转换,套用到InvokerTransformer方法中
1 | Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);//作用为c.getMethod("getRuntime", null); |
简化:
1 | Transformer[] transformers = { |
加入到反序化:
1 | Transformer[] transformers = { |
运行发现没有用,因为还有两个问题
(2)sun.reflect.annotation的AnnotationInvocationHandler.java的447行有if (memberType != null)
判断,可能没运行到memberValue.setValue
方法
因为Override.class
这个注解没有成员变量,所以判断那里是null(前面获取key就是获取成员变量),所以要换一个有成员变量的注解方法Target.class
然后改这里map.put("value","aaa");//这一步的目的是保证map里面有东西,不然for循环不会运行//与Target.class对应,为了防止那处的判断为null,key为Target.class的成员变量value
然后就成功进入了
1 | Transformer[] transformers = { |
(3)chainedTransformer.transform(Runtime.class);
这句是无法使用的,必须加进反序化里
解决方法,ConstantTransformer方法,它里面的transform唯一的作用就是返回ConstantTransformer方法的参数
最后就成功执行了
1 | Transformer[] transformers = new Transformer[]{ |
整个过程
1 | package org.emample; |
还有一条链(国外的)没有用transformedMap
,用的lazyMap
(更复杂),有时间去分析一下
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections1.java
ysoserial工具:https://github.com/frohoff/ysoserial