Experiments with JSON-IO, Serialization, Mass Assignment, and General Java Object Wizardry

Experiments with JSON-IO, Serialization, Mass Assignment, and General Java Object Wizardry

Overview – JSON Research

So before I even begin, I want to immediately layout that this JSON research is purely experimental, and that conceptually it was hard to build a working abuse case around the ideas I will be presenting. It was also difficult for me to find real word examples representing any of the issues around the technologies and design I think are potentially relevant. That being said, this is the beauty of research and using it to lay a foundation of forward thinking to address the possibilities of new problems.

I have been very fascinated with the concept of serialization as a whole, moreover the serialization of objects into different formats, and those formats back into objects. I’m sure everyone is relatively familiar with Python’s Pickle library and vulnerabilities that arise from using loads() on untrusted ‘pickled’ objects. Alvaro Munez, Dinis Cruz, and Abraham Kang presented fantastic research on vulnerabilities within XML serialization parsers that allow for remote code execution in libraries like XStream. PHP also boasts similar vulnerabilities with its implementation of serialization functionality.

My focus is currently on Java libraries that support the serialization of objects into JSON, and JSON back into objects. Originally I started looking at the Jackson JSON processing library, which I feel is probably the most common at this point. Jackson has an interesting feature that allows you to handle polymorphic types, which when used correctly allows you to enable additional information so that deserializer can instantiate and handle the correct subtype of a value. So not to get into Jackson to deeply, but what I discovered is essentially the JSON output of the serialization contained what appeared to be meta-typed info within the string, which indicated the object it was constructed from. I eventually went from Jackson to json-io, which seemed to have more of a feature set that I was looking for to do nefarious things.

json-io

https://github.com/jdereg/json-io – “Perfect Java serialization to and from JSON format”.

The most interesting feature that json-io has is its formatting. “ When an object’s type must be emitted, it is emitted as a meta-object field “@type”:”package.class” in the object.  When read, this tells the JsonReader what class to instantiate.

So consider this User class, and what it looks like after we serialize it into JSON using json-io:

public class User {

private String myage;

private String mysex;

private String mylocation;

private transient Boolean isAdmin = false;

public User(String age, String sex, String location){

myage = age;

mysex = sex;

mylocation = location;

}

public Boolean getIsAdmin() {

return isAdmin;

}

public Boolean setIsAdmin() {

return isAdmin;

}

public String getMyage() {

return myage;

}

public String getMysex() {

return mysex;

}

public String getMylocation() {

return mylocation;

}

public String profile() {

return "Profile:" + " " + myage + " " + mysex + " " + mylocation;

}

}
public class JSONTest {

public static void main(String[] args) throws IOException {

User user1 = new User("Benjamin", "Watson");

String json = JsonWriter.objectToJson(user1);

System.out.println(json);

}

}

We can see the meta-object fields within the JSON string:

{"@type":"org.rotlogix.json.io.tests.User","fname":"Benjamin","lname":"Watson"}

Then we can serialize this back into an object and cast it against the already instantiated User class:

public class JSONTest {

public static void main(String[] args) throws IOException {

User user1 = new User("Benjamin", "Watson");

String json = JsonWriter.objectToJson(user1);

User user2 = (User) JsonReader.jsonToJava(json);

System.out.println(user2);

}

}
org.rotlogix.json.io.tests.User@7d745e95

Now consider if we take a JSON string and use it to describe a non-existent class and attempt to serialize this:

json_io_foobar_class

Because the Class doesn’t exist, the serializer can’t construct a new instance from the meta-object fields within the string. However if we modify the JSON string with the correct object information, which includes assigning a value to the ‘fname’ field:

json_io_correct_user_success

We get the value back that we set within the JSON string.

Based off this example we understand that if a meta-typed string is being used to instantiate a new object through the serialization process, the object and or class must already exist. So from an attacker’s perspective, if the input is controllable in this way, you would have to discern the full path of the class in order to create a new instance, or modify fields within the object. This as you can imagine is very limited.

Failures

json_io_correct_user_success

So I thought about what if the JSON string isn’t getting cast against a class that the developer would have created, but something native to the Java language that could be manipulated for later use like the default object class. Here I spent the majority of my time attempting remote code execution with little success. The whole concept here is just completely instantiating arbitrary objects, but the problem is there is no way to call methods on them making this completely useless.

Other things I experimented around with was attempting to use Java’s URLClassLoader to load things like jar://, file:///. This presented interesting problems with class constructors and instantiation. No dice. I also encountered a lot of reflection issues with attempting to modify the field values with different access modifiers and or types.

Exception in thread “main” java.io.IOException: IllegalAccessException setting field ‘url’ on target:

Mass Assignment Considerations

So through much frustration I settled in on a couple practical use cases, which I feel are some what relevant. We want to control the creation of new objects that get casted against a class that has been blueprinted by the developer. Discerning this through stack traces from the application is possible, especially in the case of serialization errors when are just arbitrarily fuzzing. Based upon what the class does, we “could” create new instances of object and set new values on the fields that hold some importance. I am only categorizing this as Mass Assignment, because it makes the most sense to me, but I am not saying this is at all a completely accurate classification.

Let us take a look at an example User class we have set up for our vulnerable application. We can see where we have a private filed “isAdmin” set to false, with its corresponding getters and setters. If we can modify the “isAdmin” field and set it to true, then this will change the overall state of the application and essentially make us an admin for better terms.

Putting It All Together

Here is an application that allows us to create a profile (This is so simple it hurts):

json_io_create_new_profile

There is zero protection on data submitted to the application, so other vulnerabilities arise, but that is not the focus. At a high level the data submitted is used to create a new User instance. The instance is then serialized into a JSON string using json-io, which is then encoded in base64 and set as a new cookie.

json_io_profile_view

The request is redirected and validation occurs on the cookies within the scope. The cookie is decoded, deserialized, and a new instance is casted against the User class. Our profile data is then set as request attributes, which populate the overall view within the application.

If we inspect and decode the cookie, we can see the JSON string has the object’s meta information inside it, disclosing the “isAdmin” field. This happens because the entire object and its attributes become serialized. We should now be able to modify the “isAdmin” field to true, and when the validation process occurs, it will create a new instance and assign the modified value.

json_io_correct_cookie_enc_dec

Now if we replace the current value of the cookie with “isAdmin” set to true, we should see a change in our profile. I am using a Firefox plugin to substitute my cookie data.

json_io_admin_true

We have effectively set the Boolean value to true, which gets echoed back to use in our profile now. If this object instance is also being persisted in any sort of data model, then we have potentially bypassed access control completely.

Conclusion

I can imagine there are a lot of questions around why you would even develop functionality in such a way. I will admit this example feels pretty rudimentary, but I don’t think it is completely far fetched. Most of this is rooted in all the possibilities of why you would want to take data in a JSON format, serialize it, pass it around the application, and to other entities. Extensibility comes to mind. In this example, if you are using json-io to serialize something that ultimately controls the access of any given user that creates a new account, you should probably look at your class data and decide what you want and do not want to have serialized. Shout outs to @pwntester and @jonpasski for all the help, and if you want to play around with the demo application it is located here -> https://github.com/VerSprite/profile-builder

Protect Your Assets from Various Threat Actors

VerSprite’s Research and Development division (a.k.a VS-Labs) is comprised of individuals who are passionate about diving into the internals of various technologies.

Our clients rely on VerSprite’s unique offerings of zero-day vulnerability research and exploit development to protect their assets from various threat actors.

From advanced technical security training to our research for hire B.O.S.S offering, we help organizations solve their most complex technical challenges. Learn more about Research as a Service →

 

View our security advisories detailing vulnerabilities found in major products for MacOs, Windows, Android, and iOS.