freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

JAVA安全——红队面试常问问题之Shrio漏洞详解
pony686 2025-03-06 11:42:54 73851
所属地 北京

一、Apache Shiro介绍


Apache Shiro 是一个功能强大且易于使用的 Java 安全框架,它用于处理身份验证,授权,加密和会话管理
在默认情况下 , Apache Shiro 使用 CookieRememberMeManager 对用户身份进行序列化/反序列化 , 加密/解密和编码/解码 , 以供以后检索 .
因此 , 当 Apache Shiro 接收到未经身份验证的用户请求时 , 会执行以下操作来寻找他们被记住的身份.
从请求数据包中提取 Cookie 中 rememberMe 字段的值,对提取的 Cookie 值进行 Base64 解码,对 Base64 解码后的值进行 AES 解密,对解密后的字节数组调用 ObjectInputStream.readObject() 方法来反序列化.
但是默认AES加密密钥是 “硬编码” 在代码中的 . 因此 , 如果服务端采用默认加密密钥 , 那么攻击者就可以构造一个恶意对象 , 并对其进行序列化 , AES加密 , Base64编码 , 将其作为 Cookie 中 rememberMe 字段值发送 . Apache Shiro 在接收到请求时会反序列化恶意对象 , 从而执行攻击者指定的任意代码 .
Shiro 550 反序列化漏洞存在版本:shiro <1.2.4,产生原因是因为shiro接受了Cookie里面rememberMe的值,然后去进行Base64解密后,再使用aes密钥解密后的数据,进行反序列化。
构造该值为一个利用链序列化后的值进行该密钥aes加密后进行base64加密,反序列化payload内容后就可以命令执行。

二、漏洞分析


环境下载 https://github.com/yyhuni/shiroMemshell/tree/master/shirodemo
测试环境 apache-tomcat-8.5.50 jdk 1.8 202 shiro 1.2.24
下载到本地用idea打开

账号 root 密码 secret

设置好 访问

1741232090_67c917dae2c223ab97730.png!small?1741232092655
登录之后抓包 成功后 访问网站发现 在cookie多了一个 rememberMe 字段

1741232104_67c917e88dba6005f13da.png!small
在 org/apache/shiro/web/mgt/CookieRememberMeManager.java 下设置一个断点 调试打开网站

protected byte[] getRememberedSerializedIdentity(SubjectContext subjectContext) {

        if (!WebUtils.isHttp(subjectContext)) {
            if (log.isDebugEnabled()) {
                String msg = "SubjectContext argument is not an HTTP-aware instance.  This is required to obtain a " +
                        "servlet request and response in order to retrieve the rememberMe cookie. Returning " +
                        "immediately and ignoring rememberMe operation.";
                log.debug(msg);
            }
            return null;
        }

        WebSubjectContext wsc = (WebSubjectContext) subjectContext;
        if (isIdentityRemoved(wsc)) {
            return null;
        }

        HttpServletRequest request = WebUtils.getHttpRequest(wsc);
        HttpServletResponse response = WebUtils.getHttpResponse(wsc);

        String base64 = getCookie().readValue(request, response);
        // Browsers do not always remove cookies immediately (SHIRO-183)
        // ignore cookies that are scheduled for removal
        if (Cookie.DELETED_COOKIE_VALUE.equals(base64)) return null;

        if (base64 != null) {
            base64 = ensurePadding(base64);
            if (log.isTraceEnabled()) {
                log.trace("Acquired Base64 encoded identity [" + base64 + "]");
            }
            byte[] decoded = Base64.decode(base64);
            if (log.isTraceEnabled()) {
                log.trace("Base64 decoded byte array length: " + (decoded != null ? decoded.length : 0) + " bytes.");
            }
            return decoded;
        } else {
            //no cookie set - new site visitor?
            return null;
        }
    }

1741232131_67c9180327123283b646b.png!small
提交包 把cookie session那个去掉 让程序读取 rememberMe这段

GET /shirodemo_war/ HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/112.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://localhost:8081/shirodemo_war/login.jsp
Connection: close
Cookie:  rememberMe=ka1Ha+W9LDXInz0tXOyTFrFfm/M/SbsiUPeYxQGPs+IqjiGn4T9DBb4qLiZSvbLfOB9zkqIPfRKMcuWt4OvwymzOKFJkhmebyUJ7vVvttHo201UXGyjYxuYkdgX1JDTDgKOINOyb1K/soto/Rft8PON7POeVfj7EzL5Q7Ji/FIXxBvhetg4VfBs5jZW+ZKwo1I9VrYte71He1gi39pIOEzIHq78xfuIF1m3Iju8R6oKn47cpYnxGLa61xf4DE6sftCR7b0yGFqQU+hWmfXd+qxoacLYfo8MMwV8B+MIsStyGK7yGqvNwRO7l44KVH890gtS1BO84zX9vIji2JmJzWEtwpZ1kxQXqU2fx++5qwnfWovlW7wrEly6H1qCl93C1Udf4b6QWlctFvj7xDOTNw558WfSKB3Oe+N2bSSf/MtuIrbvAK+Uezl3wXnKmX94nEzJn8FsX7UZANqC8OOIqf54vL2b+BZcnnkTDkfLkNNeVstWNWt6LrwxiVCDeHhLD; Hm_lvt_42e5492fd27f48fc8becc94219516005=1678517471; _ga_WNLDH1S58P=GS1.1.1678517472.1.0.1678517484.0.0.0; _ga=GA1.1.1743648385.1678517472; __bid_n=1876a8763dc4f81b144207; Hm_lvt_1040d081eea13b44d84a4af639640d51=1681119471; FPTOKEN=CBYwY1vqboLVYjyDBqLlG6mLeS2dp0pfN+q78Dahs8zM1hQU2X8EUH0vILRgbn5C5a2G9rJOFQnpwtT+WKRF1ZweRsDFtkEc+i5KZI9DyVZpwV2Elnfrlh3ALJloXfEVtPtpJM6hhuzHlK4NY9J+jsZrMot2o5vOc6dSiKPacVyNFzIz+vvto3NgvXd/dtOXEr7cgjeul0qJM02VGtpmVtckCuIdB3Rmz/s2cj84LBRhLpP4WkiFTrdaE23Grdu84DwcziDuOWk+4iDehqlRZZQYYPghRzuGCxPeO/19d34T35r/Cok028cVe4sKLxtGvw/oUdzPKCzFABTtTk4HBt/QRviZS45E+TKD8DUgAYOd12SezamFFBLh6tW1kPshshu5hqwDFz5ZuyKakyt61A==|3uAMcHU7rpdn+Lq3u6Oy9wzSKQK2ztvNVV5V2pM18R4=|10|06b25955e8fbbbd80dff787def184fbd
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1

1741232145_67c9181191e3e58d7b8fb.png!small?1741232146781
判断传入的cookie是否为空
1741232160_67c918201a8db97c2c244.png!small?1741232161380
byte[] decoded = Base64.decode(base64); 解码
1741232193_67c918414430560c5fcfa.png!small?1741232194414
执行返回到 org/apache/shiro/mgt/AbstractRememberMeManager.java

public PrincipalCollection getRememberedPrincipals(SubjectContext subjectContext) {
        PrincipalCollection principals = null;
        try {
            byte[] bytes = getRememberedSerializedIdentity(subjectContext);
            //SHIRO-138 - only call convertBytesToPrincipals if bytes exist:
            if (bytes != null && bytes.length > 0) {
                principals = convertBytesToPrincipals(bytes, subjectContext);
            }
        } catch (RuntimeException re) {
            principals = onRememberedPrincipalFailure(re, subjectContext);
        }

        return principals;
    }

1741232210_67c91852642975443e723.png!small?1741232211698
判断 bytes != null && bytes.length > 0 进入 convertBytesToPrincipals1741232218_67c9185a33d0ace226266.png!small?1741232219920
convertBytesToPrincipals 解密 处理

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

1741232232_67c918688e4e93a1e8d28.png!small?1741232233988
可以看到 在getCipherService() 存在一些 加密信息 modename cbc
1741232240_67c9187066d63acc786e2.png!small?1741232242241
解密函数 encrypted解密内容 getDecryptionCipherKey() 这个是获取加密的参数 也就是硬编码

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;
    }

1741232251_67c9187b9cbe3c66a0c74.png!small?1741232252938
跟进 getDecryptionCipherKey() 发现在 org/apache/shiro/mgt/AbstractRememberMeManager.java
解密完成后返回 serialized
1741232260_67c9188447949983fe5e0.png!small?1741232262029
完成解密后被 deserialize调用

可试读前30%内容
¥ 19.9 全文查看
9.9元开通FVIP会员
畅读付费文章
最低0.3元/天
# 资讯 # 渗透测试 # web安全 # 系统安全 # 数据安全
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 pony686 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
红队攻防演练
pony686 LV.6
公众号:亿人安全
  • 66 文章数
  • 429 关注者
记一次域渗透从MD文档XSS漏洞到命令执行漏洞获取管理员权限
2025-04-14
记一次域渗透实战之从边界突破到域内ADCS-9漏洞利用
2025-04-09
Java安全红队面试详解 | servlet 内存马原理分析和POC编写和查杀
2025-03-25
文章目录