[java安全基础 01]SQL+反序列化
tomcat
Servlet
什么是servlet
Java Servlet是运行在 Web 服务器或应用服务器上的程序.它是作为来自Web浏览器或其他HTTP客户端的请求和HTTP服务器上的数据库或应用程序之间的中间层。
servlet的功能:
- 创建并返回基于客户请求的动态HTML页面
- 与数据库进行通信(业务逻辑处理)
如何使用servlet
servlet本身是一组接口,自定义一个类,并且实现servlet接口,这个类就具备了接受客户端请求以及做出响应的功能
浏览器不能直接访问servlet文件,只能通过映射的方式来简介访问servlet,映射需要开发者手动配置
基于文件的配置方式
基于注解的配置
servlet生命周期
先查找是不是创建了,创建了就不再初始化,而是使用之前的
销毁方法是在关闭tomcat服务时关闭
首先访问的时候是没有servlet对象的,访问的时候,底层原理才通过反射创造了一个方法,通过构造方法创造一个servlet对象,然后再执行初始化操作,初始化完了,再执行service方法,而service方法可以多次调用。
Servlet的层次结构
Servlet >GenericServlet > HttpServletHTTP
GenericServlet:只保留了Service方法
HttpServletHTTP:实现了GET和POST方法的分流
请求有很多种类型,常用的有四种:
GET读取
POST保存
PUT修改
DELETE删除
真正的开发环境中基本只会用到service方法,其他方法没必要写出来,显得冗余,实际上servlet已经提供了部分实现类来帮助高效的编程和维护,所以不需要直接实现servlet接口,只需要继承HttpServlet
重写doGet和doPOST
RESTFUL
GenericServlet 实现 Servlet 接口,同时为它的子类屏蔽了不常用的方法,子类只需要重写service方法即可。HttpServlet继承GenericServlet,根据请求类型进行分发处理,GET进入doGET方法, POST进入 doPOST方法。开发者自定义的Servlet类只需要继承 HttpServlet即可,重新doGET 和doPOST
JSP
jsp本质上就是一个servlet,jsp主要负责用户交互,将最终的界面呈现给用户,html+js+css+ava的混合文件
<%--这里可以穿插java
这里能够解决页面展示
jsp在项目中会转换为一个.jsp.java文件
--%>
单纯从开发的角度看,jsp就是在HTML中嵌入java程序,具体的嵌入方式由3种:
- jsp脚本,执行java逻辑代码<% java代码%>
- jsp声明:定义java方法
<%!
声明java 方法
%>
- jsp表达式:把java对象直接输出到HTML页面中
<%= java变量%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<%! public String test(){
return "this is test function()";
}%>
<%! String str = test();%>
<%= str%>
</body>
<%--这里可以穿插java
这里能够解决页面展示
jsp在项目中会转换为一个.jsp.java文件
--%>
</html>
jsp内置对象
request:表示一次请求,HttpServletRequest
response:表示一次响应,HttpServletResponse
pageContext:页面上下文,获取页面信息,PageConetext
session:表示一次会话,保存用户信息,httpSession
application:表示当前web应用,全局对象,保存所有用户共享信息,ServletContext
config:当前JSP对应的Serlvet的ServletConfig对象,获取当前Servlet的信息
out:向浏览器输出数据,JspWriter
page:当前JSP对应的Servlet对象,Servlet
exception:表示JSP页面发生的异常,exception
jsp本质是一个servlet,servlet可以捕获异常,servlet就是java代码嘛。
jsp不是一个java程序,它通过exception来捕获异常
常用的是:request,response,session,application,pageContext
request常用方法
1. String getParameter(String key)获取用户端传来的参数
2. Void setAtteribute(String keyObject vale)通过键值对的形式保存数据
3. Object getAttribute(String key)通过key取出value
4. RequestDispatcher getRequestDispatcher(String path)返回一个RequestDispatcher对象,该对象的forward方法用于请求转发
5. String[] getParameterValues()获取客户端传来的多个同名参数
6. void setCharacterEncoding(String charset)指定每个请求的编码
dispach分配机制
在同一个请求在服务器的传递(url没有变化),也叫做服务器跳转,和重定向不一样
dispach1.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>dispach</title>
<h1>dispach2</h1>
</head>
<body>
<% String name = (String) request.getAttribute("user");
out.write(name);%>
</body>
</html>
dispach2.jsp
<%--
Created by IntelliJ IDEA.
User: 40447
Date: 2022-11-23
Time: 21:47
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>dispach1</title>
</head>
<body>
<% String name = request.getParameter("name");
request.setAttribute("user",name);
request.getRequestDispatcher("./Dispach2.jsp").forward(request,response);
%>
</body>
</html>
request重定向
sendRedirect(String path)重定向页面直接的跳转
转发getRequestDispatcher和重定向sendRedirect的区别:
转发是将同一个请求传给下一个页面,重定向是创建一个新的请求传给下一个页面,之前的请求结束生命周期。·
转发:同一个请求在服务器之间传递,地址栏不变,也叫服务器跳转。
重定向:由客户端发送一次新的请求来访问跳转后的目标资源,地址栏改变,也叫客户端跳转。
如果两个页面之间需要通过request 来传值,则必须使用转发,不能使用重定向。用户登录,如果用户名和密码正确,则跳转到首页(转发),并且展示用户名,否则重新回到登陆页面(重定向)
dispach1.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>dispach1</title>
</head>
<body>
<% String name = request.getParameter("name");
request.setAttribute("user",name);
// request.getRequestDispatcher("./Dispach2.jsp").forward(request,response);
response.sendRedirect("Dispach2.jsp");
%>
</body>
</html>
这样写是会报错的,但是由于报错也可以知道,这个jsp是会转化为java
转发,同一个请求在服务器中传递,可以数据传递,但是重定向是客户端发起一次新的请求,url会变化
jsp内置对象作用域
page作用域:对应内置对象时pageContext
只在当前作用域有效
request作用域:对应内置对象的request
在一次请求内有效
session作用域:对应内置对象的session
一次session中有效,一次session有多个request
application作用域:对应的内置对象application
application最大的,即便关闭了重新进浏览器,也是保存着的
网站访问量统计
<%
Inter count = (Integer)application.getAttribute("count");
if(count == null){
count = 1;
application.setAttribute("count",count);
}else{
count++;
application.setAttribute("count",count);
}
%>
springboot
什么是spring
spring回一个2003年兴起的一个轻量级java开发框架
spring是为了 解决企业级应用开发的复杂性而创建的,简化开发
spring如何简化java开发
为了降低java开发的复杂性,spring采用了以下4种关键策略
- 基于pojo的清零及和最小侵入性编程,所有东西都是bean
- 通过IOC,依赖注入(DI)和面向接口实现松耦合
- 基于切面(AOP)和管理进行声明式编程
- 通过切面和末班减少样式代码,RedisTemplate,XXXtemplate
什么是springboot
Spring框架是Java平台上的一种开源应用框架.提供具有控制反转特性的容器。
Spring Boot基于Spring 开发, Spirng Boot本身并不提供 Spring框架的核心特性以及扩展功能.只是用于快速、敏捷地开发新一代基于Spring框架的应用程序。
约定大于配置的核心思想
Spring Boot的主要优点:
-
为所有Spring开发者更快的入门
-
开箱即用,提供各种默认配翟来简化项目配置
-
内嵌式容器简化Web项目
-
没有冗余代码生成和XML配置的要求
简单来说,springboot就是易于开发的spring
项目结构分析:
通过上面步骤完成了基础项目的创建。就会自动生成以下文件。
1、程序的主启动类
2、一个application.properties配置文件
3、一个测试类
4、一个pom.xml
运行原理
pom.xml
主启动类
//SpringBootApplication来标注一个主程序类
//说明这是一个SpringBoot应用
@SpringBootApplication
public class Springbootdemo01Application {
//主启动类
public static void main(String[] args) {
//这是启动了一个服务,而不是启动了一个方法
SpringApplication.run(Springbootdemo01Application.class, args);
}
}
四个原注解
@Target({ElementType.TYPE}) //ElementType.TYPE 表示注解可以声明在入口之前
@Retention(RetentionPolicy.RUNTIME) //定义注解的生命周期,此处是指,程序不管一直活
@Documented //标志给java的工具记录
@Inherited //标志子类继承
@SpringBootConfiguration //springboot的配置类
最终通过组件定义配置类
@EnableAutoConfiguration // 自动配置类
@Import({AutoConfigurationImportSelector.class}) // 自动配置选择器
sql注入
JDBC介绍
java database connectivity是一个独立于特定数据库的管理系统,通用的SQL主句库存取和凑在哦公共接口
定义了一组标准,为访问不同数据库提供了同意的途径
JDBC API
内容:供开发者调用的接口
- DriverManager接口
- Connection接口
- Statement接口
- ResultSet接口
DriverManager类
作用:驱动管理器,主要负责管理驱动
DriverManager负责驱动程序管理,数据库驱动则是为了应用程序服务的,所以DriverManager的重要任务就是提供连接的获取
在调用getConnection方法时,DriverManager会试着从初始化时加载到哪些驱动程序以及使用于当前applet或应用程序相同的类加载器,显示加载的那些驱动程序中查找合适的驱动程序
Class.forName("com.mysql.cj.jdbc.Driver")
Connection connection = DriverManager.getConnection(url,Sqluser,Sqlpass)
ResultSet resultset = statement.executeQuery(sql);
大乌鱼了,不知道为什么SQL连接不上了,配置2个多小时,解决方法:
照着这个新起了一个项目https://blog.csdn.net/yc11223344/article/details/123226302,然后导入servlet.jar,并且把mysql的那个jar也放到了这里的bin。
大乌鱼了,不知道为什么SQL连接上了,配置了1min,解决方法,把什么mevan包删除完,重新导入tomcat/bin/中的servlet.jar和mysql的那个jar(随便放哪都行)
package com.v1nt.sqldemo;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.*;
@WebServlet("/sqldemo")
public class sqltest extends HttpServlet {
//根据参数名获取参数
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
String user = req.getParameter("user");
String pass = req.getParameter("pass");
try {
// load driver
Class.forName("com.mysql.cj.jdbc.Driver");
resp.getWriter().write(user+"<br>");
String url = "jdbc:mysql://localhost:3306/security?&useSSL=false&serverTimezone=UTC";
String Sqluser = "root";
String Sqlpass = "123456";
// 获取数据库一次链接
Connection connection = DriverManager.getConnection(url,Sqluser,Sqlpass);
// String sql = "select * from users where username = '" + user + "' " +" and password = '" + pass+"';";
String sql = "select * from users where username = ? and password = ?";
// System.out.println(sql);
resp.getWriter().write(sql+"<br/>");
//修复代码
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,user);
preparedStatement.setString(2,pass);
ResultSet resultSet = preparedStatement.executeQuery();
//执行SQL
// Statement statement = connection.createStatement();
// ResultSet resultSet = statement.executeQuery(sql);
if(resultSet.next()){
resp.getWriter().write(resultSet.getString("username"));
resp.getWriter().write("login secuss");
}else {
resp.getWriter().write("login failed");
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
public sqltest()throws SQLException{
}
}
预编译:
sQL语句在程序运行前已经进行了预编译,在程序运行时第一次操作数据库之前,sQL语句已经被数据库分析编译和优化,对应的执行计划也会缓存下来并允许数据库已参数化的形式进行查询,当运行时动态地把参数传给PreprareStatement时,即便参数里有敏感字符如or '1=1'也数据库会作为一个参数一个字段的属性值来处。理而不会作为一个sQL指令,如此,就起到了sQL注入的作用了
XXE
XXE(XML External Entity Injection)全称为XML外部实体注入当允许引用外部实体且存在输入点时.恶意攻击者即可构造恶意内容访问服务器资源等操作
xml基础知识
XML用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型.是—种允许用户对自己的标记语言进行定义的源语言。此. XML文档结构包括xML声明、DTD文档类型定义(可选)、文档元素
反序列化
java反射
如果是看函数什么的,可以看第二个文章,那里全是代码,这里光是看定义确实难顶。
Java安全很大一部分可以从反序列化漏洞说起,而反序列化漏洞又可以从反射说起。
JAVA反射机制是在运行状态中,对于任意一个类.都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。反射就是把java类中的各种成分映射成一个个的Java对象(体现了java的万物皆对象的思想)
为什么需要反射Java
中的两种编译类型
-
静态编译:在编译时确定类型,绑定对象即通过。
-
动态编译:运行时确定类型,绑定对象。动态编译最大限度地发挥了Java的灵活性,体现了多态的应用,可以减低类之间的耦合性。
Java反射是Java被视为动态语言的一个关键性质。
换句话说.程序在运行时的行为都是固定的。如果想在运行时改变.就需要反射这东西了;
反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
如何获取反射
1.获取反射中的class对象(java.lang.Class)
2.通过反射创建类对象
3.通过反射获取类属性、方法、构造器
1. 获取反射中的Class对象
在反射中,要获取一个类或调用一个类的方法,我们首先需要获取到该类的Class对象。
Class类对象是:
对一个声明了的类的类对象
举例:
public class A{
properties
methods
}
A类的Class类对象,记录了A类型的信息,一个类(不是实例对象)只有一个Class对象;
大概可以理解成:
A是定义了"水",A的实例定义了是什么水,例如"可乐,雪碧,咖啡"
Class类对象,是记录了A类型的信息并且可以获取A中的属性方法,例如A是什么,A可以怎么用;
获取Class类对象有三个方法
-
使用Class.forName静态方法: Class clz = cloass.forName("java.lang.String");
-
使用.class方法 Class clz = String.class;
-
使用类对象的getClass()方法 String str = new String("Hello");Class clz =str.getClass()
forName有两个函数重载:
Class forName(String name)
Class forName(String,**boolean**initialize,ClassLoader loader)
一个有趣的点:使用功能.class
来创建Class对象的引用时,不会自动初始化该Class对象,使用forName()会自动初始化该Class对象;
例子:
public class TrainPrint {
{
System.out.println("Enpty block initial %s\n",this.getClass());
}
static {
System.out.println("Static initial %s\n",TrainPrint.class);
}
public TrainPrint(){
System.out.println("Intital %s\n",this.getClass());
}
}
加载一个类的时候,首先执行static代码,因为执行的时候会先执行静态方法,接着执行空代码块(又叫,构造代码块,初始化块),最后才会执行构造方法;
2.通过反射创建类的对象
两种方式
通过Class对象的newInstance()方法
Class clz = Apple.class;
Apple apple = (Apple)clz.newInstance();
通过Constructor对象的newInstance()方法
Class clz = Apple.class;
Constructor constructor = clz.getConstructot();
Apple apple = (Apple)constructor.newInstance();
实例
Person.java
package com.javaser.base;
public class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.javaser.base;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectionTest {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Person ksPerson = new Person("狂三", 18);
Class<? extends Person> perCls = ksPerson.getClass();
// 反射就是操作Class
// 从原型class里实现实例化对象
// Person per = perCls.newInstance();
// 有参数的实例化需要使用构造器
Constructor<? extends Person> personCon = perCls.getConstructor(String.class, int.class);
Person sxPerson = personCon.newInstance("十香", 18);
System.out.println(sxPerson);
System.out.println(ksPerson);
}
}
注意:通过CONstructor对象创建类对象可以选择特定构造方法,而通过Class对象则只能使用默认的无参的公有构造方法。
问:如果构造方法私有,如何解决?
在类对象中,有getDeclaremethod类似,带declare的,用他们来进行定义来获取
constructor.setAccessable(true);
3.通过放射获取类属性、方法、构造器
getMethod和invoke方法
- Method Class.getMethod(String name, Class<?>... parameterTypes)的作用是获得对象所声明的公开方法
该方法的第一个参数name是要获得方法的名字,第二个参数parameterTypes是按声明顺序标识该方法形参类型。
标识该方法形参类型。
public Object invoke(Object obj, Object... args)
是Method类的方法
该方法的参数一个是Object类型,也就是调用该方法的对象,第二个参数是一个可变参数类型
Class clazz = Class.forName("java.lang.Runtime");/获取反射中的class对象
Method getRuntimeMethod = clazz.getMethod("getRuntime");//获取反射类的方法
Object runtime = getRuntimeMethod.invoke(clazz);//调用方法获取runtime类
getDeclared方法系列的放射
与普通的getMethod,getConstructor区别是
- getMethod系列方法获取的是当前类中所有公共发发,包括父类继承的方法
- getDeclaredMethod系列方法获取的是当期那类中"声明"的方法,是实在写在这个类里的,包括私有的方法,但从父类里继承来的就不包含了
Class clazz = Class.forName("java.lang.Runtime");
Constructor m = clazz.getDeclaredConstructor();
m.setAccessible(true);
clazz.getMethod("exec",String.class).invoke(m.newInstance(),"calc.exec");
注意:这里使用了一个方法setAccessible,这个是必须的。我们获取到一个私有方法后,必须使用setAccessible修改它的作用域,否在仍然不可调用
java 反序列化
认识Java序列化与反序列化
-
序列化就是把对象的状态信息转换为字节序列(即可以存储或传输的形式)过程
-
反序列化即逆过程,由字节流还原成对象
注: 字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序
为什么需要java反序列化?
把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中
在网络上传送对象的字节序列
为什么会产生安全问题?
只要服务端反序列化数据,客户端传递类的readObject中代码就会自动执行,基于攻击者在服务器上运行代码的能力
可能的入口形式:
- 入口类的readObject方法直接调用危险方法(少见)
- 入口类参数包含可控类,该类有危险方法,readObject时调用
- 入口类参数中包含可控类,该类调用其他有危险函数的类
比如类型定义为Object
调用equals、hashcode、toString
重点:相同类型,同名函数
- 构造函数/静态代码块等类加载时隐式执行
共同利用条件:
继承serializeable
入口类(重写readObject参数类型宽泛,最好jdk自带)
调用链
执行类
满足这些的有HashMap等
HashMap
继承序列化:有可能会有需求
参数类型宽泛:有键值对,键值对类型宽泛
重写readObject方法:为什么需要重写:https://juejin.cn/post/6844903954774491144#heading-S
因为:HashMap为了保证键的唯一性,它就要计算键的hashcode(hash值),如果键是对象的话,在不同的JVM里面,不同的机器上,计算的hashcode是不一样的。所以需要将对象拆开,把每一个元素都拿出来单独计算,所以必须重新实现这个readObject方法。
这也是简单说法,具体还是得看看底层
示例
创建一个person类,实现Serializable接口
public class Person implements Serializable{
private static final long serialVersionUID = -ID;
private int age;
public String name;
Privaete String sex;
get...
set...
}
//Serializable接口作用只是用来表示这个类需要进行序列化,并且Serializable接口没有提供任何方法。
//相当于用Serializable标识一下
testMain.java
package com.v1nt.serializerDemo;
import java.io.*;
public class serializerTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// SerializerPerson();
DeserializerPerson();
}
public static void SerializerPerson() throws IOException {
Person tom = new Person(18,"TOM","男");
ObjectOutputStream objectoutputStream = new ObjectOutputStream(new FileOutputStream("./TEST.txt"));
objectoutputStream.writeObject(tom);
System.out.println("person对象序列化成功");
objectoutputStream.close();
}
public static void DeserializerPerson() throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("./TEST.txt"));
Person person = (Person) objectInputStream.readObject();
System.out.println(person);
System.out.println(person.getName());
objectInputStream.close();
}
}
Person.java
package com.v1nt.serializerDemo;
import java.io.Serializable;
public class Person implements Serializable {
public static final long serialVersionUID = -580L;
public String name;
private Integer age;
private String sex;
public Person( Integer age,String name, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
开始调试
下断点,调试,强制步入
F7
F7
F8
F7,选第二个方法readOrdinaryObject
,然后F8,F7看一看
进入之后,F8,看到一个readNoProxyDesc
,F7跟入看看
F8执行到这一句,有一个resolveClass
方法,跟入看看
从名字看,是一个决绝class内容的,这里有一个forName
获取一个全类名
然后跟到上面就行了,然后返回daoreadClassDesc
可以看到这里有一个newInstance
方法,这里就已经在创建对象了
F8到图示,最后会调用readSerialData还原出属性之中的值,F7跟如看看
F7跟入readSerialData
后,F8走几步,走到defaultFiles
,再F7跟入函数
卧槽,发现更错代码了,该跟反序列化Person的,跟成了urldns的,是说怎么全是hashmap
不过前面步骤一样,重新走一遍就好
F7走到此处
这一步是对获取的到fields进行赋值,多走几步F8,好像得先获取一个整型数据,所以又走了很多步骤,但最后都会回到这里
最后的setObjectfileValues
是一个将Person具体还原对象
这就算是勉强跟完了反序列化链,这里就只是跟一下是怎么样的一个流程,在哪里赋值扫盲的。
补充
1. 静态成员不能被序列化
序列化是针对对象属性的,静态成员变量属于类的
2.transient标识的不会被序列化
3.反序列化的机制就相当于在服务器上引入了一个执行代码的能力
4. MAP,HashMap
URLDNS链
反序列化安全问题
如果java应用对用户输出,即不可信数据做了反序列化处理,那么攻击者可以通过构造恶意输入,让反序列化产生非预期的对象,非预期的对象在产生过程中就有可能带来任意代码执行
Ysoserial工具
https://github.com/frohoff/ysoserial
ysoserial集合了各种java反序列化payload,它可以让用户根据自己选择的利用链,生成反序列化利用数据,通过这些数据发送给目标,从而执行用户预先定义的命令
URLDNS链
该链特点
- 使用java内置的类构造,对第三方呢库没有依赖
- 在目标没有回显的时候,能通过DNS请求得知是否存在反序列化漏洞
使用ysoserial工具生成payload
java1.8 -jar ysoserial.jar URLDNS "http://xxx.dnslog.cn">urldns.txt
编写测试类执行反序列化:
package com.v1nt.urldnsDemo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
public class urldnsTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:\\javastudy_pro\\tools\\ysoserial\\poc.txt"));
Object oj = objectInputStream.readObject();
System.out.println(oj);
}
}
分析ysoserial工作原理
生成Paylod的类在GeneratePyload.java
在idea的project struction中可以导入jar包,然后在那里看就行
getObject方法里就是gadget链的生成,这里返回的ht对象就是构造完成的恶意对象。
在URLDNS.java中有一个HashMap 定义的ht
考虑到,这是反序列化触发的,所以在HashMap中应该是有个readObject方法,于是点进去看看
开始调试
先不急,首先看到
URLDNS.JAVA文件
还是下断点,然后强制步入
走到此处获取协议,协议为http
F8走几步,此处是获取主机地址
F7步入方法看看
F7继续步入
F8走几步
走到此处,会对域名进行解析返回域名(时间有点久了,估计不行了),然后dns链会接收到请求。(我就在此处下个断点,重新获取一个url),到这里了,也就算是跟完了UELdns
我以为是连接寄了,原来是还得F8走一步
可以在DNSLOG看到:
其它
有个关键字叫transient
,被它修饰的不会参与序列化与反序列化的过程。
在URLDNS.JAVA代码中,就有被它修饰的。这样可以避免在进行序列化和反序列化中访问请求连接。
这个修饰只是在生成payload的时候有修饰
重写了Handler,和其它的方法,都返回了null,或者就是空着不管。免得造成多的请求。
这里点点找找Handler,和它的函数
然后进入URL.java,找到hashCode()
看到是一个handler对象,在看handler的定义
看到了transient,说明这里不参与反序列化
URLDNS反序列化链总结
我们使用工具生成的DNSLOG的poc,所以在导包之后看的,就应该是DNSLOG.JAVA
HashMap.readObject()
HashMap.putVal()
HashMap.hash()
URLhashCode()
URLStreamHandler.hashCode()
URLStreamHandler.getHostAddress()
关键字:transient:不序列化与反序列化