Skip to content

OnlineSessionFactory存在不安全反序列化漏洞(可导致RCE) #322

Description

@pegasus-l

漏洞背景

OnlineSessionFactory.createSession(SysUserOnline) 方法(第36-38行)使用原生ObjectInputStream.readObject()从数据库sys_user_online.session_data字段反序列化会话数据:

ByteArrayInputStream bis = new ByteArrayInputStream(userOnline.getSessionData());
ObjectInputStream ois = new ObjectInputStream(bis);
OnlineSession onlineSession = (OnlineSession) ois.readObject();

该反序列化过程存在以下安全缺陷:

  • 未配置ObjectInputFilter(JEP 290)进行类白名单过滤
  • 未重写resolveClass()进行类校验
  • 未对session_data进行HMAC完整性校验
  • 类型转换发生在readObject()之后,gadget chain已经执行

漏洞表现

如果攻击者能够向sys_user_online.session_data字段写入恶意序列化数据(通过SQL注入或数据库直接访问),下一次使用该会话的HTTP请求将触发反序列化,执行攻击者构造的gadget chain载荷,实现远程代码执行(RCE)。

该反序列化通过Shiro过滤器链在每次认证请求中触发:
OnlineSessionFilterOnlineSessionDAO.doReadSession()SysShiroService.getSession()OnlineSessionFactory.createSession()readObject()

复现步骤

环境: RuoYi v4.8.3,默认配置,MySQL 8.0

  1. 使用admin登录系统,等待1分钟让会话同步到数据库
  2. 确认会话数据已写入:
SELECT sessionId, login_name, LENGTH(session_data) as data_len FROM sys_user_online;
-- 输出示例:
-- sessionId: 70a2a937-15ee-41e7-807e-b76af74d5a78
-- data_len: 3109
  1. session_data替换为畸形的Java序列化流(ACED0005为Java序列化魔数):
UPDATE sys_user_online 
SET session_data = 0xACED00057372002E6A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C654C697374FC0F2531B5EC8E100200014C00046C6973747400104C6A6176612F7574696C2F4C6973743B787200 
WHERE sessionId = '70a2a937-15ee-41e7-807e-b76af74d5a78';
  1. 重启应用以清除Shiro的EhCache内存缓存
  2. 在浏览器中刷新页面(使用相同session)
  3. 查看应用日志:
WARN c.r.f.s.s.OnlineSessionFactory - deserialize OnlineSession failed, 
     sessionId=70a2a937-15ee-41e7-807e-b76af74d5a78
java.io.UTFDataFormatException

结果: 应用确实通过ObjectInputStream.readObject()反序列化了注入的数据,且无任何类过滤。使用ysoserial等工具生成的有效gadget chain载荷(如Spring1、Spring2)将在此处执行任意代码。

关于攻击前置条件

此漏洞需要攻击者能够写入sys_user_online.session_data字段,通常需要:

  • 应用中其他位置存在SQL注入漏洞
  • 数据库凭据泄露(如application-druid.yml暴露)
  • 共享主机环境下的数据库访问

虽然需要链式利用,但这是一个严重的纵深防御缺陷——一旦存在任何SQL注入,此反序列化触发点会将数据层面的入侵升级为服务器完全沦陷。

涉及文件

  • ruoyi-framework/.../shiro/session/OnlineSessionFactory.java(第36-38行)—— 反序列化触发点
  • ruoyi-framework/.../manager/factory/AsyncFactory.java(第62-66行)—— 序列化写入
  • ruoyi-framework/.../shiro/session/OnlineSessionDAO.java(第54-57行)—— 会话读取
  • ruoyi-framework/.../shiro/service/SysShiroService.java(第45-46行)—— 数据库查询+工厂调用

修复建议

方案一:添加ObjectInputFilter(最小改动)

ObjectInputStream ois = new ObjectInputStream(bis);
ois.setObjectInputFilter(filterInfo -> {
    Class<?> clazz = filterInfo.serialClass();
    if (clazz != null) {
        String name = clazz.getName();
        if (name.startsWith("com.ruoyi.") || 
            name.startsWith("java.lang.") || 
            name.startsWith("java.util.")) {
            return ObjectInputFilter.Status.ALLOWED;
        }
        return ObjectInputFilter.Status.REJECTED;
    }
    return ObjectInputFilter.Status.UNDECIDED;
});

方案二:替换序列化方式(推荐)

使用JSON(Jackson)替代Java原生序列化,从根本上消除反序列化攻击面。

方案三:添加HMAC完整性校验(最佳)

在序列化数据存储前添加HMAC签名,反序列化前验证签名,防止数据被篡改。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions