JFinal-RedisCache-unserializer

在Jfinal使用redis缓存时存在的反序列化问题

src/main/java/com/jfinal/plugin/redis/serializer/JdkSerializer.java#valueFromBytes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public Object valueFromBytes(byte[] bytes) {
if (bytes == null || bytes.length == 0)
return null;
ObjectInputStream objectInput = null;
try {
ByteArrayInputStream bytesInput = new ByteArrayInputStream(bytes);
objectInput = new ObjectInputStream(bytesInput);
return objectInput.readObject();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (objectInput != null)
try {
objectInput.close();
} catch (Exception e) {
LogKit.error(e.getMessage(), e);
}
}
}

可以看到调用了readObject,我们看看在哪使用了valueFromBytes方法

发现在src/main/java/com/jfinal/plugin/redis/Cache.java#get 使用了valueFromBytes方法

1
2
3
4
5
6
7
8
public <T> T get(Object key) {
Jedis jedis = getJedis();
try {
return (T)valueFromBytes(jedis.get(keyToBytes(key)));
} finally {
close(jedis);
}
}

又有多个方法调用了valueFromBytes 但是get方法是通过查询redis键所对应的值 比较 常用

1
2
3
protected Object valueFromBytes(byte[] bytes) {
return this.serializer.valueFromBytes(bytes);
}

返回的是serializer的对象中的方法,默认serializer指定的对象FstSerializer,我们需要在传入RedisPlugin为JdkSerializer才能进入readObject

在配置中需要指定

1
2
3
4
5
6
@Override
public void configPlugin(Plugins me) {
RedisPlugin redis = new RedisPlugin("evilRedis","127.0.0.1");
redis.setSerializer(new JdkSerializer());
me.add(redis);
}

使用缓存时触发

1
2
Cache userCache = Redis.use("evilRedis");
userCache.get("data");

进入redis在data这个键值中修改为Yso生成的目标有的依赖链 将反序列化的字节码存入data键所对应的值中 访问触发缓存即可RCE