Deserialization 101
César Silva <@apl3b/>
Can we kill it?
What is it?
What is it?
A8 - Insecure Deserialization
Serialization is the process of turning some object into a data format that can be restored later
Deserialization is the process of restore the serialized data
or
Reverse the serialization process
Formats
-
Binary Data
-
Text
-
XML
-
JSON
-
-
Custom Binary Formats
Reflection
Ability to examine, introspect and modify its own structure at runtime
class Test{
private string Name { get; set; }
public string formatName(){
return "My name is :" + this.Name;
}
}
// Reflection
class Main{
public void Main(string[] args){
Object test = Activator.CreateInstance("my.name.space.Test");
var my_function = test.GetType().GetMethod("formatName");
my_function.Invoke(test, null);
}
}
Vectors
- Default constructor and setters
- Special Constructor or "deserialization callbacks"
- Default constructor and reflection
Default Constructor and Setters
The serializer creates a new object using the default parameter-less constructor and uses setters to populate all the fields
More limited because we can only access public properties
Default Constructor and Setters
<src = "https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf">
Special Constructor and "Deserialization Callbacks"
The serializer creates a new object using special constructors to trigger special logic required to initialize an object
Json.Net's [OnError]
.NET's [OnDeserialized]
.NET's [OnDeserializing]
etc
Special Constructor and "Deserialization Callbacks"
public final class SerializableRenderedImage implements RenderedImage, Serializable {
protected void finalize() throws Throwable {
dispose();
// Triggers the payload when called by the garbage collection
super.finalize();
}
...
}
Default Constructor and Reflection
The serializer creates a new object using the default parameter-less constructor and uses reflection to populate all the fields
Vulnerable on destructors, some reconstructions invocations (i.e.: HashTables) and common calls (toString())
Default Constructor and Reflection
//Newtonsoft.Json.Converters.EntityKeyMemberConverter
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
EntityKeyMemberConverter.EnsureReflectionObject(objectType);
object obj = EntityKeyMemberConverter._reflectionObject.Creator(new object[0]);
EntityKeyMemberConverter.ReadAndAssertProperty(reader, "Key");
reader.ReadAndAssert();
EntityKeyMemberConverter._reflectionObject.SetValue(obj, "Key",
reader.Value.ToString());
EntityKeyMemberConverter.ReadAndAssertProperty(reader, "Type");
reader.ReadAndAssert();
Type type = Type.GetType(reader.Value.ToString());
EntityKeyMemberConverter.ReadAndAssertProperty(reader, "Value");
reader.ReadAndAssert();
EntityKeyMemberConverter._reflectionObject.SetValue(obj, "Value",
serializer.Deserialize(reader, type));
reader.ReadAndAssert();
return obj;
}
C#
using Newtonsoft.Json;
namespace deserialization_training
{
public static class SerializerHelper
{
public static dynamic Deserialize(string json)
{
// Use dynamic to use properties independently from the type
return JsonConvert.DeserializeObject<dynamic>(json,
// Include type information to allow for a more smooth casting and check
new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.All
});
}
public static string Serialize(dynamic obj) {
return JsonConvert.SerializeObject(obj);
}
}
}
Payload Example
{
'$type':'System.Windows.Data.ObjectDataProvider,\
PresentationFramework,\
Version=4.0.0.0,\
Culture=neutral,\
PublicKeyToken=31bf3856ad364e35',
'MethodName':'Start',
'MethodParameters':{
'$type':'System.Collections.ArrayList,\
mscorlib,\
Version=4.0.0.0,\
Culture=neutral,\
PublicKeyToken=b77a5c561934e089',
'$values':['cmd','/c calc']
},
'ObjectInstance':{'$type':'System.Diagnostics.Process,\
System,\
Version=4.0.0.0,\
Culture=neutral,\
PublicKeyToken=b77a5c561934e089'}
}
Java
import java.io.ObjectInputStream;
import java.io.FileInputStream;
import java.io.ObjectOutputStream;
import java.io.FileOutputStream;
import java.io.Serializable;
import java.io.IOException;
public class SerializeExample {
public static void main(String args[]) throws Exception {
VulnerableObject testObj = new VulnerableObject();
testObj.name = "test";
ObjectOutputStream oStream = new ObjectOutputStream(new FileOutputStream("test.bin"));
oStream.writeObject(testObj);
oStream.close();
ObjectInputStream iStream = new ObjectInputStream(new FileInputStream("test.bin"));
VulnerableObject readObj = (VulnerableObject)iStream.readObject();
System.out.println(readObj.name);
iStream.close()
}
}
class VulnerableObject implements Serializable {
public String name;
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
this.name = "Class_" + this.name;
}
}
0xAC 0xED
rO0
Python
import pickle
class User(object):
def __init__(self, name):
self.name = name
serialized = pickle.dumps(User('Some Username')))
user = pickle.loads(serialized)
Cool Resources
Tools
Materials
Can we kill it?
C#
using Newtonsoft.Json;
namespace deserialization_training
{
public static class SerializerHelper<T>
{
public static T Deserialize(string json)
{
// Set a type to limit the object's tree
return JsonConvert.DeserializeObject<T>(json,
// Do not use TypeNameHandling. If needed, define a CustomParser
new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.None
});
}
public static string Serialize(T obj) {
return JsonConvert.SerializeObject(obj);
}
}
}
Java
public class SecureObjectInputStream extends ObjectInputStream {
public SecureObjectInputStream(InputStream inputStream) throws IOException {
super(inputStream);
}
/**
* Only deserialize instances of our expected class
*/
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException,
ClassNotFoundException {
if (!desc.getName().equals(VulnerableObject.class.getName())) {
throw new InvalidClassException(
"Unauthorized deserialization attempt",
desc.getName());
}
return super.resolveClass(desc);
}
}
Python
Use a secure module for deserialization
hmac (verify the object's integrity)
write your own parser
Recap
- Validate all inputs
- Do not trust external data
- Do not allow for dynamic typing
- In last case, write custom parsers to enforce typing
?
Thank you
Deserialization 101
By apl3b
Deserialization 101
101 on Deserialization Vulnerabilities
- 423