Shiro-721反序列化漏洞

环境搭建

下载源码

  1. 从链接https://codeload.github.com/apache/shiro/zip/shiro-root-1.4.1下载
  2. 使用git下载
    • git clone https://github.com/apache/shiro.git
    • git checkout shiro-root-1.2.4

构建war包

  1. 进入下载的源码目录
    • mvn cd ./shiro/samples/web
    • mvn clean
    • mvn package
  2. 从链接https://github.com/jas502n/SHIRO-721下载war包

使用IDEA打开环境

使用IDEA打开下载源码的根目录,配置tomcat服务器,添加依赖,选择Exrernal Source(外部源),选择第二部生产的war包。

SDK版本选择的11

漏洞分析

密钥生成方式对比

shiro550漏洞的主要成因是shiro使用的密钥是硬编码的,在1.25<=shiro<=1.41中,shiro使用随机生成的密钥。我们看一下相关代码。

在shiro<=1.24中,可以看到shiro使用setCipherKey函数将常量DEFAULT_CIPHER_KEY_BYTES设为Key。

    public AbstractRememberMeManager() {
        this.serializer = new DefaultSerializer<PrincipalCollection>();
        this.cipherService = new AesCipherService();
        setCipherKey(DEFAULT_CIPHER_KEY_BYTES);
    }
……
    private static final byte[] DEFAULT_CIPHER_KEY_BYTES = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");

在1.25<=shiro<=1.41中,shiro使用generateNewKey()函数生成了一个新的Key

    public AbstractRememberMeManager() {
        this.serializer = new DefaultSerializer<PrincipalCollection>();
        AesCipherService cipherService = new AesCipherService();
        this.cipherService = cipherService;
        setCipherKey(cipherService.generateNewKey().getEncoded());
    }

跟进,调用了重载函数generateNewKey(getKeySize())

    public Key generateNewKey() {
        return generateNewKey(getKeySize());
    }
    public int getKeySize() {
        return keySize;
    }

再跟进,生成了一个KeyGenerator对象并调用init函数进行初始化。

查看init函数,调用了重载的双参数函数,多的那个参数是个random随机数

再调用generayeKey()函数生成密钥,跟进后发现调用engineGenerateKey()函数,生成了一个128位的随机aesKey,再调用getEncoded()方法获取key密钥序列

shiro721解密流程

AbstractRememberMeManager类的getRememberedPrincipals方法获取subjectContext对象。

跟进,getCookie获取base64编码的RemerbeMe值,判断值不为DELETED_COOKIE_VALUE,即deleteMe后,接着处理,先是调用ensurePadding方法填充“=”号,再进行base64解码。

若RemeberMe不为null且长度大于0,调用convertBytesToPrincipals将subjectContext对象转换为principals对象。

跟进,


//这是一个公共方法,接受SubjectContext对象作为参数,并返回PrincipalCollection对象。
//PrincipalCollection是Shiro框架中用于存储主体身份信息的集合。
public PrincipalCollection getRememberedPrincipals(SubjectContext subjectContext) {
//创建一个PrincipalCollection对象,初始值为null。
    PrincipalCollection principals = null;
    try {
        // 调用getRememberedSerializedIdentity方法获取记住的身份信息的字节数组。
        byte[] bytes = getRememberedSerializedIdentity(subjectContext);

        // SHIRO-138 - 只有当字节数组存在时才调用convertBytesToPrincipals方法
        if (bytes != null && bytes.length > 0) {
            // 如果字节数组存在且长度大于0,则调用convertBytesToPrincipals方法将字节数组
//转换为PrincipalCollection对象,即将记住的身份信息还原为主体的身份集合。
            principals = convertBytesToPrincipals(bytes, subjectContext);
        }
    } catch (RuntimeException re) {
        // onRememberedPrincipalFailure方法是一个用于处理记住身份信息失败的回调方法。
//在这里,它将被调用,并将运行时异常和SubjectContext对象作为参数传递给它,
//以获取处理后的PrincipalCollection对象。
        principals = onRememberedPrincipalFailure(re, subjectContext);
    }
    // 返回获取到的PrincipalCollection对象
    return principals;
}

SubjectContext是Apache Shiro中的一个接口,用于在创建或更新Subject(主体)时传递和存储相关的上下文信息。它提供了一种统一的方式来传递Subject的创建或更新所需的各种参数和属性。

SubjectContext接口定义了一些用于存储和获取Subject相关信息的方法,例如设置或获取主体的身份标识、设置或获取主体的认证状态、设置或获取主体的会话等。通过SubjectContext,可以将这些上下文信息传递给Shiro的各个组件,以便进行相应的操作和处理。

SubjectContext的具体实现通常是由Shiro的各个组件提供的,例如在Web应用中,可以使用WebSubjectContext来存储与Web请求相关的信息。

在上述代码中,SubjectContext对象作为参数传递给getRememberedPrincipals方法,用于获取记住的主体信息。通过SubjectContext,可以传递和访问获取记住身份信息所需的上下文信息,如请求数据、配置参数等。

PrincipalCollection是Apache Shiro中的一个接口,用于表示主体(Subject)的身份集合。它是一个包含多个身份信息的容器。

在Shiro中,Principal(身份)是指代表主体的标识信息,可以是用户名、邮箱、手机号等唯一标识主体的信息。PrincipalCollection则是存储一个主体可能拥有的多个身份信息的容器,例如一个用户可能同时拥有多个角色或权限。

PrincipalCollection接口提供了一些方法来处理和操作主体的身份集合,例如添加、移除、获取身份信息等。通过PrincipalCollection,可以访问主体拥有的各种身份信息,并在安全认证和授权过程中进行判断和处理。

PrincipalCollection的具体实现通常由Shiro的各个组件提供,如DefaultPrincipalCollection等。

在上述代码中,getRememberedPrincipals方法返回的就是一个PrincipalCollection对象,它代表了通过记住我(RememberMe)功能获取到的主体的身份集合。通过PrincipalCollection,可以获取和操作这些身份信息,以供后续的认证和授权操作使用。

  1. SubjectContext(主体上下文):SubjectContext是一个上下文接口,用于在创建或更新Subject(主体)时传递和存储相关的上下文信息。它提供了一种统一的方式来传递Subject的创建或更新所需的各种参数和属性。SubjectContext通常用于在主体创建或认证之前传递必要的信息,例如主体的身份标识、认证状态、会话等。
  2. PrincipalCollection(身份集合):PrincipalCollection是一个表示主体的身份集合的接口。它用于存储主体拥有的多个身份信息,例如用户可能同时拥有多个角色或权限。PrincipalCollection提供了一些方法来处理和操作主体的身份集合,例如添加、移除、获取身份信息等。PrincipalCollection主要用于表示主体的身份信息,在认证和授权过程中使用。

总结起来,SubjectContext是用于传递主体创建或更新所需的上下文信息的接口,而PrincipalCollection是用于表示主体的身份集合的接口。SubjectContext包含更广泛的上下文信息,而PrincipalCollection更专注于主体的身份信息。在不同的场景和目的下,它们扮演着不同的角色。


调用decrypt()方法解密。生成一个cipherService对象,调用它的decrypt方法解密。

cipherService是一个接口,具体实现在JcaCipherService中

调用decrypt(encrypted, key, iv)函数进行解密

再跟进,调用了crypt(ciphertext, key, iv, javax.crypto.Cipher.DECRYPT_MODE)方法

调用crypt(cipher,bytes)方法处理

解密完成后,调用deserialize()方法进行反序列化处理,与shiro550类似。

Padding Orcale Attcak

首先要学习下前置知识,CBC翻转攻击:https://goodapple.top/2022/01/06/6db157fde87a6bae/

在解密最后一个函数crypt(cipher,bytes)中有个doFinal()方法,它对密文进行异常处理,doFinal()方法有IllegalBlockSizeException和BadPaddingException这两个异常,分别用于捕获块大小异常和填充错误异常。

无论是Padding错误还是反序列化处理错误,异常都会被getRememberedPrincipals()方法捕获,并执行onRememberedPrincipalFailure()方法。onRememberedPrincipalFailure()方法调用了forgetIdentity()。该方法会调用removeFrom(),在response头部添加字段Set-Cookie: rememberMe=deleteMe。

即,Padding结果不正确的话,响应包就会返回 Set-Cookie: rememberMe=deleteMe

可以得到布尔条件:

  • Padding正确,服务器正常响应
  • Padding错误,服务器返回Set-Cookie: rememberMe=deleteMe

通过该布尔条件,可以通过不断改变“前一个Block”,改变“后1个Block密文”的明文。通过返回结果,来判断解密是否成功。进而获取“后一个Block”解密出的“MediumValue”,得到“MediumValue”就能解密密文,加密明文。大于2组的密文,攻击者可以按规则选取其中2组block进行尝试,从而达到加密解密所有Block的效果

利用 Padding Oracle Attack,我们已经可以得知明文对应的密文以及密文对应的明文,相当于知道了key值,我们又知道在 CBC 加密模式中,第 n 个密文分组可以影响第 n+1 个明文分组,那我们已经知道了密文对应的明文,这里修改第 n 个密文分组,就相当于控制第 n+1 个明文分组。

即可构造我们的恶意数据,在数据解密后通过验证,执行反序列化时触发我们的恶意数据。

利用工具

https://github.com/inspiringz/Shiro-721

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇