Skip to content

dangerous-function-deserialization

Ensure safe deserialization

The native object deserialization functions offered by many languages can potentially be repurposed for malicious effect when operating on untrusted data. Attacks leveraging deserialization functions could lead to denial of service (DOS), bypass of access controls or allow for performing remote code execution (RCE).

Examples

Insecure Example

# Using of pickle/c_pickle/_pickle with load/loads:
import pickle
data = """ cos.system(S'dir')tR. """
pickle.loads(data)

###

# Using PyYAML with load:
import yaml
document = "!!python/object/apply:os.system ['ipconfig']"
print(yaml.load(document))
class SomeController < ApplicationController
    def index
        # Passing arbitrary user data to any of the following functions is dangerous
        cfg = YAML.load(params[:config])
        cfg = CSV.load(params[:config])
        cfg = Marshal.load(params[:config])
        cfg = Marshal.restore(params[:config])

        # In the case of YAML, this can be made safe by disabling unsafe features
        cfg = YAML.load(params[:config], safe: true)
    end
end
// Passing arbitrary user data to any of the following Java API is dangerous
// - XMLdecoder with external user defined parameters
// - XStream with fromXML method (xstream version <= v1.46 is vulnerable to the serialization issue)
// - Serializable
// - ObjectInputStream with readObject
// - Uses of readObject, readObjectNodData, readResolve or readExternal
// - ObjectInputStream.readUnshared

public class Session {
    public String username;
    public boolean loggedIn;

    public void loadSession(byte[] sessionData) throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(sessionData));
        this.username = ois.readUTF();
        this.loggedIn = ois.readBoolean();
    }
}

Secure Example

# Always prefer using pure data formats such as JSON
import json
rawJSON =  '{ "name":"John Doe" }'
parsedJSON = json.loads(x)
print(parseJSON["name"])

# If you need to use YAML, make sure to use YAML parser's safe functions
import yaml
document = "!!python/object/apply:os.system ['ipconfig']"
print(yaml.safe_load(document))
require 'json'

class SomeController < ApplicationController
    def index
        # Always prefer using pure data formats such as JSON
        rawJSON = params[:config]
        parsedJSON = JSON.parse(rawJSON)
        print(parsedJSON["name"])

        # If you need to use YAML, make sure to use YAML parser's safe functions
        cfg = YAML.load(params[:config], safe: true)
    end
end
public class Session {
    public String username;
    public boolean loggedIn;

    public void loadSession(byte[] sessionData) throws Exception {
        String sessionStr = new String(sessionData)

        // Always prefer using pure data formats such as JSON
        JSONObject sessionJson = (JSONObject) new JSONParser().parse(sessionStr);
        username = (String) sessionJson.get("username")
        loggedIn = (Boolean) sessionJson.get("loggedIn")

        // If you need to use YAML, make sure to use YAML parser's safe functions
        Yaml yaml = new Yaml(new SafeConstructor());
        Map map = (Map) yaml.load(sessionStr);
        username = (String) map.get("username")
        loggedIn = (Boolean) map.get("username")
    }
}

More Information

You should prefer using pure data formats like JSON or XML. Keep in mind that even when using parsers for those data formats, using some more advanced features to that may customize the parsing could be vulnerable also. Many YAML parser may also be dangerous by default in many languages such as Python and Ruby and thus you may need to explicitly disable certain dangerous featurs of the native parser to make it safe.

If you must deserialize object coming from external sources (maybe in the case of supporting legacy protocols), you may opt to deserialize data that has been signed and where you can authenticate the origin to be trusted.