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.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值