Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to analyze the utilization chain of TemplatesImpl in FastJson deserialization RCE vulnerability

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >

Share

Shulou(Shulou.com)05/31 Report--

This article introduces how to carry out FastJson deserialization RCE vulnerability in the TemplatesImpl utilization chain analysis, the content is very detailed, interested friends can refer to, hope to be helpful to you.

0. Preface

Record some of the details of FastJson deserialization RCE vulnerability analysis and exploitation.

1. The utilization chain of TemplatesImpl about parse and parseObject

Both the parse () and parseObject () methods in FastJson can be used to deserialize JSON strings into Java objects, and parseObject () essentially calls parse () for deserialization. But parseObject () additionally converts the Java object into a JSONObject object, that is, JSON.toJSON (). So the difference in detail when deserializing is that parse () recognizes and calls the setter method of the target class and the getter method of certain conditions, while parseObject () invokes all the setter and getter methods of the target class during processing because it executes more JSON.toJSON (obj). The source code for parseObject () is as follows:

Public static JSONObject parseObject (String text) {Object obj = parse (text); if (obj instanceof JSONObject) {return (JSONObject) obj;} return (JSONObject) JSON.toJSON (obj);}

Take a simple example:

Public class FastJsonTest {public String name; public String age; public FastJsonTest () throws IOException {} public void setName (String test) {System.out.println ("name setter called"); this.name = test;} public String getName () {System.out.println ("name getter called"); return this.name;} public String getAge () {System.out.println ("age getter called") Return this.age;} public static void main (String [] args) {Object obj = JSON.parse ("{\" @ type\ ":\" fastjsontest.FastJsonTest\ ",\" name\ ":\" thisisname\ ",\" age\ ":\" thisisage\ "}"); System.out.println (obj) Object obj2 = JSON.parseObject ("{\" @ type\ ":\" fastjsontest.FastJsonTest\ ",\" name\ ":\" thisisname\ ",\" age\ ":\" thisisage\ "}); System.out.println (obj2);}}

After the above code runs, you can see that when parse () is executed, only setName () will be called. When parseObject () is executed, setName (), getAge (), and getName () are all called.

Why getOutputProperties () is triggered

It seems that deserialization of parse () to create a Java class should only call the setter method to assign member variables. What would trigger the getOutputProperties () method in the TemplatesImpl class?

In addition, there is an obvious difference of ``character between outputProperties member variable and getOutputProperties (). How is it associated with FastJson?

As mentioned in the previous section, when parse () deserializes, it actually calls some specific getter methods for field parsing, and the getOutputProperties () method in the TemplatesImpl class happens to meet this condition.

The main logic when deserializing FastJson to the Java class is as follows:

Get and save the member variables, setter, getter in the target Java class.

Parse the JSON string, process the fields one by one, and call the corresponding setter and getter to assign variables.

Let's take a look at the first step, which is handled by JavaBeanInfo.build (), where FastJson creates an filedList array that holds the member variables of the target Java class and the corresponding setter or getter method information for subsequent deserialization of fields.

The general structure of filedList is as follows:

[{name: "outputProperties", method: {clazz: {}, name: "getOutputProperties", returnType: {},...}}]

FastJson does not directly reflect and obtain the member variables of the target Java class, but processes the setter, getter and member variables respectively to intelligently extract the member variable information. The logic is as follows:

Identify the setter method name and extract the member variable name based on the setter method name. For example, if the setAge () method is identified, FastJson will extract the age variable name and insert the filedList array.

Get the member variables through clazz.getFields ().

Identify the getter method name and extract the member variable name based on the getter method name.

You can see that in JavaBeanInfo.build (), there is a piece of code that judges the getter method and, under some special conditions, extracts the member variable name from the getter method and appends it to the filedList array. GetOutputProperties () in the TemplatesImpl class satisfies this particular condition. The processing code for the getter method is:

JavaBeanInfo.javapublic static JavaBeanInfo build (Class clazz, Type type, PropertyNamingStrategy propertyNamingStrategy) {... For (Method method: clazz.getMethods ()) {/ / getter methods String methodName = method.getName (); if (methodName.length ()

< 4) { continue; } if (Modifier.isStatic(method.getModifiers())) { continue; } if (methodName.startsWith("get") && Character.isUpperCase(methodName.charAt(3))) { if (method.getParameterTypes().length != 0) { continue; } // 关键条件 if (Collection.class.isAssignableFrom(method.getReturnType()) // || Map.class.isAssignableFrom(method.getReturnType()) // || AtomicBoolean.class == method.getReturnType() // || AtomicInteger.class == method.getReturnType() // || AtomicLong.class == method.getReturnType() // ) { String propertyName; JSONField annotation = method.getAnnotation(JSONField.class); if (annotation != null && annotation.deserialize()) { continue; } if (annotation != null && annotation.name().length() >

0) {propertyName = annotation.name ();} else {propertyName = Character.toLowerCase (methodName.charAt (3)) + methodName.substring (4);} FieldInfo fieldInfo = getField (fieldList, propertyName); if (fieldInfo! = null) {continue } if (propertyNamingStrategy! = null) {propertyName = propertyNamingStrategy.translate (propertyName);} add (fieldList, new FieldInfo (propertyName, method, null, clazz, type, 0,0,0, annotation, null, null));}}.}

Next, FastJson parses the JSON string semantically. According to the field key, call the corresponding method stored in the filedList array to initialize the variable assignment. The specific logic is implemented in parseField ():

JavaBeanDeserializerpublic boolean parseField (DefaultJSONParser parser, String key, Object object, Type objectType, Map fieldValues) {JSONLexer lexer = parser.lexer; / / xxx FieldDeserializer fieldDeserializer = smartMatch (key); Return true;}

A magic smartMatch () method is called here, which replaces the _ in the field key when smartMatch (), so that _ outputProperties and getOutputProperties () can be successfully associated.

JavaBeanDeserializerpublic FieldDeserializer smartMatch (String key) {if (fieldDeserializer = = null) {boolean snakeOrkebab = false; String key2 = null; for (int I = 0; I < key.length (); + + I) {char ch = key.charAt (I); if (ch = ='_') {snakeOrkebab = true Key2 = key.replaceAll ("_", "); break;} else if (ch = ='-') {snakeOrkebab = true; key2 = key.replaceAll ("-","); break }} if (snakeOrkebab) {fieldDeserializer = getFieldDeserializer (key2); if (fieldDeserializer = = null) {for (FieldDeserializer fieldDeser: sortedFieldDeserializers) {if (fieldDeser.fieldInfo.name.equalsIgnoreCase (key2)) {fieldDeserializer = fieldDeser Break;} Why do you need to Base64 encode _ bytecodes

If you look carefully, you can see that the _ bytecodes field in PoC is Base64-encoded. Why would you do that? After analyzing the parsing process of JSON string by FastJson, it turns out that Base64 decoding will be performed when FastJson extracts the field value of byte [] array, so we need to Base64 _ bytecodes when we construct payload. The processing code for FastJson is as follows:

ObjectArrayCodec public T deserialze (DefaultJSONParser parser, Type type, Object fieldName) {final JSONLexer lexer = parser.lexer; / /. Omit part of the code if (lexer.token () = = JSONToken.LITERAL_STRING) {byte [] bytes = lexer.bytesValue (); / /. Parse the byte array value lexer.nextToken (JSONToken.COMMA) here; return (T) bytes;} / / then call JSONScanner.bytesValue () JSONScanner public byte [] bytesValue () {return IOUtils.decodeBase64 (text, np + 1, sp) } on how to carry out FastJson deserialization RCE vulnerability TemplatesImpl utilization chain analysis is shared here, I hope the above content can be of some help to you, can learn more knowledge. If you think the article is good, you can share it for more people to see.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Network Security

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report